[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*)©)) {
> + _XStoreEventCookie(dpy, ©);
> + *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*)©)) {
> + _XStoreEventCookie(dpy, ©);
> + *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*)©);
> + 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