[PATCH 1/3] Xi: fix per-device barrier handling

Peter Hutterer peter.hutterer at who-t.net
Wed Dec 12 21:22:05 PST 2012


Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 Xi/xibarriers.c        | 235 ++++++++++++++++++++++++++++++++++++++-----------
 Xi/xibarriers.h        |   3 +
 Xi/xichangehierarchy.c |   6 ++
 3 files changed, 195 insertions(+), 49 deletions(-)

diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c
index 048af62..7e8b2e6 100644
--- a/Xi/xibarriers.c
+++ b/Xi/xibarriers.c
@@ -67,19 +67,28 @@ static DevPrivateKeyRec BarrierScreenPrivateKeyRec;
 
 typedef struct PointerBarrierClient *PointerBarrierClientPtr;
 
+struct PointerBarrierDevice {
+    struct xorg_list entry;
+    int deviceid;
+    Time last_timestamp;
+    int barrier_event_id;
+    int release_event_id;
+    Bool hit;
+    Bool seen;
+};
+
 struct PointerBarrierClient {
     XID id;
     ScreenPtr screen;
     WindowPtr window;
     struct PointerBarrier barrier;
     struct xorg_list entry;
+    /* num_devices/device_ids are devices the barrier applies to */
     int num_devices;
     int *device_ids; /* num_devices */
-    Time last_timestamp;
-    int barrier_event_id;
-    int release_event_id;
-    Bool hit;
-    Bool seen;
+
+    /* per_device keeps track of devices actually blocked by barriers */
+    struct xorg_list per_device;
 };
 
 typedef struct _BarrierScreen {
@@ -90,6 +99,47 @@ typedef struct _BarrierScreen {
 #define GetBarrierScreenIfSet(s) GetBarrierScreen(s)
 #define SetBarrierScreen(s,p) dixSetPrivate(&(s)->devPrivates, BarrierScreenPrivateKey, p)
 
+static struct PointerBarrierDevice *AllocBarrierDevice(void)
+{
+    struct PointerBarrierDevice *pbd = NULL;
+
+    pbd = malloc(sizeof(struct PointerBarrierDevice));
+    if (!pbd)
+        return NULL;
+
+    pbd->deviceid = -1; /* must be set by caller */
+    pbd->barrier_event_id = 1;
+    pbd->release_event_id = 0;
+    pbd->hit = FALSE;
+    pbd->seen = FALSE;
+    xorg_list_init(&pbd->entry);
+
+    return pbd;
+}
+
+static void FreePointerBarrierClient(struct PointerBarrierClient *c)
+{
+    struct PointerBarrierDevice *pbd = NULL, *tmp = NULL;
+
+    xorg_list_for_each_entry_safe(pbd, tmp, &c->per_device, entry) {
+        free(pbd);
+    }
+    free(c);
+}
+
+static struct PointerBarrierDevice *GetBarrierDevice(struct PointerBarrierClient *c, int deviceid)
+{
+    struct PointerBarrierDevice *pbd = NULL;
+
+    xorg_list_for_each_entry(pbd, &c->per_device, entry) {
+        if (pbd->deviceid == deviceid)
+            break;
+    }
+
+    BUG_WARN(!pbd);
+    return pbd;
+}
+
 static BOOL
 barrier_is_horizontal(const struct PointerBarrier *barrier)
 {
@@ -283,9 +333,11 @@ barrier_find_nearest(BarrierScreenPtr cs, DeviceIntPtr dev,
 
     xorg_list_for_each_entry(c, &cs->barriers, entry) {
         struct PointerBarrier *b = &c->barrier;
+        struct PointerBarrierDevice *pbd;
         double distance;
 
-        if (c->seen)
+        pbd = GetBarrierDevice(c, dev->id);
+        if (pbd->seen)
             continue;
 
         if (!barrier_is_blocking_direction(b, dir))
@@ -358,6 +410,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen,
         .root = screen->root->drawable.id,
     };
     InternalEvent *barrier_events = events;
+    DeviceIntPtr master;
 
     if (nevents)
         *nevents = 0;
@@ -365,6 +418,13 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen,
     if (xorg_list_is_empty(&cs->barriers) || IsFloating(dev))
         goto out;
 
+    /**
+     * This function is only called for slave devices, but pointer-barriers
+     * are for master-devices only. Flip the device to the master here,
+     * continue with that.
+     */
+    master = GetMaster(dev, MASTER_POINTER);
+
     /* How this works:
      * Given the origin and the movement vector, get the nearest barrier
      * to the origin that is blocking the movement.
@@ -375,16 +435,19 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen,
     dir = barrier_get_direction(current_x, current_y, x, y);
 
     while (dir != 0) {
-        c = barrier_find_nearest(cs, dev, dir, current_x, current_y, x, y);
+        struct PointerBarrierDevice *pbd;
+
+        c = barrier_find_nearest(cs, master, dir, current_x, current_y, x, y);
         if (!c)
             break;
 
         nearest = &c->barrier;
 
-        c->seen = TRUE;
-        c->hit = TRUE;
+        pbd = GetBarrierDevice(c, master->id);
+        pbd->seen = TRUE;
+        pbd->hit = TRUE;
 
-        if (c->barrier_event_id == c->release_event_id)
+        if (pbd->barrier_event_id == pbd->release_event_id)
             continue;
 
         ev.type = ET_BarrierHit;
@@ -400,12 +463,12 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen,
         }
 
         ev.flags = 0;
-        ev.event_id = c->barrier_event_id;
+        ev.event_id = pbd->barrier_event_id;
         ev.barrierid = c->id;
 
-        ev.dt = ms - c->last_timestamp;
+        ev.dt = ms - pbd->last_timestamp;
         ev.window = c->window->drawable.id;
-        c->last_timestamp = ms;
+        pbd->last_timestamp = ms;
 
         /* root x/y is filled in later */
 
@@ -415,28 +478,31 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen,
     }
 
     xorg_list_for_each_entry(c, &cs->barriers, entry) {
+        struct PointerBarrierDevice *pbd;
         int flags = 0;
-        c->seen = FALSE;
-        if (!c->hit)
+
+        pbd = GetBarrierDevice(c, master->id);
+        pbd->seen = FALSE;
+        if (!pbd->hit)
             continue;
 
         if (barrier_inside_hit_box(&c->barrier, x, y))
             continue;
 
-        c->hit = FALSE;
+        pbd->hit = FALSE;
 
         ev.type = ET_BarrierLeave;
 
-        if (c->barrier_event_id == c->release_event_id)
+        if (pbd->barrier_event_id == pbd->release_event_id)
             flags |= XIBarrierPointerReleased;
 
         ev.flags = flags;
-        ev.event_id = c->barrier_event_id;
+        ev.event_id = pbd->barrier_event_id;
         ev.barrierid = c->id;
 
-        ev.dt = ms - c->last_timestamp;
+        ev.dt = ms - pbd->last_timestamp;
         ev.window = c->window->drawable.id;
-        c->last_timestamp = ms;
+        pbd->last_timestamp = ms;
 
         /* root x/y is filled in later */
 
@@ -446,7 +512,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen,
 
         /* If we've left the hit box, this is the
          * start of a new event ID. */
-        c->barrier_event_id++;
+        pbd->barrier_event_id++;
     }
 
  out:
@@ -479,6 +545,7 @@ CreatePointerBarrierClient(ClientPtr client,
     int i;
     struct PointerBarrierClient *ret;
     CARD16 *in_devices;
+    DeviceIntPtr dev;
 
     size = sizeof(*ret) + sizeof(DeviceIntPtr) * stuff->num_devices;
     ret = malloc(size);
@@ -487,6 +554,8 @@ CreatePointerBarrierClient(ClientPtr client,
         return BadAlloc;
     }
 
+    xorg_list_init(&ret->per_device);
+
     err = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess);
     if (err != Success) {
         client->errorValue = stuff->window;
@@ -524,11 +593,25 @@ CreatePointerBarrierClient(ClientPtr client,
         ret->device_ids[i] = device_id;
     }
 
+    /* Alloc one per master pointer, they're the ones that can be blocked */
+    xorg_list_init(&ret->per_device);
+    nt_list_for_each_entry(dev, inputInfo.devices, next) {
+        struct PointerBarrierDevice *pbd;
+
+        if (dev->type != MASTER_POINTER)
+            continue;
+
+        pbd = AllocBarrierDevice();
+        if (!pbd) {
+            err = BadAlloc;
+            goto error;
+        }
+        pbd->deviceid = dev->id;
+
+        xorg_list_add(&pbd->entry, &ret->per_device);
+    }
+
     ret->id = stuff->barrier;
-    ret->barrier_event_id = 1;
-    ret->release_event_id = 0;
-    ret->hit = FALSE;
-    ret->seen = FALSE;
     ret->barrier.x1 = stuff->x1;
     ret->barrier.x2 = stuff->x2;
     ret->barrier.y1 = stuff->y1;
@@ -547,7 +630,7 @@ CreatePointerBarrierClient(ClientPtr client,
 
  error:
     *client_out = NULL;
-    free(ret);
+    FreePointerBarrierClient(ret);
     return err;
 }
 
@@ -556,13 +639,15 @@ BarrierFreeBarrier(void *data, XID id)
 {
     struct PointerBarrierClient *c;
     Time ms = GetTimeInMillis();
+    DeviceIntPtr dev = NULL;
+    ScreenPtr screen;
 
     c = container_of(data, struct PointerBarrierClient, barrier);
+    screen = c->screen;
 
-    /* FIXME: this is really broken for multidevice */
-    if (c->hit) {
-        DeviceIntPtr dev = NULL;
-        ScreenPtr screen = c->screen;
+    for (dev = inputInfo.devices; dev; dev = dev->next) {
+        struct PointerBarrierDevice *pbd;
+        int root_x, root_y;
         BarrierEvent ev = {
             .header = ET_Internal,
             .type = ET_BarrierLeave,
@@ -577,36 +662,79 @@ BarrierFreeBarrier(void *data, XID id)
             .dy = 0,
             /* .root_x */
             /* .root_y */
-            .dt = ms - c->last_timestamp,
-            .event_id = c->barrier_event_id,
+            /* .dt */
+            /* .event_id */
             .flags = XIBarrierPointerReleased,
         };
 
-        for (dev = inputInfo.devices; dev; dev = dev->next) {
-            int root_x, root_y;
 
-            if (dev->type != MASTER_POINTER)
-                continue;
+        if (dev->type != MASTER_POINTER)
+            continue;
 
-            if (!barrier_blocks_device(c, dev))
-                continue;
+        pbd = GetBarrierDevice(c, dev->id);
+        if (!pbd->hit)
+            continue;
 
-            ev.deviceid = dev->id;
+        ev.deviceid = dev->id;
+        ev.event_id = pbd->barrier_event_id,
+        ev.dt = ms - pbd->last_timestamp,
 
-            GetSpritePosition(dev, &root_x, &root_y);
-            ev.root_x = root_x;
-            ev.root_y = root_y;
+        GetSpritePosition(dev, &root_x, &root_y);
+        ev.root_x = root_x;
+        ev.root_y = root_y;
 
-            mieqEnqueue(dev, (InternalEvent *) &ev);
-        }
+        mieqEnqueue(dev, (InternalEvent *) &ev);
     }
 
     xorg_list_del(&c->entry);
 
-    free(c);
+    FreePointerBarrierClient(c);
     return Success;
 }
 
+static void add_master_func(pointer res, XID id, pointer devid)
+{
+    struct PointerBarrier *b;
+    struct PointerBarrierClient *barrier;
+    struct PointerBarrierDevice *pbd;
+    int *deviceid = devid;
+
+    b = res;
+    barrier = container_of(b, struct PointerBarrierClient, barrier);
+
+
+    pbd = AllocBarrierDevice();
+    pbd->deviceid = *deviceid;
+
+    xorg_list_add(&pbd->entry, &barrier->per_device);
+}
+
+static void remove_master_func(pointer res, XID id, pointer devid)
+{
+    struct PointerBarrierDevice *pbd;
+    struct PointerBarrierClient *barrier;
+    struct PointerBarrier *b;
+    int *deviceid = devid;
+
+    b = res;
+    barrier = container_of(b, struct PointerBarrierClient, barrier);
+
+    pbd = GetBarrierDevice(barrier, *deviceid);
+    xorg_list_del(&pbd->entry);
+    free(pbd);
+}
+
+void XIBarrierNewMasterDevice(ClientPtr client, int deviceid)
+{
+    FindClientResourcesByType(client, PointerBarrierType, add_master_func, &deviceid);
+}
+
+void XIBarrierRemoveMasterDevice(ClientPtr client, int deviceid)
+{
+    /* FIXME: send LeaveNotify */
+    FindClientResourcesByType(client, PointerBarrierType, remove_master_func, &deviceid);
+}
+
 int
 XICreatePointerBarrier(ClientPtr client,
                        xXFixesCreatePointerBarrierReq * stuff)
@@ -698,14 +826,19 @@ ProcXIBarrierReleasePointer(ClientPtr client)
 
     info = (xXIBarrierReleasePointerInfo*) &stuff[1];
     for (i = 0; i < stuff->num_barriers; i++, info++) {
+        struct PointerBarrierDevice *pbd;
+        DeviceIntPtr dev;
         CARD32 barrier_id, event_id;
         _X_UNUSED CARD32 device_id;
 
         barrier_id = info->barrier;
         event_id = info->eventid;
 
-        /* FIXME: per-device releases */
-        device_id = info->deviceid;
+        err = dixLookupDevice(&dev, info->deviceid, client, DixReadAccess);
+        if (err != Success) {
+            client->errorValue = BadDevice;
+            return err;
+        }
 
         err = dixLookupResourceByType((void **) &b, barrier_id,
                                       PointerBarrierType, client, DixReadAccess);
@@ -717,9 +850,13 @@ ProcXIBarrierReleasePointer(ClientPtr client)
         if (CLIENT_ID(barrier_id) != client->index)
             return BadAccess;
 
+
         barrier = container_of(b, struct PointerBarrierClient, barrier);
-        if (barrier->barrier_event_id == event_id)
-            barrier->release_event_id = event_id;
+
+        pbd = GetBarrierDevice(barrier, dev->id);
+
+        if (pbd->barrier_event_id == event_id)
+            pbd->release_event_id = event_id;
     }
 
     return Success;
diff --git a/Xi/xibarriers.h b/Xi/xibarriers.h
index bdcb0b2..11e84ec 100644
--- a/Xi/xibarriers.h
+++ b/Xi/xibarriers.h
@@ -42,4 +42,7 @@ XIBarrierInit(void);
 int SProcXIBarrierReleasePointer(ClientPtr client);
 int ProcXIBarrierReleasePointer(ClientPtr client);
 
+void XIBarrierNewMasterDevice(ClientPtr client, int deviceid);
+void XIBarrierRemoveMasterDevice(ClientPtr client, int deviceid);
+
 #endif /* _XIBARRIERS_H_ */
diff --git a/Xi/xichangehierarchy.c b/Xi/xichangehierarchy.c
index 89f16d8..a2cb832 100644
--- a/Xi/xichangehierarchy.c
+++ b/Xi/xichangehierarchy.c
@@ -52,6 +52,7 @@
 #include "xkbsrv.h"
 
 #include "xichangehierarchy.h"
+#include "xibarriers.h"
 
 /**
  * Send the current state of the device hierarchy to all clients.
@@ -189,6 +190,8 @@ add_master(ClientPtr client, xXIAddMasterInfo * c, int flags[MAXDEVICES])
     flags[XTestptr->id] |= XISlaveAttached;
     flags[XTestkeybd->id] |= XISlaveAttached;
 
+    XIBarrierNewMasterDevice(client, ptr->id);
+
  unwind:
     free(name);
     return rc;
@@ -293,6 +296,8 @@ remove_master(ClientPtr client, xXIRemoveMasterInfo * r, int flags[MAXDEVICES])
         }
     }
 
+    XIBarrierRemoveMasterDevice(client, ptr->id);
+
     /* disable the remove the devices, XTest devices must be done first
        else the sprites they rely on will be destroyed  */
     DisableDevice(XTestptr, FALSE);
@@ -313,6 +318,7 @@ remove_master(ClientPtr client, xXIRemoveMasterInfo * r, int flags[MAXDEVICES])
     flags[keybd->id] |= XIMasterRemoved;
     flags[ptr->id] |= XIMasterRemoved;
 
+
  unwind:
     return rc;
 }
-- 
1.8.0.2



More information about the xorg-devel mailing list