/* Copyright (C) 2003-2006 Jamey Sharp, Josh Triplett * This file is licensed under the MIT license. See the file COPYING. */ #ifdef HAVE_CONFIG_H #include #endif #include "Xlibint.h" #include "locking.h" #include "Xxcbint.h" #include #include #include static void _XCBLockDisplay(Display *dpy) { if(dpy->xcb->lock_fns.lock_display) dpy->xcb->lock_fns.lock_display(dpy); if(!dpy->lock || dpy->lock->locking_level == 0) xcb_xlib_lock(dpy->xcb->connection); if(!(dpy->flags & XlibDisplayIOError)) _XGetXCBBuffer(dpy); } /* XXX: If you change this function, update _XReply's copy of its guts! */ static void _XCBUnlockDisplay(Display *dpy) { if(!(dpy->flags & XlibDisplayIOError)) { _XPutXCBBuffer(dpy); assert(dpy->xcb->partial_request == 0); assert(xcb_get_request_sent(dpy->xcb->connection) == dpy->request); /* Traditional Xlib does this in _XSend; see the Xlib/XCB version * of that function for why we do it here instead. */ _XSetSeqSyncFunction(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); } int _XCBInitDisplayLock(Display *dpy) { if(!dpy->lock_fns && !(dpy->lock_fns = Xcalloc(1, sizeof(*dpy->lock_fns)))) return 0; dpy->xcb->lock_fns.lock_display = dpy->lock_fns->lock_display; dpy->lock_fns->lock_display = _XCBLockDisplay; dpy->xcb->lock_fns.unlock_display = dpy->lock_fns->unlock_display; dpy->lock_fns->unlock_display = _XCBUnlockDisplay; return 1; } void _XCBShutdownDisplayLock(Display *dpy) { if(dpy->lock_fns) { Xfree((char *)dpy->lock_fns); dpy->lock_fns = NULL; } } void _XGetXCBBuffer(Display *dpy) { static const xReq dummy_request; unsigned int xcb_req = xcb_get_request_sent(dpy->xcb->connection); if(xcb_connection_has_error(dpy->xcb->connection)) _XIOError(dpy); /* if Xlib has a partial request pending then XCB doesn't know about * the current request yet */ if(dpy->xcb->partial_request) ++xcb_req; assert(XCB_SEQUENCE_COMPARE(xcb_req, >=, dpy->request)); dpy->request = xcb_req; dpy->last_req = (char *) &dummy_request; } static size_t request_length(struct iovec *vec) { /* we have at least part of a request. dig out the length field. * note that length fields are always in vec[0]: Xlib doesn't split * fixed-length request parts. */ size_t len; assert(vec[0].iov_len >= 4); len = ((uint16_t *) vec[0].iov_base)[1]; if(len == 0) { /* it's a bigrequest. dig out the *real* length field. */ assert(vec[0].iov_len >= 8); len = ((uint32_t *) vec[0].iov_base)[1]; } return len << 2; } static inline int issue_complete_request(Display *dpy, int veclen, struct iovec *vec) { xcb_protocol_request_t xcb_req = { 0 }; unsigned int sequence; int flags = XCB_REQUEST_RAW; int i; size_t len; /* skip empty iovecs. if no iovecs remain, we're done. */ assert(veclen >= 0); while(veclen > 0 && vec[0].iov_len == 0) --veclen, ++vec; if(!veclen) return 0; len = request_length(vec); /* do we have enough data for a complete request? how many iovec * elements does it span? */ for(i = 0; i < veclen; ++i) { size_t oldlen = len; len -= vec[i].iov_len; /* if len is now 0 or has wrapped, we have enough data. */ if((len - 1) > oldlen) break; } if(i == veclen) return 0; /* we have enough data to issue one complete request. the remaining * code can't fail. */ /* len says how far we overshot our data needs. (it's "negative" if * we actually overshot, or 0 if we're right on.) */ vec[i].iov_len += len; xcb_req.count = i + 1; xcb_req.opcode = ((uint8_t *) vec[0].iov_base)[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; /* XCB will always skip request 0; account for that in the Xlib count */ if (xcb_get_request_sent(dpy->xcb->connection) == 0xffffffff) dpy->request++; /* send the accumulated request. */ sequence = xcb_send_request(dpy->xcb->connection, flags, vec, &xcb_req); if(!sequence) _XIOError(dpy); /* update the iovecs to refer only to data not yet sent. */ vec[i].iov_len = -len; /* 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(flags & XCB_REQUEST_CHECKED || dpy->async_handlers) { PendingRequest *req = malloc(sizeof(PendingRequest)); assert(req); req->next = 0; req->waiters = -1; req->sequence = sequence; *dpy->xcb->pending_requests_tail = req; dpy->xcb->pending_requests_tail = &req->next; } return 1; } void _XPutXCBBuffer(Display *dpy) { static char const pad[3]; const int padsize = -dpy->xcb->request_extra_size & 3; xcb_connection_t *c = dpy->xcb->connection; _XExtension *ext; struct iovec iov[6]; assert_sequence_less(dpy->last_request_read, dpy->request); assert_sequence_less(xcb_get_request_sent(c), dpy->request); for(ext = dpy->flushes; ext; ext = ext->next_flush) { ext->before_flush(dpy, &ext->codes, dpy->buffer, dpy->bufptr - dpy->buffer); if(dpy->xcb->request_extra) { ext->before_flush(dpy, &ext->codes, dpy->xcb->request_extra, dpy->xcb->request_extra_size); if(padsize) ext->before_flush(dpy, &ext->codes, pad, padsize); } } iov[2].iov_base = dpy->xcb->partial_request; iov[2].iov_len = dpy->xcb->partial_request_offset; iov[3].iov_base = dpy->buffer; iov[3].iov_len = dpy->bufptr - dpy->buffer; iov[4].iov_base = (caddr_t) dpy->xcb->request_extra; iov[4].iov_len = dpy->xcb->request_extra_size; iov[5].iov_base = (caddr_t) pad; iov[5].iov_len = padsize; while(issue_complete_request(dpy, 4, iov + 2)) /* empty */; /* first discard any completed partial_request. */ if(iov[2].iov_len == 0 && dpy->xcb->partial_request) { free(dpy->xcb->partial_request); dpy->xcb->partial_request = 0; dpy->xcb->partial_request_offset = 0; } /* is there anything to copy into partial_request? */ if(iov[3].iov_len != 0 || iov[4].iov_len != 0 || iov[5].iov_len != 0) { int i; if(!dpy->xcb->partial_request) { size_t len = request_length(iov + 3); assert(!dpy->xcb->partial_request_offset); dpy->xcb->partial_request = malloc(len); assert(dpy->xcb->partial_request); } for(i = 3; i < sizeof(iov) / sizeof(*iov); ++i) { memcpy(dpy->xcb->partial_request + dpy->xcb->partial_request_offset, iov[i].iov_base, iov[i].iov_len); dpy->xcb->partial_request_offset += iov[i].iov_len; } } dpy->xcb->request_extra = 0; dpy->xcb->request_extra_size = 0; dpy->bufptr = dpy->buffer; }