xserver: Branch 'master' - 23 commits

Keith Packard keithp at kemper.freedesktop.org
Thu Dec 8 21:08:26 PST 2011


 Xi/exevents.c                  |   88 ++++++------
 Xi/ungrdevb.c                  |   32 ++--
 Xi/ungrdevk.c                  |   31 ++--
 Xi/xichangehierarchy.c         |   15 ++
 Xi/xigrabdev.c                 |   15 +-
 Xi/xipassivegrab.c             |   50 ++++---
 Xi/xiselectev.c                |    5 
 dix/devices.c                  |    3 
 dix/events.c                   |  288 ++++++++++++++++++++---------------------
 dix/grabs.c                    |   84 ++++++++++-
 dix/inpututils.c               |  181 +++++++++++++++++++++++++
 hw/xfree86/common/xf86Module.h |    2 
 hw/xfree86/xaa/xaalocal.h      |    4 
 include/dix.h                  |    1 
 include/dixgrabs.h             |    4 
 include/input.h                |    1 
 include/inputstr.h             |   22 +--
 include/inpututils.h           |   12 +
 include/list.h                 |   81 ++++++++---
 include/misc.h                 |    9 +
 test/input.c                   |   67 +++++++++
 test/list.c                    |   37 +++++
 test/xi2/Makefile.am           |    5 
 test/xi2/xi2.c                 |  129 ++++++++++++++++++
 xfixes/cursor.c                |    4 
 25 files changed, 894 insertions(+), 276 deletions(-)

New commits:
commit 522f8bcc0360d6a117e929a004bc956ab92037e9
Merge: 6369b59... 2abe83d...
Author: Keith Packard <keithp at keithp.com>
Date:   Thu Dec 8 20:57:26 2011 -0800

    Merge remote-tracking branch 'whot/for-keith'

commit 2abe83df686ed64c4f4df711ac3c1fd12131c2e4
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Mon Dec 5 14:02:51 2011 +1000

    include: add BUG_WARN_MSG for custom error message on bug condition
    
    __BUG_WARN_MSG is a simple helper to enable call with and without varargs. I
    couldn't find a way to otherwise do this without getting gcc warnings.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/include/misc.h b/include/misc.h
index ef86fa5..d9811ca 100644
--- a/include/misc.h
+++ b/include/misc.h
@@ -359,11 +359,18 @@ typedef struct _CharInfo *CharInfoPtr; /* also in fonts/include/font.h */
 extern _X_EXPORT unsigned long globalSerialNumber;
 extern _X_EXPORT unsigned long serverGeneration;
 
-#define BUG_WARN(cond)                                                    \
+/* Don't use this directly, use BUG_WARN or BUG_WARN_MSG instead */
+#define __BUG_WARN_MSG(cond, with_msg, ...)                                \
           do { if (cond) {                                                \
               ErrorF("BUG: triggered 'if (" #cond ")'\nBUG: %s:%d in %s()\n",     \
                       __FILE__, __LINE__, __func__);                      \
+              if (with_msg) ErrorF(__VA_ARGS__);                          \
               xorg_backtrace();                                           \
           } } while(0)
 
+#define BUG_WARN_MSG(cond, msg, ...)                                      \
+          __BUG_WARN_MSG(cond, 1, msg, __VA_ARGS__)
+
+#define BUG_WARN(cond)  __BUG_WARN_MSG(cond, 0, NULL)
+
 #endif /* MISC_H */
commit 4fc797f3756611a97767f407e1af0b6a7cf2f1d9
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Nov 11 16:25:30 2011 +1000

    xfree86: include xorg-config.h from xaalocal.h
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Alan Coopersmith <alan.coopersmith at oracle.com>

diff --git a/hw/xfree86/xaa/xaalocal.h b/hw/xfree86/xaa/xaalocal.h
index a9a70da..0780fb1 100644
--- a/hw/xfree86/xaa/xaalocal.h
+++ b/hw/xfree86/xaa/xaalocal.h
@@ -2,6 +2,10 @@
 #ifndef _XAALOCAL_H
 #define _XAALOCAL_H
 
+#ifdef HAVE_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
 /* This file is very unorganized ! */
 
 
commit 8c9589c71d47f287588314d77ddbfcc22cd04c8a
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Mon Dec 5 11:55:58 2011 +1000

    Xi: rename "state" to "corestate" in ProcessDeviceEvents
    
    'state' is shadowed by the XKB 'state' as well (which feeds into the event
    too), so rename this one to clarify that this is the core event state only.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/Xi/exevents.c b/Xi/exevents.c
index 532f04e..b2e82ec 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -885,7 +885,7 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
     int key = 0, rootX, rootY;
     ButtonClassPtr b;
     int ret = 0;
-    int state, i;
+    int corestate, i;
     DeviceIntPtr mouse = NULL, kbd = NULL;
     DeviceEvent *event = &ev->device_event;
 
@@ -915,9 +915,9 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
             mouse = NULL;
     }
 
-    /* State needs to be assembled BEFORE the device is updated. */
-    state = (kbd && kbd->key) ? XkbStateFieldFromRec(&kbd->key->xkbInfo->state) : 0;
-    state |= (mouse && mouse->button) ? (mouse->button->state) : 0;
+    /* core state needs to be assembled BEFORE the device is updated. */
+    corestate = (kbd && kbd->key) ? XkbStateFieldFromRec(&kbd->key->xkbInfo->state) : 0;
+    corestate |= (mouse && mouse->button) ? (mouse->button->state) : 0;
 
     for (i = 0; mouse && mouse->button && i < mouse->button->numButtons; i++)
         if (BitIsOn(mouse->button->down, i))
@@ -965,7 +965,7 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
             event->root_x = rootX;
             event->root_y = rootY;
             NoticeEventTime((InternalEvent*)event);
-            event->corestate = state;
+            event->corestate = corestate;
             key = event->detail.key;
             break;
         default:
commit 631516a4aa9858874ee197444cd93d91b97a1089
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Dec 2 15:47:58 2011 +1000

    Xi: check button mapping value _before_ assigning it
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/Xi/exevents.c b/Xi/exevents.c
index 5e446ec..532f04e 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -1002,11 +1002,9 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
                 deactivateDeviceGrab = TRUE;
             break;
         case ET_ButtonPress:
-            event->detail.button = b->map[key];
-            if (!event->detail.button) { /* there's no button 0 */
-                event->detail.button = key;
+            if (b->map[key] == 0) /* there's no button 0 */
                 return;
-            }
+            event->detail.button = b->map[key];
             if (!grab && CheckDeviceGrabs(device, event, 0))
             {
                 /* if a passive grab was activated, the event has been sent
@@ -1015,11 +1013,9 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
             }
             break;
         case ET_ButtonRelease:
-            event->detail.button = b->map[key];
-            if (!event->detail.button) { /* there's no button 0 */
-                event->detail.button = key;
+            if (b->map[key] == 0) /* there's no button 0 */
                 return;
-            }
+            event->detail.button = b->map[key];
             if (grab && !b->buttonsDown &&
                 device->deviceGrab.fromPassiveGrab &&
                 (device->deviceGrab.grab->type == ButtonPress ||
commit a1304d6cb69301899c3c8450d6bf3e68573599df
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Mon Dec 5 11:26:30 2011 +1000

    Xi: skip superfluous cast
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/Xi/exevents.c b/Xi/exevents.c
index 7a84c6b..5e446ec 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -1033,7 +1033,7 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
 
     if (grab)
         DeliverGrabbedEvent((InternalEvent*)event, device, deactivateDeviceGrab);
-    else if (device->focus && !IsPointerEvent((InternalEvent*)ev))
+    else if (device->focus && !IsPointerEvent(ev))
         DeliverFocusedEvent(device, (InternalEvent*)event,
                             GetSpriteWindow(device));
     else
commit 1ab50be938524dcd4a9e56d27e3b96a27c2db2c0
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Wed Nov 30 09:06:06 2011 +1000

    xfixes: don't dereference a NULL cursor
    
    If the new cursor is the NULL cursor, don't dereference it and use zeros
    instead.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Jeremy Huddleston <jeremyhu at apple.com>

diff --git a/xfixes/cursor.c b/xfixes/cursor.c
index 2950e45..53f9f20 100644
--- a/xfixes/cursor.c
+++ b/xfixes/cursor.c
@@ -179,9 +179,9 @@ CursorDisplayCursor (DeviceIntPtr pDev,
 		ev.type = XFixesEventBase + XFixesCursorNotify;
 		ev.subtype = XFixesDisplayCursorNotify;
 		ev.window = e->pWindow->drawable.id;
-		ev.cursorSerial = pCursor->serialNumber;
+		ev.cursorSerial = pCursor ? pCursor->serialNumber : 0;
 		ev.timestamp = currentTime.milliseconds;
-		ev.name = pCursor->name;
+		ev.name = pCursor ? pCursor->name : None;
 		WriteEventsToClient (e->pClient, 1, (xEvent *) &ev);
 	    }
 	}
commit d2ebbcdaf6b13d70eee704b1764ff349e1be22a0
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Tue Nov 29 16:15:37 2011 +1000

    Xi: when removing a device, reset ClientPointers where needed
    
    if a client had the to-be-removed device as ClientPointer, reset to NULL.
    
    Fixes #43165
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Jeremy Huddleston <jeremyhu at apple.com>

diff --git a/Xi/xichangehierarchy.c b/Xi/xichangehierarchy.c
index 614d231..a8bc761 100644
--- a/Xi/xichangehierarchy.c
+++ b/Xi/xichangehierarchy.c
@@ -201,6 +201,19 @@ unwind:
 }
 
 static int
