xserver: Branch 'master' - 21 commits

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Sun May 30 10:55:21 UTC 2021


 .gitlab-ci.yml                                               |    2 
 .gitlab-ci/cross-prereqs-build.sh                            |    2 
 .gitlab-ci/debian-install.sh                                 |    9 
 Xi/exevents.c                                                |  248 +++++++
 Xi/extinit.c                                                 |   80 ++
 Xi/xipassivegrab.c                                           |   29 
 Xi/xiquerydevice.c                                           |   57 +
 Xi/xiselectev.c                                              |   53 +
 dix/Makefile.am                                              |    1 
 dix/devices.c                                                |   27 
 dix/dispatch.c                                               |    1 
 dix/eventconvert.c                                           |  164 ++++
 dix/events.c                                                 |  110 +++
 dix/gestures.c                                               |  362 +++++++++++
 dix/getevents.c                                              |  136 ++++
 dix/grabs.c                                                  |    7 
 dix/inpututils.c                                             |   33 +
 dix/meson.build                                              |    1 
 hw/xfree86/common/xf86Module.h                               |    2 
 hw/xfree86/common/xf86Xinput.c                               |   52 +
 hw/xfree86/common/xf86Xinput.h                               |   14 
 hw/xfree86/drivers/inputtest/xf86-input-inputtest-protocol.h |   30 
 hw/xfree86/drivers/inputtest/xf86-input-inputtest.c          |   57 +
 include/dix.h                                                |   11 
 include/dixgrabs.h                                           |    1 
 include/eventconvert.h                                       |    3 
 include/events.h                                             |    1 
 include/eventstr.h                                           |   39 +
 include/exevents.h                                           |   11 
 include/input.h                                              |   57 +
 include/inputstr.h                                           |   30 
 include/inpututils.h                                         |    2 
 include/protocol-versions.h                                  |    2 
 meson.build                                                  |    2 
 mi/mieq.c                                                    |    8 
 test/xi2/protocol-xipassivegrabdevice.c                      |    2 
 test/xi2/protocol-xiselectevents.c                           |   33 -
 37 files changed, 1654 insertions(+), 25 deletions(-)

New commits:
commit eb6f8daca5dc15af321d0bcc54cd6cb8b6779257
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:48 2021 +0300

    Xi: Work around broken libxcb that doesn't ignore unknown device classes
    
    libxcb 14.1 and older are not forwards-compatible with new device
    classes as it does not properly ignore unknown device classes. Since
    breaking libxcb would break quite a lot of applications, we instead
    report Gesture device class only if the client advertised support for XI
    2.4.
    
    Clients may still not work in cases when a client advertises XI 2.4
    support and then a completely separate module within the client uses
    broken libxcb to call XIQueryDevice.
    
    Signed-off-by: Povilas Kanapickas <povilas at radix.lt>

diff --git a/Xi/xiquerydevice.c b/Xi/xiquerydevice.c
index 5c6799a7c..e4731a119 100644
--- a/Xi/xiquerydevice.c
+++ b/Xi/xiquerydevice.c
@@ -43,6 +43,9 @@
 #include "xace.h"
 #include "inpututils.h"
 
+#include "exglobals.h"
+#include "privates.h"
+
 #include "xiquerydevice.h"
 
 static Bool ShouldSkipDevice(ClientPtr client, int deviceid, DeviceIntPtr d);
@@ -467,6 +470,22 @@ SwapTouchInfo(DeviceIntPtr dev, xXITouchInfo * touch)
     swaps(&touch->sourceid);
 }
 
+static Bool ShouldListGestureInfo(ClientPtr client)
+{
+    /* libxcb 14.1 and older are not forwards-compatible with new device classes as it does not
+     * properly ignore unknown device classes. Since breaking libxcb would break quite a lot of
+     * applications, we instead report Gesture device class only if the client advertised support
+     * for XI 2.4. Clients may still not work in cases when a client advertises XI 2.4 support
+     * and then a completely separate module within the client uses broken libxcb to call
+     * XIQueryDevice.
+     */
+    XIClientPtr pXIClient = dixLookupPrivate(&client->devPrivates, XIClientPrivateKey);
+    if (pXIClient->major_version) {
+        return version_compare(pXIClient->major_version, pXIClient->minor_version, 2, 4) >= 0;
+    }
+    return FALSE;
+}
+
 /**
  * List gesture information
  *
@@ -594,7 +613,7 @@ ListDeviceClasses(ClientPtr client, DeviceIntPtr dev,
         total_len += len;
     }
 
-    if (dev->gesture) {
+    if (dev->gesture && ShouldListGestureInfo(client)) {
         (*nclasses)++;
         len = ListGestureInfo(dev, (xXIGestureInfo *) any);
         any += len;
commit 7e692633fb9ab8e1ed2a88c3abb4fe04144c0a80
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:47 2021 +0300

    Xi: Implement gesture support for XIQueryDevice

diff --git a/Xi/xiquerydevice.c b/Xi/xiquerydevice.c
index fbb51fe81..5c6799a7c 100644
--- a/Xi/xiquerydevice.c
+++ b/Xi/xiquerydevice.c
@@ -234,6 +234,9 @@ SizeDeviceClasses(DeviceIntPtr dev)
     if (dev->touch)
         len += sizeof(xXITouchInfo);
 
+    if (dev->gesture)
+        len += sizeof(xXIGestureInfo);
+
     return len;
 }
 
@@ -464,6 +467,30 @@ SwapTouchInfo(DeviceIntPtr dev, xXITouchInfo * touch)
     swaps(&touch->sourceid);
 }
 
+/**
+ * List gesture information
+ *
+ * @return The number of bytes written into info.
+ */
+static int
+ListGestureInfo(DeviceIntPtr dev, xXIGestureInfo * gesture)
+{
+    gesture->type = XIGestureClass;
+    gesture->length = sizeof(xXIGestureInfo) >> 2;
+    gesture->sourceid = dev->gesture->sourceid;
+    gesture->num_touches = dev->gesture->max_touches;
+
+    return gesture->length << 2;
+}
+
+static void
+SwapGestureInfo(DeviceIntPtr dev, xXIGestureInfo * gesture)
+{
+    swaps(&gesture->type);
+    swaps(&gesture->length);
+    swaps(&gesture->sourceid);
+}
+
 int
 GetDeviceUse(DeviceIntPtr dev, uint16_t * attachment)
 {
@@ -567,6 +594,13 @@ ListDeviceClasses(ClientPtr client, DeviceIntPtr dev,
         total_len += len;
     }
 
+    if (dev->gesture) {
+        (*nclasses)++;
+        len = ListGestureInfo(dev, (xXIGestureInfo *) any);
+        any += len;
+        total_len += len;
+    }
+
     return total_len;
 }
 
@@ -598,7 +632,9 @@ SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo * info)
         case XITouchClass:
             SwapTouchInfo(dev, (xXITouchInfo *) any);
             break;
-
+        case XIGestureClass:
+            SwapGestureInfo(dev, (xXIGestureInfo *) any);
+            break;
         }
 
         any += len * 4;
commit 0886254f96f40e59193ccbb0e3acbd5ae92dbaa3
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:46 2021 +0300

    xfree86: Bump input minor ABI due to addition touchpad gestures
    
    Signed-off-by: Povilas Kanapickas <povilas at radix.lt>

diff --git a/hw/xfree86/common/xf86Module.h b/hw/xfree86/common/xf86Module.h
index 7b478d5c2..1eb09bca3 100644
--- a/hw/xfree86/common/xf86Module.h
+++ b/hw/xfree86/common/xf86Module.h
@@ -75,7 +75,7 @@
  */
 #define ABI_ANSIC_VERSION	SET_ABI_VERSION(0, 4)
 #define ABI_VIDEODRV_VERSION	SET_ABI_VERSION(25, 2)
-#define ABI_XINPUT_VERSION	SET_ABI_VERSION(24, 3)
+#define ABI_XINPUT_VERSION	SET_ABI_VERSION(24, 4)
 #define ABI_EXTENSION_VERSION	SET_ABI_VERSION(10, 0)
 
 #define MODINFOSTRING1	0xef23fdc5
commit 1cdc3b5d146818993c5a9436d17e1d0888054c38
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:45 2021 +0300

    xfree86: Implement gesture support for test input driver

