[PATCH 16/42] dix: if we run out of space for new touch events, resize the queue

Peter Hutterer peter.hutterer at who-t.net
Wed Dec 14 19:01:53 PST 2011


The SIGIO handler forces us to drop the current touch and schedule the
actual resize for later. Should not happen if the device sets up the
TouchClassRec with the correct number of touchpoints.

Co-authored-by: Daniel Stone <daniel at fooishbar.org>
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 dix/touch.c  |   67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 test/touch.c |   51 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 117 insertions(+), 1 deletions(-)

diff --git a/dix/touch.c b/dix/touch.c
index f9d1617..f59cfad 100644
--- a/dix/touch.c
+++ b/dix/touch.c
@@ -34,6 +34,9 @@
 #include "eventstr.h"
 #include "exevents.h"
 
+/* If a touch queue resize is needed, the device id's bit is set. */
+static unsigned char resize_waiting[(MAXDEVICES + 7)/8];
+
 /**
  * Some documentation about touch points:
  * The driver submits touch events with it's own (unique) touch point ID.
@@ -54,6 +57,60 @@
  */
 
 /**
+ * Check which devices need a bigger touch event queue and grow their
+ * last.touches by half it's current size.
+ *
+ * @param client Always the serverClient
+ * @param closure Always NULL
+ *
+ * @return Always True. If we fail to grow we probably will topple over soon
+ * anyway and re-executing this won't help.
+ */
+static Bool
+TouchResizeQueue(ClientPtr client, pointer closure)
+{
+    int i;
+
+    OsBlockSignals();
+
+    /* first two ids are reserved */
+    for (i = 2; i < MAXDEVICES; i++)
+    {
+        DeviceIntPtr dev;
+        DDXTouchPointInfoPtr tmp;
+        size_t size;
+
+        if (!BitIsOn(resize_waiting, i))
+            continue;
+
+        ClearBit(resize_waiting, i);
+
+        /* device may have disappeared by now */
+        dixLookupDevice(&dev, i, serverClient, DixWriteAccess);
+        if (!dev)
+            continue;
+
+        /* Need to grow the queue means dropping events. Grow sufficiently so we
+         * don't need to do it often */
+        size = dev->last.num_touches + dev->last.num_touches/2 + 1;
+
+        tmp = realloc(dev->last.touches, size *  sizeof(*dev->last.touches));
+        if (tmp)
+        {
+            int i;
+            dev->last.touches = tmp;
+            for (i = dev->last.num_touches; i < size; i++)
+                TouchInitDDXTouchPoint(dev, &dev->last.touches[i]);
+            dev->last.num_touches = size;
+        }
+
+    }
+    OsReleaseSignals();
+
+    return TRUE;
+}
+
+/**
  * Given the DDX-facing ID (which is _not_ DeviceEvent::detail.touch), find the
  * associated DDXTouchPointInfoRec.
  *
@@ -133,7 +190,15 @@ TouchBeginDDXTouch(DeviceIntPtr dev, uint32_t ddx_id)
         return ti;
     }
 
-    /* If we get here, then we've run out of touches, drop the event */
+    /* If we get here, then we've run out of touches and we need to drop the
+     * event (we're inside the SIGIO handler here) schedule a WorkProc to
+     * grow the queue for us for next time. */
+    ErrorF("%s: not enough space for touch events. Dropping this event.\n", dev->name);
+    if (!BitIsOn(resize_waiting, dev->id)) {
+        SetBit(resize_waiting, dev->id);
+        QueueWorkProc(TouchResizeQueue, serverClient, NULL);
+    }
+
     return NULL;
 }
 
diff --git a/test/touch.c b/test/touch.c
index 5b8e567..1ea8f0c 100644
--- a/test/touch.c
+++ b/test/touch.c
@@ -29,6 +29,56 @@
 #include "inputstr.h"
 #include "assert.h"
 
+static void touch_grow_queue(void)
+{
+    DeviceIntRec dev;
+    ValuatorClassRec val;
+    TouchClassRec touch;
+    size_t size, new_size;
+    int i;
+
+    memset(&dev, 0, sizeof(dev));
+    dev.id = 2;
+    dev.valuator = &val;
+    val.numAxes = 5;
+    dev.touch = &touch;
+    inputInfo.devices = &dev;
+
+    size = 5;
+
+    dev.last.num_touches = size;
+    dev.last.touches = calloc(dev.last.num_touches, sizeof(*dev.last.touches));
+    assert(dev.last.touches);
+    for (i = 0; i < size; i++) {
+        dev.last.touches[i].active = TRUE;
+        dev.last.touches[i].ddx_id = i;
+        dev.last.touches[i].client_id = i * 2;
+    }
+
+    /* no more space, should've scheduled a workproc */
+    assert(TouchBeginDDXTouch(&dev, 1234) == NULL);
+    ProcessWorkQueue();
+
+    new_size = size + size/2 + 1;
+    assert(dev.last.num_touches == new_size);
+
+    /* make sure we haven't touched those */
+    for (i = 0; i < size; i++) {
+        DDXTouchPointInfoPtr t = &dev.last.touches[i];
+        assert(t->active == TRUE);
+        assert(t->ddx_id == i);
+        assert(t->client_id == i * 2);
+    }
+
+    /* make sure those are zero-initialized */
+    for (i = size; i < new_size; i++) {
+        DDXTouchPointInfoPtr t = &dev.last.touches[i];
+        assert(t->active == FALSE);
+        assert(t->client_id == 0);
+        assert(t->ddx_id == 0);
+    }
+}
+
 static void touch_find_ddxid(void)
 {
     DeviceIntRec dev;
@@ -142,6 +192,7 @@ static void touch_begin_ddxtouch(void)
 
 int main(int argc, char** argv)
 {
+    touch_grow_queue();
     touch_find_ddxid();
     touch_begin_ddxtouch();
 
-- 
1.7.7.1



More information about the xorg-devel mailing list