[PATCH] Add generic event cookie handling to libX11. (v3)

Julien Cristau jcristau at debian.org
Wed Jul 8 07:10:59 PDT 2009


Hi Peter,

a couple questions below.

On Tue, Jul  7, 2009 at 17:04:15 +1000, Peter Hutterer wrote:

> Generic events require more bytes than Xlib provides in the standard XEvent.
> Memory allocated by the extension and stored as pointers inside the event is
> prone to leak by simple 'while (1) { XNextEvent(...); }' loops.
> 
> This patch adds cookie handling for generic events. Extensions may register
> a cookie handler in addition to the normal event vectors. If an extension
> has registered a cookie handler, _all_ generic events for this extensions
> must be handled through cookies. Otherwise, the default event handler is
> used.
> 
> The cookie handler must return an XGenericEventCookie with a pointer to the
> data.The rest of the event (type, serialNumber, etc.) are to be filled as
> normal. When a client retrieves such a cookie event, the data is stored in
> an internal queue (the 'cookiejar'). This data is freed on the next call to
> XNextEvent().
> 
> New extension interfaces:
>     XESetWireToEventCookie(display, extension_number, cookie_handler)
> 
> Where cookie_handler must set cookie->data. The data pointer is of arbitray
> size and type but must be a single memory block. This memory block
> represents the actual extension's event.
> 
> New client interfaces:
>     XGetEventData(display, *cookie);
>     XFreeEventData(display, *cookie);
> 
> If the client needs the actual event data, it must call XGetEventData() with
> the cookie. This returns the data pointer (and removes it from the cookie
> jar) and the client is then responsible for freeing the event with
> XFreeEventData(). It is safe to call either function with a non-cookie
> event. Events unclaimed or not handled by the XGetEventData() are cleaned up
> automatically.
> 
> Example client code:
>     XEvent event;
>     XGenericEventCookie *cookie = &ev;
> 
>     XNextEvent(display, &event);
>     if (XGetEventData(display, cookie)) {
>         XIEvent *xievent = cookie->data;
>         ...
>     } else if (cookie->type == GenericEvent) {
>         /* handle generic event */
>     } else {
>         /* handle extension/core event */
>     }
>     XFreeEventData(display, cookie);
> 
> Cookies are not multi-threading safe. Clients that use XGetEventData() must
> lock between XNextEvent and XGetEventData to avoid other threads freeing
> cookies.
> 
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> ---
>  include/X11/Xlib.h    |   21 +++++
>  include/X11/Xlibint.h |   72 ++++++++++++++++
>  man/Makefile.am       |    9 ++
>  man/XGetEventData.man |  223 +++++++++++++++++++++++++++++++++++++++++++++++++
>  src/ChkIfEv.c         |    1 +
>  src/ChkMaskEv.c       |    4 +
>  src/ChkTypEv.c        |    4 +
>  src/ChkTypWEv.c       |    4 +
>  src/ChkWinEv.c        |    4 +
>  src/FreeEventData.c   |   42 +++++++++
>  src/GetEventData.c    |   42 +++++++++
>  src/IfEvent.c         |    1 +
>  src/InitExt.c         |   43 ++++++++++
>  src/Makefile.am       |    2 +
>  src/MaskEvent.c       |    4 +
>  src/NextEvent.c       |    4 +
>  src/OpenDis.c         |   11 +++
>  src/PeekEvent.c       |    5 +
>  src/PeekIfEv.c        |    7 ++
>  src/PutBEvent.c       |   19 ++++-
>  src/WinEvent.c        |    4 +
>  src/XlibInt.c         |  212 ++++++++++++++++++++++++++++++++++++++++++++++-
>  src/utlist.h          |  116 +++++++++++++++++++++++++
>  23 files changed, 851 insertions(+), 3 deletions(-)
>  create mode 100644 man/XGetEventData.man
>  create mode 100644 src/FreeEventData.c
>  create mode 100644 src/GetEventData.c
>  create mode 100644 src/utlist.h
> 
> diff --git a/include/X11/Xlib.h b/include/X11/Xlib.h
> index 682988c..2e735ee 100644
> --- a/include/X11/Xlib.h
> +++ b/include/X11/Xlib.h
> @@ -966,6 +966,17 @@ typedef struct
>      int            evtype;       /* actual event type. */
>      } XGenericEvent;
>  
> +typedef struct {
> +    int            type;         /* of event. Always GenericEvent */
> +    unsigned long  serial;       /* # of last request processed */
> +    Bool           send_event;   /* true if from SendEvent request */
> +    Display        *display;     /* Display the event was read from */
> +    int            extension;    /* major opcode of extension that caused the event */
> +    int            evtype;       /* actual event type. */
> +    unsigned int   cookie;
> +    void           *data;
> +} XGenericEventCookie;
> +

Any reason this (and XGenericEvent) isn't added to the XEvent union?
That would avoid a bunch of casts later on.

>  /*
>   * this union is defined so Xlib can always use the same sized
>   * event structure internally, to avoid memory fragmentation.
> @@ -3998,6 +4009,16 @@ extern int _Xwctomb(
>      wchar_t			/* wc */
>  );
>  
> +extern Bool XGetEventData(
> +    Display*			/* dpy */,
> +    XGenericEventCookie*	/* cookie*/
> +);
> +
> +extern void XFreeEventData(
> +    Display*			/* dpy */,
> +    XGenericEventCookie*	/* cookie*/
> +);
> +
>  _XFUNCPROTOEND
>  
>  #endif /* _XLIB_H_ */
> diff --git a/include/X11/Xlibint.h b/include/X11/Xlibint.h
> index 4f3755f..2acfc76 100644
> --- a/include/X11/Xlibint.h
> +++ b/include/X11/Xlibint.h
> @@ -185,6 +185,20 @@ struct _XDisplay
>  	struct _XkbInfoRec *xkb_info; /* XKB info */
>  	struct _XtransConnInfo *trans_conn; /* transport connection object */
>  	struct _X11XCBPrivate *xcb; /* XCB glue private data */
> +
> +	/* Generic event cookie handling */
> +	unsigned int next_cookie; /* next event cookie */