diff --git a/hw/xfree86/drivers/inputtest/xf86-input-inputtest-protocol.h b/hw/xfree86/drivers/inputtest/xf86-input-inputtest-protocol.h
index 267532348..46837f3ba 100644
--- a/hw/xfree86/drivers/inputtest/xf86-input-inputtest-protocol.h
+++ b/hw/xfree86/drivers/inputtest/xf86-input-inputtest-protocol.h
@@ -31,7 +31,7 @@ extern "C" {
 #include <stdint.h>
 
 #define XF86IT_PROTOCOL_VERSION_MAJOR 1
-#define XF86IT_PROTOCOL_VERSION_MINOR 0
+#define XF86IT_PROTOCOL_VERSION_MINOR 1
 
 enum xf86ITResponseType {
     XF86IT_RESPONSE_SERVER_VERSION,
@@ -70,6 +70,8 @@ enum xf86ITEventType {
     XF86IT_EVENT_BUTTON,
     XF86IT_EVENT_KEY,
     XF86IT_EVENT_TOUCH,
+    XF86IT_EVENT_GESTURE_PINCH,
+    XF86IT_EVENT_GESTURE_SWIPE,
 };
 
 typedef struct {
@@ -127,6 +129,30 @@ typedef struct {
     xf86ITValuatorData valuators;
 } xf86ITEventTouch;
 
+typedef struct {
+    xf86ITEventHeader header;
+    uint16_t gesture_type;
+    uint16_t num_touches;
+    uint32_t flags;
+    double delta_x;
+    double delta_y;
+    double delta_unaccel_x;
+    double delta_unaccel_y;
+    double scale;
+    double delta_angle;
+} xf86ITEventGesturePinch;
+
+typedef struct {
+    xf86ITEventHeader header;
+    uint16_t gesture_type;
+    uint16_t num_touches;
+    uint32_t flags;
+    double delta_x;
+    double delta_y;
+    double delta_unaccel_x;
+    double delta_unaccel_y;
+} xf86ITEventGestureSwipe;
+
 typedef union {
     xf86ITEventHeader header;
     xf86ITEventClientVersion version;
@@ -135,6 +161,8 @@ typedef union {
     xf86ITEventButton button;
     xf86ITEventKey key;
     xf86ITEventTouch touch;
+    xf86ITEventGesturePinch pinch;
+    xf86ITEventGestureSwipe swipe;
 } xf86ITEventAny;
 
 #ifdef __cplusplus
diff --git a/hw/xfree86/drivers/inputtest/xf86-input-inputtest.c b/hw/xfree86/drivers/inputtest/xf86-input-inputtest.c
index 147d46e0d..70f8fe964 100644
--- a/hw/xfree86/drivers/inputtest/xf86-input-inputtest.c
+++ b/hw/xfree86/drivers/inputtest/xf86-input-inputtest.c
@@ -58,6 +58,7 @@
 enum xf86ITDeviceType {
     DEVICE_KEYBOARD = 1,
     DEVICE_POINTER,
+    DEVICE_POINTER_GESTURE,
     DEVICE_POINTER_ABS,
     DEVICE_POINTER_ABS_PROXIMITY,
     DEVICE_TOUCH,
@@ -474,6 +475,14 @@ init_touch(InputInfoPtr pInfo)
     InitTouchClassDeviceStruct(dev, ntouches, XIDirectTouch, 2);
 }
 
+static void
+init_gesture(InputInfoPtr pInfo)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    int ntouches = TOUCH_MAX_SLOTS;
+    InitGestureClassDeviceStruct(dev, ntouches);
+}
+
 static void
 device_init(DeviceIntPtr dev)
 {
@@ -489,6 +498,10 @@ device_init(DeviceIntPtr dev)
         case DEVICE_POINTER:
             init_pointer(pInfo);
             break;
+        case DEVICE_POINTER_GESTURE:
+            init_pointer(pInfo);
+            init_gesture(pInfo);
+            break;
         case DEVICE_POINTER_ABS:
             init_pointer_absolute(pInfo);
             break;
@@ -678,6 +691,37 @@ handle_touch(InputInfoPtr pInfo, xf86ITEventTouch *event)
     xf86PostTouchEvent(dev, event->touchid, event->touch_type, 0, mask);
 }
 
+static void
+handle_gesture_swipe(InputInfoPtr pInfo, xf86ITEventGestureSwipe *event)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    xf86ITDevicePtr driver_data = pInfo->private;
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Handling gesture swipe event\n");
+
+    driver_data->last_event_num++;
+
+    xf86PostGestureSwipeEvent(dev, event->gesture_type, event->num_touches, event->flags,
+                              event->delta_x, event->delta_y,
+                              event->delta_unaccel_x, event->delta_unaccel_y);
+}
+
+static void
+handle_gesture_pinch(InputInfoPtr pInfo, xf86ITEventGesturePinch *event)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    xf86ITDevicePtr driver_data = pInfo->private;
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Handling gesture pinch event\n");
+
+    driver_data->last_event_num++;
+
+    xf86PostGesturePinchEvent(dev, event->gesture_type, event->num_touches, event->flags,
+                              event->delta_x, event->delta_y,
+                              event->delta_unaccel_x, event->delta_unaccel_y,
+                              event->scale, event->delta_angle);
+}
+
 static void
 client_new_handle_event(InputInfoPtr pInfo, xf86ITEventAny *event)
 {
@@ -715,6 +759,12 @@ client_ready_handle_event(InputInfoPtr pInfo, xf86ITEventAny *event)
         case XF86IT_EVENT_TOUCH:
             handle_touch(pInfo, &event->touch);
             break;
+        case XF86IT_EVENT_GESTURE_PINCH:
+            handle_gesture_pinch(pInfo, &(event->pinch));
+            break;
+        case XF86IT_EVENT_GESTURE_SWIPE:
+            handle_gesture_swipe(pInfo, &(event->swipe));
+            break;
         case XF86IT_EVENT_CLIENT_VERSION:
             xf86IDrvMsg(pInfo, X_ERROR, "Only single ClientVersion event is allowed\n");
             teardown_client_connection(pInfo);
@@ -759,6 +809,8 @@ is_supported_event(enum xf86ITEventType type)
         case XF86IT_EVENT_BUTTON:
         case XF86IT_EVENT_KEY:
         case XF86IT_EVENT_TOUCH:
+        case XF86IT_EVENT_GESTURE_PINCH:
+        case XF86IT_EVENT_GESTURE_SWIPE:
             return true;
     }
     return false;
@@ -775,6 +827,8 @@ get_event_size(enum xf86ITEventType type)
         case XF86IT_EVENT_BUTTON: return sizeof(xf86ITEventButton);
         case XF86IT_EVENT_KEY: return sizeof(xf86ITEventKey);
         case XF86IT_EVENT_TOUCH: return sizeof(xf86ITEventTouch);
+        case XF86IT_EVENT_GESTURE_PINCH: return sizeof(xf86ITEventGesturePinch);
+        case XF86IT_EVENT_GESTURE_SWIPE: return sizeof(xf86ITEventGestureSwipe);
     }
     abort();
 }
@@ -864,6 +918,7 @@ get_type_name(InputInfoPtr pInfo, xf86ITDevicePtr driver_data)
     switch (driver_data->device_type) {
         case DEVICE_TOUCH: return XI_TOUCHSCREEN;
         case DEVICE_POINTER: return XI_MOUSE;
+        case DEVICE_POINTER_GESTURE: return XI_TOUCHPAD;
         case DEVICE_POINTER_ABS: return XI_MOUSE;
         case DEVICE_POINTER_ABS_PROXIMITY: return XI_TABLET;
         case DEVICE_KEYBOARD: return XI_KEYBOARD;
@@ -986,6 +1041,8 @@ pre_init(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
         driver_data->device_type = DEVICE_KEYBOARD;
     } else if (strcmp(device_type_option, "Pointer") == 0) {
         driver_data->device_type = DEVICE_POINTER;
+    } else if (strcmp(device_type_option, "PointerGesture") == 0) {
+        driver_data->device_type = DEVICE_POINTER_GESTURE;
     } else if (strcmp(device_type_option, "PointerAbsolute") == 0) {
         driver_data->device_type = DEVICE_POINTER_ABS;
     } else if (strcmp(device_type_option, "PointerAbsoluteProximity") == 0) {
commit dccc0275f4f3740cc625fb60ab1d014d6ec10709
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:44 2021 +0300

    Bump XI protocol version to 2.4

diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index 8e149b27f..25abee5b7 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -144,7 +144,7 @@
 
 /* X Input */
 #define SERVER_XI_MAJOR_VERSION			2
-#define SERVER_XI_MINOR_VERSION			3
+#define SERVER_XI_MINOR_VERSION			4
 
 /* XKB */
 #define SERVER_XKB_MAJOR_VERSION		1
commit 75feb1366809bfb6d50734c10ab11258783a2e9d
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:43 2021 +0300

    Xi: Include gestures among events that may freeze device

diff --git a/Xi/exevents.c b/Xi/exevents.c
index f878dd212..9d4886212 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -1766,6 +1766,7 @@ ProcessGestureEvent(InternalEvent *ev, DeviceIntPtr dev)
     GestureInfoPtr gi;
     DeviceIntPtr kbd;
     Bool deactivateGestureGrab = FALSE;
+    Bool delivered = FALSE;
 
     if (!dev->gesture)
         return;
@@ -1795,7 +1796,11 @@ ProcessGestureEvent(InternalEvent *ev, DeviceIntPtr dev)
             GrabIsGestureGrab(dev->deviceGrab.grab))
         deactivateGestureGrab = TRUE;
 
-    DeliverGestureEventToOwner(dev, gi, ev);
+    delivered = DeliverGestureEventToOwner(dev, gi, ev);
+
+    if (delivered && !deactivateGestureGrab &&
+            (IsGestureBeginEvent(ev) || IsGestureEndEvent(ev)))
+        FreezeThisEventIfNeededForSyncGrab(dev, ev);
 
     if (IsGestureEndEvent(ev))
         GestureEndGesture(gi);
commit 5163fc8bc28ce8bc2703cddcd9f2775ebc311766
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:42 2021 +0300

    Implement gesture processing logic

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,
@@ -1509,6 +1518,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.
@@ -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);
commit d3c52df16105de5ac37e196a49b173e426caf417
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:41 2021 +0300

    hw/xfree86: Implement public APIs to submit gesture events

diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c
index a2b2de392..8cf3f62f0 100644
--- a/hw/xfree86/common/xf86Xinput.c
+++ b/hw/xfree86/common/xf86Xinput.c
@@ -1590,6 +1590,58 @@ xf86PostTouchEvent(DeviceIntPtr dev, uint32_t touchid, uint16_t type,
     QueueTouchEvents(dev, type, touchid, flags, mask);
 }
 
