diff --git a/src/Xxcbint.h b/src/Xxcbint.h index 4ef13d2f..d8293259 100644 --- a/src/Xxcbint.h +++ b/src/Xxcbint.h @@ -27,6 +27,7 @@ typedef struct _X11XCBPrivate { PendingRequest *pending_requests; PendingRequest *pending_requests_tail; xcb_generic_event_t *next_event; + void *next_response; char *real_bufmax; char *reply_data; int reply_length; diff --git a/src/xcb_io.c b/src/xcb_io.c index 0bd1fdf9..4be75408 100644 --- a/src/xcb_io.c +++ b/src/xcb_io.c @@ -282,22 +282,83 @@ static xcb_generic_reply_t *poll_for_event(Display *dpy, Bool queued_only) static xcb_generic_reply_t *poll_for_response(Display *dpy) { void *response; - xcb_generic_error_t *error; + xcb_generic_reply_t *event; PendingRequest *req; - while(!(response = poll_for_event(dpy, False)) && - (req = dpy->xcb->pending_requests) && - !req->reply_waiter) + + while(1) { + xcb_generic_error_t *error = NULL; uint64_t request; + Bool poll_queued_only = dpy->xcb->next_response != NULL; - if(!xcb_poll_for_reply64(dpy->xcb->connection, req->sequence, - &response, &error)) { - /* xcb_poll_for_reply64 may have read events even if - * there is no reply. */ - response = poll_for_event(dpy, True); + /* Step 1: is there an event in our queue before the next + * reply/error? Return that first. + * + * If we don't have a reply/error saved from an earlier + * invocation we check incoming events too, otherwise only + * the ones already queued. + */ + response = poll_for_event(dpy, poll_queued_only); + if(response) break; + + /* Step 2: + * Response is NULL, i.e. we have no events. + * If we are not waiting for a reply or some other thread + * had dibs on the next reply, exit. + */ + req = dpy->xcb->pending_requests; + if(!req || req->reply_waiter) + break; + + /* Step 3: + * We have some response (error or reply) related to req + * saved from an earlier invocation of this function. Let's + * use that one. + */ + if(dpy->xcb->next_response) + { + if (((xcb_generic_reply_t*)dpy->xcb->next_response)->response_type == X_Error) + { + error = dpy->xcb->next_response; + response = NULL; + } + else + { + response = dpy->xcb->next_response; + error = NULL; + } + dpy->xcb->next_response = NULL; + } + else + { + /* Step 4: pull down the next response from the wire. This + * should be the 99% case. + * xcb_poll_for_reply64() may also pull down events that + * happened before the reply. + */ + if(!xcb_poll_for_reply64(dpy->xcb->connection, req->sequence, + &response, &error)) { + /* if there is no reply/error, xcb_poll_for_reply64 + * may have read events. Return that. */ + response = poll_for_event(dpy, True); + break; + } + + /* Step 5: we have a new response, but we may also have some + * events that happened before that response. Return those + * first and save our reply/error for the next invocation. + */ + event = poll_for_event(dpy, True); + if(event) + { + dpy->xcb->next_response = error ? error : response; + response = event; + break; + } } + /* Step 6: actually handle the reply/error now... */ request = X_DPY_GET_REQUEST(dpy); if(XLIB_SEQUENCE_COMPARE(req->sequence, >, request)) {