what happens when next_cookie wraps?
(... thinking about it, i guess if you keep 2^32 events around you have
other issues)

> +	/* vector for wire to generic event, index is (extension - 128) */
> +	Bool (*generic_event_vec[128])(
> +		Display *	/* dpy */,
> +		XGenericEventCookie *	/* Xlib event */,
> +		xEvent *	/* wire event */);
> +	/* vector for event copy, index is (extension - 128) */
> +	Bool (*generic_event_copy_vec[128])(
> +		Display *	/* dpy */,
> +		XGenericEventCookie *	/* in */,
> +		XGenericEventCookie *   /* out*/);
> +	void *cookiejar;  /* cookie events returned but not claimed */
>  };
>  
>  #define XAllocIDs(dpy,ids,n) (*(dpy)->idlist_alloc)(dpy,ids,n)
> @@ -1005,6 +1019,19 @@ extern Bool _XUnknownWireEvent(
>      XEvent*	/* re */,
>      xEvent*	/* event */
>  );
> +
> +extern Bool _XUnknownWireEventCookie(
> +    Display*	/* dpy */,
> +    XGenericEventCookie*	/* re */,
> +    xEvent*	/* event */
> +);
> +
> +extern Bool _XUnknownCopyEventCookie(
> +    Display*	/* dpy */,
> +    XGenericEventCookie*	/* in */,
> +    XGenericEventCookie*	/* out */
> +);
> +
>  extern Status _XUnknownNativeEvent(
>      Display*	/* dpy */,
>      XEvent*	/* re */,
> @@ -1157,6 +1184,31 @@ extern Bool (*XESetWireToEvent(
>      Display*, XEvent*, xEvent*
>  );
>  
> +extern Bool (*XESetWireToEventCookie(
> +    Display*		/* display */,
> +    int			/* extension */,
> +    Bool (*) (
> +	       Display*			/* display */,
> +               XGenericEventCookie*	/* re */,
> +               xEvent*			/* event */
> +             )		/* proc */
> +))(
> +    Display*, XGenericEventCookie*, xEvent*
> +);
> +
> +extern Bool (*XESetCopyEventCookie(
> +    Display*		/* display */,
> +    int			/* extension */,
> +    Bool (*) (
> +	       Display*			/* display */,
> +               XGenericEventCookie*	/* in */,
> +               XGenericEventCookie*	/* out */
> +             )		/* proc */
> +))(
> +    Display*, XGenericEventCookie*, XGenericEventCookie*
> +);
> +
> +
>  extern Status (*XESetEventToWire(
>      Display*		/* display */,
>      int			/* event_number */,
> @@ -1321,6 +1373,26 @@ int _XPutBackEvent (
>      register Display *dpy,
>      register XEvent *event);
>  
> +extern Bool _XIsEventCookie(
> +        Display *dpy,
> +        XEvent *ev);
> +
> +extern void _XFreeEventCookies(
> +        Display *dpy);
> +
> +extern void _XStoreEventCookie(
> +        Display *dpy,
> +        XEvent *ev);
> +
> +extern Bool _XFetchEventCookie(
> +        Display *dpy,
> +        XGenericEventCookie *ev);
> +
> +extern Bool _XCopyEventCookie(
> +        Display *dpy,
> +        XGenericEventCookie *in,
> +        XGenericEventCookie *out);
> +
>  _XFUNCPROTOEND
>  
>  #endif /* _XLIBINT_H_ */
> diff --git a/man/Makefile.am b/man/Makefile.am
> index b19c5d3..b21d8fb 100644
> --- a/man/Makefile.am
> +++ b/man/Makefile.am
> @@ -75,6 +75,7 @@ libman_PRE = \
>  	XGravityEvent.man \
>  	XrmGetFileDatabase.man \
>  	XrmGetResource.man \
> +	XGetEventData.man \
>  	XGetVisualInfo.man \
>  	XGetWindowAttributes.man \
>  	XGetWindowProperty.man \
> @@ -271,6 +272,7 @@ all_shadows =                                \
>      $(XGraphicsExposeEvent_shadows)          \
>      $(XrmGetFileDatabase_shadows)            \
>      $(XrmGetResource_shadows)                \
> +    $(XGetEventData_shadows)                 \
>      $(XGetVisualInfo_shadows)                \
>      $(XGetWindowAttributes_shadows)          \
>      $(XGetWindowProperty_shadows)            \
> @@ -658,6 +660,10 @@ XrmGetResource_shadows =                    \
>      XrmQGetSearchList                       \
>      XrmQGetSearchResource
>  
> +XGetEventData_shadows =                     \
> +    XFreeEventData                          \
> +    XGenericEventCookie
> +
>  XGetVisualInfo_shadows =                    \
>      XMatchVisualInfo                        \
>      XVisualIDFromVisual                     \
> @@ -1223,6 +1229,9 @@ shadows.DONE:
>  	(for i in $(XrmGetResource_shadows:=. at LIB_MAN_SUFFIX@) ; do \
>  	 echo .so man$(LIB_MAN_DIR_SUFFIX)/XrmGetResource.$(LIB_MAN_SUFFIX) > $$i; \
>  	 done)
> +	(for i in $(XGetEventData_shadows:=. at LIB_MAN_SUFFIX@) ; do \
> +	 echo .so man$(LIB_MAN_DIR_SUFFIX)/XGetEventData.$(LIB_MAN_SUFFIX) > $$i; \
> +	 done)
>  	(for i in $(XGetVisualInfo_shadows:=. at LIB_MAN_SUFFIX@) ; do \
>  	 echo .so man$(LIB_MAN_DIR_SUFFIX)/XGetVisualInfo.$(LIB_MAN_SUFFIX) > $$i; \
>  	 done)
> diff --git a/man/XGetEventData.man b/man/XGetEventData.man
> new file mode 100644
> index 0000000..e1d9c74
> --- /dev/null
> +++ b/man/XGetEventData.man
> @@ -0,0 +1,223 @@
> +.\" Copyright \(co 2009 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.
> +.\"
> +.ds xT X Toolkit Intrinsics \- C Language Interface
> +.ds xW Athena X Widgets \- C Language X Toolkit Interface
> +.ds xL Xlib \- C Language X Interface
> +.ds xC Inter-Client Communication Conventions Manual
> +.na
> +.de Ds
> +.nf
> +.\\$1D \\$2 \\$1
> +.ft 1
> +.\".ps \\n(PS
> +.\".if \\n(VS>=40 .vs \\n(VSu
> +.\".if \\n(VS<=39 .vs \\n(VSp
> +..
> +.de De
> +.ce 0
> +.if \\n(BD .DF
> +.nr BD 0
> +.in \\n(OIu
> +.if \\n(TM .ls 2
> +.sp \\n(DDu
> +.fi
> +..
> +.de FD
> +.LP
> +.KS
> +.TA .5i 3i
> +.ta .5i 3i
> +.nf
> +..
> +.de FN
> +.fi
> +.KE
> +.LP
> +..
> +.de IN		\" send an index entry to the stderr
> +..
> +.de C{
> +.KS
> +.nf
> +.D
> +.\"
> +.\"	choose appropriate monospace font
> +.\"	the imagen conditional, 480,
> +.\"	may be changed to L if LB is too
> +.\"	heavy for your eyes...
> +.\"
> +.ie "\\*(.T"480" .ft L
> +.el .ie "\\*(.T"300" .ft L
> +.el .ie "\\*(.T"202" .ft PO
> +.el .ie "\\*(.T"aps" .ft CW
> +.el .ft R
> +.ps \\n(PS
> +.ie \\n(VS>40 .vs \\n(VSu
> +.el .vs \\n(VSp
> +..
> +.de C}
> +.DE
> +.R
> +..
> +.de Pn
> +.ie t \\$1\fB\^\\$2\^\fR\\$3
> +.el \\$1\fI\^\\$2\^\fP\\$3
> +..
> +.de ZN
> +.ie t \fB\^\\$1\^\fR\\$2
> +.el \fI\^\\$1\^\fP\\$2
> +..
> +.de hN
> +.ie t <\fB\\$1\fR>\\$2
> +.el <\fI\\$1\fP>\\$2
> +..
> +.de NT
> +.ne 7
> +.ds NO Note
> +.if \\n(.$>$1 .if !'\\$2'C' .ds NO \\$2
> +.if \\n(.$ .if !'\\$1'C' .ds NO \\$1
> +.ie n .sp
> +.el .sp 10p
> +.TB
> +.ce
> +\\*(NO
> +.ie n .sp
> +.el .sp 5p
> +.if '\\$1'C' .ce 99
> +.if '\\$2'C' .ce 99
> +.in +5n
> +.ll -5n
> +.R
> +..
> +.		\" Note End -- doug kraft 3/85
> +.de NE
> +.ce 0
> +.in -5n
> +.ll +5n
> +.ie n .sp
> +.el .sp 10p
> +..
> +.ny0
> +.TH XGetEventData __libmansuffix__ __xorgversion__ "XLIB FUNCTIONS"
> +.SH NAME
> +XGetEventData, XFreeEventData, XGenericEventCookie \- retrieve and free additional event data through cookies.
> +.SH SYNTAX
> +.HP
> +Bool XGetEventData\^(\^Display *\fIdisplay\fP\^, XGenericEventCookie *\fIcookie\fP\^);
> +.HP
> +void XFreeEventData\^(\^Display *\fIdisplay\fP\^, XGenericEventCookie *\fIcookie\fP\^);
> +.HP
> +.SH ARGUMENTS
> +.IP \fIdisplay\fP 1i
> +Specifies the connection to the X server.
> +.IP \fIcookie\fP 1i
> +Specifies the cookie to free or retrieve the data for.
> +
> +.SH STRUCTURES
> +.Ds 0
> +.TA .5i 3i
> +.ta .5i 3i
> +typedef struct {
> +	int type;
> +	unsigned long serial;
> +	Bool send_event;
> +	Display *display;
> +	int extension;
> +	int evtype;
> +	unsigned int cookie;
> +	void *data;
> +} XGenericEventCookie;
> +.De
> +
> +.SH DESCRIPTION
> +Some extension
> +.ZN XGenericEvents
> +require additional memory to store information.
> +For these events, the library 
> +.ZN XGenericEventCookie
> +with a token ('cookie') unique to this event. The
> +.ZN XGenericEventCookie 's
> +data pointer is undefined until
> +.ZN XGetEventData
> +is called.
> +
> +The
> +.ZN XGetEventData
> +function retrieves this extra data for the given cookie. No round-trip to
> +the server is required. If the cookie is invalid or the
> +event is not an event handled by cookie handlers,
> +.ZN False
> +is returned. If
> +.ZN XGetEventData
> +returns
> +.ZN True ,
> +the cookie's data pointer points to the memory containing the event
> +information. A client must call
> +.ZN XFreeEventData
> +to free this memory.
> +.ZN XGetEventData
> +returns
> +.ZN False
> +for multiple calls for the same event cookie.
> +
> +The
> +.ZN XFreeEventData
> +function frees the data associated with a cookie. A client must call
> +.ZN XFreeEventData
> +for each cookie claimed with
> +.ZN XGetEventData .
> +
> +.SH EXAMPLE CODE
> +.Ds 0
> +.TA .5i 3i
> +.ta .5i 3i
> +XEvent event;
> +XGenericEventCookie *cookie = &ev;
> +
> +XNextEvent(display, &event);
> +if (XGetEventData(display, cookie)) {
> +    handle_cookie_event(cookie->data);
> +} else
> +    handle_event(&event);
> +}
> +XFreeEventData(display, cookie);
> +.De
> +
> +.SH NOTES
> +A cookie is defined as unclaimed if it has been returned to the client
> +through
> +.ZN XNextEvent
> +but its data has not been retrieved via
> +.ZN XGetEventData .
> +Subsequent calls to
> +.ZN XNextEvent
> +may free memory associated with unclaimed cookies.
> +Multi-threaded X clients must ensure that
> +.ZN XGetEventData
> +is called before the next call to
> +.ZN XNextEvent .
> +
> +.SH "SEE ALSO"
> +XNextEvent(3X11),

__libmansuffix__

> +.br
> +\fI\*(xL\fP
> +
> diff --git a/src/ChkIfEv.c b/src/ChkIfEv.c
> index 9bef64f..66f0c3c 100644
> --- a/src/ChkIfEv.c
> +++ b/src/ChkIfEv.c
> @@ -62,6 +62,7 @@ Bool XCheckIfEvent (
>  		   && (*predicate)(dpy, &qelt->event, arg)) {
>  		    *event = qelt->event;
>  		    _XDeq(dpy, prev, qelt);
> +		    _XStoreEventCookie(dpy, event);
>  		    UnlockDisplay(dpy);
>  		    return True;
>  		}
> diff --git a/src/ChkMaskEv.c b/src/ChkMaskEv.c
> index bc1a279..6fb71c9 100644
> --- a/src/ChkMaskEv.c
> +++ b/src/ChkMaskEv.c
> @@ -54,6 +54,10 @@ Bool XCheckMaskEvent (
>  	int n;			/* time through count */
>  
>          LockDisplay(dpy);
> +
> +	/* Delete unclaimed cookies */
> +	_XFreeEventCookies(dpy);
> +
>  	prev = NULL;
>  	for (n = 3; --n >= 0;) {
>  	    for (qelt = prev ? prev->next : dpy->head;
> diff --git a/src/ChkTypEv.c b/src/ChkTypEv.c
> index fc4c7d1..f64ebd3 100644
> --- a/src/ChkTypEv.c
> +++ b/src/ChkTypEv.c
> @@ -48,6 +48,10 @@ Bool XCheckTypedEvent (
>  	int n;			/* time through count */
>  
>          LockDisplay(dpy);
> +
> +	/* Delete unclaimed cookies */
> +	_XFreeEventCookies(dpy);
> +
>  	prev = NULL;
>  	for (n = 3; --n >= 0;) {
>  	    for (qelt = prev ? prev->next : dpy->head;
> diff --git a/src/ChkTypWEv.c b/src/ChkTypWEv.c
> index 6214c36..d791eb2 100644
> --- a/src/ChkTypWEv.c
> +++ b/src/ChkTypWEv.c
> @@ -49,6 +49,10 @@ Bool XCheckTypedWindowEvent (
>  	int n;			/* time through count */
>  
>          LockDisplay(dpy);
> +
> +	/* Delete unclaimed cookies */
> +	_XFreeEventCookies(dpy);
> +
>  	prev = NULL;
>  	for (n = 3; --n >= 0;) {
>  	    for (qelt = prev ? prev->next : dpy->head;
> diff --git a/src/ChkWinEv.c b/src/ChkWinEv.c
> index 1fd975c..b753c5f 100644
> --- a/src/ChkWinEv.c
> +++ b/src/ChkWinEv.c
> @@ -54,6 +54,10 @@ Bool XCheckWindowEvent (
>  	int n;			/* time through count */
>  
>          LockDisplay(dpy);
> +
> +	/* Delete unclaimed cookies */
> +	_XFreeEventCookies(dpy);
> +
>  	prev = NULL;
>  	for (n = 3; --n >= 0;) {
>  	    for (qelt = prev ? prev->next : dpy->head;
> diff --git a/src/FreeEventData.c b/src/FreeEventData.c
> new file mode 100644
> index 0000000..e2d40b0
> --- /dev/null
> +++ b/src/FreeEventData.c
> @@ -0,0 +1,42 @@
> +/*
> + * Copyright © 2009 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_CONFIG_H
> +#include <config.h>
> +#endif
> +#include "Xlibint.h"
> +
> +void
> +XFreeEventData(Display *dpy, XGenericEventCookie *event)
> +{
> +
> +    if (_XIsEventCookie(dpy, (XEvent*)event) && event->data)
> +    {
> +        XFree(event->data);
> +        event->data = NULL;
> +        event->cookie = 0;
> +    }
> +    return;
> +}
> +
> diff --git a/src/GetEventData.c b/src/GetEventData.c
> new file mode 100644
> index 0000000..b93dee6
> --- /dev/null
> +++ b/src/GetEventData.c
> @@ -0,0 +1,42 @@
> +/*
> + * Copyright © 2009 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_CONFIG_H
> +#include <config.h>
> +#endif
> +#include "Xlibint.h"
> +
> +Bool
> +XGetEventData(Display *dpy, XGenericEventCookie *event)
> +{
> +    Bool rc;
> +    LockDisplay(dpy);
> +
> +    rc = _XFetchEventCookie(dpy, event);
> +
> +    UnlockDisplay(dpy);
> +
> +    return rc;
> +}
> +
> diff --git a/src/IfEvent.c b/src/IfEvent.c
> index 05a2b82..64c8f46 100644
> --- a/src/IfEvent.c
> +++ b/src/IfEvent.c
> @@ -61,6 +61,7 @@ XIfEvent (
>  		   && (*predicate)(dpy, &qelt->event, arg)) {
>  		    *event = qelt->event;
>  		    _XDeq(dpy, prev, qelt);
> +		    _XStoreEventCookie(dpy, event);
>  		    UnlockDisplay(dpy);
>  		    return 0;
>  		}
> diff --git a/src/InitExt.c b/src/InitExt.c
> index 92fc44a..0e6c94e 100644
> --- a/src/InitExt.c
> +++ b/src/InitExt.c
> @@ -253,6 +253,49 @@ WireToEventType XESetWireToEvent(
>  	return (WireToEventType)oldproc;
>  }
>  
> +typedef Bool (*WireToEventCookieType) (
> +    Display*	/* display */,
> +    XGenericEventCookie*	/* re */,
> +    xEvent*	/* event */
> +);
> +
> +WireToEventCookieType XESetWireToEventCookie(
> +    Display *dpy,       /* display */
> +    int extension,      /* extension major opcode */
> +    WireToEventCookieType proc /* routine to call for generic events */
> +    )
> +{
> +	WireToEventCookieType oldproc;
> +	if (proc == NULL) proc = (WireToEventCookieType)_XUnknownWireEventCookie;
> +	LockDisplay (dpy);
> +	oldproc = dpy->generic_event_vec[extension & 0x7F];
> +	dpy->generic_event_vec[extension & 0x7F] = proc;
> +	UnlockDisplay (dpy);
> +	return (WireToEventCookieType)oldproc;
> +}
> +
> +typedef Bool (*CopyEventCookieType) (
> +    Display*	/* display */,
> +    XGenericEventCookie*	/* in */,
> +    XGenericEventCookie*	/* out */
> +);
> +
> +CopyEventCookieType XESetCopyEventCookie(
> +    Display *dpy,       /* display */
> +    int extension,      /* extension major opcode */
> +    CopyEventCookieType proc /* routine to copy generic events */
> +    )
> +{
> +	CopyEventCookieType oldproc;
> +	if (proc == NULL) proc = (CopyEventCookieType)_XUnknownCopyEventCookie;
> +	LockDisplay (dpy);
> +	oldproc = dpy->generic_event_copy_vec[extension & 0x7F];
> +	dpy->generic_event_copy_vec[extension & 0x7F] = proc;
> +	UnlockDisplay (dpy);
> +	return (CopyEventCookieType)oldproc;
> +}
> +
> +
>  typedef Status (*EventToWireType) (
>      Display*	/* display */,
>      XEvent*	/* re */,
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 86a846e..958d542 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -130,6 +130,7 @@ libX11_la_SOURCES = \
>                    FreeCols.c \
>                    FreeCurs.c \
>                    FreeEData.c \
> +                  FreeEventData.c \
>                    FreeGC.c \
>                    FreePix.c \
>                    FSSaver.c \
> @@ -139,6 +140,7 @@ libX11_la_SOURCES = \
>                    GetAtomNm.c \
>                    GetColor.c \
>                    GetDflt.c \
> +                  GetEventData.c \
>                    GetFPath.c \
>                    GetFProp.c \
>                    GetGCVals.c \
> diff --git a/src/MaskEvent.c b/src/MaskEvent.c
> index acd903f..14af00c 100644
> --- a/src/MaskEvent.c
> +++ b/src/MaskEvent.c
> @@ -53,6 +53,10 @@ XMaskEvent (
>  	unsigned long qe_serial = 0;
>  
>          LockDisplay(dpy);
> +
> +	/* Delete unclaimed cookies */
> +	_XFreeEventCookies(dpy);
> +
>  	prev = NULL;
>  	while (1) {
>  	    for (qelt = prev ? prev->next : dpy->head;
> diff --git a/src/NextEvent.c b/src/NextEvent.c
> index d4f72e7..1dc8384 100644
> --- a/src/NextEvent.c
> +++ b/src/NextEvent.c
> @@ -46,11 +46,15 @@ XNextEvent (
>  
>  	LockDisplay(dpy);
>  
> +	/* Delete unclaimed cookies */
> +	_XFreeEventCookies(dpy);
> +
>  	if (dpy->head == NULL)
>  	    _XReadEvents(dpy);
>  	qelt = dpy->head;
>  	*event = qelt->event;
>  	_XDeq(dpy, NULL, qelt);
> +	_XStoreEventCookie(dpy, event);
>  	UnlockDisplay(dpy);
>  	return 0;
>  }
> diff --git a/src/OpenDis.c b/src/OpenDis.c
> index 29ac65c..230ae56 100644
> --- a/src/OpenDis.c
> +++ b/src/OpenDis.c
> @@ -790,6 +790,17 @@ _XBigReqHandler(
>  
>  void _XFreeDisplayStructure(Display *dpy)
>  {
> +	/* move all cookies in the EQ to the jar, then free them. */
> +	if (dpy->qfree) {
> +	    _XQEvent *qelt = dpy->qfree;
> +	    while (qelt) {
> +		if (_XIsEventCookie(dpy, &qelt->event))
> +		    _XStoreEventCookie(dpy, &qelt->event);
> +		qelt = qelt->next;
> +	    }
> +        }
> +	if (dpy->cookiejar)
> +	    _XFreeEventCookies(dpy);
>  	while (dpy->ext_procs) {
>  	    _XExtension *ext = dpy->ext_procs;
>  	    dpy->ext_procs = ext->next;
> diff --git a/src/PeekEvent.c b/src/PeekEvent.c
> index 6eec4a0..ca7a616 100644
> --- a/src/PeekEvent.c
> +++ b/src/PeekEvent.c
> @@ -43,10 +43,15 @@ XPeekEvent (
>  	register Display *dpy,
>  	register XEvent *event)
>  {
> +	XEvent copy;
>  	LockDisplay(dpy);
>  	if (dpy->head == NULL)
>  	    _XReadEvents(dpy);
>  	*event = (dpy->head)->event;
> +	if (_XCopyEventCookie(dpy, (XGenericEventCookie*)event, (XGenericEventCookie*)&copy)) {
> +	    _XStoreEventCookie(dpy, &copy);
> +	    *event = copy;
> +	}
>  	UnlockDisplay(dpy);
>  	return 1;
>  }
> diff --git a/src/PeekIfEv.c b/src/PeekIfEv.c
> index 1d5b1ab..3a27eea 100644
> --- a/src/PeekIfEv.c
> +++ b/src/PeekIfEv.c
> @@ -60,7 +60,14 @@ XPeekIfEvent (
>  		 prev = qelt, qelt = qelt->next) {
>  		if(qelt->qserial_num > qe_serial
>  		   && (*predicate)(dpy, &qelt->event, arg)) {
> +		    XEvent copy;
>  		    *event = qelt->event;
> +		    if (_XCopyEventCookie(dpy,
> +				(XGenericEventCookie*)event,
> +				(XGenericEventCookie*)&copy)) {
> +			_XStoreEventCookie(dpy, &copy);
> +			*event = copy;
> +		    }
>  		    UnlockDisplay(dpy);
>  		    return 0;
>  		}
> diff --git a/src/PutBEvent.c b/src/PutBEvent.c
> index eca44f3..03f6950 100644
> --- a/src/PutBEvent.c
> +++ b/src/PutBEvent.c
> @@ -41,6 +41,7 @@ _XPutBackEvent (
>      register XEvent *event)
>  	{
>  	register _XQEvent *qelt;
> +	XEvent store = *event;
>  
>  	if (!dpy->qfree) {
>      	    if ((dpy->qfree = (_XQEvent *) Xmalloc (sizeof (_XQEvent))) == NULL) {
> @@ -48,11 +49,27 @@ _XPutBackEvent (
>  	    }
>  	    dpy->qfree->next = NULL;
>  	}
> +
> +	/* unclaimed cookie? */
> +	if (_XIsEventCookie(dpy, event))
> +	{
> +	    XEvent copy = {0};
> +	    XGenericEventCookie *cookie = (XGenericEventCookie*)event;
> +            /* if not claimed, then just fetch and store again */
> +	    if (!cookie->data) {
> +		_XFetchEventCookie(dpy, cookie);
> +		store = *event;
> +	    } else { /* if claimed, copy, client must free */
> +		_XCopyEventCookie(dpy, cookie, (XGenericEventCookie*)&copy);
> +		store = copy;
> +	    }
> +	}
> +
>  	qelt = dpy->qfree;
>  	dpy->qfree = qelt->next;
>  	qelt->qserial_num = dpy->next_event_serial_num++;
>  	qelt->next = dpy->head;
> -	qelt->event = *event;
> +	qelt->event = store;
>  	dpy->head = qelt;
>  	if (dpy->tail == NULL)
>  	    dpy->tail = qelt;
> diff --git a/src/WinEvent.c b/src/WinEvent.c
> index c6daf2a..bcdf981 100644
> --- a/src/WinEvent.c
> +++ b/src/WinEvent.c
> @@ -56,6 +56,10 @@ XWindowEvent (
>  	unsigned long qe_serial = 0;
>  
>          LockDisplay(dpy);
> +
> +	/* Delete unclaimed cookies */
> +	_XFreeEventCookies(dpy);
> +
>  	prev = NULL;
>  	while (1) {
>  	    for (qelt = prev ? prev->next : dpy->head;
> diff --git a/src/XlibInt.c b/src/XlibInt.c
> index 320e808..57d04dd 100644
> --- a/src/XlibInt.c
> +++ b/src/XlibInt.c
> @@ -2258,6 +2258,157 @@ void _XEatData(
>  }
>  #endif /* !USE_XCB */
>  
> +/* Cookie jar implementation
> +   dpy->cookiejar is a linked list. _XEnq receives the events but leaves
> +   them in the normal EQ. _XStoreEvent returns the cookie event (minus
> +   data pointer) and adds it to the cookiejar. _XDeq just removes
> +   the entry like any other event but resets the data pointer for
> +   cookie events (to avoid double-free, the memory is re-used by Xlib).
> +
> +   _XFetchEventCookie (called from XGetEventData) removes a cookie from the
> +   jar. _XFreeEventCookies removes all unclaimed cookies from the jar
> +   (called by XNextEvent).
> +
> +   _XFreeDisplayStructure calls _XFreeEventCookies for each cookie in the
> +   normal EQ.
> + */
> +
> +#include "utlist.h"
> +struct stored_event {
> +    XGenericEventCookie ev;
> +    struct stored_event *prev;
> +    struct stored_event *next;
> +};
> +
> +Bool
> +_XIsEventCookie(Display *dpy, XEvent *ev)
> +{
> +    XGenericEventCookie *cookie = (XGenericEventCookie*)ev;
> +    return (cookie->type == GenericEvent &&
> +	    dpy->generic_event_vec[cookie->extension & 0x7F] != NULL);
> +}
> +
> +/**
> + * Free all events in the event list.
> + */
> +void
> +_XFreeEventCookies(Display *dpy)
> +{
> +    struct stored_event **head, *e, *tmp;
> +
> +    if (!dpy->cookiejar)
> +        return;
> +
> +    head = (struct stored_event**)&dpy->cookiejar;
> +
> +    DL_FOREACH_SAFE(*head, e, tmp) {
> +        XFree(e->ev.data);
> +        XFree(e);
> +        if (dpy->cookiejar == e)
> +            dpy->cookiejar = NULL;
> +    }
> +}
> +
> +/**
> + * Add an event to the display's event list. This event must be freed on the
> + * next call to XNextEvent().
> + */
> +void
> +_XStoreEventCookie(Display *dpy, XEvent *event)
> +{
> +    Bool found = False;
> +    XGenericEventCookie* cookie = (XGenericEventCookie*)event;
> +    struct stored_event **head, *add;
> +
> +    if (!_XIsEventCookie(dpy, event))
> +        return;
> +
> +    head = (struct stored_event**)(&dpy->cookiejar);
> +
> +    /* protect against duplicates (multiple XPeekEvents) */
> +    DL_FOREACH(*head, add) {
> +        if (add->ev.cookie == cookie->cookie &&
> +            add->ev.extension == cookie->extension &&
> +            add->ev.evtype == cookie->evtype) {
> +            found = True;
> +            return;

should this be break instead of return?  otherwise 'found' seems
useless.

Cheers,
Julien

> +        }
> +    }
> +
> +    if (!found) {
> +        add = Xmalloc(sizeof(struct stored_event));
> +        if (!add) {
> +            ESET(ENOMEM);
> +            _XIOError(dpy);
> +        }
> +        add->ev = *cookie;
> +        DL_APPEND(*head, add);
> +    }
> +    cookie->data = NULL; /* don't return data yet, must be claimed */
> +}
> +
> +/**
> + * Return the event with the given cookie and remove it from the list.
> + */
> +Bool
> +_XFetchEventCookie(Display *dpy, XGenericEventCookie* ev)
> +{
> +    Bool ret = False;
> +    _XQEvent *qelt;
> +    struct stored_event **head, *event;
> +    head = (struct stored_event**)&dpy->cookiejar;
> +
> +    if (!_XIsEventCookie(dpy, (XEvent*)ev))
> +        return ret;
> +
> +    DL_FOREACH(*head, event) {
> +        if (event->ev.cookie == ev->cookie &&
> +            event->ev.extension == ev->extension &&
> +            event->ev.evtype == ev->evtype) {
> +            *ev = event->ev;
> +            DL_DELETE(*head, event);
> +            Xfree(event);
> +            ret = True;
> +            break;
> +        }
> +    }
> +
> +    /* An event retrieved with XPeekEvent + XGetEventData needs to
> +     * be wiped in the EQ. That is, it remains in the EQ but
> +     * data is NULL to avoid double-free. */
> +    if (ret && (qelt = dpy->head)) {
> +        if (_XIsEventCookie(dpy, &qelt->event)) {
> +            XGenericEventCookie *cookie = (XGenericEventCookie*)&qelt->event;
> +            if (cookie->cookie == ev->cookie &&
> +                cookie->extension == ev->extension &&
> +                cookie->evtype == ev->evtype) {
> +                cookie->data = NULL;
> +            }
> +        }
> +    }
> +
> +    return ret;
> +}
> +
> +Bool
> +_XCopyEventCookie(Display *dpy, XGenericEventCookie *in, XGenericEventCookie *out)
> +{
> +    Bool ret = False;
> +    int extension;
> +
> +    if (!_XIsEventCookie(dpy, (XEvent*)in) || !out)
> +        return ret;
> +
> +    extension = in->extension & 0x7F;
> +
> +    if (!dpy->generic_event_copy_vec[extension])
> +        return ret;
> +
> +    ret = ((*dpy->generic_event_copy_vec[extension])(dpy, in, out));
> +    out->cookie = ret ? ++dpy->next_cookie  : 0;
> +    return ret;
> +}
> +
>  
>  /*
>   * _XEnq - Place event packets on the display's queue.
> @@ -2269,6 +2420,7 @@ void _XEnq(
>  	register xEvent *event)
>  {
>  	register _XQEvent *qelt;
> +	int type, extension;
>  
>  	if ((qelt = dpy->qfree)) {
>  		/* If dpy->qfree is non-NULL do this, else malloc a new one. */
> @@ -2281,8 +2433,29 @@ void _XEnq(
>  		_XIOError(dpy);
>  	}
>  	qelt->next = NULL;
> -	/* go call through display to find proper event reformatter */
> -	if ((*dpy->event_vec[event->u.u.type & 0177])(dpy, &qelt->event, event)) {
> +
> +	type = event->u.u.type & 0177;
> +	extension = ((xGenericEvent*)event)->extension;
> +	/* If an extension has registerd a generic_event_vec handler, then
> +	 * it can handle event cookies. Otherwise, proceed with the normal
> +	 * event handlers.
> +	 *
> +	 * If the generic_event_vec is called, qelt->event is a event cookie
> +	 * with the data pointer and the "free" pointer set. Data pointer is
> +	 * some memory allocated by the extension.
> +	 */
> +        if (type == GenericEvent && dpy->generic_event_vec[extension & 0x7F]) {
> +	    XGenericEventCookie *cookie = (XGenericEventCookie*)&qelt->event;
> +	    (*dpy->generic_event_vec[extension & 0x7F])(dpy, cookie, event);
> +	    cookie->cookie = ++dpy->next_cookie;
> +
> +	    qelt->qserial_num = dpy->next_event_serial_num++;
> +	    if (dpy->tail)	dpy->tail->next = qelt;
> +	    else		dpy->head = qelt;
> +
> +	    dpy->tail = qelt;
> +	    dpy->qlen++;
> +	} else if ((*dpy->event_vec[type])(dpy, &qelt->event, event)) {
>  	    qelt->qserial_num = dpy->next_event_serial_num++;
>  	    if (dpy->tail)	dpy->tail->next = qelt;
>  	    else 		dpy->head = qelt;
> @@ -2316,6 +2489,13 @@ void _XDeq(
>      qelt->next = dpy->qfree;
>      dpy->qfree = qelt;
>      dpy->qlen--;
> +
> +    if (_XIsEventCookie(dpy, &qelt->event)) {
> +	XGenericEventCookie* cookie = (XGenericEventCookie*)&qelt->event;
> +	/* dpy->qfree is re-used, reset memory to avoid double free on
> +	 * _XFreeDisplayStructure */
> +	cookie->data = NULL;
> +    }
>  }
>  
>  /*
> @@ -2337,6 +2517,34 @@ _XUnknownWireEvent(
>  	return(False);
>  }
>  
> +Bool
> +_XUnknownWireEventCookie(
> +    Display *dpy,	/* pointer to display structure */
> +    XGenericEventCookie *re,	/* pointer to where event should be reformatted */
> +    xEvent *event)	/* wire protocol event */
> +{
> +#ifdef notdef
> +	fprintf(stderr,
> +	    "Xlib: unhandled wire cookie event! extension number = %d, display = %x\n.",
> +			((xGenericEvent*)event)->extension, dpy);
> +#endif
> +	return(False);
> +}
> +
> +Bool
> +_XUnknownCopyEventCookie(
> +    Display *dpy,	/* pointer to display structure */
> +    XGenericEventCookie *in,	/* source */
> +    XGenericEventCookie *out)	/* destination */
> +{
> +#ifdef notdef
> +	fprintf(stderr,
> +	    "Xlib: unhandled cookie event copy! extension number = %d, display = %x\n.",
> +			((XGenericEventCookie*)in)->extension, dpy);
> +#endif
> +	return(False);
> +}
> +
>  /*ARGSUSED*/
>  Status
>  _XUnknownNativeEvent(
> diff --git a/src/utlist.h b/src/utlist.h
> new file mode 100644
> index 0000000..215c2c6
> --- /dev/null
> +++ b/src/utlist.h
> @@ -0,0 +1,116 @@
> +/*
> +Copyright (c) 2007-2009, Troy D. Hanson
> +All rights reserved.
> +
> +Redistribution and use in source and binary forms, with or without
> +modification, are permitted provided that the following conditions are met:
> +
> +    * Redistributions of source code must retain the above copyright
> +      notice, this list of conditions and the following disclaimer.
> +
> +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
> +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
> +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
> +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
> +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> +*/
> +
> +#ifndef UTLIST_H
> +#define UTLIST_H
> +
> +#define UTLIST_VERSION 1.7
> +
> +/* From: http://uthash.sourceforge.net/utlist.html */
> +/*
> + * This file contains macros to manipulate singly and doubly-linked lists.
> + *
> + * 1. LL_ macros:  singly-linked lists.
> + * 2. DL_ macros:  doubly-linked lists.
> + * 3. CDL_ macros: circular doubly-linked lists.
> + *
> + * To use singly-linked lists, your structure must have a "next" pointer.
> + * To use doubly-linked lists, your structure must "prev" and "next" pointers.
> + * Either way, the pointer to the head of the list must be initialized to NULL.
> + *
> + * ----------------.EXAMPLE -------------------------
> + * struct item {
> + *      int id;
> + *      struct item *prev, *next;
> + * }
> + *
> + * struct item *list = NULL:
> + *
> + * int main() {
> + *      struct item *item;
> + *      ... allocate and populate item ...
> + *      DL_APPEND(list, item);
> + * }
> + * --------------------------------------------------
> + *
> + * For doubly-linked lists, the append and delete macros are O(1)
> + * For singly-linked lists, append and delete are O(n) but prepend is O(1)
> + * The sort macro is O(n log(n)) for all types of single/double/circular lists.
> + */
> +
> +
> +/******************************************************************************
> + * doubly linked list macros (non-circular)                                   *
> + *****************************************************************************/
> +#define DL_PREPEND(head,add)                                                     \
> +do {                                                                             \
> + (add)->next = head;                                                             \
> + if (head) {                                                                     \
> +   (add)->prev = (head)->prev;                                                   \
> +   (head)->prev = (add);                                                         \
> + } else {                                                                        \
> +   (add)->prev = (add);                                                          \
> + }                                                                               \
> + (head) = (add);                                                                 \
> +} while (0)
> +
> +#define DL_APPEND(head,add)                                                      \
> +do {                                                                             \
> +  if (head) {                                                                    \
> +      (add)->prev = (head)->prev;                                                \
> +      (head)->prev->next = (add);                                                \
> +      (head)->prev = (add);                                                      \
> +      (add)->next = NULL;                                                        \
> +  } else {                                                                       \
> +      (head)=(add);                                                              \
> +      (head)->prev = (head);                                                     \
> +      (head)->next = NULL;                                                       \
> +  }                                                                              \
> +} while (0);
> +
> +#define DL_DELETE(head,del)                                                      \
> +do {                                                                             \
> +  if ((del)->prev == (del)) {                                                    \
> +      (head)=NULL;                                                               \
> +  } else if ((del)==(head)) {                                                    \
> +      (del)->next->prev = (del)->prev;                                           \
> +      (head) = (del)->next;                                                      \
> +  } else {                                                                       \
> +      (del)->prev->next = (del)->next;                                           \
> +      if ((del)->next) {                                                         \
> +          (del)->next->prev = (del)->prev;                                       \
> +      } else {                                                                   \
> +          (head)->prev = (del)->prev;                                            \
> +      }                                                                          \
> +  }                                                                              \
> +} while (0);
> +
> +
> +#define DL_FOREACH(head,el)                                                      \
> +    for(el=head;el;el=el->next)
> +
> +#define DL_FOREACH_SAFE(head,el,tmp)                                             \
> +    for(el=head,tmp=el->next;el;el=tmp,tmp=(el) ? (el->next) : NULL)
> +
> +#endif /* UTLIST_H */
> +


More information about the xorg-devel mailing list