+/**
+ * Post a gesture pinch event.  The driver is responsible for maintaining the
+ * correct event sequence (GesturePinchBegin, GesturePinchUpdate,
+ * GesturePinchEnd).
+ *
+ * @param dev The device to post the event for
+ * @param type One of XI_GesturePinchBegin, XI_GesturePinchUpdate,
+ *        XI_GesturePinchEnd
+ * @param num_touches The number of touches in the gesture
+ * @param flags Flags for this event
+ * @param delta_x,delta_y accelerated relative motion delta
+ * @param delta_unaccel_x,delta_unaccel_y unaccelerated relative motion delta
+ * @param scale absolute scale of a pinch gesture
+ * @param delta_angle the ange delta in degrees between the last and the current pinch event.
+ */
+void
+xf86PostGesturePinchEvent(DeviceIntPtr dev, uint16_t type,
+                          uint16_t num_touches, uint32_t flags,
+                          double delta_x, double delta_y,
+                          double delta_unaccel_x,
+                          double delta_unaccel_y,
+                          double scale, double delta_angle)
+{
+    QueueGesturePinchEvents(dev, type, num_touches, flags, delta_x, delta_y,
+                            delta_unaccel_x, delta_unaccel_y,
+                            scale, delta_angle);
+}
+
+/**
+ * Post a gesture swipe event.  The driver is responsible for maintaining the
+ * correct event sequence (GestureSwipeBegin, GestureSwipeUpdate,
+ * GestureSwipeEnd).
+ *
+ * @param dev The device to post the event for
+ * @param type One of XI_GestureSwipeBegin, XI_GestureSwipeUpdate,
+ *        XI_GestureSwipeEnd
+ * @param num_touches The number of touches in the gesture
+ * @param flags Flags for this event
+ * @param delta_x,delta_y accelerated relative motion delta
+ * @param delta_unaccel_x,delta_unaccel_y unaccelerated relative motion delta
+ */
+void
+xf86PostGestureSwipeEvent(DeviceIntPtr dev, uint16_t type,
+                          uint16_t num_touches, uint32_t flags,
+                          double delta_x, double delta_y,
+                          double delta_unaccel_x,
+                          double delta_unaccel_y)
+{
+    QueueGestureSwipeEvents(dev, type, num_touches, flags, delta_x, delta_y,
+                            delta_unaccel_x, delta_unaccel_y);
+}
+
 void
 xf86InputEnableVTProbe(void)
 {
diff --git a/hw/xfree86/common/xf86Xinput.h b/hw/xfree86/common/xf86Xinput.h
index 749a80d4b..e73aff269 100644
--- a/hw/xfree86/common/xf86Xinput.h
+++ b/hw/xfree86/common/xf86Xinput.h
@@ -158,6 +158,20 @@ extern _X_EXPORT void xf86PostKeyboardEvent(DeviceIntPtr device,
 extern _X_EXPORT void xf86PostTouchEvent(DeviceIntPtr dev, uint32_t touchid,
                                          uint16_t type, uint32_t flags,
                                          const ValuatorMask *mask);
+extern _X_EXPORT void xf86PostGesturePinchEvent(DeviceIntPtr dev, uint16_t type,
+                                                uint16_t num_touches,
+                                                uint32_t flags,
+                                                double delta_x, double delta_y,
+                                                double delta_unaccel_x,
+                                                double delta_unaccel_y,
+                                                double scale, double delta_angle);
+extern _X_EXPORT void xf86PostGestureSwipeEvent(DeviceIntPtr dev, uint16_t type,
+                                                uint16_t num_touches,
+                                                uint32_t flags,
+                                                double delta_x, double delta_y,
+                                                double delta_unaccel_x,
+                                                double delta_unaccel_y);
+
 extern _X_EXPORT InputInfoPtr xf86FirstLocalDevice(void);
 extern _X_EXPORT int xf86ScaleAxis(int Cx, int to_max, int to_min, int from_max,
                                    int from_min);
commit 100a2ad6da4ba90f8d489c7a2ed3f3f0ac879a6f
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:40 2021 +0300

    dix: Implement gesture event submission code path

diff --git a/dix/getevents.c b/dix/getevents.c
index 5dceec39b..368e0fb2a 100644
--- a/dix/getevents.c
+++ b/dix/getevents.c
@@ -2099,3 +2099,139 @@ PostSyntheticMotion(DeviceIntPtr pDev,
     /* FIXME: MD/SD considerations? */
     (*pDev->public.processInputProc) ((InternalEvent *) &ev, pDev);
 }
+
+void
+InitGestureEvent(InternalEvent *ievent, DeviceIntPtr dev, CARD32 ms,
+                 int type, uint16_t num_touches, uint32_t flags,
+                 double delta_x, double delta_y,
+                 double delta_unaccel_x, double delta_unaccel_y,
+                 double scale, double delta_angle)
+{
+    ScreenPtr scr = dev->spriteInfo->sprite->hotPhys.pScreen;
+    GestureEvent *event = &ievent->gesture_event;
+    double screenx = 0.0, screeny = 0.0;        /* desktop coordinate system */
+
+    init_gesture_event(event, dev, ms);
+
+    screenx = dev->spriteInfo->sprite->hotPhys.x;
+    screeny = dev->spriteInfo->sprite->hotPhys.y;
+
+    event->type = type;
+    event->root = scr->root->drawable.id;
+    event->root_x = screenx - scr->x;
+    event->root_y = screeny - scr->y;
+    event->num_touches = num_touches;
+    event->flags = flags;
+
+    event->delta_x = delta_x;
+    event->delta_y = delta_y;
+    event->delta_unaccel_x = delta_unaccel_x;
+    event->delta_unaccel_y = delta_unaccel_y;
+    event->scale = scale;
+    event->delta_angle = delta_angle;
+}
+
+/**
+ * Get events for a pinch or swipe gesture.
+ *
+ * events is not NULL-terminated; the return value is the number of events.
+ * The DDX is responsible for allocating the event structure in the first
+ * place via GetMaximumEventsNum(), and for freeing it.
+ *
+ * @param[out] events The list of events generated
+ * @param dev The device to generate the events for
+ * @param type XI_Gesture{Pinch,Swipe}{Begin,Update,End}
+ * @prama num_touches The number of touches in the gesture
+ * @param flags Event flags
+ * @param delta_x,delta_y accelerated relative motion delta
+ * @param delta_unaccel_x,delta_unaccel_y unaccelerated relative motion delta
+ * @param scale (valid only to pinch events) absolute scale of a pinch gesture
+ * @param delta_angle (valid only to pinch events) the ange delta in degrees between the last and
+ *        the current pinch event.
+ */
+int
+GetGestureEvents(InternalEvent *events, DeviceIntPtr dev,
+                 uint16_t type, uint16_t num_touches, uint32_t flags,
+                 double delta_x, double delta_y,
+                 double delta_unaccel_x, double delta_unaccel_y,
+                 double scale, double delta_angle)
+
+{
+    GestureClassPtr g = dev->gesture;
+    CARD32 ms = GetTimeInMillis();
+    enum EventType evtype;
+    int num_events = 0;
+    uint32_t evflags = 0;
+
+    if (!dev->enabled || !g)
+        return 0;
+
+    if (!IsMaster(dev))
+        events = UpdateFromMaster(events, dev, DEVCHANGE_POINTER_EVENT,
+                                  &num_events);
+
+    switch (type) {
+    case XI_GesturePinchBegin:
+        evtype = ET_GesturePinchBegin;
+        break;
+    case XI_GesturePinchUpdate:
+        evtype = ET_GesturePinchUpdate;
+        break;
+    case XI_GesturePinchEnd:
+        evtype = ET_GesturePinchEnd;
+        if (flags & XIGesturePinchEventCancelled)
+            evflags |= GESTURE_CANCELLED;
+        break;
+    case XI_GestureSwipeBegin:
+        evtype = ET_GestureSwipeBegin;
+        break;
+    case XI_GestureSwipeUpdate:
+        evtype = ET_GestureSwipeUpdate;
+        break;
+    case XI_GestureSwipeEnd:
+        evtype = ET_GestureSwipeEnd;
+        if (flags & XIGestureSwipeEventCancelled)
+            evflags |= GESTURE_CANCELLED;
+        break;
+    default:
+        return 0;
+    }
+
+    InitGestureEvent(events, dev, ms, evtype, num_touches, evflags,
+                     delta_x, delta_y, delta_unaccel_x, delta_unaccel_y,
+                     scale, delta_angle);
+    num_events++;
+
+    return num_events;
+}
+
+void
+QueueGesturePinchEvents(DeviceIntPtr dev, uint16_t type,
+                        uint16_t num_touches, uint32_t flags,
+                        double delta_x, double delta_y,
+                        double delta_unaccel_x,
+                        double delta_unaccel_y,
+                        double scale, double delta_angle)
+{
+    int nevents;
+    nevents = GetGestureEvents(InputEventList, dev, type, num_touches, flags,
+                               delta_x, delta_y,
+                               delta_unaccel_x, delta_unaccel_y,
+                               scale, delta_angle);
+    queueEventList(dev, InputEventList, nevents);
+}
+
+void
+QueueGestureSwipeEvents(DeviceIntPtr dev, uint16_t type,
+                        uint16_t num_touches, uint32_t flags,
+                        double delta_x, double delta_y,
+                        double delta_unaccel_x,
+                        double delta_unaccel_y)
+{
+    int nevents;
+    nevents = GetGestureEvents(InputEventList, dev, type, num_touches, flags,
+                               delta_x, delta_y,
+                               delta_unaccel_x, delta_unaccel_y,
+                               0.0, 0.0);
+    queueEventList(dev, InputEventList, nevents);
+}
diff --git a/dix/inpututils.c b/dix/inpututils.c
index b60ffff0d..6906f997b 100644
--- a/dix/inpututils.c
+++ b/dix/inpututils.c
@@ -746,6 +746,21 @@ init_device_event(DeviceEvent *event, DeviceIntPtr dev, Time ms,
     event->source_type = source_type;
 }
 
+/**
+ * Initializes the given gesture event to zero (or default values),
+ * for the given device.
+ */
+void
+init_gesture_event(GestureEvent *event, DeviceIntPtr dev, Time ms)
+{
+    memset(event, 0, sizeof(GestureEvent));
+    event->header = ET_Internal;
+    event->length = sizeof(GestureEvent);
+    event->time = ms;
+    event->deviceid = dev->id;
+    event->sourceid = dev->id;
+}
+
 int
 event_get_corestate(DeviceIntPtr mouse, DeviceIntPtr kbd)
 {
diff --git a/include/input.h b/include/input.h
index d607e6b97..c19e74969 100644
--- a/include/input.h
+++ b/include/input.h
@@ -496,6 +496,33 @@ void QueueTouchEvents(DeviceIntPtr device,
                       uint32_t ddx_touchid,
                       int flags, const ValuatorMask *mask);
 
+void InitGestureEvent(InternalEvent *ievent, DeviceIntPtr dev, CARD32 ms,
+                      int type, uint16_t num_touches, uint32_t flags,
+                      double delta_x, double delta_y,
+                      double delta_unaccel_x, double delta_unaccel_y,
+                      double scale, double delta_angle);
+
+int GetGestureEvents(InternalEvent *events, DeviceIntPtr dev,
+                     uint16_t type, uint16_t num_touches, uint32_t flags,
+                     double delta_x, double delta_y,
+                     double delta_unaccel_x,
+                     double delta_unaccel_y,
+                     double scale, double delta_angle);
+
+
+void QueueGesturePinchEvents(DeviceIntPtr dev, uint16_t type,
+                             uint16_t num_touches, uint32_t flags,
+                             double delta_x, double delta_y,
+                             double delta_unaccel_x,
+                             double delta_unaccel_y,
+                             double scale, double delta_angle);
+
+void QueueGestureSwipeEvents(DeviceIntPtr dev, uint16_t type,
+                             uint16_t num_touches, uint32_t flags,
+                             double delta_x, double delta_y,
+                             double delta_unaccel_x,
+                             double delta_unaccel_y);
+
 extern int GetTouchOwnershipEvents(InternalEvent *events,
                                    DeviceIntPtr pDev,
                                    TouchPointInfoPtr ti,
diff --git a/include/inpututils.h b/include/inpututils.h
index 2dfe122c9..489e1d9b7 100644
--- a/include/inpututils.h
+++ b/include/inpututils.h
@@ -46,6 +46,7 @@ struct _ValuatorMask {
 extern void verify_internal_event(const InternalEvent *ev);
 extern void init_device_event(DeviceEvent *event, DeviceIntPtr dev, Time ms,
                               enum DeviceEventSource event_source);
+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);
commit 7656a9c8dddbc57adfd4fd05f8e26b1845ac8738
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:39 2021 +0300

    dix: Implement internal gesture state handling

diff --git a/Xi/exevents.c b/Xi/exevents.c
index 83ea387ab..16acd5814 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -691,6 +691,25 @@ DeepCopyPointerClasses(DeviceIntPtr from, DeviceIntPtr to)
     /* Don't remove touch class if from->touch is non-existent. The to device
      * may have an active touch grab, so we need to keep the touch class record
      * around. */
+
+    if (from->gesture) {
+        if (!to->gesture) {
+            classes = to->unused_classes;
+            to->gesture = classes->gesture;
+            if (!to->gesture) {
+                if (!InitGestureClassDeviceStruct(to, from->gesture->max_touches))
+                    FatalError("[Xi] no memory for class shift.\n");
+            }
+            else
+                classes->gesture = NULL;
+        }
+
+        to->gesture->sourceid = from->gesture->sourceid;
+        /* to->gesture->gesture is separate on the master,  don't copy */
+    }
+    /* Don't remove gesture class if from->gesture is non-existent. The to device
+     * may have an active gesture grab, so we need to keep the gesture class record
+     * around. */
 }
 
 /**
diff --git a/dix/Makefile.am b/dix/Makefile.am
index 652a08519..e1498cd56 100644
--- a/dix/Makefile.am
+++ b/dix/Makefile.am
@@ -23,6 +23,7 @@ libdix_la_SOURCES = 	\
 	extension.c	\
 	gc.c		\
 	getevents.c	\
+	gestures.c	\
 	globals.c	\
 	glyphcurs.c	\
 	grabs.c		\
diff --git a/dix/devices.c b/dix/devices.c
index 85d899c20..29f3051aa 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -1670,6 +1670,32 @@ InitTouchClassDeviceStruct(DeviceIntPtr device, unsigned int max_touches,
     return FALSE;
 }
 
+/**
+ * Sets up gesture capabilities on @device.
+ *
+ * @max_touches The maximum number of simultaneous touches, or 0 for unlimited.
+ */
+Bool
+InitGestureClassDeviceStruct(DeviceIntPtr device, unsigned int max_touches)
+{
+    GestureClassPtr g;
+
+    BUG_RETURN_VAL(device == NULL, FALSE);
+    BUG_RETURN_VAL(device->gesture != NULL, FALSE);
+
+    g = calloc(1, sizeof(*g));
+    if (!g)
+        return FALSE;
+
+    g->sourceid = device->id;
+    g->max_touches = max_touches;
+    GestureInitGestureInfo(&g->gesture);
+
+    device->gesture = g;
+
+    return TRUE;
+}
+
 /*
  * Check if the given buffer contains elements between low (inclusive) and
  * high (inclusive) only.
diff --git a/dix/gestures.c b/dix/gestures.c
new file mode 100644
index 000000000..7e4057deb
--- /dev/null
+++ b/dix/gestures.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2011 Collabra Ltd.
+ * Copyright © 2011 Red Hat, Inc.
+ * Copyright © 2020 Povilas Kanapickas  <povilas at radix.lt>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include "inputstr.h"
+#include "scrnintstr.h"
+#include "dixgrabs.h"
+
+#include "eventstr.h"
+#include "exevents.h"
+#include "exglobals.h"
+#include "inpututils.h"
+#include "eventconvert.h"
+#include "windowstr.h"
+#include "mi.h"
+
+Bool
+GestureInitGestureInfo(GestureInfoPtr gi)
+{
+    memset(gi, 0, sizeof(*gi));
+
+    gi->sprite.spriteTrace = calloc(32, sizeof(*gi->sprite.spriteTrace));
+    if (!gi->sprite.spriteTrace) {
+        return FALSE;
+    }
+    gi->sprite.spriteTraceSize = 32;
+    gi->sprite.spriteTrace[0] = screenInfo.screens[0]->root;
+    gi->sprite.hot.pScreen = screenInfo.screens[0];
+    gi->sprite.hotPhys.pScreen = screenInfo.screens[0];
+
+    return TRUE;
+}
diff --git a/dix/meson.build b/dix/meson.build
index ddc5cc392..fbbcf8646 100644
--- a/dix/meson.build
+++ b/dix/meson.build
@@ -12,6 +12,7 @@ srcs_dix = [
     'eventconvert.c',
     'extension.c',
     'gc.c',
+    'gestures.c',
     'getevents.c',
     'globals.c',
     'glyphcurs.c',
diff --git a/include/input.h b/include/input.h
index 8252914a6..d607e6b97 100644
--- a/include/input.h
+++ b/include/input.h
@@ -153,7 +153,9 @@ typedef struct _ValuatorClassRec *ValuatorClassPtr;
 typedef struct _ClassesRec *ClassesPtr;
 typedef struct _SpriteRec *SpritePtr;
 typedef struct _TouchClassRec *TouchClassPtr;
+typedef struct _GestureClassRec *GestureClassPtr;
 typedef struct _TouchPointInfo *TouchPointInfoPtr;
+typedef struct _GestureInfo *GestureInfoPtr;
 typedef struct _DDXTouchPointInfo *DDXTouchPointInfoPtr;
 typedef union _GrabMask GrabMask;
 
@@ -340,6 +342,9 @@ extern _X_EXPORT Bool InitTouchClassDeviceStruct(DeviceIntPtr /*device */ ,
                                                  unsigned int /*mode */ ,
                                                  unsigned int /*numAxes */ );
 
+extern _X_EXPORT Bool InitGestureClassDeviceStruct(DeviceIntPtr device,
+                                                   unsigned int max_touches);
+
 typedef void (*BellProcPtr) (int percent,
                              DeviceIntPtr device,
                              void *ctrl,
@@ -576,6 +581,12 @@ enum TouchListenerType {
     TOUCH_LISTENER_POINTER_REGULAR,
 };
 
+enum GestureListenerType {
+    GESTURE_LISTENER_GRAB,
+    GESTURE_LISTENER_NONGESTURE_GRAB,
+    GESTURE_LISTENER_REGULAR
+};
+
 extern void TouchInitDDXTouchPoint(DeviceIntPtr dev,
                                    DDXTouchPointInfoPtr ddxtouch);
 extern DDXTouchPointInfoPtr TouchBeginDDXTouch(DeviceIntPtr dev,
@@ -621,6 +632,7 @@ extern void TouchEndPhysicallyActiveTouches(DeviceIntPtr dev);
 extern void TouchEmitTouchEnd(DeviceIntPtr dev, TouchPointInfoPtr ti, int flags, XID resource);
 extern void TouchAcceptAndEnd(DeviceIntPtr dev, int touchid);
 
+extern Bool GestureInitGestureInfo(GestureInfoPtr gesture);
 /* misc event helpers */
 extern Mask GetEventMask(DeviceIntPtr dev, xEvent *ev, InputClientsPtr clients);
 extern Mask GetEventFilter(DeviceIntPtr dev, xEvent *event);
diff --git a/include/inputstr.h b/include/inputstr.h
index 1cf871530..ec11b39bb 100644
--- a/include/inputstr.h
+++ b/include/inputstr.h
@@ -352,6 +352,32 @@ typedef struct _TouchClassRec {
     Mask motionMask;
 } TouchClassRec;
 
+typedef struct _GestureListener {
+    XID listener;           /* grabs/event selection IDs receiving
+                             * events for this gesture */
+    int resource_type;      /* listener's resource type */
+    enum GestureListenerType type;
+    WindowPtr window;
+    GrabPtr grab;
+} GestureListener;
+
+typedef struct _GestureInfo {
+    int sourceid;               /* Source device's ID for this gesture */
+    Bool active;                /* whether or not the gesture is active */
+    uint8_t type;               /* Gesture type: either ET_GesturePinchBegin or
+                                   ET_GestureSwipeBegin. Valid if active == TRUE */
+    int num_touches;            /* The number of touches in the gesture */
+    SpriteRec sprite;           /* window trace for delivery */
+    GestureListener listener;   /* the listener that will receive events */
+    Bool has_listener;          /* true if listener has been setup already */
+} GestureInfoRec;
+
+typedef struct _GestureClassRec {
+    int sourceid;
+    GestureInfoRec gesture;
+    unsigned short max_touches; /* maximum number of touches, may be 0 */
+} GestureClassRec;
+
 typedef struct _ButtonClassRec {
     int sourceid;
     CARD8 numButtons;
@@ -435,6 +461,7 @@ typedef struct _ClassesRec {
     KeyClassPtr key;
     ValuatorClassPtr valuator;
     TouchClassPtr touch;
+    GestureClassPtr gesture;
     ButtonClassPtr button;
     FocusClassPtr focus;
     ProximityClassPtr proximity;
@@ -550,6 +577,7 @@ typedef struct _DeviceIntRec {
     KeyClassPtr key;
     ValuatorClassPtr valuator;
     TouchClassPtr touch;
+    GestureClassPtr gesture;
     ButtonClassPtr button;
     FocusClassPtr focus;
     ProximityClassPtr proximity;
commit 0bf4123fd3bc945d150884e6ea38bd2b81c0994b
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:38 2021 +0300

    dix: Implement gesture event fixups before delivery

diff --git a/dix/events.c b/dix/events.c
index 20a40ecb2..e2455a4bb 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -2545,6 +2545,44 @@ FixUpXI2DeviceEventFromWindow(SpritePtr pSprite, int evtype,
             (pSprite->hot.pScreen == pWin->drawable.pScreen);
 }
 
+static void
+FixUpXI2PinchEventFromWindow(SpritePtr pSprite, xXIGesturePinchEvent *event,
+                             WindowPtr pWin, Window child)
+{
+    event->root = RootWindow(pSprite)->drawable.id;
+    event->event = pWin->drawable.id;
+
+    if (pSprite->hot.pScreen == pWin->drawable.pScreen) {
+        event->event_x = event->root_x - double_to_fp1616(pWin->drawable.x);
+        event->event_y = event->root_y - double_to_fp1616(pWin->drawable.y);
+        event->child = child;
+    }
+    else {
+        event->event_x = 0;
+        event->event_y = 0;
+        event->child = None;
+    }
+}
+
+static void
+FixUpXI2SwipeEventFromWindow(SpritePtr pSprite, xXIGestureSwipeEvent *event,
+                             WindowPtr pWin, Window child)
+{
+    event->root = RootWindow(pSprite)->drawable.id;
+    event->event = pWin->drawable.id;
+
+    if (pSprite->hot.pScreen == pWin->drawable.pScreen) {
+        event->event_x = event->root_x - double_to_fp1616(pWin->drawable.x);
+        event->event_y = event->root_y - double_to_fp1616(pWin->drawable.y);
+        event->child = child;
+    }
+    else {
+        event->event_x = 0;
+        event->event_y = 0;
+        event->child = None;
+    }
+}
+
 /**
  * Adjust event fields to comply with the window properties.
  *
@@ -2578,6 +2616,18 @@ FixUpEventFromWindow(SpritePtr pSprite,
         case XI_BarrierHit:
         case XI_BarrierLeave:
             return;
+        case XI_GesturePinchBegin:
+        case XI_GesturePinchUpdate:
+        case XI_GesturePinchEnd:
+            FixUpXI2PinchEventFromWindow(pSprite,
+                                         (xXIGesturePinchEvent*) xE, pWin, child);
+            break;
+        case XI_GestureSwipeBegin:
+        case XI_GestureSwipeUpdate:
+        case XI_GestureSwipeEnd:
+            FixUpXI2SwipeEventFromWindow(pSprite,
+                                         (xXIGestureSwipeEvent*) xE, pWin, child);
+            break;
         default:
             FixUpXI2DeviceEventFromWindow(pSprite, evtype,
                                           (xXIDeviceEvent*) xE, pWin, child);
commit 227f601de31aabf6aaa897477e42e6b7f8f7c963
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:37 2021 +0300

    xi: Implement conversions from internal to Xi2 gesture event structs

diff --git a/Xi/extinit.c b/Xi/extinit.c
index 8d9120866..4e5a13fc5 100644
--- a/Xi/extinit.c
+++ b/Xi/extinit.c
@@ -850,6 +850,74 @@ SBarrierEvent(xXIBarrierEvent * from,
     swapl(&to->eventid);
 }
 
+static void
+SGesturePinchEvent(xXIGesturePinchEvent* from,
+                   xXIGesturePinchEvent* to)
+{
+    *to = *from;
+
+    swaps(&to->sequenceNumber);
+    swapl(&to->length);
+    swaps(&to->evtype);
+    swaps(&to->deviceid);
+    swapl(&to->time);
+    swapl(&to->detail);
+    swapl(&to->root);
+    swapl(&to->event);
+    swapl(&to->child);
+    swapl(&to->root_x);
+    swapl(&to->root_y);
+    swapl(&to->event_x);
+    swapl(&to->event_y);
+
+    swapl(&to->delta_x);
+    swapl(&to->delta_y);
+    swapl(&to->delta_unaccel_x);
+    swapl(&to->delta_unaccel_y);
+    swapl(&to->scale);
+    swapl(&to->delta_angle);
+    swaps(&to->sourceid);
+
+    swapl(&to->mods.base_mods);
+    swapl(&to->mods.latched_mods);
+    swapl(&to->mods.locked_mods);
+    swapl(&to->mods.effective_mods);
+    swapl(&to->flags);
+}
+
+static void
+SGestureSwipeEvent(xXIGestureSwipeEvent* from,
+                   xXIGestureSwipeEvent* to)
+{
+    *to = *from;
+
+    swaps(&to->sequenceNumber);
+    swapl(&to->length);
+    swaps(&to->evtype);
+    swaps(&to->deviceid);
+    swapl(&to->time);
+    swapl(&to->detail);
+    swapl(&to->root);
+    swapl(&to->event);
+    swapl(&to->child);
+    swapl(&to->root_x);
+    swapl(&to->root_y);
+    swapl(&to->event_x);
+    swapl(&to->event_y);
+
+    swapl(&to->delta_x);
+    swapl(&to->delta_y);
+    swapl(&to->delta_unaccel_x);
+    swapl(&to->delta_unaccel_y);
+    swaps(&to->sourceid);
+
+    swapl(&to->mods.base_mods);
+    swapl(&to->mods.latched_mods);
+    swapl(&to->mods.locked_mods);
+    swapl(&to->mods.effective_mods);
+    swapl(&to->flags);
+}
+
 /** Event swapping function for XI2 events. */
 void _X_COLD
 XI2EventSwap(xGenericEvent *from, xGenericEvent *to)
@@ -901,6 +969,18 @@ XI2EventSwap(xGenericEvent *from, xGenericEvent *to)
         SBarrierEvent((xXIBarrierEvent *) from,
                       (xXIBarrierEvent *) to);
         break;
+    case XI_GesturePinchBegin:
+    case XI_GesturePinchUpdate:
+    case XI_GesturePinchEnd:
+        SGesturePinchEvent((xXIGesturePinchEvent*) from,
+                           (xXIGesturePinchEvent*) to);
+        break;
+    case XI_GestureSwipeBegin:
+    case XI_GestureSwipeUpdate:
+    case XI_GestureSwipeEnd:
+        SGestureSwipeEvent((xXIGestureSwipeEvent*) from,
+                           (xXIGestureSwipeEvent*) to);
+        break;
     default:
         ErrorF("[Xi] Unknown event type to swap. This is a bug.\n");
         break;
diff --git a/dix/eventconvert.c b/dix/eventconvert.c
index 9bba2c7c5..53b8c79e3 100644
--- a/dix/eventconvert.c
+++ b/dix/eventconvert.c
@@ -59,6 +59,8 @@ static int eventToDeviceEvent(DeviceEvent *ev, xEvent **xi);
 static int eventToRawEvent(RawDeviceEvent *ev, xEvent **xi);
 static int eventToBarrierEvent(BarrierEvent *ev, xEvent **xi);
 static int eventToTouchOwnershipEvent(TouchOwnershipEvent *ev, xEvent **xi);
+static int eventToGestureSwipeEvent(GestureEvent *ev, xEvent **xi);
+static int eventToGesturePinchEvent(GestureEvent *ev, xEvent **xi);
 
 /* Do not use, read comments below */
 BOOL EventIsKeyRepeat(xEvent *event);
@@ -163,6 +165,12 @@ EventToCore(InternalEvent *event, xEvent **core_out, int *count_out)
     case ET_TouchOwnership:
     case ET_BarrierHit:
     case ET_BarrierLeave:
+    case ET_GesturePinchBegin:
+    case ET_GesturePinchUpdate:
+    case ET_GesturePinchEnd:
+    case ET_GestureSwipeBegin:
+    case ET_GestureSwipeUpdate:
+    case ET_GestureSwipeEnd:
         ret = BadMatch;
         break;
     default:
@@ -221,6 +229,12 @@ EventToXI(InternalEvent *ev, xEvent **xi, int *count)
     case ET_TouchOwnership:
     case ET_BarrierHit:
     case ET_BarrierLeave:
+    case ET_GesturePinchBegin:
+    case ET_GesturePinchUpdate:
+    case ET_GesturePinchEnd:
+    case ET_GestureSwipeBegin:
+    case ET_GestureSwipeUpdate:
+    case ET_GestureSwipeEnd:
         *count = 0;
         *xi = NULL;
         return BadMatch;
@@ -285,6 +299,14 @@ EventToXI2(InternalEvent *ev, xEvent **xi)
     case ET_BarrierHit:
     case ET_BarrierLeave:
         return eventToBarrierEvent(&ev->barrier_event, xi);
+    case ET_GesturePinchBegin:
+    case ET_GesturePinchUpdate:
+    case ET_GesturePinchEnd:
+        return eventToGesturePinchEvent(&ev->gesture_event, xi);
+    case ET_GestureSwipeBegin:
+    case ET_GestureSwipeUpdate:
+    case ET_GestureSwipeEnd:
+        return eventToGestureSwipeEvent(&ev->gesture_event, xi);
     default:
         break;
     }
@@ -816,6 +838,88 @@ eventToBarrierEvent(BarrierEvent *ev, xEvent **xi)
     return Success;
 }
 
+int
+eventToGesturePinchEvent(GestureEvent *ev, xEvent **xi)
+{
+    int len = sizeof(xXIGesturePinchEvent);
+    xXIGesturePinchEvent *xpe;
+
+    *xi = calloc(1, len);
+    xpe = (xXIGesturePinchEvent *) * xi;
+    xpe->type = GenericEvent;
+    xpe->extension = IReqCode;
+    xpe->evtype = GetXI2Type(ev->type);
+    xpe->time = ev->time;
+    xpe->length = bytes_to_int32(len - sizeof(xEvent));
+    xpe->detail = ev->num_touches;
+
+    xpe->root = ev->root;
+    xpe->deviceid = ev->deviceid;
+    xpe->sourceid = ev->sourceid;
+    xpe->root_x = double_to_fp1616(ev->root_x);
+    xpe->root_y = double_to_fp1616(ev->root_y);
+    xpe->flags |= (ev->flags & GESTURE_CANCELLED) ? XIGesturePinchEventCancelled : 0;
+
+    xpe->delta_x = double_to_fp1616(ev->delta_x);
+    xpe->delta_y = double_to_fp1616(ev->delta_y);
+    xpe->delta_unaccel_x = double_to_fp1616(ev->delta_unaccel_x);
+    xpe->delta_unaccel_y = double_to_fp1616(ev->delta_unaccel_y);
+    xpe->scale = double_to_fp1616(ev->scale);
+    xpe->delta_angle = double_to_fp1616(ev->delta_angle);
+
+    xpe->mods.base_mods = ev->mods.base;
+    xpe->mods.latched_mods = ev->mods.latched;
+    xpe->mods.locked_mods = ev->mods.locked;
+    xpe->mods.effective_mods = ev->mods.effective;
+
+    xpe->group.base_group = ev->group.base;
+    xpe->group.latched_group = ev->group.latched;
+    xpe->group.locked_group = ev->group.locked;
+    xpe->group.effective_group = ev->group.effective;
+
+    return Success;
+}
+
+int
+eventToGestureSwipeEvent(GestureEvent *ev, xEvent **xi)
+{
+    int len = sizeof(xXIGestureSwipeEvent);
+    xXIGestureSwipeEvent *xde;
+
+    *xi = calloc(1, len);
+    xde = (xXIGestureSwipeEvent *) * xi;
+    xde->type = GenericEvent;
+    xde->extension = IReqCode;
+    xde->evtype = GetXI2Type(ev->type);
+    xde->time = ev->time;
+    xde->length = bytes_to_int32(len - sizeof(xEvent));
+    xde->detail = ev->num_touches;
+
+    xde->root = ev->root;
+    xde->deviceid = ev->deviceid;
+    xde->sourceid = ev->sourceid;
+    xde->root_x = double_to_fp1616(ev->root_x);
+    xde->root_y = double_to_fp1616(ev->root_y);
+    xde->flags |= (ev->flags & GESTURE_CANCELLED) ? XIGestureSwipeEventCancelled : 0;
+
+    xde->delta_x = double_to_fp1616(ev->delta_x);
+    xde->delta_y = double_to_fp1616(ev->delta_y);
+    xde->delta_unaccel_x = double_to_fp1616(ev->delta_unaccel_x);
+    xde->delta_unaccel_y = double_to_fp1616(ev->delta_unaccel_y);
+
+    xde->mods.base_mods = ev->mods.base;
+    xde->mods.latched_mods = ev->mods.latched;
+    xde->mods.locked_mods = ev->mods.locked;
+    xde->mods.effective_mods = ev->mods.effective;
+
+    xde->group.base_group = ev->group.base;
+    xde->group.latched_group = ev->group.latched;
+    xde->group.locked_group = ev->group.locked;
+    xde->group.effective_group = ev->group.effective;
+
+    return Success;
+}
+
 /**
  * Return the corresponding core type for the given event or 0 if no core
  * equivalent exists.
diff --git a/include/input.h b/include/input.h
index 98fdf0aed..8252914a6 100644
--- a/include/input.h
+++ b/include/input.h
@@ -105,6 +105,9 @@ SOFTWARE.
 #define TOUCH_POINTER_EMULATED  (1 << 5)        /* touch event may be pointer emulated */
 #define TOUCH_END               (1 << 6)        /* really end this touch now */
 
+/* GetGestureEvent flags */
+#define GESTURE_CANCELLED       (1 << 0)
+
 /*int constants for pointer acceleration schemes*/
 #define PtrAccelNoOp            0
 #define PtrAccelPredictable     1
commit f3462178caf09a8777b8671eb03949b75c8ee07a
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:36 2021 +0300

    test/xi2: Verify that XI_GestureSwipeEnd is ignored when outside mask

diff --git a/test/xi2/protocol-xiselectevents.c b/test/xi2/protocol-xiselectevents.c
index 6753bdfa6..7301c46b9 100644
--- a/test/xi2/protocol-xiselectevents.c
+++ b/test/xi2/protocol-xiselectevents.c
@@ -233,6 +233,17 @@ request_XISelectEvents_masks(xXISelectEventsReq * req)
         }
 
         /* Test 5:
+         * Mask len is 1 and XI_GestureSwipeEnd is set outside the mask.
+         * That bit should be ignored -> Success
+         */
+        bits = (unsigned char *) &mask[1];
+        mask->mask_len = 1;
+        memset(bits, 0, 5);
+        SetBit(bits, XI_ButtonPress); // does not matter which one
+        SetBit(bits, XI_GestureSwipeEnd);
+        request_XISelectEvent(req, Success);
+
+        /* Test 6:
          * HierarchyChanged bit is BadValue for devices other than
          * XIAllDevices
          */
@@ -247,7 +258,7 @@ request_XISelectEvents_masks(xXISelectEventsReq * req)
             request_XISelectEvent(req, BadValue);
         }
 
-        /* Test 6:
+        /* Test 7:
          * All bits set minus hierarchy changed bit -> Success
          */
         bits = (unsigned char *) &mask[1];
commit 407a2234b32d9cb3fed2cc7e8069bb41b98e143f
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:35 2021 +0300

    test/xi2: Update tests for gesture event types

diff --git a/test/xi2/protocol-xipassivegrabdevice.c b/test/xi2/protocol-xipassivegrabdevice.c
index 142bcecb7..dc4602d5d 100644
--- a/test/xi2/protocol-xipassivegrabdevice.c
+++ b/test/xi2/protocol-xipassivegrabdevice.c
@@ -194,7 +194,7 @@ test_XIPassiveGrabDevice(void)
     request->deviceid = XIAllMasterDevices;
 
     printf("Testing invalid grab types\n");
-    for (i = XIGrabtypeTouchBegin + 1; i < 0xFF; i++) {
+    for (i = XIGrabtypeGestureSwipeBegin + 1; i < 0xFF; i++) {
         request->grab_type = i;
         request_XIPassiveGrabDevice(&client_request, request, BadValue,
                                     request->grab_type);
diff --git a/test/xi2/protocol-xiselectevents.c b/test/xi2/protocol-xiselectevents.c
index 6c94ea73c..6753bdfa6 100644
--- a/test/xi2/protocol-xiselectevents.c
+++ b/test/xi2/protocol-xiselectevents.c
@@ -124,6 +124,16 @@ _set_bit(unsigned char *bits, int bit)
         SetBit(bits, XI_TouchUpdate);
         SetBit(bits, XI_TouchEnd);
     }
+    if (bit >= XI_GesturePinchBegin && bit <= XI_GesturePinchEnd) {
+        SetBit(bits, XI_GesturePinchBegin);
+        SetBit(bits, XI_GesturePinchUpdate);
+        SetBit(bits, XI_GesturePinchEnd);
+    }
+    if (bit >= XI_GestureSwipeBegin && bit <= XI_GestureSwipeEnd) {
+        SetBit(bits, XI_GestureSwipeBegin);
+        SetBit(bits, XI_GestureSwipeUpdate);
+        SetBit(bits, XI_GestureSwipeEnd);
+    }
 }
 
 static void
@@ -135,6 +145,16 @@ _clear_bit(unsigned char *bits, int bit)
         ClearBit(bits, XI_TouchUpdate);
         ClearBit(bits, XI_TouchEnd);
     }
+    if (bit >= XI_GesturePinchBegin && bit <= XI_GesturePinchEnd) {
+        ClearBit(bits, XI_GesturePinchBegin);
+        ClearBit(bits, XI_GesturePinchUpdate);
+        ClearBit(bits, XI_GesturePinchEnd);
+    }
+    if (bit >= XI_GestureSwipeBegin && bit <= XI_GestureSwipeEnd) {
+        ClearBit(bits, XI_GestureSwipeBegin);
+        ClearBit(bits, XI_GestureSwipeUpdate);
+        ClearBit(bits, XI_GestureSwipeEnd);
+    }
 }
 
 static void
commit f83f7dbb1c1be406b82da9bca12be3c43d6bfeee
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:34 2021 +0300

    xi: Bump max supported XI2 event type

diff --git a/include/inputstr.h b/include/inputstr.h
index 33d440b2c..1cf871530 100644
--- a/include/inputstr.h
+++ b/include/inputstr.h
@@ -75,7 +75,7 @@ extern _X_EXPORT int CountBits(const uint8_t * mask, int len);
  * events to the protocol, the server will not support these events until
  * this number here is bumped.
  */
-#define XI2LASTEVENT    XI_BarrierLeave
+#define XI2LASTEVENT    XI_GestureSwipeEnd
 #define XI2MASKSIZE     ((XI2LASTEVENT >> 3) + 1)       /* no of bytes for masks */
 
 /**
commit 81909546397eb180ed8983bdb80f5673dbae71ac
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:33 2021 +0300

    xi: Implement selection logic for gesture event types

diff --git a/Xi/xiselectev.c b/Xi/xiselectev.c
index 0266a8d30..edcb8a0d3 100644
--- a/Xi/xiselectev.c
+++ b/Xi/xiselectev.c
@@ -226,8 +226,41 @@ ProcXISelectEvents(ClientPtr client)
                 return BadValue;
             }
 
-            /* Only one client per window may select for touch events on the
-             * same devices, including master devices.
+            /* All three pinch gesture events must be selected at once */
+            if ((BitIsOn(bits, XI_GesturePinchBegin) ||
+                 BitIsOn(bits, XI_GesturePinchUpdate) ||
+                 BitIsOn(bits, XI_GesturePinchEnd)) &&
+                (!BitIsOn(bits, XI_GesturePinchBegin) ||
+                 !BitIsOn(bits, XI_GesturePinchUpdate) ||
+                 !BitIsOn(bits, XI_GesturePinchEnd))) {
+                client->errorValue = XI_GesturePinchBegin;
+                return BadValue;
+            }
+
+            /* All three swipe gesture events must be selected at once. Note
+               that the XI_GestureSwipeEnd is at index 32 which is on the next
+               4-byte mask element */
+            if (evmask->mask_len == 1 &&
+                (BitIsOn(bits, XI_GestureSwipeBegin) ||
+                 BitIsOn(bits, XI_GestureSwipeUpdate)))
+            {
+                client->errorValue = XI_GestureSwipeBegin;
+                return BadValue;
+            }
+
+            if (evmask->mask_len >= 2 &&
+                (BitIsOn(bits, XI_GestureSwipeBegin) ||
+                 BitIsOn(bits, XI_GestureSwipeUpdate) ||
+                 BitIsOn(bits, XI_GestureSwipeEnd)) &&
+                (!BitIsOn(bits, XI_GestureSwipeBegin) ||
+                 !BitIsOn(bits, XI_GestureSwipeUpdate) ||
+                 !BitIsOn(bits, XI_GestureSwipeEnd))) {
+                client->errorValue = XI_GestureSwipeBegin;
+                return BadValue;
+            }
+
+            /* Only one client per window may select for touch or gesture events
+             * on the same devices, including master devices.
              * XXX: This breaks if a device goes from floating to attached. */
             if (BitIsOn(bits, XI_TouchBegin)) {
                 rc = check_for_touch_selection_conflicts(client,
@@ -237,6 +270,22 @@ ProcXISelectEvents(ClientPtr client)
                 if (rc != Success)
                     return rc;
             }
+            if (BitIsOn(bits, XI_GesturePinchBegin)) {
+                rc = check_for_touch_selection_conflicts(client,
+                                                         win,
+                                                         evmask->deviceid,
+                                                         XI_GesturePinchBegin);
+                if (rc != Success)
+                    return rc;
+            }
+            if (BitIsOn(bits, XI_GestureSwipeBegin)) {
+                rc = check_for_touch_selection_conflicts(client,
+                                                         win,
+                                                         evmask->deviceid,
+                                                         XI_GestureSwipeBegin);
+                if (rc != Success)
+                    return rc;
+            }
         }
 
         if (XICheckInvalidMaskBits(client, (unsigned char *) &evmask[1],
commit 080bac39c844bd16b7e1493797c6e7644badf0ab
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:32 2021 +0300

    xi: Implement grab support for new gesture event types

diff --git a/Xi/exevents.c b/Xi/exevents.c
index c8751ce18..83ea387ab 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -2427,8 +2427,8 @@ GrabWindow(ClientPtr client, DeviceIntPtr dev, int type,
 
 /* Touch grab */
 int
-GrabTouch(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr mod_dev,
-          GrabParameters *param, GrabMask *mask)
+GrabTouchOrGesture(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr mod_dev,
+                   int type, GrabParameters *param, GrabMask *mask)
 {
     WindowPtr pWin;
     GrabPtr grab;
@@ -2446,7 +2446,7 @@ GrabTouch(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr mod_dev,
         return rc;
 
     grab = CreateGrab(client->index, dev, mod_dev, pWin, XI2,
-                      mask, param, XI_TouchBegin, 0, NullWindow, NullCursor);
+                      mask, param, type, 0, NullWindow, NullCursor);
     if (!grab)
         return BadAlloc;
 
diff --git a/Xi/xipassivegrab.c b/Xi/xipassivegrab.c
index d30f51f3c..2769fb7c9 100644
--- a/Xi/xipassivegrab.c
+++ b/Xi/xipassivegrab.c
@@ -114,14 +114,18 @@ ProcXIPassiveGrabDevice(ClientPtr client)
         stuff->grab_type != XIGrabtypeKeycode &&
         stuff->grab_type != XIGrabtypeEnter &&
         stuff->grab_type != XIGrabtypeFocusIn &&
-        stuff->grab_type != XIGrabtypeTouchBegin) {
+        stuff->grab_type != XIGrabtypeTouchBegin &&
+        stuff->grab_type != XIGrabtypeGesturePinchBegin &&
+        stuff->grab_type != XIGrabtypeGestureSwipeBegin) {
         client->errorValue = stuff->grab_type;
         return BadValue;
     }
 
     if ((stuff->grab_type == XIGrabtypeEnter ||
          stuff->grab_type == XIGrabtypeFocusIn ||
-         stuff->grab_type == XIGrabtypeTouchBegin) && stuff->detail != 0) {
+         stuff->grab_type == XIGrabtypeTouchBegin ||
+         stuff->grab_type == XIGrabtypeGesturePinchBegin ||
+         stuff->grab_type == XIGrabtypeGestureSwipeBegin) && stuff->detail != 0) {
         client->errorValue = stuff->detail;
         return BadValue;
     }
@@ -217,7 +221,16 @@ ProcXIPassiveGrabDevice(ClientPtr client)
             status = GrabWindow(client, dev, stuff->grab_type, &param, &mask);
             break;
         case XIGrabtypeTouchBegin:
-            status = GrabTouch(client, dev, mod_dev, &param, &mask);
+            status = GrabTouchOrGesture(client, dev, mod_dev, XI_TouchBegin,
+                                        &param, &mask);
+            break;
+        case XIGrabtypeGesturePinchBegin:
+            status = GrabTouchOrGesture(client, dev, mod_dev,
+                                        XI_GesturePinchBegin, &param, &mask);
+            break;
+        case XIGrabtypeGestureSwipeBegin:
+            status = GrabTouchOrGesture(client, dev, mod_dev,
+                                        XI_GestureSwipeBegin, &param, &mask);
             break;
         }
 
@@ -307,7 +320,9 @@ ProcXIPassiveUngrabDevice(ClientPtr client)
         stuff->grab_type != XIGrabtypeKeycode &&
         stuff->grab_type != XIGrabtypeEnter &&
         stuff->grab_type != XIGrabtypeFocusIn &&
-        stuff->grab_type != XIGrabtypeTouchBegin) {
+        stuff->grab_type != XIGrabtypeTouchBegin &&
+        stuff->grab_type != XIGrabtypeGesturePinchBegin &&
+        stuff->grab_type != XIGrabtypeGestureSwipeBegin) {
         client->errorValue = stuff->grab_type;
         return BadValue;
     }
@@ -348,6 +363,12 @@ ProcXIPassiveUngrabDevice(ClientPtr client)
     case XIGrabtypeTouchBegin:
         tempGrab->type = XI_TouchBegin;
         break;
+    case XIGrabtypeGesturePinchBegin:
+        tempGrab->type = XI_GesturePinchBegin;
+        break;
+    case XIGrabtypeGestureSwipeBegin:
+        tempGrab->type = XI_GestureSwipeBegin;
+        break;
     }
     tempGrab->grabtype = XI2;
     tempGrab->modifierDevice = mod_dev;
