[Xcb] [PATCH 6/8] Add event queue splitting
Uli Schlachter
psychon at znc.in
Thu Nov 7 11:14:29 CET 2013
On 07.11.2013 04:45, Keith Packard wrote:
> This allows apps to peel off certain XGE events into separate queues
> for custom handling. Designed to support the Present extension
>
> Signed-off-by: Keith Packard <keithp at keithp.com>
> ---
> src/xcb.h | 28 ++++++++++
> src/xcb_in.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
> src/xcbint.h | 1 +
> 3 files changed, 193 insertions(+), 3 deletions(-)
>
> diff --git a/src/xcb.h b/src/xcb.h
> index c251330..cde820d 100644
> --- a/src/xcb.h
> +++ b/src/xcb.h
> @@ -287,6 +287,34 @@ xcb_generic_event_t *xcb_poll_for_event(xcb_connection_t *c);
> */
> xcb_generic_event_t *xcb_poll_for_queued_event(xcb_connection_t *c);
>
> +typedef struct xcb_special_event xcb_special_event_t;
For bikeshedding, I would call this xcb_special_event_filter_t, because this is
not actually an event. Having said that, feel free to ignore me.
> +/**
> + * @brief Returns the next event from a special queue
> + */
> +xcb_generic_event_t *xcb_poll_for_special_event(xcb_connection_t *c,
> + xcb_special_event_t *se);
> +
> +/**
> + * @brief Returns the next event from a special queue, blocking until one arrives
> + */
> +xcb_generic_event_t *xcb_wait_for_special_event(xcb_connection_t *c,
> + xcb_special_event_t *se);
> +
> +/**
> + * @brief Listen for a special event
> + */
> +xcb_special_event_t *xcb_register_for_special_xge(xcb_connection_t *c,
> + uint8_t extension,
> + uint32_t eid,
> + uint32_t *stamp);
> +
> +/**
> + * @brief Stop listening for a special event
> + */
> +void xcb_unregister_for_special_event(xcb_connection_t *c,
> + xcb_special_event_t *se);
> +
> /**
> * @brief Return the error for a request, or NULL if none can ever arrive.
> * @param c: The connection to the X server.
> diff --git a/src/xcb_in.c b/src/xcb_in.c
> index 0a78ae6..9239c93 100644
> --- a/src/xcb_in.c
> +++ b/src/xcb_in.c
> @@ -61,6 +61,23 @@ struct event_list {
> struct event_list *next;
> };
>
> +struct xcb_special_event {
> +
> + struct xcb_special_event *next;
> +
> + /* Match XGE events for the specific extension and event ID (the
> + * first 32 bit word after evtype)
> + */
> + uint8_t extension;
> + uint32_t eid;
> + uint32_t *stamp;
> +
> + struct event_list *events;
> + struct event_list **events_tail;
> +
> + pthread_cond_t special_event_cond;
> +};
> +
> struct reply_list {
> void *reply;
> struct reply_list *next;
> @@ -105,6 +122,46 @@ static int read_fds(xcb_connection_t *c, int *fds, int nfd)
> }
> #endif
>
> +typedef struct xcb_ge_special_event_t {
> + uint8_t response_type; /**< */
> + uint8_t extension; /**< */
> + uint16_t sequence; /**< */
> + uint32_t length; /**< */
> + uint16_t evtype; /**< */
> + uint8_t pad0[2]; /**< */
> + uint32_t eid; /**< */
> + uint8_t pad1[16]; /**< */
> +} xcb_ge_special_event_t;
> +
> +static int event_special(xcb_connection_t *c,
> + struct event_list *event)
> +{
> + struct xcb_special_event *special_event;
> + struct xcb_ge_special_event_t *ges = (void *) event->event;
> +
> + /* Special events are always XGE events */
> + if ((ges->response_type & 0x7f) != XCB_XGE_EVENT)
> + return 0;
> +
> + for (special_event = c->in.special_events;
> + special_event;
> + special_event = special_event->next)
> + {
> + if (ges->extension == special_event->extension &&
> + ges->eid == special_event->eid)
> + {
> + *special_event->events_tail = event;
> + special_event->events_tail = &event->next;
> + if (special_event->stamp)
> + ++(*special_event->stamp);
> + pthread_cond_signal(&special_event->special_event_cond);
> + return 1;
> + }
> + }
> +
> + return 0;
> +}
> +
> static int read_packet(xcb_connection_t *c)
> {
> xcb_generic_reply_t genrep;
> @@ -269,9 +326,12 @@ static int read_packet(xcb_connection_t *c)
> }
> event->event = buf;
> event->next = 0;
> - *c->in.events_tail = event;
> - c->in.events_tail = &event->next;
> - pthread_cond_signal(&c->in.event_cond);
> +
> + if (!event_special(c, event)) {
> + *c->in.events_tail = event;
> + c->in.events_tail = &event->next;
> + pthread_cond_signal(&c->in.event_cond);
> + }
> return 1; /* I have something for you... */
> }
>
> @@ -614,6 +674,107 @@ xcb_generic_error_t *xcb_request_check(xcb_connection_t *c, xcb_void_cookie_t co
> return ret;
> }
>
> +static xcb_generic_event_t *get_special_event(xcb_connection_t *c,
> + xcb_special_event_t *se)
> +{
> + xcb_generic_event_t *event = NULL;
> + struct event_list *events;
> +
> + if ((events = se->events) != NULL) {
> + event = events->event;
> + if (!(se->events = events->next))
> + se->events_tail = &se->events;
> + free (events);
> + }
> + return event;
> +}
> +
> +xcb_generic_event_t *xcb_poll_for_special_event(xcb_connection_t *c,
> + xcb_special_event_t *se)
> +{
> + xcb_generic_event_t *event;
> +
> + if(c->has_error)
> + return 0;
> + pthread_mutex_lock(&c->iolock);
> + event = get_special_event(c, se);
> + pthread_mutex_unlock(&c->iolock);
> + return event;
> +}
> +
> +xcb_generic_event_t *xcb_wait_for_special_event(xcb_connection_t *c,
> + xcb_special_event_t *se)
> +{
> + xcb_generic_event_t *event;
> +
> + if(c->has_error)
> + return 0;
> + pthread_mutex_lock(&c->iolock);
> +
> + /* get_special_event returns 0 on empty list. */
> + while(!(event = get_special_event(c, se)))
> + if(!_xcb_conn_wait(c, &se->special_event_cond, 0, 0))
> + break;
> +
> + pthread_mutex_unlock(&c->iolock);
> + return event;
> +}
> +
> +xcb_special_event_t *
> +xcb_register_for_special_xge(xcb_connection_t *c,
> + uint8_t extension,
> + uint32_t eid,
> + uint32_t *stamp)
> +{
> + xcb_special_event_t *se;
Add this, please:
if(c->has_error)
return NULL;
> + pthread_mutex_lock(&c->iolock);
> + for (se = c->in.special_events; se; se = se->next) {
> + if (se->extension == extension &&
> + se->eid == eid)
> + break;
> + }
> + if (!se) {
> + se = calloc(1, sizeof(xcb_special_event_t));
> + se->extension = extension;
> + se->eid = eid;
> +
> + se->events = NULL;
> + se->events_tail = &se->events;
> +
> + pthread_cond_init(&se->special_event_cond, 0);
> +
> + se->next = c->in.special_events;
> + c->in.special_events = se;
> + }
> + se->stamp = stamp;
> + pthread_mutex_unlock(&c->iolock);
> + return se;
> +}
Uhm, this function just updates stamp if it calls for already existing events? I
don't like that, because this way the caller doesn't know if it has to call
_unregister again or not.
Is there any reason why this cannot return NULL on "collisisions"?
Oh and: Please check for calloc() failures.
> +void
> +xcb_unregister_for_special_event(xcb_connection_t *c,
> + xcb_special_event_t *se)
> +{
> + xcb_special_event_t *s, **prev;
> + struct event_list *events, *next;
if(c->has_error)
return;
> + pthread_mutex_lock(&c->iolock);
> +
> + for (prev = &c->in.special_events; (s = *prev) != NULL; prev = &(s->next)) {
> + if (s == se) {
> + *prev = se->next;
> + for (events = se->events; events; events = next) {
> + next = events->next;
> + free (events->event);
> + free (events);
> + }
> + pthread_cond_destroy(&se->special_event_cond);
> + free (se);
> + break;
> + }
> + }
> + pthread_mutex_unlock(&c->iolock);
> +}
> +
> /* Private interface */
>
> int _xcb_in_init(_xcb_in *in)
> diff --git a/src/xcbint.h b/src/xcbint.h
> index bbc5398..364dc59 100644
> --- a/src/xcbint.h
> +++ b/src/xcbint.h
> @@ -150,6 +150,7 @@ typedef struct _xcb_in {
> #if HAVE_SENDMSG
> _xcb_fd in_fd;
> #endif
> + struct xcb_special_event *special_events;
> } _xcb_in;
>
> int _xcb_in_init(_xcb_in *in);
>
--
"In the beginning the Universe was created. This has made a lot of
people very angry and has been widely regarded as a bad move."
More information about the Xcb
mailing list