[PATCH] Add generic event cookie handling to libX11.

Peter Hutterer peter.hutterer at who-t.net
Thu Jun 25 18:27:43 PDT 2009


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    |   23 +++++
 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        |    5 +
 src/PutBEvent.c       |   18 ++++-
 src/WinEvent.c        |    4 +
 src/XlibInt.c         |  184 ++++++++++++++++++++++++++++++++++++++++-
 src/utlist.h          |  116 +++++++++++++++++++++++++
 23 files changed, 822 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..2c30bc0 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;
+
 /*
  * this union is defined so Xlib can always use the same sized
  * event structure internally, to avoid memory fragmentation.
@@ -1003,6 +1014,8 @@ typedef union _XEvent {
 	XMappingEvent xmapping;
 	XErrorEvent xerror;
 	XKeymapEvent xkeymap;
+	XGenericEvent xgeneric;
+	XGenericEventCookie xcookie;
 	long pad[24];
 } XEvent;
 #endif
@@ -3998,6 +4011,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 */
+	/* 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),
+.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..344fe02 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, &event->xcookie, &copy.xcookie)) {
+	    _XStoreEventCookie(dpy, &copy);
+	    *event = copy;
+	}
 	UnlockDisplay(dpy);
 	return 1;
 }
diff --git a/src/PeekIfEv.c b/src/PeekIfEv.c
index 1d5b1ab..5105b6a 100644
--- a/src/PeekIfEv.c
+++ b/src/PeekIfEv.c
@@ -60,7 +60,12 @@ 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, &event->xcookie, &copy.xcookie)) {
+			_XStoreEventCookie(dpy, &copy);
+			*event = copy;
+		    }
 		    UnlockDisplay(dpy);
 		    return 0;
 		}
diff --git a/src/PutBEvent.c b/src/PutBEvent.c
index eca44f3..03a9cd2 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,26 @@ _XPutBackEvent (
 	    }
 	    dpy->qfree->next = NULL;
 	}
+
+	/* unclaimed cookie? */
+	if (_XIsEventCookie(dpy, event))
+	{
+	    XEvent copy = {0};
+            /* if not claimed, then just fetch and store again */
+	    if (!event->xcookie.data) {
+		_XFetchEventCookie(dpy, &event->xcookie);
+		store = *event;
+	    } else { /* if claimed, copy, client must free */
+		_XCopyEventCookie(dpy, &event->xcookie, &copy.xcookie);
+		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..74cc322 100644
--- a/src/XlibInt.c
+++ b/src/XlibInt.c
@@ -2258,6 +2258,129 @@ 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)
+{
+    return (ev->xcookie.type == GenericEvent &&
+	    dpy->generic_event_vec[ev->xcookie.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)
+{
+    XGenericEventCookie* cookie = &event->xcookie;
+    struct stored_event **head, *add;
+
+    if (!_XIsEventCookie(dpy, event))
+        return;
+
+    head = (struct stored_event**)(&dpy->cookiejar);
+
+    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;
+        }
+    }
+
+    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 +2392,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 +2405,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 = &qelt->event.xcookie;
+	    (*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 +2461,13 @@ void _XDeq(
     qelt->next = dpy->qfree;
     dpy->qfree = qelt;
     dpy->qlen--;
+
+    if (_XIsEventCookie(dpy, &qelt->event)) {
+	XGenericEventCookie* cookie = &qelt->event.xcookie;
+	/* dpy->qfree is re-used, reset memory to avoid double free on
+	 * _XFreeDisplayStructure */
+	cookie->data = NULL;
+    }
 }
 
 /*
@@ -2337,6 +2489,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.",
+			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 */
+
-- 
1.6.3.rc1.2.g0164.dirty



More information about the xorg-devel mailing list