diff --git a/dix/grabs.c b/dix/grabs.c
index 2a307a2b9..53ba1d6b9 100644
--- a/dix/grabs.c
+++ b/dix/grabs.c
@@ -716,3 +716,10 @@ GrabIsKeyboardGrab(GrabPtr grab)
     return (grab->type == KeyPress ||
             grab->type == DeviceKeyPress || grab->type == XI_KeyPress);
 }
+
+Bool
+GrabIsGestureGrab(GrabPtr grab)
+{
+    return (grab->type == XI_GesturePinchBegin ||
+            grab->type == XI_GestureSwipeBegin);
+}
diff --git a/include/dixgrabs.h b/include/dixgrabs.h
index 3bd80132b..dc1068fef 100644
--- a/include/dixgrabs.h
+++ b/include/dixgrabs.h
@@ -61,4 +61,5 @@ extern _X_EXPORT Bool DeletePassiveGrabFromList(GrabPtr /* pMinuendGrab */ );
 
 extern Bool GrabIsPointerGrab(GrabPtr grab);
 extern Bool GrabIsKeyboardGrab(GrabPtr grab);
+extern Bool GrabIsGestureGrab(GrabPtr grab);
 #endif                          /* DIXGRABS_H */
diff --git a/include/exevents.h b/include/exevents.h
index 321fc422d..c900c7b2c 100644
--- a/include/exevents.h
+++ b/include/exevents.h
@@ -192,11 +192,12 @@ extern int
             GrabMask * /* eventMask */ );
 
 extern int