+disable_clientpointer(DeviceIntPtr dev)
+{
+    int i;
+
+    for (i = 0; i < currentMaxClients; i++)
+    {
+        ClientPtr client = clients[i];
+        if (client && client->clientPtr == dev)
+            client->clientPtr = NULL;
+    }
+}
+
+static int
 remove_master(ClientPtr client, xXIRemoveMasterInfo *r,
               int flags[MAXDEVICES])
 {
@@ -250,6 +263,8 @@ remove_master(ClientPtr client, xXIRemoveMasterInfo *r,
     if (rc != Success)
         goto unwind;
 
+    disable_clientpointer(ptr);
+
     /* Disabling sends the devices floating, reattach them if
      * desired. */
     if (r->return_mode == XIAttachToMaster)
commit 27425f07b29e0ddaa782345c1899273ca742891e
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Wed Nov 9 14:45:02 2011 +1000

    dix: use BUG_WARN for input mask size issues
    
    Yes, we're likely corrupting memory here but really this is unlikely to be
    triggered other than a real bug in the server. In which case a stacktrace is
    going to be more useful than any silent error handling.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/dix/inpututils.c b/dix/inpututils.c
index 05d4c7c..60f9fa0 100644
--- a/dix/inpututils.c
+++ b/dix/inpututils.c
@@ -972,8 +972,9 @@ xi2mask_isset(XI2Mask* mask, const DeviceIntPtr dev, int event_type)
 {
     int set = 0;
 
-    if (dev->id < 0 || dev->id >= mask->nmasks || event_type >= mask->mask_size)
-        return 0;
+    BUG_WARN(dev->id < 0);
+    BUG_WARN(dev->id >= mask->nmasks);
+    BUG_WARN(bits_to_bytes(event_type + 1) > mask->mask_size);
 
     set = !!BitIsOn(mask->masks[XIAllDevices], event_type);
     if (!set)
@@ -990,8 +991,9 @@ xi2mask_isset(XI2Mask* mask, const DeviceIntPtr dev, int event_type)
 void
 xi2mask_set(XI2Mask *mask, int deviceid, int event_type)
 {
-    if (deviceid < 0 || deviceid >= mask->nmasks || event_type >= mask->mask_size)
-        return;
+    BUG_WARN(deviceid < 0);
+    BUG_WARN(deviceid >= mask->nmasks);
+    BUG_WARN(bits_to_bytes(event_type + 1) > mask->mask_size);
 
     SetBit(mask->masks[deviceid], event_type);
 }
@@ -1005,8 +1007,7 @@ xi2mask_zero(XI2Mask *mask, int deviceid)
 {
     int i;
 
-    if (deviceid > 0 && deviceid >= mask->nmasks)
-        return;
+    BUG_WARN(deviceid > 0 && deviceid >= mask->nmasks);
 
     if (deviceid >= 0)
         memset(mask->masks[deviceid], 0, mask->mask_size);
@@ -1055,8 +1056,8 @@ xi2mask_mask_size(const XI2Mask *mask)
 void
 xi2mask_set_one_mask(XI2Mask *xi2mask, int deviceid, const unsigned char *mask, size_t mask_size)
 {
-    if (deviceid < 0 || deviceid >= xi2mask->nmasks)
-        return;
+    BUG_WARN(deviceid < 0);
+    BUG_WARN(deviceid >= xi2mask->nmasks);
 
     memcpy(xi2mask->masks[deviceid], mask, min(xi2mask->mask_size, mask_size));
 }
@@ -1067,8 +1068,8 @@ xi2mask_set_one_mask(XI2Mask *xi2mask, int deviceid, const unsigned char *mask,
 const unsigned char*
 xi2mask_get_one_mask(const XI2Mask *mask, int deviceid)
 {
-    if (deviceid < 0 || deviceid >= mask->nmasks)
-        return NULL;
+    BUG_WARN(deviceid < 0);
+    BUG_WARN(deviceid >= mask->nmasks);
 
     return mask->masks[deviceid];
 }
commit 9b570ecbda954227c89938ee6f94b9efd192d3c6
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Dec 9 10:48:05 2011 +1000

    xfree86: bump the input ABI
    
    The last few patches broke the ABI, bump it for convenience.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>

diff --git a/hw/xfree86/common/xf86Module.h b/hw/xfree86/common/xf86Module.h
index a9645e7..d354643 100644
--- a/hw/xfree86/common/xf86Module.h
+++ b/hw/xfree86/common/xf86Module.h
@@ -83,7 +83,7 @@ typedef enum {
  */
 #define ABI_ANSIC_VERSION	SET_ABI_VERSION(0, 4)
 #define ABI_VIDEODRV_VERSION	SET_ABI_VERSION(12, 0)
-#define ABI_XINPUT_VERSION	SET_ABI_VERSION(14, 0)
+#define ABI_XINPUT_VERSION	SET_ABI_VERSION(15, 0)
 #define ABI_EXTENSION_VERSION	SET_ABI_VERSION(6, 0)
 #define ABI_FONT_VERSION	SET_ABI_VERSION(0, 6)
 
commit 86bb3781b336c09e4279136ed81974de5acdba7f
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Nov 4 11:29:01 2011 +1000

    input: swap the server over to use the XI2mask struct
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/Xi/exevents.c b/Xi/exevents.c
index 8ef9746..7a84c6b 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -1631,6 +1631,7 @@ SelectForWindow(DeviceIntPtr dev, WindowPtr pWin, ClientPtr client,
 static void
 FreeInputClient(InputClientsPtr *other)
 {
+    xi2mask_free(&(*other)->xi2mask);
     free(*other);
     *other = NULL;
 }
@@ -1653,6 +1654,9 @@ AddExtensionClient(WindowPtr pWin, ClientPtr client, Mask mask, int mskidx)
 	return BadAlloc;
     if (!pWin->optional->inputMasks && !MakeInputMasks(pWin))
 	goto bail;
+    others->xi2mask = xi2mask_new();
+    if (!others->xi2mask)
+        goto bail;
     others->mask[mskidx] = mask;
     others->resource = FakeClientID(client->index);
     others->next = pWin->optional->inputMasks->inputClients;
@@ -1674,6 +1678,12 @@ MakeInputMasks(WindowPtr pWin)
     imasks = calloc(1, sizeof(struct _OtherInputMasks));
     if (!imasks)
 	return FALSE;
+    imasks->xi2mask = xi2mask_new();
+    if (!imasks->xi2mask)
+    {
+        free(imasks);
+        return FALSE;
+    }
     pWin->optional->inputMasks = imasks;
     return TRUE;
 }
@@ -1681,6 +1691,7 @@ MakeInputMasks(WindowPtr pWin)
 static void
 FreeInputMask(OtherInputMasks **imask)
 {
+    xi2mask_free(&(*imask)->xi2mask);
     free(*imask);
     *imask = NULL;
 }
@@ -1691,20 +1702,17 @@ RecalculateDeviceDeliverableEvents(WindowPtr pWin)
     InputClientsPtr others;
     struct _OtherInputMasks *inputMasks;	/* default: NULL */
     WindowPtr pChild, tmp;
-    int i, j;
+    int i;
 
     pChild = pWin;
     while (1) {
 	if ((inputMasks = wOtherInputMasks(pChild)) != 0) {
-            for (i = 0; i < EMASKSIZE; i++)
-                memset(inputMasks->xi2mask[i], 0, sizeof(inputMasks->xi2mask[i]));
+            xi2mask_zero(inputMasks->xi2mask, -1);
 	    for (others = inputMasks->inputClients; others;
 		 others = others->next) {
 		for (i = 0; i < EMASKSIZE; i++)
 		    inputMasks->inputEvents[i] |= others->mask[i];
-                for (i = 0; i < EMASKSIZE; i++)
-                    for (j = 0; j < XI2MASKSIZE; j++)
-                        inputMasks->xi2mask[i][j] |= others->xi2mask[i][j];
+                xi2mask_merge(inputMasks->xi2mask, others->xi2mask);
 	    }
 	    for (i = 0; i < EMASKSIZE; i++)
 		inputMasks->deliverableEvents[i] = inputMasks->inputEvents[i];
@@ -2188,14 +2196,12 @@ XISetEventMask(DeviceIntPtr dev, WindowPtr win, ClientPtr client,
 	for (others = wOtherInputMasks(win)->inputClients; others;
 	     others = others->next) {
 	    if (SameClient(others, client)) {
-                memset(others->xi2mask[dev->id], 0,
-                       sizeof(others->xi2mask[dev->id]));
+                xi2mask_zero(others->xi2mask, dev->id);
                 break;
             }
         }
     }
 
-    len = min(len, sizeof(others->xi2mask[dev->id]));
 
     if (len && !others)
     {
@@ -2204,11 +2210,14 @@ XISetEventMask(DeviceIntPtr dev, WindowPtr win, ClientPtr client,
         others= wOtherInputMasks(win)->inputClients;
     }
 
-    if (others)
-        memset(others->xi2mask[dev->id], 0, sizeof(others->xi2mask[dev->id]));
+    if (others) {
+        xi2mask_zero(others->xi2mask, dev->id);
+        len = min(len, xi2mask_mask_size(others->xi2mask));
+    }
 
-    if (len)
-        memcpy(others->xi2mask[dev->id], mask, len);
+    if (len) {
+        xi2mask_set_one_mask(others->xi2mask, dev->id, mask, len);
+    }
 
     RecalculateDeviceDeliverableEvents(win);
 
diff --git a/Xi/xigrabdev.c b/Xi/xigrabdev.c
index a9b655c..1cfbf24 100644
--- a/Xi/xigrabdev.c
+++ b/Xi/xigrabdev.c
@@ -41,6 +41,7 @@
 #include "exglobals.h" /* BadDevice */
 #include "exevents.h"
 #include "xigrabdev.h"
+#include "inpututils.h"
 
 int
 SProcXIGrabDevice(ClientPtr client)
@@ -64,7 +65,7 @@ ProcXIGrabDevice(ClientPtr client)
     xXIGrabDeviceReply rep;
     int ret = Success;
     uint8_t status;
-    GrabMask mask;
+    GrabMask mask = { 0 };
     int mask_len;
 
     REQUEST(xXIGrabDeviceReq);
@@ -81,9 +82,13 @@ ProcXIGrabDevice(ClientPtr client)
                                stuff->mask_len * 4) != Success)
         return BadValue;
 
-    mask_len = min(sizeof(mask.xi2mask[stuff->deviceid]), stuff->mask_len * 4);
-    memset(mask.xi2mask, 0, sizeof(mask.xi2mask));
-    memcpy(mask.xi2mask, (char*)&stuff[1], mask_len);
+    mask.xi2mask = xi2mask_new();
+    if (!mask.xi2mask)
+        return BadAlloc;
+
+    mask_len = min(xi2mask_mask_size(mask.xi2mask), stuff->mask_len * 4);
+    /* FIXME: I think the old code was broken here */
+    xi2mask_set_one_mask(mask.xi2mask, dev->id, (unsigned char*)&stuff[1], mask_len);
 
     ret = GrabDevice(client, dev, stuff->grab_mode,
                      stuff->paired_device_mode,
@@ -96,6 +101,8 @@ ProcXIGrabDevice(ClientPtr client)
                      None /* confineTo */,
                      &status);
 
+    xi2mask_free(&mask.xi2mask);
+
     if (ret != Success)
         return ret;
 
diff --git a/Xi/xipassivegrab.c b/Xi/xipassivegrab.c
index 4fa887a..4860757 100644
--- a/Xi/xipassivegrab.c
+++ b/Xi/xipassivegrab.c
@@ -44,6 +44,7 @@
 #include "xipassivegrab.h"
 #include "dixgrabs.h"
 #include "misc.h"
+#include "inpututils.h"
 
 int
 SProcXIPassiveGrabDevice(ClientPtr client)
@@ -82,7 +83,7 @@ ProcXIPassiveGrabDevice(ClientPtr client)
     int i, ret = Success;
     uint32_t *modifiers;
     xXIGrabModifierInfo *modifiers_failed;
-    GrabMask mask;
+    GrabMask mask = { 0 };
     GrabParameters param;
     void *tmp;
     int mask_len;
@@ -124,9 +125,12 @@ ProcXIPassiveGrabDevice(ClientPtr client)
                                stuff->mask_len * 4) != Success)
         return BadValue;
 
-    mask_len = min(sizeof(mask.xi2mask[stuff->deviceid]), stuff->mask_len * 4);
-    memset(mask.xi2mask, 0, sizeof(mask.xi2mask));
-    memcpy(mask.xi2mask[stuff->deviceid], &stuff[1], mask_len * 4);
+    mask.xi2mask = xi2mask_new();
+    if (!mask.xi2mask)
+        return BadAlloc;
+
+    mask_len = min(xi2mask_mask_size(mask.xi2mask), stuff->mask_len * 4);
+    xi2mask_set_one_mask(mask.xi2mask, stuff->deviceid, (unsigned char*)&stuff[1], mask_len * 4);
 
     rep.repType = X_Reply;
     rep.RepType = X_XIPassiveGrabDevice;
@@ -212,6 +216,7 @@ ProcXIPassiveGrabDevice(ClientPtr client)
 
     free(modifiers_failed);
 out:
+    xi2mask_free(&mask.xi2mask);
     return ret;
 }
 
diff --git a/Xi/xiselectev.c b/Xi/xiselectev.c
index 3af4f1f..ee14edb 100644
--- a/Xi/xiselectev.c
+++ b/Xi/xiselectev.c
@@ -33,6 +33,7 @@
 #include "exglobals.h"
 #include "exevents.h"
 #include <X11/extensions/XI2proto.h>
+#include "inpututils.h"
 
 #include "xiselectev.h"
 
@@ -249,7 +250,7 @@ ProcXIGetSelectedEvents(ClientPtr client)
     for (i = 0; i < MAXDEVICES; i++)
     {
         int j;
-        unsigned char *devmask = others->xi2mask[i];
+        const unsigned char *devmask = xi2mask_get_one_mask(others->xi2mask, i);
 
         if (i > 2)
         {
@@ -259,7 +260,7 @@ ProcXIGetSelectedEvents(ClientPtr client)
         }
 
 
-        for (j = XI2MASKSIZE - 1; j >= 0; j--)
+        for (j = xi2mask_mask_size(others->xi2mask) - 1; j >= 0; j--)
         {
             if (devmask[j] != 0)
             {
diff --git a/dix/events.c b/dix/events.c
index 8f8531c..59caa91 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -420,12 +420,6 @@ GetXI2EventFilterMask(int evtype)
     return (1 << (evtype % 8));
 }
 
-static inline int
-GetXI2EventFilterOffset(int evtype)
-{
-    return (evtype / 8);
-}
-
 /**
  * For the given event, return the matching event filter. This filter may then
  * be AND'ed with the selected event mask.
@@ -459,12 +453,15 @@ GetEventFilter(DeviceIntPtr dev, xEvent *event)
  * for the event_type.
  */
 static int
-GetXI2MaskByte(unsigned char xi2mask[][XI2MASKSIZE], DeviceIntPtr dev, int event_type)
+GetXI2MaskByte(XI2Mask *mask, DeviceIntPtr dev, int event_type)
 {
-    int byte = GetXI2EventFilterOffset(event_type);
-    return xi2mask[dev->id][byte] |
-           xi2mask[XIAllDevices][byte] |
-           (IsMaster(dev) ? xi2mask[XIAllMasterDevices][byte] : 0);
+    /* we just return the matching filter because that's the only use
+     * for this mask anyway.
+     */
+    if (xi2mask_isset(mask, dev, event_type))
+        return GetXI2EventFilterMask(event_type);
+    else
+        return 0;
 }
 
 
@@ -476,16 +473,14 @@ Bool
 WindowXI2MaskIsset(DeviceIntPtr dev, WindowPtr win, xEvent* ev)
 {
     OtherInputMasks *inputMasks = wOtherInputMasks(win);
-    int filter;
     int evtype;
 
     if (!inputMasks || xi2_get_type(ev) == 0)
         return 0;
 
     evtype = ((xGenericEvent*)ev)->evtype;
-    filter = GetEventFilter(dev, ev);
 
-    return !!(GetXI2MaskByte(inputMasks->xi2mask, dev, evtype) & filter);
+    return xi2mask_isset(inputMasks->xi2mask, dev, evtype);
 }
 
 Mask
@@ -2011,8 +2006,7 @@ ActivateImplicitGrab(DeviceIntPtr dev, ClientPtr client, WindowPtr win,
     tempGrab->deviceMask = (inputMasks) ? inputMasks->inputEvents[dev->id]: 0;
 
     if (inputMasks)
-        memcpy(tempGrab->xi2mask, inputMasks->xi2mask,
-               sizeof(tempGrab->xi2mask));
+        xi2mask_merge(tempGrab->xi2mask, inputMasks->xi2mask);
 
     (*dev->deviceGrab.ActivateGrab)(dev, tempGrab,
                                     currentTime, TRUE | ImplicitGrabMask);
@@ -2561,10 +2555,7 @@ EventIsDeliverable(DeviceIntPtr dev, InternalEvent* event, WindowPtr win)
 
     if ((type = GetXI2Type(event)) != 0)
     {
-        filter = GetXI2EventFilterMask(type);
-
-        if (inputMasks &&
-            (GetXI2MaskByte(inputMasks->xi2mask,  dev, type) & filter))
+        if (inputMasks && xi2mask_isset(inputMasks->xi2mask, dev, type))
             rc |= EVENT_XI2_MASK;
     }
 
@@ -4155,12 +4146,11 @@ DeliverGrabbedEvent(InternalEvent *event, DeviceIntPtr thisDev,
             if (rc == Success)
             {
                 int evtype = xi2_get_type(xi2);
-                mask = GetXI2MaskByte(grab->xi2mask, thisDev, evtype);
+                mask = xi2mask_isset(grab->xi2mask, thisDev, evtype);
                 /* try XI2 event */
                 FixUpEventFromWindow(pSprite, xi2, grab->window, None, TRUE);
                 /* XXX: XACE */
-                deliveries = TryClientEvents(rClient(grab), thisDev, xi2, 1, mask,
-                        GetEventFilter(thisDev, xi2), grab);
+                deliveries = TryClientEvents(rClient(grab), thisDev, xi2, 1, mask, 1, grab);
             } else if (rc != BadMatch)
                 ErrorF("[dix] %s: XI2 conversion failed in DGE (%d, %d). Skipping delivery.\n",
                         thisDev->name, event->any.type, rc);
@@ -4634,9 +4624,8 @@ DeviceEnterLeaveEvent(
     if (grab)
     {
         Mask mask;
-        mask = GetXI2MaskByte(grab->xi2mask, mouse, type);
-        TryClientEvents(rClient(grab), mouse, (xEvent*)event, 1, mask,
-                        filter, grab);
+        mask = xi2mask_isset(grab->xi2mask, mouse, type);
+        TryClientEvents(rClient(grab), mouse, (xEvent*)event, 1, mask, 1, grab);
     } else {
         if (!WindowXI2MaskIsset(mouse, pWin, (xEvent*)event))
             goto out;
@@ -5100,7 +5089,7 @@ GrabDevice(ClientPtr client, DeviceIntPtr dev,
 	else if (grabtype == GRABTYPE_XI)
 	    tempGrab->eventMask = mask->xi;
 	else
-	    memcpy(tempGrab->xi2mask, mask->xi2mask, sizeof(tempGrab->xi2mask));
+            xi2mask_merge(tempGrab->xi2mask, mask->xi2mask);
 	tempGrab->device = dev;
 	tempGrab->cursor = cursor;
 	tempGrab->confineTo = confineTo;
diff --git a/dix/grabs.c b/dix/grabs.c
index a1d56c5..aced130 100644
--- a/dix/grabs.c
+++ b/dix/grabs.c
@@ -60,6 +60,7 @@ SOFTWARE.
 #include "dixgrabs.h"
 #include "xace.h"
 #include "exevents.h"
+#include "inpututils.h"
 
 #define BITMASK(i) (((Mask)1) << ((i) & 31))
 #define MASKIDX(i) ((i) >> 5)
@@ -122,13 +123,15 @@ PrintDeviceGrabInfo(DeviceIntPtr dev)
     }
     else if (grab->grabtype == GRABTYPE_XI2)
     {
-        for (i = 0; i < EMASKSIZE; i++)
+        for (i = 0; i < xi2mask_num_masks(grab->xi2mask); i++)
         {
+            const unsigned char *mask;
             int print;
             print = 0;
             for (j = 0; j < XI2MASKSIZE; j++)
             {
-                if (grab->xi2mask[i][j])
+                mask = xi2mask_get_one_mask(grab->xi2mask, i);
+                if (mask[j])
                 {
                     print = 1;
                     break;
@@ -137,8 +140,8 @@ PrintDeviceGrabInfo(DeviceIntPtr dev)
             if (!print)
                 continue;
             ErrorF("      xi2 event mask for device %d: 0x", dev->id);
-            for (j = 0; j < XI2MASKSIZE; j++)
-                ErrorF("%x", grab->xi2mask[i][j]);
+            for (j = 0; j < xi2mask_mask_size(grab->xi2mask); j++)
+                ErrorF("%x", mask[j]);
             ErrorF("\n");
         }
     }
@@ -185,6 +188,14 @@ AllocGrab(void)
 {
     GrabPtr grab = calloc(1, sizeof(GrabRec));
 
+    if (grab) {
+        grab->xi2mask = xi2mask_new();
+        if (!grab->xi2mask) {
+            free(grab);
+            grab = NULL;
+        }
+    }
+
     return grab;
 }
 
@@ -227,7 +238,7 @@ CreateGrab(
     grab->next = NULL;
 
     if (grabtype == GRABTYPE_XI2)
-        memcpy(grab->xi2mask, mask->xi2mask, sizeof(mask->xi2mask));
+        xi2mask_merge(grab->xi2mask, mask->xi2mask);
     if (cursor)
 	cursor->refcnt++;
     return grab;
@@ -243,6 +254,7 @@ FreeGrab(GrabPtr pGrab)
     if (pGrab->cursor)
 	FreeCursor(pGrab->cursor, (Cursor)0);
 
+    xi2mask_free(&pGrab->xi2mask);
     free(pGrab);
 }
 
@@ -251,6 +263,7 @@ CopyGrab(GrabPtr dst, const GrabPtr src)
 {
     Mask *mdetails_mask = NULL;
     Mask *details_mask = NULL;
+    XI2Mask *xi2mask;
 
     if (src->cursor)
         src->cursor->refcnt++;
@@ -273,9 +286,24 @@ CopyGrab(GrabPtr dst, const GrabPtr src)
         memcpy(details_mask, src->detail.pMask, len);
     }
 
+    if (!dst->xi2mask) {
+        xi2mask = xi2mask_new();
+        if (!xi2mask) {
+            free(mdetails_mask);
+            free(details_mask);
+            return FALSE;
+        }
+    } else {
+        xi2mask = dst->xi2mask;
+        xi2mask_zero(xi2mask, -1);
+    }
+
     *dst = *src;
     dst->modifiersDetail.pMask = mdetails_mask;
     dst->detail.pMask = details_mask;
+    dst->xi2mask = xi2mask;
+
+    xi2mask_merge(dst->xi2mask, src->xi2mask);
 
     return TRUE;
 }
diff --git a/include/inputstr.h b/include/inputstr.h
index a73ace8..5634f3c 100644
--- a/include/inputstr.h
+++ b/include/inputstr.h
@@ -118,7 +118,7 @@ typedef struct _InputClients {
     XID			resource; /**< id for putting into resource manager */
     Mask		mask[EMASKSIZE]; /**< Actual XI event mask, deviceid is index */
     /** XI2 event masks. One per device, each bit is a mask of (1 << type) */
-    unsigned char       xi2mask[EMASKSIZE][XI2MASKSIZE];
+    struct _XI2Mask     *xi2mask;
 } InputClients;
 
 /**
@@ -148,7 +148,7 @@ typedef struct _OtherInputMasks {
     /** The clients that selected for events */
     InputClientsPtr	inputClients;
     /* XI2 event masks. One per device, each bit is a mask of (1 << type) */
-    unsigned char       xi2mask[EMASKSIZE][XI2MASKSIZE];
+    struct _XI2Mask     *xi2mask;
 } OtherInputMasks;
 
 /*
@@ -176,7 +176,7 @@ typedef enum {
 union _GrabMask {
     Mask core;
     Mask xi;
-    char xi2mask[EMASKSIZE][XI2MASKSIZE];
+    struct _XI2Mask *xi2mask;
 };
 
 /**
@@ -210,7 +210,7 @@ typedef struct _GrabRec {
     Mask		eventMask;
     Mask                deviceMask;     
     /* XI2 event masks. One per device, each bit is a mask of (1 << type) */
-    unsigned char       xi2mask[EMASKSIZE][XI2MASKSIZE];
+    struct _XI2Mask *xi2mask;
 } GrabRec;
 
 /**
commit b8b90cd1610331ff12fa3f70bf372670af7795ec
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Thu Nov 3 13:39:59 2011 +1000

    Add a new XI2Mask struct and a few helper functions.
    
    The current XI2 mask handling is handy for copying (fixed size arrays) but a
    pain to deal with otherwise. Add a struct for XI2 masks and the required
    accessors.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/dix/inpututils.c b/dix/inpututils.c
index c152b2d..05d4c7c 100644
--- a/dix/inpututils.c
+++ b/dix/inpututils.c
@@ -892,3 +892,183 @@ double_to_fp3232(double in)
     ret.frac = frac_d;
     return ret;
 }
+
+/**
+ * DO NOT USE THIS FUNCTION. It only exists for the test cases. Use
+ * xi2mask_new() instead to get the standard sized masks.
+ *
+ * @param nmasks The number of masks (== number of devices)
+ * @param size The size of the masks in bytes
+ * @return The new mask or NULL on allocation error.
+ */
+XI2Mask*
+xi2mask_new_with_size(size_t nmasks, size_t size)
+{
+    int i;
+
+    XI2Mask *mask = calloc(1, sizeof(*mask));
+    if (!mask)
+        return NULL;
+
+
+    mask->nmasks = nmasks;
+    mask->mask_size = size;
+
+    mask->masks = calloc(mask->nmasks, sizeof(*mask->masks));
+    if (!mask->masks)
+        goto unwind;
+
+    for (i = 0; i < mask->nmasks; i++) {
+        mask->masks[i] = calloc(1, mask->mask_size);
+        if (!mask->masks[i])
+            goto unwind;
+    }
+    return mask;
+
+unwind:
+    xi2mask_free(&mask);
+    return NULL;
+}
+
+
+/**
+ * Create a new XI2 mask of the standard size, i.e. for all devices + fake
+ * devices and for the highest supported XI2 event type.
+ *
+ * @return The new mask or NULL on allocation error.
+ */
+XI2Mask*
+xi2mask_new(void)
+{
+    return xi2mask_new_with_size(EMASKSIZE, XI2MASKSIZE);
+}
+
+/**
+ * Frees memory associated with mask and resets mask to NULL.
+ */
+void
+xi2mask_free(XI2Mask** mask)
+{
+    int i;
+
+    if (!(*mask))
+        return;
+
+    for (i = 0; (*mask)->masks && i < (*mask)->nmasks; i++)
+        free((*mask)->masks[i]);
+    free((*mask)->masks);
+    free((*mask));
+    *mask = NULL;
+}
+
+/**
+ * Test if the bit for event type is set for this device, or the
+ * XIAllDevices/XIAllMasterDevices (if applicable) is set.
+ *
+ * @return TRUE if the bit is set, FALSE otherwise
+ */
+Bool
+xi2mask_isset(XI2Mask* mask, const DeviceIntPtr dev, int event_type)
+{
+    int set = 0;
+
+    if (dev->id < 0 || dev->id >= mask->nmasks || event_type >= mask->mask_size)
+        return 0;
+
+    set = !!BitIsOn(mask->masks[XIAllDevices], event_type);
+    if (!set)
+        set = !!BitIsOn(mask->masks[dev->id], event_type);
+    if (!set && IsMaster(dev))
+        set = !!BitIsOn(mask->masks[XIAllMasterDevices], event_type);
+
+    return set;
+}
+
+/**
+ * Set the mask bit for this event type for this device.
+ */
+void
+xi2mask_set(XI2Mask *mask, int deviceid, int event_type)
+{
+    if (deviceid < 0 || deviceid >= mask->nmasks || event_type >= mask->mask_size)
+        return;
+
+    SetBit(mask->masks[deviceid], event_type);
+}
+
+/**
+ * Zero out the xi2mask, for the deviceid given. If the deviceid is < 0, all
+ * masks are zeroed.
+ */
+void
+xi2mask_zero(XI2Mask *mask, int deviceid)
+{
+    int i;
+
+    if (deviceid > 0 && deviceid >= mask->nmasks)
+        return;
+
+    if (deviceid >= 0)
+        memset(mask->masks[deviceid], 0, mask->mask_size);
+    else
+        for (i = 0; i < mask->nmasks; i++)
+            memset(mask->masks[i], 0, mask->mask_size);
+}
+
+/**
+ * Merge source into dest, i.e. dest |= source.
+ * If the masks are of different size, only the overlapping section is merged.
+ */
+void
+xi2mask_merge(XI2Mask *dest, const XI2Mask *source)
+{
+    int i, j;
+
+    for (i = 0; i < min(dest->nmasks, source->nmasks); i++)
+        for (j = 0; j < min(dest->mask_size, source->mask_size); j++)
+            dest->masks[i][j] |= source->masks[i][j];
+}
+
+/**
+ * @return The number of masks in mask
+ */
+size_t
+xi2mask_num_masks(const XI2Mask *mask)
+{
+    return mask->nmasks;
+}
+
+/**
+ * @return The size of each mask in bytes
+ */
+size_t
+xi2mask_mask_size(const XI2Mask *mask)
+{
+    return mask->mask_size;
+}
+
+/**
+ * Set the mask for the given deviceid to the source mask.
+ * If the mask given is larger than the target memory, only the overlapping
+ * parts are copied.
+ */
+void
+xi2mask_set_one_mask(XI2Mask *xi2mask, int deviceid, const unsigned char *mask, size_t mask_size)
+{
+    if (deviceid < 0 || deviceid >= xi2mask->nmasks)
+        return;
+
+    memcpy(xi2mask->masks[deviceid], mask, min(xi2mask->mask_size, mask_size));
+}
+
+/**
+ * Get a reference to the XI2mask for this particular device.
+ */
+const unsigned char*
+xi2mask_get_one_mask(const XI2Mask *mask, int deviceid)
+{
+    if (deviceid < 0 || deviceid >= mask->nmasks)
+        return NULL;
+
+    return mask->masks[deviceid];
+}
diff --git a/include/input.h b/include/input.h
index 8e7b47a..8b0c18e 100644
--- a/include/input.h
+++ b/include/input.h
@@ -205,6 +205,7 @@ extern _X_EXPORT KeybdCtrl	defaultKeyboardControl;
 extern _X_EXPORT PtrCtrl	defaultPointerControl;
 
 typedef struct _InputOption InputOption;
+typedef struct _XI2Mask XI2Mask;
 
 typedef struct _InputAttributes {
     char                *product;
diff --git a/include/inputstr.h b/include/inputstr.h
index f9cb8fe..a73ace8 100644
--- a/include/inputstr.h
+++ b/include/inputstr.h
@@ -622,4 +622,10 @@ static inline WindowPtr DeepestSpriteWin(SpritePtr sprite)
     return sprite->spriteTrace[sprite->spriteTraceGood - 1];
 }
 
+struct _XI2Mask {
+    unsigned char **masks;      /* event mask in masks[deviceid][event type byte] */
+    size_t nmasks;              /* number of masks */
+    size_t mask_size;           /* size of each mask in bytes */
+};
+
 #endif /* INPUTSTRUCT_H */
diff --git a/include/inpututils.h b/include/inpututils.h
index 2832ed5..5f9dfec 100644
--- a/include/inpututils.h
+++ b/include/inpututils.h
@@ -46,4 +46,16 @@ FP1616 double_to_fp1616(double in);
 double fp1616_to_double(FP1616 in);
 double fp3232_to_double(FP3232 in);
 
+
+XI2Mask* xi2mask_new(void);
+XI2Mask* xi2mask_new_with_size(size_t, size_t); /* don't use it */
+void xi2mask_free(XI2Mask** mask);
+Bool xi2mask_isset(XI2Mask* mask, const DeviceIntPtr dev, int event_type);
+void xi2mask_set(XI2Mask *mask, int deviceid, int event_type);
+void xi2mask_zero(XI2Mask *mask, int deviceid);
+void xi2mask_merge(XI2Mask *dest, const XI2Mask *source);
+size_t xi2mask_num_masks(const XI2Mask *mask);
+size_t xi2mask_mask_size(const XI2Mask *mask);
+void xi2mask_set_one_mask(XI2Mask *xi2mask, int deviceid, const unsigned char *mask, size_t mask_size);
+const unsigned char* xi2mask_get_one_mask(const XI2Mask *xi2mask, int deviceid);
 #endif
diff --git a/test/xi2/Makefile.am b/test/xi2/Makefile.am
index c6e93e7..913ba0f 100644
--- a/test/xi2/Makefile.am
+++ b/test/xi2/Makefile.am
@@ -10,7 +10,8 @@ noinst_PROGRAMS =  \
         protocol-xipassivegrabdevice \
         protocol-xiquerypointer \
         protocol-xiwarppointer \
-        protocol-eventconvert
+        protocol-eventconvert \
+        xi2
 
 TESTS=$(noinst_PROGRAMS)
 TESTS_ENVIRONMENT = $(XORG_MALLOC_DEBUG_ENV)
@@ -34,6 +35,7 @@ protocol_xiquerypointer_LDADD=$(TEST_LDADD)
 protocol_xipassivegrabdevice_LDADD=$(TEST_LDADD)
 protocol_xiwarppointer_LDADD=$(TEST_LDADD)
 protocol_eventconvert_LDADD=$(TEST_LDADD)
+xi2_LDADD=$(TEST_LDADD)
 
 protocol_xiqueryversion_LDFLAGS=$(AM_LDFLAGS) -Wl,-wrap,WriteToClient
 protocol_xiquerydevice_LDFLAGS=$(AM_LDFLAGS) -Wl,-wrap,WriteToClient
@@ -44,6 +46,7 @@ protocol_xigetclientpointer_LDFLAGS=$(AM_LDFLAGS) -Wl,-wrap,WriteToClient -Wl,-w
 protocol_xipassivegrabdevice_LDFLAGS=$(AM_LDFLAGS) -Wl,-wrap,GrabButton -Wl,-wrap,dixLookupWindow -Wl,-wrap,WriteToClient
 protocol_xiquerypointer_LDFLAGS=$(AM_LDFLAGS) -Wl,-wrap,WriteToClient -Wl,-wrap,dixLookupWindow
 protocol_xiwarppointer_LDFLAGS=$(AM_LDFLAGS) -Wl,-wrap,WriteToClient -Wl,-wrap,dixLookupWindow
+xi2_LDFLAGS=$(AM_LDFLAGS)
 
 protocol_xiqueryversion_SOURCES=$(COMMON_SOURCES) protocol-xiqueryversion.c
 protocol_xiquerydevice_SOURCES=$(COMMON_SOURCES) protocol-xiquerydevice.c
diff --git a/test/xi2/xi2.c b/test/xi2/xi2.c
new file mode 100644
index 0000000..5143caf
--- /dev/null
+++ b/test/xi2/xi2.c
@@ -0,0 +1,129 @@
+/**
+ * Copyright © 2011 Red Hat, Inc.
+ *
+ *  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 <stdint.h>
+#include "inpututils.h"
+#include "inputstr.h"
+#include "assert.h"
+
+static void xi2mask_test(void)
+{
+    XI2Mask *xi2mask = NULL,
+            *mergemask = NULL;
+    unsigned char *mask;
+    DeviceIntRec dev;
+    int i;
+
+    /* size >= nmasks * 2 for the test cases below */
+    xi2mask = xi2mask_new_with_size(MAXDEVICES + 2, (MAXDEVICES + 2) * 2);
+    assert(xi2mask);
+    assert(xi2mask->nmasks > 0);
+    assert(xi2mask->mask_size > 0);
+
+    assert(xi2mask_mask_size(xi2mask) == xi2mask->mask_size);
+    assert(xi2mask_num_masks(xi2mask) == xi2mask->nmasks);
+
+    mask = calloc(1, xi2mask_mask_size(xi2mask));
+    /* ensure zeros */
+    for (i = 0; i < xi2mask_num_masks(xi2mask); i++) {
+        const unsigned char *m = xi2mask_get_one_mask(xi2mask, i);
+        assert(memcmp(mask, m, xi2mask_mask_size(xi2mask)) == 0);
+    }
+
+    /* set various bits */
+    for (i = 0; i < xi2mask_num_masks(xi2mask); i++) {
+        const unsigned char *m;
+        xi2mask_set(xi2mask, i, i);
+
+        dev.id = i;
+        assert(xi2mask_isset(xi2mask, &dev, i));
+
+        m = xi2mask_get_one_mask(xi2mask, i);
+        SetBit(mask, i);
+        assert(memcmp(mask, m, xi2mask_mask_size(xi2mask)) == 0);
+        ClearBit(mask, i);
+    }
+
+    /* ensure zeros one-by-one */
+    for (i = 0; i < xi2mask_num_masks(xi2mask); i++) {
+        const unsigned char *m = xi2mask_get_one_mask(xi2mask, i);
+        assert(memcmp(mask, m, xi2mask_mask_size(xi2mask)) != 0);
+        xi2mask_zero(xi2mask, i);
+        assert(memcmp(mask, m, xi2mask_mask_size(xi2mask)) == 0);
+    }
+
+    /* re-set, zero all */
+    for (i = 0; i < xi2mask_num_masks(xi2mask); i++)
+        xi2mask_set(xi2mask, i, i);
+    xi2mask_zero(xi2mask, -1);
+
+    for (i = 0; i < xi2mask_num_masks(xi2mask); i++) {
+        const unsigned char *m = xi2mask_get_one_mask(xi2mask, i);
+        assert(memcmp(mask, m, xi2mask_mask_size(xi2mask)) == 0);
+    }
+
+    for (i = 0; i < xi2mask_num_masks(xi2mask); i++) {
+        const unsigned char *m;
+        SetBit(mask, i);
+        xi2mask_set_one_mask(xi2mask, i, mask, xi2mask_mask_size(xi2mask));
+        m = xi2mask_get_one_mask(xi2mask, i);
+        assert(memcmp(mask, m, xi2mask_mask_size(xi2mask)) == 0);
+        ClearBit(mask, i);
+    }
+
+    mergemask = xi2mask_new_with_size(MAXDEVICES + 2, (MAXDEVICES + 2) * 2);
+    for (i = 0; i < xi2mask_num_masks(mergemask); i++) {
+        dev.id = i;
+        xi2mask_set(mergemask, i, i * 2);
+    }
+
+    /* xi2mask still has all i bits set, should now also have all i * 2 bits */
+    xi2mask_merge(xi2mask, mergemask);
+    for (i = 0; i < xi2mask_num_masks(mergemask); i++) {
+        const unsigned char *m = xi2mask_get_one_mask(xi2mask, i);
+        SetBit(mask, i);
+        SetBit(mask, i * 2);
+        assert(memcmp(mask, m, xi2mask_mask_size(xi2mask)) == 0);
+        ClearBit(mask, i);
+        ClearBit(mask, i * 2);
+    }
+
+    xi2mask_free(&xi2mask);
+    assert(xi2mask == NULL);
+
+    xi2mask_free(&mergemask);
+    assert(mergemask == NULL);
+    free(mask);
+}
+
+
+int main(int argc, char** argv)
+{
+    xi2mask_test();
+
+    return 0;
+}
commit 4bc2761ad5ec2d0668aec639780ffb136605fbc8
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Nov 4 14:16:37 2011 +1000

    dix: switch the dev->deviceGrab.activeGrab from GrabRec to GrabPtr
    
    This breaks the input ABI.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/dix/devices.c b/dix/devices.c
index 37cbcdb..e448eab 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -73,6 +73,7 @@ SOFTWARE.
 #include "dixevents.h"
 #include "mipointer.h"
 #include "eventstr.h"
+#include "dixgrabs.h"
 
 #include <X11/extensions/XI.h>
 #include <X11/extensions/XI2.h>
@@ -273,6 +274,7 @@ AddInputDevice(ClientPtr client, DeviceProc deviceProc, Bool autoStart)
     dev->deviceGrab.grabTime = currentTime;
     dev->deviceGrab.ActivateGrab = ActivateKeyboardGrab;
     dev->deviceGrab.DeactivateGrab = DeactivateKeyboardGrab;
+    dev->deviceGrab.activeGrab = AllocGrab();
 
     XkbSetExtension(dev, ProcessKeyboardEvent);
 
@@ -941,6 +943,7 @@ CloseDevice(DeviceIntPtr dev)
         }
     }
 
+    FreeGrab(dev->deviceGrab.activeGrab);
     free(dev->deviceGrab.sync.event);
     free(dev->config_info);     /* Allocated in xf86ActivateDevice. */
     free(dev->last.scroll);
diff --git a/dix/events.c b/dix/events.c
index bd7e93b..8f8531c 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -1509,8 +1509,8 @@ ActivatePointerGrab(DeviceIntPtr mouse, GrabPtr grab,
 	grabinfo->grabTime = time;
     if (grab->cursor)
 	grab->cursor->refcnt++;
-    CopyGrab(&grabinfo->activeGrab, grab);
-    grabinfo->grab = &grabinfo->activeGrab;
+    CopyGrab(grabinfo->activeGrab, grab);
+    grabinfo->grab = grabinfo->activeGrab;
     grabinfo->fromPassiveGrab = isPassive;
     grabinfo->implicitGrab = autoGrab & ImplicitGrabMask;
     PostNewCursor(mouse);
@@ -1586,8 +1586,8 @@ ActivateKeyboardGrab(DeviceIntPtr keybd, GrabPtr grab, TimeStamp time, Bool pass
 	grabinfo->grabTime = syncEvents.time;
     else
 	grabinfo->grabTime = time;
-    CopyGrab(&grabinfo->activeGrab, grab);
-    grabinfo->grab = &grabinfo->activeGrab;
+    CopyGrab(grabinfo->activeGrab, grab);
+    grabinfo->grab = grabinfo->activeGrab;
     grabinfo->fromPassiveGrab = passive;
     grabinfo->implicitGrab = passive & ImplicitGrabMask;
     CheckGrabForSyncs(keybd, (Bool)grab->keyboardMode, (Bool)grab->pointerMode);
diff --git a/include/inputstr.h b/include/inputstr.h
index 0568e0c..f9cb8fe 100644
--- a/include/inputstr.h
+++ b/include/inputstr.h
@@ -451,7 +451,7 @@ typedef struct _GrabInfoRec {
     TimeStamp	    grabTime;
     Bool            fromPassiveGrab;    /* true if from passive grab */
     Bool            implicitGrab;       /* implicit from ButtonPress */
-    GrabRec         activeGrab;
+    GrabPtr         activeGrab;
     GrabPtr         grab;
     CARD8           activatingKey;
     void	    (*ActivateGrab) (
commit b601ea769f1b8a4d7f19e9d4a13541c78e865fe5
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Nov 4 10:47:27 2011 +1000

    dix: allocate temporary grabs on the heap
    
    Once grabs start having nested memory locations, we can't just use the
    GrabRec on the stack anymore, we need to alloc/copy/free the grabs.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/Xi/exevents.c b/Xi/exevents.c
index 20495e7..8ef9746 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -2024,20 +2024,25 @@ CheckDeviceGrabAndHintWindow(WindowPtr pWin, int type,
 	dev->valuator->motionHintWindow = pWin;
     else if ((type == DeviceButtonPress) && (!grab) &&
 	     (deliveryMask & DeviceButtonGrabMask)) {
-	GrabRec tempGrab;
+	GrabPtr tempGrab;
 
-	tempGrab.device = dev;
-	tempGrab.resource = client->clientAsMask;
-	tempGrab.window = pWin;
-	tempGrab.ownerEvents =
+	tempGrab = AllocGrab();
+	if (!tempGrab)
+	    return;
+
+	tempGrab->device = dev;
+	tempGrab->resource = client->clientAsMask;
+	tempGrab->window = pWin;
+	tempGrab->ownerEvents =
 	    (deliveryMask & DeviceOwnerGrabButtonMask) ? TRUE : FALSE;
-	tempGrab.eventMask = deliveryMask;
-	tempGrab.keyboardMode = GrabModeAsync;
-	tempGrab.pointerMode = GrabModeAsync;
-	tempGrab.confineTo = NullWindow;
-	tempGrab.cursor = NullCursor;
-        tempGrab.next = NULL;
-	(*dev->deviceGrab.ActivateGrab) (dev, &tempGrab, currentTime, TRUE);
+	tempGrab->eventMask = deliveryMask;
+	tempGrab->keyboardMode = GrabModeAsync;
+	tempGrab->pointerMode = GrabModeAsync;
+	tempGrab->confineTo = NullWindow;
+	tempGrab->cursor = NullCursor;
+	tempGrab->next = NULL;
+	(*dev->deviceGrab.ActivateGrab) (dev, tempGrab, currentTime, TRUE);
+	FreeGrab(tempGrab);
     }
 }
 
diff --git a/Xi/ungrdevb.c b/Xi/ungrdevb.c
index 9e9ece4..6280248 100644
--- a/Xi/ungrdevb.c
+++ b/Xi/ungrdevb.c
@@ -96,7 +96,7 @@ ProcXUngrabDeviceButton(ClientPtr client)
     DeviceIntPtr dev;
     DeviceIntPtr mdev;
     WindowPtr pWin;
-    GrabRec temporaryGrab;
+    GrabPtr temporaryGrab;
     int rc;
 
     REQUEST(xUngrabDeviceButtonReq);
@@ -126,17 +126,23 @@ ProcXUngrabDeviceButton(ClientPtr client)
 	(stuff->modifiers & ~AllModifiersMask))
 	return BadValue;
 
-    temporaryGrab.resource = client->clientAsMask;
-    temporaryGrab.device = dev;
-    temporaryGrab.window = pWin;
-    temporaryGrab.type = DeviceButtonPress;
-    temporaryGrab.grabtype = GRABTYPE_XI;
-    temporaryGrab.modifierDevice = mdev;
-    temporaryGrab.modifiersDetail.exact = stuff->modifiers;
-    temporaryGrab.modifiersDetail.pMask = NULL;
-    temporaryGrab.detail.exact = stuff->button;
-    temporaryGrab.detail.pMask = NULL;
-
-    DeletePassiveGrabFromList(&temporaryGrab);
+    temporaryGrab = AllocGrab();
+    if (!temporaryGrab)
+        return BadAlloc;
+
+    temporaryGrab->resource = client->clientAsMask;
+    temporaryGrab->device = dev;
+    temporaryGrab->window = pWin;
+    temporaryGrab->type = DeviceButtonPress;
+    temporaryGrab->grabtype = GRABTYPE_XI;
+    temporaryGrab->modifierDevice = mdev;
+    temporaryGrab->modifiersDetail.exact = stuff->modifiers;
+    temporaryGrab->modifiersDetail.pMask = NULL;
+    temporaryGrab->detail.exact = stuff->button;
+    temporaryGrab->detail.pMask = NULL;
+
+    DeletePassiveGrabFromList(temporaryGrab);
+
+    FreeGrab(temporaryGrab);
     return Success;
 }
diff --git a/Xi/ungrdevk.c b/Xi/ungrdevk.c
index 526347d..b0d83cb 100644
--- a/Xi/ungrdevk.c
+++ b/Xi/ungrdevk.c
@@ -98,7 +98,7 @@ ProcXUngrabDeviceKey(ClientPtr client)
     DeviceIntPtr dev;
     DeviceIntPtr mdev;
     WindowPtr pWin;
-    GrabRec temporaryGrab;
+    GrabPtr temporaryGrab;
     int rc;
 
     REQUEST(xUngrabDeviceKeyReq);
@@ -133,17 +133,22 @@ ProcXUngrabDeviceKey(ClientPtr client)
 	(stuff->modifiers & ~AllModifiersMask))
 	return BadValue;
 
-    temporaryGrab.resource = client->clientAsMask;
-    temporaryGrab.device = dev;
-    temporaryGrab.window = pWin;
-    temporaryGrab.type = DeviceKeyPress;
-    temporaryGrab.grabtype = GRABTYPE_XI;
-    temporaryGrab.modifierDevice = mdev;
-    temporaryGrab.modifiersDetail.exact = stuff->modifiers;
-    temporaryGrab.modifiersDetail.pMask = NULL;
-    temporaryGrab.detail.exact = stuff->key;
-    temporaryGrab.detail.pMask = NULL;
-
-    DeletePassiveGrabFromList(&temporaryGrab);
+    temporaryGrab = AllocGrab();
+    if (!temporaryGrab)
+        return BadAlloc;
+
+    temporaryGrab->resource = client->clientAsMask;
+    temporaryGrab->device = dev;
+    temporaryGrab->window = pWin;
+    temporaryGrab->type = DeviceKeyPress;
+    temporaryGrab->grabtype = GRABTYPE_XI;
+    temporaryGrab->modifierDevice = mdev;
+    temporaryGrab->modifiersDetail.exact = stuff->modifiers;
+    temporaryGrab->modifiersDetail.pMask = NULL;
+    temporaryGrab->detail.exact = stuff->key;
+    temporaryGrab->detail.pMask = NULL;
+
+    DeletePassiveGrabFromList(temporaryGrab);
+    FreeGrab(temporaryGrab);
     return Success;
 }
diff --git a/Xi/xipassivegrab.c b/Xi/xipassivegrab.c
index 2f13a95..4fa887a 100644
--- a/Xi/xipassivegrab.c
+++ b/Xi/xipassivegrab.c
@@ -253,7 +253,7 @@ ProcXIPassiveUngrabDevice(ClientPtr client)
 {
     DeviceIntPtr dev, mod_dev;
     WindowPtr win;
-    GrabRec tempGrab;
+    GrabPtr tempGrab;
     uint32_t* modifiers;
     int i, rc;
 
@@ -293,29 +293,36 @@ ProcXIPassiveUngrabDevice(ClientPtr client)
 
     mod_dev = (IsFloating(dev)) ? dev : GetMaster(dev, MASTER_KEYBOARD);
 
-    tempGrab.resource = client->clientAsMask;
-    tempGrab.device = dev;
-    tempGrab.window = win;
+
+    tempGrab = AllocGrab();
+    if (!tempGrab)
+        return BadAlloc;
+
+    tempGrab->resource = client->clientAsMask;
+    tempGrab->device = dev;
+    tempGrab->window = win;
     switch(stuff->grab_type)
     {
-        case XIGrabtypeButton:  tempGrab.type = XI_ButtonPress; break;
-        case XIGrabtypeKeycode:  tempGrab.type = XI_KeyPress;    break;
-        case XIGrabtypeEnter:   tempGrab.type = XI_Enter;       break;
-        case XIGrabtypeFocusIn: tempGrab.type = XI_FocusIn;     break;
+        case XIGrabtypeButton:  tempGrab->type = XI_ButtonPress; break;
+        case XIGrabtypeKeycode:  tempGrab->type = XI_KeyPress;    break;
+        case XIGrabtypeEnter:   tempGrab->type = XI_Enter;       break;
+        case XIGrabtypeFocusIn: tempGrab->type = XI_FocusIn;     break;
     }
-    tempGrab.grabtype = GRABTYPE_XI2;
-    tempGrab.modifierDevice = mod_dev;
-    tempGrab.modifiersDetail.pMask = NULL;
-    tempGrab.detail.exact = stuff->detail;
-    tempGrab.detail.pMask = NULL;
+    tempGrab->grabtype = GRABTYPE_XI2;
+    tempGrab->modifierDevice = mod_dev;
+    tempGrab->modifiersDetail.pMask = NULL;
+    tempGrab->detail.exact = stuff->detail;
+    tempGrab->detail.pMask = NULL;
 
     modifiers = (uint32_t*)&stuff[1];
 
     for (i = 0; i < stuff->num_modifiers; i++, modifiers++)
     {
-        tempGrab.modifiersDetail.exact = *modifiers;
-        DeletePassiveGrabFromList(&tempGrab);
+        tempGrab->modifiersDetail.exact = *modifiers;
+        DeletePassiveGrabFromList(tempGrab);
     }
 
+    FreeGrab(tempGrab);
+
     return Success;
 }
diff --git a/dix/events.c b/dix/events.c
index c1c296d..bd7e93b 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -1976,7 +1976,7 @@ static BOOL
 ActivateImplicitGrab(DeviceIntPtr dev, ClientPtr client, WindowPtr win,
                      xEvent *event, Mask deliveryMask)
 {
-    GrabRec tempGrab;
+    GrabPtr tempGrab;
     OtherInputMasks *inputMasks;
     CARD8 type = event->u.u.type;
     GrabType grabtype;
@@ -1990,30 +1990,33 @@ ActivateImplicitGrab(DeviceIntPtr dev, ClientPtr client, WindowPtr win,
     else
         return FALSE;
 
-    memset(&tempGrab, 0, sizeof(GrabRec));
-    tempGrab.next = NULL;
-    tempGrab.device = dev;
-    tempGrab.resource = client->clientAsMask;
-    tempGrab.window = win;
-    tempGrab.ownerEvents = (deliveryMask & OwnerGrabButtonMask) ? TRUE : FALSE;
-    tempGrab.eventMask = deliveryMask;
-    tempGrab.keyboardMode = GrabModeAsync;
-    tempGrab.pointerMode = GrabModeAsync;
-    tempGrab.confineTo = NullWindow;
-    tempGrab.cursor = NullCursor;
-    tempGrab.type = type;
-    tempGrab.grabtype = grabtype;
+    tempGrab = AllocGrab();
+    if (!tempGrab)
+        return FALSE;
+    tempGrab->next = NULL;
+    tempGrab->device = dev;
+    tempGrab->resource = client->clientAsMask;
+    tempGrab->window = win;
+    tempGrab->ownerEvents = (deliveryMask & OwnerGrabButtonMask) ? TRUE : FALSE;
+    tempGrab->eventMask = deliveryMask;
+    tempGrab->keyboardMode = GrabModeAsync;
+    tempGrab->pointerMode = GrabModeAsync;
+    tempGrab->confineTo = NullWindow;
+    tempGrab->cursor = NullCursor;
+    tempGrab->type = type;
+    tempGrab->grabtype = grabtype;
 
     /* get the XI and XI2 device mask */
     inputMasks = wOtherInputMasks(win);
-    tempGrab.deviceMask = (inputMasks) ? inputMasks->inputEvents[dev->id]: 0;
+    tempGrab->deviceMask = (inputMasks) ? inputMasks->inputEvents[dev->id]: 0;
 
     if (inputMasks)
-        memcpy(tempGrab.xi2mask, inputMasks->xi2mask,
-               sizeof(tempGrab.xi2mask));
+        memcpy(tempGrab->xi2mask, inputMasks->xi2mask,
+               sizeof(tempGrab->xi2mask));
 
-    (*dev->deviceGrab.ActivateGrab)(dev, &tempGrab,
+    (*dev->deviceGrab.ActivateGrab)(dev, tempGrab,
                                     currentTime, TRUE | ImplicitGrabMask);
+    FreeGrab(tempGrab);
     return TRUE;
 }
 
@@ -3657,7 +3660,7 @@ CheckPassiveGrabsOnWindow(
 {
     SpritePtr pSprite = device->spriteInfo->sprite;
     GrabPtr grab = wPassiveGrabs(pWin);
-    GrabRec tempGrab;
+    GrabPtr tempGrab;
     GrabInfoPtr grabinfo;
 #define CORE_MATCH      0x1
 #define XI_MATCH        0x2
@@ -3666,27 +3669,30 @@ CheckPassiveGrabsOnWindow(
 
     if (!grab)
 	return NULL;
+
+    tempGrab = AllocGrab();
+
     /* Fill out the grab details, but leave the type for later before
      * comparing */
     switch (event->any.type)
     {
         case ET_KeyPress:
         case ET_KeyRelease:
-            tempGrab.detail.exact = event->device_event.detail.key;
+            tempGrab->detail.exact = event->device_event.detail.key;
             break;
         case ET_ButtonPress:
         case ET_ButtonRelease:
-            tempGrab.detail.exact = event->device_event.detail.button;
+            tempGrab->detail.exact = event->device_event.detail.button;
             break;
         default:
-            tempGrab.detail.exact = 0;
+            tempGrab->detail.exact = 0;
             break;
     }
-    tempGrab.window = pWin;
-    tempGrab.device = device;
-    tempGrab.detail.pMask = NULL;
-    tempGrab.modifiersDetail.pMask = NULL;
-    tempGrab.next = NULL;
+    tempGrab->window = pWin;
+    tempGrab->device = device;
+    tempGrab->detail.pMask = NULL;
+    tempGrab->modifiersDetail.pMask = NULL;
+    tempGrab->next = NULL;
     for (; grab; grab = grab->next)
     {
 	DeviceIntPtr	gdev;
@@ -3711,29 +3717,29 @@ CheckPassiveGrabsOnWindow(
 
         if (gdev && gdev->key)
             xkbi= gdev->key->xkbInfo;
-	tempGrab.modifierDevice = grab->modifierDevice;
-        tempGrab.modifiersDetail.exact = xkbi ? xkbi->state.grab_mods : 0;
+        tempGrab->modifierDevice = grab->modifierDevice;
+        tempGrab->modifiersDetail.exact = xkbi ? xkbi->state.grab_mods : 0;
 
         /* Check for XI2 and XI grabs first */
-        tempGrab.type = GetXI2Type(event);
-        tempGrab.grabtype = GRABTYPE_XI2;
-        if (GrabMatchesSecond(&tempGrab, grab, FALSE))
+        tempGrab->type = GetXI2Type(event);
+        tempGrab->grabtype = GRABTYPE_XI2;
+        if (GrabMatchesSecond(tempGrab, grab, FALSE))
             match = XI2_MATCH;
 
         if (!match)
         {
-            tempGrab.grabtype = GRABTYPE_XI;
-            if ((tempGrab.type = GetXIType(event)) &&
-                (GrabMatchesSecond(&tempGrab, grab, FALSE)))
+            tempGrab->grabtype = GRABTYPE_XI;
+            if ((tempGrab->type = GetXIType(event)) &&
+                (GrabMatchesSecond(tempGrab, grab, FALSE)))
                 match = XI_MATCH;
         }
 
         /* Check for a core grab (ignore the device when comparing) */
         if (!match && checkCore)
         {
-            tempGrab.grabtype = GRABTYPE_CORE;
-            if ((tempGrab.type = GetCoreType(event)) &&
-                (GrabMatchesSecond(&tempGrab, grab, TRUE)))
+            tempGrab->grabtype = GRABTYPE_CORE;
+            if ((tempGrab->type = GetCoreType(event)) &&
+                (GrabMatchesSecond(tempGrab, grab, TRUE)))
                 match = CORE_MATCH;
         }
 
@@ -3761,7 +3767,7 @@ CheckPassiveGrabsOnWindow(
                Since XGrabDeviceButton requires to specify the
                modifierDevice explicitly, we don't override this choice.
                */
-            if (tempGrab.type < GenericEvent)
+            if (tempGrab->type < GenericEvent)
             {
                 grab->device = device;
                 grab->modifierDevice = GetMaster(device, MASTER_KEYBOARD);
@@ -3800,7 +3806,7 @@ CheckPassiveGrabsOnWindow(
         if (match & (XI_MATCH | CORE_MATCH))
         {
             event->device_event.corestate &= 0x1f00;
-            event->device_event.corestate |= tempGrab.modifiersDetail.exact &
+            event->device_event.corestate |= tempGrab->modifiersDetail.exact &
                                               (~0x1f00);
         }
 
@@ -3861,6 +3867,7 @@ CheckPassiveGrabsOnWindow(
         break;
     }
 
+    FreeGrab(tempGrab);
     return grab;
 #undef CORE_MATCH
 #undef XI_MATCH
@@ -5078,29 +5085,30 @@ GrabDevice(ClientPtr client, DeviceIntPtr dev,
 	*status = GrabFrozen;
     else
     {
-	GrabRec tempGrab;
+	GrabPtr tempGrab;
 
-        /* Otherwise segfaults happen on grabbed MPX devices */
-        memset(&tempGrab, 0, sizeof(GrabRec));
+	tempGrab = AllocGrab();
 
-        tempGrab.next = NULL;
-	tempGrab.window = pWin;
-	tempGrab.resource = client->clientAsMask;
-	tempGrab.ownerEvents = ownerEvents;
-	tempGrab.keyboardMode = keyboard_mode;
-	tempGrab.pointerMode = pointer_mode;
+	tempGrab->next = NULL;
+	tempGrab->window = pWin;
+	tempGrab->resource = client->clientAsMask;
+	tempGrab->ownerEvents = ownerEvents;
+	tempGrab->keyboardMode = keyboard_mode;
+	tempGrab->pointerMode = pointer_mode;
 	if (grabtype == GRABTYPE_CORE)
-	    tempGrab.eventMask = mask->core;
+	    tempGrab->eventMask = mask->core;
 	else if (grabtype == GRABTYPE_XI)
-	    tempGrab.eventMask = mask->xi;
+	    tempGrab->eventMask = mask->xi;
 	else
-	    memcpy(tempGrab.xi2mask, mask->xi2mask, sizeof(tempGrab.xi2mask));
-	tempGrab.device = dev;
-	tempGrab.cursor = cursor;
-	tempGrab.confineTo = confineTo;
-	tempGrab.grabtype = grabtype;
-	(*grabInfo->ActivateGrab)(dev, &tempGrab, time, FALSE);
+	    memcpy(tempGrab->xi2mask, mask->xi2mask, sizeof(tempGrab->xi2mask));
+	tempGrab->device = dev;
+	tempGrab->cursor = cursor;
+	tempGrab->confineTo = confineTo;
+	tempGrab->grabtype = grabtype;
+	(*grabInfo->ActivateGrab)(dev, tempGrab, time, FALSE);
 	*status = GrabSuccess;
+
+	FreeGrab(tempGrab);
     }
     return Success;
 }
@@ -5419,7 +5427,7 @@ ProcUngrabKey(ClientPtr client)
 {
     REQUEST(xUngrabKeyReq);
     WindowPtr pWin;
-    GrabRec tempGrab;
+    GrabPtr tempGrab;
     DeviceIntPtr keybd = PickKeyboard(client);
     int rc;
 
@@ -5441,21 +5449,27 @@ ProcUngrabKey(ClientPtr client)
 	client->errorValue = stuff->modifiers;
 	return BadValue;
     }
-    tempGrab.resource = client->clientAsMask;
-    tempGrab.device = keybd;
-    tempGrab.window = pWin;
-    tempGrab.modifiersDetail.exact = stuff->modifiers;
-    tempGrab.modifiersDetail.pMask = NULL;
-    tempGrab.modifierDevice = keybd;
-    tempGrab.type = KeyPress;
-    tempGrab.grabtype = GRABTYPE_CORE;
-    tempGrab.detail.exact = stuff->key;
-    tempGrab.detail.pMask = NULL;
-    tempGrab.next = NULL;
-
-    if (!DeletePassiveGrabFromList(&tempGrab))
-	return BadAlloc;
-    return Success;
+    tempGrab = AllocGrab();
+    if (!tempGrab)
+        return BadAlloc;
+    tempGrab->resource = client->clientAsMask;
+    tempGrab->device = keybd;
+    tempGrab->window = pWin;
+    tempGrab->modifiersDetail.exact = stuff->modifiers;
+    tempGrab->modifiersDetail.pMask = NULL;
+    tempGrab->modifierDevice = keybd;
+    tempGrab->type = KeyPress;
+    tempGrab->grabtype = GRABTYPE_CORE;
+    tempGrab->detail.exact = stuff->key;
+    tempGrab->detail.pMask = NULL;
+    tempGrab->next = NULL;
+
+    if (!DeletePassiveGrabFromList(tempGrab))
+        rc = BadAlloc;
+
+    FreeGrab(tempGrab);
+
+    return rc;
 }
 
 /**
@@ -5619,7 +5633,7 @@ ProcUngrabButton(ClientPtr client)
 {
     REQUEST(xUngrabButtonReq);
     WindowPtr pWin;
-    GrabRec tempGrab;
+    GrabPtr tempGrab;
     int rc;
     DeviceIntPtr ptr;
 
@@ -5636,21 +5650,26 @@ ProcUngrabButton(ClientPtr client)
 
     ptr = PickPointer(client);
 
-    tempGrab.resource = client->clientAsMask;
-    tempGrab.device = ptr;
-    tempGrab.window = pWin;
-    tempGrab.modifiersDetail.exact = stuff->modifiers;
-    tempGrab.modifiersDetail.pMask = NULL;
-    tempGrab.modifierDevice = GetMaster(ptr, MASTER_KEYBOARD);
-    tempGrab.type = ButtonPress;
-    tempGrab.detail.exact = stuff->button;
-    tempGrab.grabtype = GRABTYPE_CORE;
-    tempGrab.detail.pMask = NULL;
-    tempGrab.next = NULL;
-
-    if (!DeletePassiveGrabFromList(&tempGrab))
-	return BadAlloc;
-    return Success;
+    tempGrab = AllocGrab();
+    if (!tempGrab)
+        return BadAlloc;
+    tempGrab->resource = client->clientAsMask;
+    tempGrab->device = ptr;
+    tempGrab->window = pWin;
+    tempGrab->modifiersDetail.exact = stuff->modifiers;
+    tempGrab->modifiersDetail.pMask = NULL;
+    tempGrab->modifierDevice = GetMaster(ptr, MASTER_KEYBOARD);
+    tempGrab->type = ButtonPress;
+    tempGrab->detail.exact = stuff->button;
+    tempGrab->grabtype = GRABTYPE_CORE;
+    tempGrab->detail.pMask = NULL;
+    tempGrab->next = NULL;
+
+    if (!DeletePassiveGrabFromList(tempGrab))
+        rc = BadAlloc;
+
+    FreeGrab(tempGrab);
+    return rc;
 }
 
 /**
commit b0e9e2e32616d09c30a02b9d0ae9db0b13e150d1
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Nov 4 10:44:31 2011 +1000

    dix: add CopyGrab() function
    
    Not really needed at this point, but will be once touch support is added.
    Since grabs are now expected to be allocated/freed with AllocGrab and
    FreeGrab, CopyGrab must increase the refcount and duplicate the modifier
    masks. Until the callers are switched to use FreeGrab, this introduces
    memleaks.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/dix/events.c b/dix/events.c
index 3c21a96..c1c296d 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -1509,7 +1509,7 @@ ActivatePointerGrab(DeviceIntPtr mouse, GrabPtr grab,
 	grabinfo->grabTime = time;
     if (grab->cursor)
 	grab->cursor->refcnt++;
-    grabinfo->activeGrab = *grab;
+    CopyGrab(&grabinfo->activeGrab, grab);
     grabinfo->grab = &grabinfo->activeGrab;
     grabinfo->fromPassiveGrab = isPassive;
     grabinfo->implicitGrab = autoGrab & ImplicitGrabMask;
@@ -1586,7 +1586,7 @@ ActivateKeyboardGrab(DeviceIntPtr keybd, GrabPtr grab, TimeStamp time, Bool pass
 	grabinfo->grabTime = syncEvents.time;
     else
 	grabinfo->grabTime = time;
-    grabinfo->activeGrab = *grab;
+    CopyGrab(&grabinfo->activeGrab, grab);
     grabinfo->grab = &grabinfo->activeGrab;
     grabinfo->fromPassiveGrab = passive;
     grabinfo->implicitGrab = passive & ImplicitGrabMask;
diff --git a/dix/grabs.c b/dix/grabs.c
index 3b07186..a1d56c5 100644
--- a/dix/grabs.c
+++ b/dix/grabs.c
@@ -246,6 +246,40 @@ FreeGrab(GrabPtr pGrab)
     free(pGrab);
 }
 
+Bool
+CopyGrab(GrabPtr dst, const GrabPtr src)
+{
+    Mask *mdetails_mask = NULL;
+    Mask *details_mask = NULL;
+
+    if (src->cursor)
+        src->cursor->refcnt++;
+
+    if (src->modifiersDetail.pMask) {
+        int len = MasksPerDetailMask * sizeof(Mask);
+        mdetails_mask = malloc(len);
+        if (!mdetails_mask)
+            return FALSE;
+        memcpy(mdetails_mask, src->modifiersDetail.pMask, len);
+    }
+
+    if (src->detail.pMask) {
+        int len = MasksPerDetailMask * sizeof(Mask);
+        details_mask = malloc(len);
+        if (!details_mask) {
+            free(mdetails_mask);
+            return FALSE;
+        }
+        memcpy(details_mask, src->detail.pMask, len);
+    }
+
+    *dst = *src;
+    dst->modifiersDetail.pMask = mdetails_mask;
+    dst->detail.pMask = details_mask;
+
+    return TRUE;
+}
+
 int
 DeletePassiveGrab(pointer value, XID id)
 {
diff --git a/include/dixgrabs.h b/include/dixgrabs.h
index 2ed8a54..65ff45d 100644
--- a/include/dixgrabs.h
+++ b/include/dixgrabs.h
@@ -33,6 +33,7 @@ extern void UngrabAllDevices(Bool kill_client);
 
 extern GrabPtr AllocGrab(void);
 extern void FreeGrab(GrabPtr grab);
+extern Bool CopyGrab(GrabPtr dst, const GrabPtr src);
 
 extern GrabPtr CreateGrab(
 	int /* client */,
commit 347f377f3b3f8c9d230d6309ec8ae92aa86d78b7
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Thu Nov 3 16:12:09 2011 +1000

    dix: add AllocGrab helper function
    
    Not needed since the GrabRec is a self-contained struct but will be needed
    for the xi2 input mask rework.
    FreeGrab already exists, make it available to other callers.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/dix/grabs.c b/dix/grabs.c
index c28356d..3b07186 100644
--- a/dix/grabs.c
+++ b/dix/grabs.c
@@ -181,6 +181,14 @@ UngrabAllDevices(Bool kill_client)
 }
 
 GrabPtr
+AllocGrab(void)
+{
+    GrabPtr grab = calloc(1, sizeof(GrabRec));
+
+    return grab;
+}
+
+GrabPtr
 CreateGrab(
     int client,
     DeviceIntPtr device,
@@ -196,7 +204,7 @@ CreateGrab(
 {
     GrabPtr grab;
 
-    grab = calloc(1, sizeof(GrabRec));
+    grab = AllocGrab();
     if (!grab)
 	return (GrabPtr)NULL;
     grab->resource = FakeClientID(client);
@@ -226,7 +234,7 @@ CreateGrab(
 
 }
 
-static void
+void
 FreeGrab(GrabPtr pGrab)
 {
     free(pGrab->modifiersDetail.pMask);
diff --git a/include/dixgrabs.h b/include/dixgrabs.h
index 229c8bb..2ed8a54 100644
--- a/include/dixgrabs.h
+++ b/include/dixgrabs.h
@@ -31,6 +31,9 @@ struct _GrabParameters;
 extern void PrintDeviceGrabInfo(DeviceIntPtr dev);
 extern void UngrabAllDevices(Bool kill_client);
 
+extern GrabPtr AllocGrab(void);
+extern void FreeGrab(GrabPtr grab);
+
 extern GrabPtr CreateGrab(
 	int /* client */,
 	DeviceIntPtr /* device */,
commit 7af23259d88f4c28ed21140f82cc03b3724c06bb
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Mon Nov 21 11:41:12 2011 -0800

    dix: switch the syncEvent queue to a struct list
    
    No effective functionality change, just cleanup to make this code slightly
    more sane.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/dix/events.c b/dix/events.c
index 4847db0..3c21a96 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -1132,12 +1132,14 @@ NoticeEventTime(InternalEvent *ev)
 void
 EnqueueEvent(InternalEvent *ev, DeviceIntPtr device)
 {
-    QdEventPtr	tail = *syncEvents.pendtail;
+    QdEventPtr	tail;
     QdEventPtr	qe;
     SpritePtr	pSprite = device->spriteInfo->sprite;
     int		eventlen;
     DeviceEvent *event = &ev->device_event;
 
+    tail = list_last_entry(&syncEvents.pending, QdEventRec, next);
+
     NoticeTime((InternalEvent*)event);
 
     /* Fix for key repeating bug. */
@@ -1196,15 +1198,13 @@ EnqueueEvent(InternalEvent *ev, DeviceIntPtr device)
     qe = malloc(sizeof(QdEventRec) + eventlen);
     if (!qe)
 	return;
-    qe->next = (QdEventPtr)NULL;
+    list_init(&qe->next);
     qe->device = device;
     qe->pScreen = pSprite->hotPhys.pScreen;
     qe->months = currentTime.months;
     qe->event = (InternalEvent *)(qe + 1);
     memcpy(qe->event, event, eventlen);
-    if (tail)
-	syncEvents.pendtail = &tail->next;
-    *syncEvents.pendtail = qe;
+    list_append(&qe->next, &syncEvents.pending);
 }
 
 /**
@@ -1216,22 +1216,20 @@ EnqueueEvent(InternalEvent *ev, DeviceIntPtr device)
  * If there is none, we're done. If there is at least one device that is not
  * frozen, then re-run from the beginning of the event queue.
  */
-static void
+void
 PlayReleasedEvents(void)
 {
-    QdEventPtr *prev, qe;
+    QdEventPtr tmp;
+    QdEventPtr qe;
     DeviceIntPtr dev;
     DeviceIntPtr pDev;
 
-    prev = &syncEvents.pending;
-    while ( (qe = *prev) )
-    {
+restart:
+    list_for_each_entry_safe(qe, tmp, &syncEvents.pending, next) {
 	if (!qe->device->deviceGrab.sync.frozen)
 	{
-	    *prev = qe->next;
-            pDev = qe->device;
-	    if (*syncEvents.pendtail == *prev)
-		syncEvents.pendtail = prev;
+	    list_del(&qe->next);
+	    pDev = qe->device;
 	    if (qe->event->any.type == ET_Motion)
 		CheckVirtualMotion(pDev, qe, NullWindow);
 	    syncEvents.time.months = qe->months;
@@ -1268,12 +1266,11 @@ PlayReleasedEvents(void)
 		;
 	    if (!dev)
 		break;
+
 	    /* Playing the event may have unfrozen another device. */
 	    /* So to play it safe, restart at the head of the queue */
-	    prev = &syncEvents.pending;
+	    goto restart;
 	}
-	else
-	    prev = &qe->next;
     }
 }
 
@@ -1314,7 +1311,8 @@ ComputeFreezes(void)
     for (dev = inputInfo.devices; dev; dev = dev->next)
 	FreezeThaw(dev, dev->deviceGrab.sync.other ||
                 (dev->deviceGrab.sync.state >= FROZEN));
-    if (syncEvents.playingEvents || (!replayDev && !syncEvents.pending))
+    if (syncEvents.playingEvents ||
+        (!replayDev && list_is_empty(&syncEvents.pending)))
 	return;
     syncEvents.playingEvents = TRUE;
     if (replayDev)
@@ -5258,6 +5256,7 @@ void
 InitEvents(void)
 {
     int i;
+    QdEventPtr qe, tmp;
 
     inputInfo.numDevices = 0;
     inputInfo.devices = (DeviceIntPtr)NULL;
@@ -5271,13 +5270,10 @@ InitEvents(void)
 
     syncEvents.replayDev = (DeviceIntPtr)NULL;
     syncEvents.replayWin = NullWindow;
-    while (syncEvents.pending)
-    {
-	QdEventPtr next = syncEvents.pending->next;
-	free(syncEvents.pending);
-	syncEvents.pending = next;
-    }
-    syncEvents.pendtail = &syncEvents.pending;
+    if (syncEvents.pending.next)
+        list_for_each_entry_safe(qe, tmp, &syncEvents.pending, next)
+            free(qe);
+    list_init(&syncEvents.pending);
     syncEvents.playingEvents = FALSE;
     syncEvents.time.months = 0;
     syncEvents.time.milliseconds = 0;	/* hardly matters */
diff --git a/include/dix.h b/include/dix.h
index 34661f3..8e35d2c 100644
--- a/include/dix.h
+++ b/include/dix.h
@@ -339,6 +339,7 @@ extern _X_EXPORT void NoticeEventTime(InternalEvent *ev);
 extern void EnqueueEvent(
     InternalEvent * /* ev */,
     DeviceIntPtr  /* device */);
+extern void PlayReleasedEvents(void);
 
 extern void ActivatePointerGrab(
     DeviceIntPtr /* mouse */,
diff --git a/include/inputstr.h b/include/inputstr.h
index f482a22..0568e0c 100644
--- a/include/inputstr.h
+++ b/include/inputstr.h
@@ -575,7 +575,7 @@ extern _X_EXPORT InputInfo inputInfo;
 /* for keeping the events for devices grabbed synchronously */
 typedef struct _QdEvent *QdEventPtr;
 typedef struct _QdEvent {
-    QdEventPtr		next;
+    struct list		next;
     DeviceIntPtr	device;
     ScreenPtr		pScreen;	/* what screen the pointer was on */
     unsigned long	months;		/* milliseconds is in the event */
@@ -591,8 +591,8 @@ typedef struct _QdEvent {
  * replayed and processed as if they would come from the device directly.
  */
 typedef struct _EventSyncInfo {
-    QdEventPtr          pending, /**<  list of queued events */
-                        *pendtail; /**< last event in list */
+    struct list         pending;
+
     /** The device to replay events for. Only set in AllowEvents(), in which
      * case it is set to the device specified in the request. */
     DeviceIntPtr        replayDev;      /* kludgy rock to put flag for */
diff --git a/test/input.c b/test/input.c
index 5b4c8c1..c44e5f6 100644
--- a/test/input.c
+++ b/test/input.c
@@ -1674,8 +1674,75 @@ mieq_test(void) {
     mieqFini();
 }
 
+/* Simple check that we're replaying events in-order */
+static void
+process_input_proc(InternalEvent *ev, DeviceIntPtr device)
+{
+    static int last_evtype = -1;
+
+    if (ev->any.header == 0xac)
+        last_evtype = -1;
+
+    assert(ev->any.type == ++last_evtype);
+}
+
+static void
+dix_enqueue_events(void) {
+#define NEVENTS 5
+    DeviceIntRec dev;
+    InternalEvent ev[NEVENTS];
+    SpriteInfoRec spriteInfo;
+    SpriteRec sprite;
+    QdEventPtr qe;
+    int i;
+
+    memset(&dev, 0, sizeof(dev));
+    dev.public.processInputProc = process_input_proc;
+
+    memset(&spriteInfo, 0, sizeof(spriteInfo));
+    memset(&sprite, 0, sizeof(sprite));
+    dev.spriteInfo = &spriteInfo;
+    spriteInfo.sprite = &sprite;
+
+    InitEvents();
+    assert(list_is_empty(&syncEvents.pending));
+
+    /* this way PlayReleasedEvents really runs through all events in the
+     * queue */
+    inputInfo.devices = &dev;
+
+    /* to reset process_input_proc */
+    ev[0].any.header = 0xac;
+
+    for (i = 0; i < NEVENTS; i++)
+    {
+        ev[i].any.length = sizeof(*ev);
+        ev[i].any.type = i;
+        EnqueueEvent(&ev[i], &dev);
+        assert(!list_is_empty(&syncEvents.pending));
+        qe = list_last_entry(&syncEvents.pending, QdEventRec, next);
+        assert(memcmp(qe->event, &ev[i], ev[i].any.length) == 0);
+        qe = list_first_entry(&syncEvents.pending, QdEventRec, next);
+        assert(memcmp(qe->event, &ev[0], ev[i].any.length) == 0);
+    }
+
+    /* calls process_input_proc */
+    dev.deviceGrab.sync.frozen = 1;
+    PlayReleasedEvents();
+    assert(!list_is_empty(&syncEvents.pending));
+
+
+    dev.deviceGrab.sync.frozen = 0;
+    PlayReleasedEvents();
+    assert(list_is_empty(&syncEvents.pending));
+
+    inputInfo.devices = NULL;
+}
+
+
 int main(int argc, char** argv)
 {
+    dix_enqueue_events();
     dix_double_fp_conversion();
     dix_input_valuator_masks();
     dix_input_attributes();
commit fac464e310b82fadcedf790798d1016c4805640b
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Dec 2 08:52:53 2011 +1000

    include: rename "foos" to "list_of_foos" in the struct list documentation
    
    Makes things a little easier to read.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/include/list.h b/include/list.h
index 77cc5f9..6ec2bac 100644
--- a/include/list.h
+++ b/include/list.h
@@ -36,16 +36,16 @@
  *
  *     struct bar {
  *          ...
- *          struct foo *foos; -----> struct foo {}, struct foo {}, struct foo{}
+ *          struct foo *list_of_foos; -----> struct foo {}, struct foo {}, struct foo{}
  *          ...
  *     }
  *
- * We need one list head in bar and a list element in all foos (both are of
+ * We need one list head in bar and a list element in all list_of_foos (both are of
  * data type 'struct list').
  *
  *     struct bar {
  *          ...
- *          struct list foos;
+ *          struct list list_of_foos;
  *          ...
  *     }
  *
@@ -59,27 +59,27 @@
  *
  *     struct bar bar;
  *     ...
- *     list_init(&bar.foos);
+ *     list_init(&bar.list_of_foos);
  *
  * Then we create the first element and add it to this list:
  *
  *     struct foo *foo = malloc(...);
  *     ....
- *     list_add(&foo->entry, &bar.foos);
+ *     list_add(&foo->entry, &bar.list_of_foos);
  *
  * Repeat the above for each element you want to add to the list. Deleting
  * works with the element itself.
  *      list_del(&foo->entry);
  *      free(foo);
  *
- * Note: calling list_del(&bar.foos) will set bar.foos to an empty
+ * Note: calling list_del(&bar.list_of_foos) will set bar.list_of_foos to an empty
  * list again.
  *
  * Looping through the list requires a 'struct foo' as iterator and the
  * name of the field the subnodes use.
  *
  * struct foo *iterator;
- * list_for_each_entry(iterator, &bar.foos, entry) {
+ * list_for_each_entry(iterator, &bar.list_of_foos, entry) {
  *      if (iterator->something == ...)
  *             ...
  * }
@@ -88,7 +88,7 @@
  * loop. You need to run the safe for-each loop instead:
  *
  * struct foo *iterator, *next;
- * list_for_each_entry_safe(iterator, next, &bar.foos, entry) {
+ * list_for_each_entry_safe(iterator, next, &bar.list_of_foos, entry) {
  *      if (...)
  *              list_del(&iterator->entry);
  * }
@@ -113,7 +113,7 @@ struct list {
  * Initialize the list as an empty list.
  *
  * Example:
- * list_init(&bar->foos);
+ * list_init(&bar->list_of_foos);
  *
  * @param The list to initialized.
  */
@@ -144,7 +144,7 @@ __list_add(struct list *entry,
  *
  * Example:
  * struct foo *newfoo = malloc(...);
- * list_add(&newfoo->entry, &bar->foos);
+ * list_add(&newfoo->entry, &bar->list_of_foos);
  *
  * @param entry The new element to prepend to the list.
  * @param head The existing list.
@@ -165,7 +165,7 @@ list_add(struct list *entry, struct list *head)
  *
  * Example:
  * struct foo *newfoo = malloc(...);
- * list_append(&newfoo->entry, &bar->foos);
+ * list_append(&newfoo->entry, &bar->list_of_foos);
  *
  * @param entry The new element to prepend to the list.
  * @param head The existing list.
@@ -209,7 +209,7 @@ list_del(struct list *entry)
  * Check if the list is empty.
  *
  * Example:
- * list_is_empty(&bar->foos);
+ * list_is_empty(&bar->list_of_foos);
  *
  * @return True if the list contains one or more elements or False otherwise.
  */
@@ -248,7 +248,7 @@ list_is_empty(struct list *head)
  *
  * Example:
  * struct foo *first;
- * first = list_first_entry(&bar->foos, struct foo, foos);
+ * first = list_first_entry(&bar->list_of_foos, struct foo, list_of_foos);
  *
  * @param ptr The list head
  * @param type Data type of the list element to retrieve
@@ -263,7 +263,7 @@ list_is_empty(struct list *head)
  *
  * Example:
  * struct foo *first;
- * first = list_last_entry(&bar->foos, struct foo, foos);
+ * first = list_last_entry(&bar->list_of_foos, struct foo, list_of_foos);
  *
  * @param ptr The list head
  * @param type Data type of the list element to retrieve
@@ -281,7 +281,7 @@ list_is_empty(struct list *head)
  *
  * Example:
  * struct foo *iterator;
- * list_for_each_entry(iterator, &bar->foos, entry) {
+ * list_for_each_entry(iterator, &bar->list_of_foos, entry) {
  *      [modify iterator]
  * }
  *
commit 7dfe8c32a96d3f96c8aaeb2802f5b122e381a1e4
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Dec 2 08:51:24 2011 +1000

    include: update struct list documentation to use one set of structs only
    
    The example at the top of the file used a struct bar and a list of struct
    foos. Use those two throughout instead of a different struct foo for the
    examples and for the API documentation.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/include/list.h b/include/list.h
index 677fd92..77cc5f9 100644
--- a/include/list.h
+++ b/include/list.h
@@ -97,14 +97,8 @@
 
 /**
  * The linkage struct for list nodes. This struct must be part of your
- * to-be-linked struct.
- *
- * Example:
- * struct foo {
- *      int a;
- *      void *b;
- *      struct list *mylist;
- * }
+ * to-be-linked struct. struct list is required for both the head of the
+ * list and for each list node.
  *
  * Position and name of the struct list field is irrelevant.
  * There are no requirements that elements of a list are of the same type.
@@ -119,7 +113,7 @@ struct list {
  * Initialize the list as an empty list.
  *
  * Example:
- * list_init(&foo->mylist);
+ * list_init(&bar->foos);
  *
  * @param The list to initialized.
  */
@@ -150,7 +144,7 @@ __list_add(struct list *entry,
  *
  * Example:
  * struct foo *newfoo = malloc(...);
- * list_add(&newfoo->mylist, &foo->mylist);
+ * list_add(&newfoo->entry, &bar->foos);
  *
  * @param entry The new element to prepend to the list.
  * @param head The existing list.
@@ -171,7 +165,7 @@ list_add(struct list *entry, struct list *head)
  *
  * Example:
  * struct foo *newfoo = malloc(...);
- * list_append(&newfoo->mylist, &foo->mylist);
+ * list_append(&newfoo->entry, &bar->foos);
  *
  * @param entry The new element to prepend to the list.
  * @param head The existing list.
@@ -200,7 +194,7 @@ __list_del(struct list *prev, struct list *next)
  * the list but rather reset the list as empty list.
  *
  * Example:
- * list_del(&newfoo->mylist);
+ * list_del(&foo->entry);
  *
  * @param entry The element to remove.
  */
@@ -215,7 +209,7 @@ list_del(struct list *entry)
  * Check if the list is empty.
  *
  * Example:
- * list_is_empty(&foo->mylist);
+ * list_is_empty(&bar->foos);
  *
  * @return True if the list contains one or more elements or False otherwise.
  */
@@ -230,7 +224,7 @@ list_is_empty(struct list *head)
  *
  * Example:
  * struct foo* f;
- * f = container_of(&foo->mylist, struct foo, mylist);
+ * f = container_of(&foo->entry, struct foo, entry);
  * assert(f == foo);
  *
  * @param ptr Pointer to the struct list.
@@ -254,7 +248,7 @@ list_is_empty(struct list *head)
  *
  * Example:
  * struct foo *first;
- * first = list_first_entry(&foo->mylist, struct foo, mylist);
+ * first = list_first_entry(&bar->foos, struct foo, foos);
  *
  * @param ptr The list head
  * @param type Data type of the list element to retrieve
@@ -269,7 +263,7 @@ list_is_empty(struct list *head)
  *
  * Example:
  * struct foo *first;
- * first = list_last_entry(&foo->mylist, struct foo, mylist);
+ * first = list_last_entry(&bar->foos, struct foo, foos);
  *
  * @param ptr The list head
  * @param type Data type of the list element to retrieve
@@ -287,7 +281,7 @@ list_is_empty(struct list *head)
  *
  * Example:
  * struct foo *iterator;
- * list_for_each_entry(iterator, &foo->mylist, mylist) {
+ * list_for_each_entry(iterator, &bar->foos, entry) {
  *      [modify iterator]
  * }
  *
commit 18539c89eca8f6e272ead2b631365da530065ae7
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Dec 2 08:51:04 2011 +1000

    include: a new list element does not need initialization, state so
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/include/list.h b/include/list.h
index 733a579..677fd92 100644
--- a/include/list.h
+++ b/include/list.h
@@ -141,7 +141,8 @@ __list_add(struct list *entry,
 }
 
 /**
- * Insert a new element after the given list head.
+ * Insert a new element after the given list head. The new element does not
+ * need to be initialised as empty list.
  * The list changes from:
  *      head → some element → ...
  * to
commit 58dc73314b6508121ca094bbcf00612fe19ed69f
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Fri Dec 2 08:43:45 2011 +1000

    include: point to the tests in list.c as examples
    
    Even with the documentation, the list.c tests are the best examples.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/include/list.h b/include/list.h
index 73ff0be..733a579 100644
--- a/include/list.h
+++ b/include/list.h
@@ -28,6 +28,7 @@
 
 /**
  * @file Classic doubly-link circular list implementation.
+ * For real usage examples of the linked list, see the file test/list.c
  *
  * Example:
  * We need to keep a list of struct foo in the parent struct bar, i.e. what
commit c8e451a8e719ba432bcfa8976774c07307087809
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Thu Dec 1 14:12:11 2011 +1000

    include: add list_last_entry to get the tail of a list
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/include/list.h b/include/list.h
index f8659f0..73ff0be 100644
--- a/include/list.h
+++ b/include/list.h
@@ -262,6 +262,21 @@ list_is_empty(struct list *head)
 #define list_first_entry(ptr, type, member) \
     list_entry((ptr)->next, type, member)
 
+/**
+ * Retrieve the last list entry for the given listpointer.
+ *
+ * Example:
+ * struct foo *first;
+ * first = list_last_entry(&foo->mylist, struct foo, mylist);
+ *
+ * @param ptr The list head
+ * @param type Data type of the list element to retrieve
+ * @param member Member name of the struct list field in the list element.
+ * @return A pointer to the last list element.
+ */
+#define list_last_entry(ptr, type, member) \
+    list_entry((ptr)->prev, type, member)
+
 #define __container_of(ptr, sample, member)				\
     (void *)((char *)(ptr)						\
 	     - ((char *)&(sample)->member - (char *)(sample)))
diff --git a/test/list.c b/test/list.c
index b96182e..ffb85ef 100644
--- a/test/list.c
+++ b/test/list.c
@@ -103,14 +103,20 @@ test_list_append(void)
 
     c = list_first_entry(&parent.children, struct child, node);
     assert(memcmp(c, &child[0], sizeof(struct child)) == 0);
+    c = list_last_entry(&parent.children, struct child, node);
+    assert(memcmp(c, &child[0], sizeof(struct child)) == 0);
 
     list_append(&child[1].node, &parent.children);
     c = list_first_entry(&parent.children, struct child, node);
     assert(memcmp(c, &child[0], sizeof(struct child)) == 0);
+    c = list_last_entry(&parent.children, struct child, node);
+    assert(memcmp(c, &child[1], sizeof(struct child)) == 0);
 
     list_append(&child[2].node, &parent.children);
     c = list_first_entry(&parent.children, struct child, node);
     assert(memcmp(c, &child[0], sizeof(struct child)) == 0);
+    c = list_last_entry(&parent.children, struct child, node);
+    assert(memcmp(c, &child[2], sizeof(struct child)) == 0);
 
     i = 0;
     list_for_each_entry(c, &parent.children, node) {
commit 6acebf9e1298939593b942ec91ae9ec9e74faa19
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Thu Dec 1 13:35:50 2011 +1000

    include: add list_append()
    
    The existing list_add() prepends to the list, but in some cases we need the
    list ordered in the way we append the elements.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Chase Douglas <chase.douglas at canonical.com>

diff --git a/include/list.h b/include/list.h
index 4706e17..f8659f0 100644
--- a/include/list.h
+++ b/include/list.h
@@ -159,6 +159,28 @@ list_add(struct list *entry, struct list *head)
     __list_add(entry, head, head->next);
 }
 
+/**
+ * Append a new element to the end of the list given with this list head.
+ *
+ * The list changes from:
+ *      head → some element → ... → lastelement
+ * to
+ *      head → some element → ... → lastelement → new element
+ *
+ * Example:
+ * struct foo *newfoo = malloc(...);
+ * list_append(&newfoo->mylist, &foo->mylist);
+ *
+ * @param entry The new element to prepend to the list.
+ * @param head The existing list.
+ */
+static inline void
+list_append(struct list *entry, struct list *head)
+{
+    __list_add(entry, head->prev, head);
+}
+
+
 static inline void
 __list_del(struct list *prev, struct list *next)
 {
diff --git a/test/list.c b/test/list.c
index f7d7bff..b96182e 100644
--- a/test/list.c
+++ b/test/list.c
@@ -89,6 +89,36 @@ test_list_add(void)
 };
 
 static void
+test_list_append(void)
+{
+    struct parent parent = {0};
+    struct child child[3];
+    struct child *c;
+    int i;
+
+    list_init(&parent.children);
+
+    list_append(&child[0].node, &parent.children);
+    assert(!list_is_empty(&parent.children));
+
+    c = list_first_entry(&parent.children, struct child, node);
+    assert(memcmp(c, &child[0], sizeof(struct child)) == 0);
+
+    list_append(&child[1].node, &parent.children);
+    c = list_first_entry(&parent.children, struct child, node);
+    assert(memcmp(c, &child[0], sizeof(struct child)) == 0);
+
+    list_append(&child[2].node, &parent.children);
+    c = list_first_entry(&parent.children, struct child, node);
+    assert(memcmp(c, &child[0], sizeof(struct child)) == 0);
+
+    i = 0;
+    list_for_each_entry(c, &parent.children, node) {
+        assert(memcmp(c, &child[i++], sizeof(struct child)) == 0);
+    }
+};
+
+static void
 test_list_del(void)
 {
     struct parent parent = {0};
@@ -325,6 +355,7 @@ int main(int argc, char** argv)
 {
     test_list_init();
     test_list_add();
+    test_list_append();
     test_list_del();
     test_list_for_each();
 


More information about the xorg-commit mailing list