diff options
Diffstat (limited to 'libX11/src/xcb_io.c')
-rw-r--r-- | libX11/src/xcb_io.c | 300 |
1 files changed, 194 insertions, 106 deletions
diff --git a/libX11/src/xcb_io.c b/libX11/src/xcb_io.c index 5f0718496..4f0159c98 100644 --- a/libX11/src/xcb_io.c +++ b/libX11/src/xcb_io.c @@ -3,14 +3,53 @@ #include "Xlibint.h" #include "locking.h" +#include "Xprivate.h" #include "Xxcbint.h" #include <xcb/xcbext.h> -#include <xcb/xcbxlib.h> #include <assert.h> +#include <inttypes.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> +static void return_socket(void *closure) +{ + Display *dpy = closure; + LockDisplay(dpy); + _XSend(dpy, NULL, 0); + dpy->bufmax = dpy->buffer; + UnlockDisplay(dpy); +} + +static void require_socket(Display *dpy) +{ + if(dpy->bufmax == dpy->buffer) + { + uint64_t sent; + int flags = 0; + /* if we don't own the event queue, we have to ask XCB + * to set our errors aside for us. */ + if(dpy->xcb->event_owner != XlibOwnsEventQueue) + flags = XCB_REQUEST_CHECKED; + if(!xcb_take_socket(dpy->xcb->connection, return_socket, dpy, + flags, &sent)) + _XIOError(dpy); + /* Xlib uses unsigned long for sequence numbers. XCB + * uses 64-bit internally, but currently exposes an + * unsigned int API. If these differ, Xlib cannot track + * the full 64-bit sequence number if 32-bit wrap + * happens while Xlib does not own the socket. A + * complete fix would be to make XCB's public API use + * 64-bit sequence numbers. */ + assert(!(sizeof(unsigned long) > sizeof(unsigned int) + && dpy->xcb->event_owner == XlibOwnsEventQueue + && (sent - dpy->last_request_read >= (UINT64_C(1) << 32)))); + dpy->xcb->last_flushed = dpy->request = sent; + dpy->bufmax = dpy->xcb->real_bufmax; + } +} + /* Call internal connection callbacks for any fds that are currently * ready to read. This function will not block unless one of the * callbacks blocks. @@ -30,7 +69,7 @@ */ static void check_internal_connections(Display *dpy) { - struct _XConnectionInfo *ilist; + struct _XConnectionInfo *ilist; fd_set r_mask; struct timeval tv; int result; @@ -68,15 +107,6 @@ static void check_internal_connections(Display *dpy) } } -static void condition_wait(Display *dpy, xcondition_t cv) -{ - _XPutXCBBuffer(dpy); - xcb_xlib_unlock(dpy->xcb->connection); - ConditionWait(dpy, cv); - xcb_xlib_lock(dpy->xcb->connection); - _XGetXCBBuffer(dpy); -} - static void call_handlers(Display *dpy, xcb_generic_reply_t *buf) { _XAsyncHandler *async, *next; @@ -96,16 +126,37 @@ static xcb_generic_event_t * wait_or_poll_for_event(Display *dpy, int wait) xcb_generic_event_t *event; if(wait) { - UnlockDisplay(dpy); - event = xcb_wait_for_event(c); - LockDisplay(dpy); + if(dpy->xcb->event_waiter) + { + ConditionWait(dpy, dpy->xcb->event_notify); + event = xcb_poll_for_event(c); + } + else + { + dpy->xcb->event_waiter = 1; + UnlockDisplay(dpy); + event = xcb_wait_for_event(c); + LockDisplay(dpy); + dpy->xcb->event_waiter = 0; + ConditionBroadcast(dpy, dpy->xcb->event_notify); + } } else event = xcb_poll_for_event(c); return event; } -static void process_responses(Display *dpy, int wait_for_first_event, xcb_generic_error_t **current_error, unsigned int current_request) +/* Widen a 32-bit sequence number into a native-word-size (unsigned long) + * sequence number. Treating the comparison as a 1 and shifting it avoids a + * conditional branch, and shifting by 16 twice avoids a compiler warning when + * sizeof(unsigned long) == 4. */ +static void widen(unsigned long *wide, unsigned int narrow) +{ + unsigned long new = (*wide & ~0xFFFFFFFFUL) | narrow; + *wide = new + ((unsigned long) (new < *wide) << 16 << 16); +} + +static void process_responses(Display *dpy, int wait_for_first_event, xcb_generic_error_t **current_error, unsigned long current_request) { void *reply; xcb_generic_event_t *event = dpy->xcb->next_event; @@ -114,24 +165,42 @@ static void process_responses(Display *dpy, int wait_for_first_event, xcb_generi if(!event && dpy->xcb->event_owner == XlibOwnsEventQueue) event = wait_or_poll_for_event(dpy, wait_for_first_event); + require_socket(dpy); + while(1) { PendingRequest *req = dpy->xcb->pending_requests; - assert(!(req && current_request && !XCB_SEQUENCE_COMPARE(req->sequence, <=, current_request))); - if(event && (!req || XCB_SEQUENCE_COMPARE(event->full_sequence, <=, req->sequence))) + unsigned long event_sequence = dpy->last_request_read; + if(event) + widen(&event_sequence, event->full_sequence); + assert(!(req && current_request && !XLIB_SEQUENCE_COMPARE(req->sequence, <=, current_request))); + if(event && (!req || XLIB_SEQUENCE_COMPARE(event_sequence, <=, req->sequence))) { - dpy->last_request_read = event->full_sequence; + dpy->last_request_read = event_sequence; if(event->response_type != X_Error) { + /* GenericEvents may be > 32 bytes. In this + * case, the event struct is trailed by the + * additional bytes. the xcb_generic_event_t + * struct uses 4 bytes for internal numbering, + * so we need to shift the trailing data to be + * after the first 32 bytes. */ + if (event->response_type == GenericEvent && + ((xcb_ge_event_t*)event)->length) + { + memmove(&event->full_sequence, + &event[1], + ((xcb_ge_event_t*)event)->length * 4); + } _XEnq(dpy, (xEvent *) event); wait_for_first_event = 0; } - else if(current_error && event->full_sequence == current_request) + else if(current_error && event_sequence == current_request) { /* This can only occur when called from * _XReply, which doesn't need a new event. */ *current_error = (xcb_generic_error_t *) event; - event = 0; + event = NULL; break; } else @@ -139,22 +208,13 @@ static void process_responses(Display *dpy, int wait_for_first_event, xcb_generi free(event); event = wait_or_poll_for_event(dpy, wait_for_first_event); } - else if(req && req->waiters != -1) + else if(req && req->sequence == current_request) { - if(req->sequence == current_request) - break; - if(!current_request && !wait_for_first_event) - break; - dpy->xcb->next_event = event; - req->waiters++; - assert(req->waiters > 0); - condition_wait(dpy, &req->condition); - --req->waiters; - event = dpy->xcb->next_event; + break; } else if(req && xcb_poll_for_reply(dpy->xcb->connection, req->sequence, &reply, &error)) { - unsigned int sequence = req->sequence; + uint64_t sequence = req->sequence; if(!reply) { dpy->xcb->pending_requests = req->next; @@ -179,8 +239,7 @@ static void process_responses(Display *dpy, int wait_for_first_event, xcb_generi if(xcb_connection_has_error(c)) _XIOError(dpy); - assert_sequence_less(dpy->last_request_read, dpy->request); - assert(!wait_for_first_event); + assert(XLIB_SEQUENCE_COMPARE(dpy->last_request_read, <=, dpy->request)); } int _XEventsQueued(Display *dpy, int mode) @@ -191,10 +250,10 @@ int _XEventsQueued(Display *dpy, int mode) return 0; if(mode == QueuedAfterFlush) - _XSend(dpy, 0, 0); + _XSend(dpy, NULL, 0); else check_internal_connections(dpy); - process_responses(dpy, 0, 0, 0); + process_responses(dpy, 0, NULL, 0); return dpy->qlen; } @@ -205,11 +264,13 @@ void _XReadEvents(Display *dpy) { if(dpy->flags & XlibDisplayIOError) return; - _XSend(dpy, 0, 0); + _XSend(dpy, NULL, 0); if(dpy->xcb->event_owner != XlibOwnsEventQueue) return; check_internal_connections(dpy); - process_responses(dpy, 1, 0, 0); + do { + process_responses(dpy, 1, NULL, 0); + } while (dpy->qlen == 0); } /* @@ -221,32 +282,61 @@ void _XReadEvents(Display *dpy) */ void _XSend(Display *dpy, const char *data, long size) { + static const xReq dummy_request; + static char const pad[3]; + struct iovec vec[3]; + uint64_t requests; + _XExtension *ext; xcb_connection_t *c = dpy->xcb->connection; if(dpy->flags & XlibDisplayIOError) return; - assert(!dpy->xcb->request_extra); - dpy->xcb->request_extra = data; - dpy->xcb->request_extra_size = size; + if(dpy->bufptr == dpy->buffer && !size) + return; - /* give dpy->buffer to XCB */ - _XPutXCBBuffer(dpy); + /* iff we asked XCB to set aside errors, we must pick those up + * eventually. iff there are async handlers, we may have just + * issued requests that will generate replies. in either case, + * we need to remember to check later. */ + if(dpy->xcb->event_owner != XlibOwnsEventQueue || dpy->async_handlers) + { + uint64_t sequence; + for(sequence = dpy->xcb->last_flushed; sequence < dpy->request; ++sequence) + { + PendingRequest *req = malloc(sizeof(PendingRequest)); + assert(req); + req->next = NULL; + req->sequence = sequence; + *dpy->xcb->pending_requests_tail = req; + dpy->xcb->pending_requests_tail = &req->next; + } + } + requests = dpy->request - dpy->xcb->last_flushed; + dpy->xcb->last_flushed = dpy->request; - if(xcb_flush(c) <= 0) - _XIOError(dpy); + vec[0].iov_base = dpy->buffer; + vec[0].iov_len = dpy->bufptr - dpy->buffer; + vec[1].iov_base = (caddr_t) data; + vec[1].iov_len = size; + vec[2].iov_base = (caddr_t) pad; + vec[2].iov_len = -size & 3; + + for(ext = dpy->flushes; ext; ext = ext->next_flush) + { + int i; + for(i = 0; i < 3; ++i) + if(vec[i].iov_len) + ext->before_flush(dpy, &ext->codes, vec[i].iov_base, vec[i].iov_len); + } - /* get a new dpy->buffer */ - _XGetXCBBuffer(dpy); + if(xcb_writev(c, vec, 3, requests) < 0) + _XIOError(dpy); + dpy->bufptr = dpy->buffer; + dpy->last_req = (char *) &dummy_request; check_internal_connections(dpy); - /* A straight port of XlibInt.c would call _XSetSeqSyncFunction - * here. However that does no good: unlike traditional Xlib, - * Xlib/XCB almost never calls _XFlush because _XPutXCBBuffer - * automatically pushes requests down into XCB, so Xlib's buffer - * is empty most of the time. Since setting a synchandler has no - * effect until after UnlockDisplay returns, we may as well do - * the check in _XUnlockDisplay. */ + _XSetSeqSyncFunction(dpy); } /* @@ -255,24 +345,29 @@ void _XSend(Display *dpy, const char *data, long size) */ void _XFlush(Display *dpy) { - _XSend(dpy, 0, 0); + require_socket(dpy); + _XSend(dpy, NULL, 0); _XEventsQueued(dpy, QueuedAfterReading); } -static int -_XIDHandler(Display *dpy) +static const XID inval_id = ~0UL; + +int _XIDHandler(Display *dpy) { - XID next = xcb_generate_id(dpy->xcb->connection); + XID next; + + if (dpy->xcb->next_xid != inval_id) + return 0; + + next = xcb_generate_id(dpy->xcb->connection); LockDisplay(dpy); dpy->xcb->next_xid = next; - if(dpy->flags & XlibDisplayPrivSync) - { - dpy->synchandler = dpy->savedsynchandler; - dpy->flags &= ~XlibDisplayPrivSync; - } +#ifdef XTHREADS + if (dpy->lock) + (*dpy->lock->user_unlock_display)(dpy); +#endif UnlockDisplay(dpy); - SyncHandle(); return 0; } @@ -280,14 +375,13 @@ _XIDHandler(Display *dpy) XID _XAllocID(Display *dpy) { XID ret = dpy->xcb->next_xid; - dpy->xcb->next_xid = 0; - - if(!(dpy->flags & XlibDisplayPrivSync)) - { - dpy->savedsynchandler = dpy->synchandler; - dpy->flags |= XlibDisplayPrivSync; - } - dpy->synchandler = _XIDHandler; + assert (ret != inval_id); +#ifdef XTHREADS + if (dpy->lock) + (*dpy->lock->user_lock_display)(dpy); +#endif + dpy->xcb->next_xid = inval_id; + _XSetPrivSyncFunction(dpy); return ret; } @@ -295,10 +389,18 @@ XID _XAllocID(Display *dpy) void _XAllocIDs(Display *dpy, XID *ids, int count) { int i; - _XPutXCBBuffer(dpy); +#ifdef XTHREADS + if (dpy->lock) + (*dpy->lock->user_lock_display)(dpy); + UnlockDisplay(dpy); +#endif for (i = 0; i < count; i++) ids[i] = xcb_generate_id(dpy->xcb->connection); - _XGetXCBBuffer(dpy); +#ifdef XTHREADS + LockDisplay(dpy); + if (dpy->lock) + (*dpy->lock->user_unlock_display)(dpy); +#endif } static void _XFreeReplyData(Display *dpy, Bool force) @@ -306,22 +408,15 @@ static void _XFreeReplyData(Display *dpy, Bool force) if(!force && dpy->xcb->reply_consumed < dpy->xcb->reply_length) return; free(dpy->xcb->reply_data); - dpy->xcb->reply_data = 0; + dpy->xcb->reply_data = NULL; } static PendingRequest * insert_pending_request(Display *dpy) { PendingRequest **cur = &dpy->xcb->pending_requests; - while(*cur && XCB_SEQUENCE_COMPARE((*cur)->sequence, <, dpy->request)) + while(*cur && XLIB_SEQUENCE_COMPARE((*cur)->sequence, <, dpy->request)) cur = &((*cur)->next); - if(*cur && (*cur)->sequence == dpy->request) - { - /* Replacing an existing PendingRequest should only happen once, - when calling _XReply, and the replaced PendingRequest must - not have a condition set. */ - assert((*cur)->waiters == -1); - } - else + if(!*cur || (*cur)->sequence != dpy->request) { PendingRequest *node = malloc(sizeof(PendingRequest)); assert(node); @@ -331,8 +426,6 @@ static PendingRequest * insert_pending_request(Display *dpy) dpy->xcb->pending_requests_tail = &(node->next); *cur = node; } - (*cur)->waiters = 0; - xcondition_init(&((*cur)->condition)); return *cur; } @@ -354,27 +447,15 @@ Status _XReply(Display *dpy, xReply *rep, int extra, Bool discard) if(dpy->flags & XlibDisplayIOError) return 0; - /* Internals of UnlockDisplay done by hand here, so that we can - insert_pending_request *after* we _XPutXCBBuffer, but before we - unlock the display. */ - _XPutXCBBuffer(dpy); + _XSend(dpy, NULL, 0); current = insert_pending_request(dpy); - if(!dpy->lock || dpy->lock->locking_level == 0) - xcb_xlib_unlock(dpy->xcb->connection); - if(dpy->xcb->lock_fns.unlock_display) - dpy->xcb->lock_fns.unlock_display(dpy); + /* FIXME: drop the Display lock while waiting? + * Complicates process_responses. */ reply = xcb_wait_for_reply(c, current->sequence, &error); - LockDisplay(dpy); check_internal_connections(dpy); process_responses(dpy, 0, &error, current->sequence); - if(current->waiters) - { /* The ConditionBroadcast macro contains an if; braces needed here. */ - ConditionBroadcast(dpy, ¤t->condition); - } - --current->waiters; - if(error) { _XExtension *ext; @@ -396,27 +477,34 @@ Status _XReply(Display *dpy, xReply *rep, int extra, Bool discard) { case X_LookupColor: case X_AllocNamedColor: + free(error); return 0; } break; case BadFont: - if(err->majorCode == X_QueryFont) + if(err->majorCode == X_QueryFont) { + free(error); return 0; + } break; case BadAlloc: case BadAccess: + free(error); return 0; } - /* + /* * we better see if there is an extension who may * want to suppress the error. */ for(ext = dpy->ext_procs; ext; ext = ext->next) - if(ext->error && ext->error(dpy, err, &ext->codes, &ret_code)) + if(ext->error && ext->error(dpy, err, &ext->codes, &ret_code)) { + free(error); return ret_code; + } - _XError(dpy, (xError *) error); + _XError(dpy, err); + free(error); return 0; } @@ -451,7 +539,7 @@ int _XRead(Display *dpy, char *data, long size) assert(size >= 0); if(size == 0) return 0; - assert(dpy->xcb->reply_data != 0); + assert(dpy->xcb->reply_data != NULL); assert(dpy->xcb->reply_consumed + size <= dpy->xcb->reply_length); memcpy(data, dpy->xcb->reply_data + dpy->xcb->reply_consumed, size); dpy->xcb->reply_consumed += size; |