- GrabTouch(ClientPtr /* client */ ,
-           DeviceIntPtr /* dev */ ,
-           DeviceIntPtr /* mod_dev */ ,
-           GrabParameters * /* param */ ,
-           GrabMask * /* eventMask */ );
+ GrabTouchOrGesture(ClientPtr /* client */ ,
+                    DeviceIntPtr /* dev */ ,
+                    DeviceIntPtr /* mod_dev */ ,
+                    int /* type */ ,
+                    GrabParameters * /* param */ ,
+                    GrabMask * /* eventMask */ );
 
 extern int
  SelectForWindow(DeviceIntPtr /* dev */ ,
commit b544a1fdb8fa7e7efc7dde66089e901d957061d9
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:31 2021 +0300

    xi: Implement internal gesture event struct

diff --git a/include/events.h b/include/events.h
index 0cbd8c84a..544dfad02 100644
--- a/include/events.h
+++ b/include/events.h
@@ -28,6 +28,7 @@ typedef struct _DeviceEvent DeviceEvent;
 typedef struct _DeviceChangedEvent DeviceChangedEvent;
 typedef struct _TouchOwnershipEvent TouchOwnershipEvent;
 typedef struct _BarrierEvent BarrierEvent;
+typedef struct _GestureEvent GestureEvent;
 
 #ifdef XFreeXDGA
 typedef struct _DGAEvent DGAEvent;
diff --git a/include/eventstr.h b/include/eventstr.h
index 16df595d6..93308f9b2 100644
--- a/include/eventstr.h
+++ b/include/eventstr.h
@@ -266,6 +266,38 @@ struct _BarrierEvent {
     uint32_t flags;
 };
 
+struct _GestureEvent {
+    unsigned char header; /**< Always ET_Internal */
+    enum EventType type;  /**< One of ET_Gesture{Pinch,Swipe}{Begin,Update,End} */
+    int length;           /**< Length in bytes */
+    Time time;            /**< Time in ms */
+    int deviceid;         /**< Device to post this event for */
+    int sourceid;         /**< The physical source device */
+    uint32_t num_touches; /**< The number of touches in this gesture */
+    double root_x;        /**< Pos relative to root window */
+    double root_y;        /**< Pos relative to root window */
+    double delta_x;
+    double delta_y;
+    double delta_unaccel_x;
+    double delta_unaccel_y;
+    double scale;         /**< Only on ET_GesturePinch{Begin,Update} */
+    double delta_angle;   /**< Only on ET_GesturePinch{Begin,Update} */
+    struct {
+        uint32_t base;    /**< XKB base modifiers */
+        uint32_t latched; /**< XKB latched modifiers */
+        uint32_t locked;  /**< XKB locked modifiers */
+        uint32_t effective;/**< XKB effective modifiers */
+    } mods;
+    struct {
+        uint8_t base;    /**< XKB base group */
+        uint8_t latched; /**< XKB latched group */
+        uint8_t locked;  /**< XKB locked group */
+        uint8_t effective;/**< XKB effective group */
+    } group;
+    Window root;      /**< Root window of the event */
+    uint32_t flags;   /**< Flags to be copied into the generated event */
+};
+
 #ifdef XQUARTZ
 #define XQUARTZ_EVENT_MAXARGS 5
 struct _XQuartzEvent {
@@ -300,6 +332,7 @@ union _InternalEvent {
 #ifdef XQUARTZ
     XQuartzEvent xquartz_event;
 #endif
+    GestureEvent gesture_event;
 };
 
 #endif
commit 22fa31ed561ed1e78bf3651f28e11882b1eac51f
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:30 2021 +0300

    dix: Add new internal event enums for gesture events

diff --git a/Xi/exevents.c b/Xi/exevents.c
index dd3e90ae5..c8751ce18 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -95,6 +95,7 @@ SOFTWARE.
 #include "exevents.h"
 #include "extnsionst.h"
 #include "exglobals.h"
+#include "eventstr.h"
 #include "dixevents.h"          /* DeliverFocusedEvent */
 #include "dixgrabs.h"           /* CreateGrab() */
 #include "scrnintstr.h"
@@ -168,6 +169,49 @@ IsTouchEvent(InternalEvent *event)
     return FALSE;
 }
 
+Bool
+IsGestureEvent(InternalEvent *event)
+{
+    switch (event->any.type) {
+    case ET_GesturePinchBegin:
+    case ET_GesturePinchUpdate:
+    case ET_GesturePinchEnd:
+    case ET_GestureSwipeBegin:
+    case ET_GestureSwipeUpdate:
+    case ET_GestureSwipeEnd:
+        return TRUE;
+    default:
+        break;
+    }
+    return FALSE;
+}
+
+Bool
+IsGestureBeginEvent(InternalEvent *event)
+{
+    switch (event->any.type) {
+    case ET_GesturePinchBegin:
+    case ET_GestureSwipeBegin:
+        return TRUE;
+    default:
+        break;
+    }
+    return FALSE;
+}
+
+Bool
+IsGestureEndEvent(InternalEvent *event)
+{
+    switch (event->any.type) {
+    case ET_GesturePinchEnd:
+    case ET_GestureSwipeEnd:
+        return TRUE;
+    default:
+        break;
+    }
+    return FALSE;
+}
+
 /**
  * @return the device matching the deviceid of the device set in the event, or
  * NULL if the event is not an XInput event.
diff --git a/dix/eventconvert.c b/dix/eventconvert.c
index 4a07b6b6f..9bba2c7c5 100644
--- a/dix/eventconvert.c
+++ b/dix/eventconvert.c
@@ -969,8 +969,68 @@ GetXI2Type(enum EventType type)
     case ET_BarrierLeave:
         xi2type = XI_BarrierLeave;
         break;
+    case ET_GesturePinchBegin:
+        xi2type = XI_GesturePinchBegin;
+        break;
+    case ET_GesturePinchUpdate:
+        xi2type = XI_GesturePinchUpdate;
+        break;
+    case ET_GesturePinchEnd:
+        xi2type = XI_GesturePinchEnd;
+        break;
+    case ET_GestureSwipeBegin:
+        xi2type = XI_GestureSwipeBegin;
+        break;
+    case ET_GestureSwipeUpdate:
+        xi2type = XI_GestureSwipeUpdate;
+        break;
+    case ET_GestureSwipeEnd:
+        xi2type = XI_GestureSwipeEnd;
+        break;
     default:
         break;
     }
     return xi2type;
 }
+
+/**
+ * Converts a gesture type to corresponding Gesture{Pinch,Swipe}Begin.
+ * Returns 0 if the input type is not a gesture.
+ */
+enum EventType
+GestureTypeToBegin(enum EventType type)
+{
+    switch (type) {
+    case ET_GesturePinchBegin:
+    case ET_GesturePinchUpdate:
+    case ET_GesturePinchEnd:
+        return ET_GesturePinchBegin;
+    case ET_GestureSwipeBegin:
+    case ET_GestureSwipeUpdate:
+    case ET_GestureSwipeEnd:
+        return ET_GestureSwipeBegin;
+    default:
+        return 0;
+    }
+}
+
+/**
+ * Converts a gesture type to corresponding Gesture{Pinch,Swipe}End.
+ * Returns 0 if the input type is not a gesture.
+ */
+enum EventType
+GestureTypeToEnd(enum EventType type)
+{
+    switch (type) {
+    case ET_GesturePinchBegin:
+    case ET_GesturePinchUpdate:
+    case ET_GesturePinchEnd:
+        return ET_GesturePinchEnd;
+    case ET_GestureSwipeBegin:
+    case ET_GestureSwipeUpdate:
+    case ET_GestureSwipeEnd:
+        return ET_GestureSwipeEnd;
+    default:
+        return 0;
+    }
+}
diff --git a/include/dix.h b/include/dix.h
index ece8b6f76..07d3607f2 100644
--- a/include/dix.h
+++ b/include/dix.h
@@ -610,6 +610,13 @@ extern Bool
 IsPointerEvent(InternalEvent *event);
 extern Bool
 IsTouchEvent(InternalEvent *event);
+Bool
+IsGestureEvent(InternalEvent *event);
+Bool
+IsGestureBeginEvent(InternalEvent *event);
+Bool
+IsGestureEndEvent(InternalEvent *event);
+
 extern _X_EXPORT Bool
 IsMaster(DeviceIntPtr dev);
 extern _X_EXPORT Bool
diff --git a/include/eventconvert.h b/include/eventconvert.h
index 01172f0ee..cf425f7a9 100644
--- a/include/eventconvert.h
+++ b/include/eventconvert.h
@@ -36,4 +36,7 @@ _X_INTERNAL int GetCoreType(enum EventType type);
 _X_INTERNAL int GetXIType(enum EventType type);
 _X_INTERNAL int GetXI2Type(enum EventType type);
 
+_X_INTERNAL enum EventType GestureTypeToBegin(enum EventType type);
+_X_INTERNAL enum EventType GestureTypeToEnd(enum EventType type);
+
 #endif                          /* _EVENTCONVERT_H_ */
diff --git a/include/eventstr.h b/include/eventstr.h
index bf3b95fe4..16df595d6 100644
--- a/include/eventstr.h
+++ b/include/eventstr.h
@@ -75,6 +75,12 @@ enum EventType {
     ET_XQuartz,
     ET_BarrierHit,
     ET_BarrierLeave,
+    ET_GesturePinchBegin,
+    ET_GesturePinchUpdate,
+    ET_GesturePinchEnd,
+    ET_GestureSwipeBegin,
+    ET_GestureSwipeUpdate,
+    ET_GestureSwipeEnd,
     ET_Internal = 0xFF          /* First byte */
 };
 
commit 8e504d8b36eb8f23bf8cfa46143d046cc6b8ea51
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:29 2021 +0300

    meson: Depend on inputproto 2.3.99.1
    
    Signed-off-by: Povilas Kanapickas <povilas at radix.lt>

diff --git a/meson.build b/meson.build
index 35770ba5e..fdb101f80 100644
--- a/meson.build
+++ b/meson.build
@@ -70,7 +70,7 @@ xproto_dep = dependency('xproto', version: '>= 7.0.31')
 randrproto_dep = dependency('randrproto', version: '>= 1.6.0')
 renderproto_dep = dependency('renderproto', version: '>= 0.11')
 xextproto_dep = dependency('xextproto', version: '>= 7.2.99.901')
-inputproto_dep = dependency('inputproto', version: '>= 2.3')
+inputproto_dep = dependency('inputproto', version: '>= 2.3.99.1')
 kbproto_dep = dependency('kbproto', version: '>= 1.0.3')
 fontsproto_dep = dependency('fontsproto', version: '>= 2.1.3')
 fixesproto_dep = dependency('fixesproto', version: '>= 5.0')
commit 2acde60147797393e0ee9428cff0f74cdc0a9227
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sun May 30 13:26:28 2021 +0300

    .gitlab-ci: Install xorgproto 2021.4.99.2 from git
    
    This is required for the support of upcoming XI 2.4 protocol.
    
    Signed-off-by: Povilas Kanapickas <povilas at radix.lt>

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 75b2aed31..6cdb346c7 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,7 +11,7 @@ variables:
     UPSTREAM_REPO: xorg/xserver
     FDO_DISTRIBUTION_VERSION: buster-slim
     FDO_DISTRIBUTION_EXEC: 'env FDO_CI_CONCURRENT=${FDO_CI_CONCURRENT} bash .gitlab-ci/debian-install.sh'
-    FDO_DISTRIBUTION_TAG: "2021-04-06"
+    FDO_DISTRIBUTION_TAG: "2021-05-17"
 
 include:
   - project: 'freedesktop/ci-templates'
diff --git a/.gitlab-ci/cross-prereqs-build.sh b/.gitlab-ci/cross-prereqs-build.sh
index 3decf707b..4596667a1 100755
--- a/.gitlab-ci/cross-prereqs-build.sh
+++ b/.gitlab-ci/cross-prereqs-build.sh
@@ -49,7 +49,7 @@ build 'https://gitlab.freedesktop.org/pixman/pixman.git' 'pixman-0.38.4'
 build 'https://gitlab.freedesktop.org/xorg/lib/pthread-stubs.git' '0.4'
 # we can't use the xorgproto pkgconfig files from /usr/share/pkgconfig, because
 # these would add -I/usr/include to CFLAGS, which breaks cross-compilation
-build 'https://gitlab.freedesktop.org/xorg/proto/xorgproto.git' 'xorgproto-2019.1' '--datadir=/lib'
+build 'https://gitlab.freedesktop.org/xorg/proto/xorgproto.git' 'xorgproto-2021.4.99.2' '--datadir=/lib'
 build 'https://gitlab.freedesktop.org/xorg/lib/libXau.git' 'libXau-1.0.9'
 build 'https://gitlab.freedesktop.org/xorg/proto/xcbproto.git' 'xcb-proto-1.14'
 build 'https://gitlab.freedesktop.org/xorg/lib/libxcb.git' 'libxcb-1.14'
diff --git a/.gitlab-ci/debian-install.sh b/.gitlab-ci/debian-install.sh
index 60d76c831..963533cda 100644
--- a/.gitlab-ci/debian-install.sh
+++ b/.gitlab-ci/debian-install.sh
@@ -98,7 +98,6 @@ apt-get install -y \
 	python3-mako \
 	python3-numpy \
 	python3-six \
-	x11proto-dev \
 	xfonts-utils \
 	xkb-data \
 	xtrans-dev \
@@ -108,6 +107,14 @@ apt-get install -y \
 
 cd /root
 
+# xserver requires xorgproto >= 2021.4.99.2 for XI 2.3.99.1
+git clone https://gitlab.freedesktop.org/xorg/proto/xorgproto.git --depth 1 --branch=xorgproto-2021.4.99.2
+pushd xorgproto
+./autogen.sh
+make -j${FDO_CI_CONCURRENT:-4} install
+popd
+rm -rf xorgproto
+
 # weston 9.0 requires libwayland >= 1.18
 git clone https://gitlab.freedesktop.org/wayland/wayland.git --depth 1 --branch=1.18.0
 cd wayland


More information about the xorg-commit mailing list