diff options
Diffstat (limited to 'libX11/src/xcb_io.c')
-rw-r--r-- | libX11/src/xcb_io.c | 1518 |
1 files changed, 759 insertions, 759 deletions
diff --git a/libX11/src/xcb_io.c b/libX11/src/xcb_io.c index bac9bdf0b..300ef571f 100644 --- a/libX11/src/xcb_io.c +++ b/libX11/src/xcb_io.c @@ -1,759 +1,759 @@ -/* 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 <config.h>
-#endif
-
-#include "Xlibint.h"
-#include "locking.h"
-#include "Xprivate.h"
-#include "Xxcbint.h"
-#include <xcb/xcbext.h>
-
-#include <assert.h>
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-
-#define xcb_fail_assert(_message, _var) { \
- unsigned int _var = 1; \
- fprintf(stderr, "[xcb] Aborting, sorry about that.\n"); \
- assert(!_var); \
-}
-
-#define throw_thread_fail_assert(_message, _var) { \
- fprintf(stderr, "[xcb] " _message "\n"); \
- fprintf(stderr, "[xcb] Most likely this is a multi-threaded client " \
- "and XInitThreads has not been called\n"); \
- xcb_fail_assert(_message, _var); \
-}
-
-/* XXX: It would probably be most useful if we stored the last-processed
- * request, so we could find the offender from the message. */
-#define throw_extlib_fail_assert(_message, _var) { \
- fprintf(stderr, "[xcb] " _message "\n"); \
- fprintf(stderr, "[xcb] This is most likely caused by a broken X " \
- "extension library\n"); \
- xcb_fail_assert(_message, _var); \
-}
-
-static void return_socket(void *closure)
-{
- Display *dpy = closure;
- InternalLockDisplay(dpy, /* don't skip user locks */ 0);
- _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. */
- if (sizeof(unsigned long) > sizeof(unsigned int) &&
- dpy->xcb->event_owner == XlibOwnsEventQueue &&
- (sent - dpy->last_request_read >= (UINT64_C(1) << 32))) {
- throw_thread_fail_assert("Sequence number wrapped "
- "beyond 32 bits while Xlib "
- "did not own the socket",
- xcb_xlib_seq_number_wrapped);
- }
- 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.
- *
- * This code borrowed from _XWaitForReadable. Inverse call tree:
- * _XRead
- * _XWaitForWritable
- * _XFlush
- * _XSend
- * _XEventsQueued
- * _XReadEvents
- * _XRead[0-9]+
- * _XAllocIDs
- * _XReply
- * _XEatData
- * _XReadPad
- */
-static void check_internal_connections(Display *dpy)
-{
- struct _XConnectionInfo *ilist;
- fd_set r_mask;
- struct timeval tv;
- int result;
- int highest_fd = -1;
-
- if(dpy->flags & XlibDisplayProcConni || !dpy->im_fd_info)
- return;
-
- FD_ZERO(&r_mask);
- for(ilist = dpy->im_fd_info; ilist; ilist = ilist->next)
- {
- assert(ilist->fd >= 0);
- FD_SET(ilist->fd, &r_mask);
- if(ilist->fd > highest_fd)
- highest_fd = ilist->fd;
- }
- assert(highest_fd >= 0);
-
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- result = select(highest_fd + 1, &r_mask, NULL, NULL, &tv);
-
- if(result == -1)
- {
- if(errno == EINTR)
- return;
- _XIOError(dpy);
- }
-
- for(ilist = dpy->im_fd_info; result && ilist; ilist = ilist->next)
- if(FD_ISSET(ilist->fd, &r_mask))
- {
- _XProcessInternalConnection(dpy, ilist);
- --result;
- }
-}
-
-static PendingRequest *append_pending_request(Display *dpy, unsigned long sequence)
-{
- PendingRequest *node = malloc(sizeof(PendingRequest));
- assert(node);
- node->next = NULL;
- node->sequence = sequence;
- node->reply_waiter = 0;
- if(dpy->xcb->pending_requests_tail)
- {
- if (XLIB_SEQUENCE_COMPARE(dpy->xcb->pending_requests_tail->sequence,
- >=, node->sequence))
- throw_thread_fail_assert("Unknown sequence number "
- "while appending request",
- xcb_xlib_unknown_seq_number);
- if (dpy->xcb->pending_requests_tail->next != NULL)
- throw_thread_fail_assert("Unknown request in queue "
- "while appending request",
- xcb_xlib_unknown_req_pending);
- dpy->xcb->pending_requests_tail->next = node;
- }
- else
- dpy->xcb->pending_requests = node;
- dpy->xcb->pending_requests_tail = node;
- return node;
-}
-
-static void dequeue_pending_request(Display *dpy, PendingRequest *req)
-{
- if (req != dpy->xcb->pending_requests)
- throw_thread_fail_assert("Unknown request in queue while "
- "dequeuing",
- xcb_xlib_unknown_req_in_deq);
-
- dpy->xcb->pending_requests = req->next;
- if(!dpy->xcb->pending_requests)
- {
- if (req != dpy->xcb->pending_requests_tail)
- throw_thread_fail_assert("Unknown request in queue "
- "while dequeuing",
- xcb_xlib_unknown_req_in_deq);
- dpy->xcb->pending_requests_tail = NULL;
- }
- else if (XLIB_SEQUENCE_COMPARE(req->sequence, >=,
- dpy->xcb->pending_requests->sequence))
- throw_thread_fail_assert("Unknown sequence number while "
- "dequeuing request",
- xcb_xlib_threads_sequence_lost);
-
- free(req);
-}
-
-static int handle_error(Display *dpy, xError *err, Bool in_XReply)
-{
- _XExtension *ext;
- int ret_code;
- /* Oddly, Xlib only allows extensions to suppress errors when
- * those errors were seen by _XReply. */
- if(in_XReply)
- /*
- * 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))
- return ret_code;
- _XError(dpy, err);
- return 0;
-}
-
-/* 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);
-}
-
-/* Thread-safety rules:
- *
- * At most one thread can be reading from XCB's event queue at a time.
- * If you are not the current event-reading thread and you need to find
- * out if an event is available, you must wait.
- *
- * The same rule applies for reading replies.
- *
- * A single thread cannot be both the the event-reading and the
- * reply-reading thread at the same time.
- *
- * We always look at both the current event and the first pending reply
- * to decide which to process next.
- *
- * We always process all responses in sequence-number order, which may
- * mean waiting for another thread (either the event_waiter or the
- * reply_waiter) to handle an earlier response before we can process or
- * return a later one. If so, we wait on the corresponding condition
- * variable for that thread to process the response and wake us up.
- */
-
-static xcb_generic_reply_t *poll_for_event(Display *dpy)
-{
- /* Make sure the Display's sequence numbers are valid */
- require_socket(dpy);
-
- /* Precondition: This thread can safely get events from XCB. */
- assert(dpy->xcb->event_owner == XlibOwnsEventQueue && !dpy->xcb->event_waiter);
-
- if(!dpy->xcb->next_event)
- dpy->xcb->next_event = xcb_poll_for_event(dpy->xcb->connection);
-
- if(dpy->xcb->next_event)
- {
- PendingRequest *req = dpy->xcb->pending_requests;
- xcb_generic_event_t *event = dpy->xcb->next_event;
- unsigned long event_sequence = dpy->last_request_read;
- widen(&event_sequence, event->full_sequence);
- if(!req || XLIB_SEQUENCE_COMPARE(event_sequence, <, req->sequence)
- || (event->response_type != X_Error && event_sequence == req->sequence))
- {
- if (XLIB_SEQUENCE_COMPARE(event_sequence, >,
- dpy->request))
- {
- throw_thread_fail_assert("Unknown sequence "
- "number while "
- "processing queue",
- xcb_xlib_threads_sequence_lost);
- }
- dpy->last_request_read = event_sequence;
- dpy->xcb->next_event = NULL;
- return (xcb_generic_reply_t *) event;
- }
- }
- return NULL;
-}
-
-static xcb_generic_reply_t *poll_for_response(Display *dpy)
-{
- void *response;
- xcb_generic_error_t *error;
- PendingRequest *req;
- while(!(response = poll_for_event(dpy)) &&
- (req = dpy->xcb->pending_requests) &&
- !req->reply_waiter &&
- xcb_poll_for_reply(dpy->xcb->connection, req->sequence, &response, &error))
- {
- if(XLIB_SEQUENCE_COMPARE(req->sequence, >, dpy->request))
- {
- throw_thread_fail_assert("Unknown sequence number "
- "while awaiting reply",
- xcb_xlib_threads_sequence_lost);
- }
- dpy->last_request_read = req->sequence;
- if(response)
- break;
- dequeue_pending_request(dpy, req);
- if(error)
- return (xcb_generic_reply_t *) error;
- }
- return response;
-}
-
-static void handle_response(Display *dpy, xcb_generic_reply_t *response, Bool in_XReply)
-{
- _XAsyncHandler *async, *next;
- switch(response->response_type)
- {
- case X_Reply:
- for(async = dpy->async_handlers; async; async = next)
- {
- next = async->next;
- if(async->handler(dpy, (xReply *) response, (char *) response, sizeof(xReply) + (response->length << 2), async->data))
- break;
- }
- break;
-
- case X_Error:
- handle_error(dpy, (xError *) response, in_XReply);
- break;
-
- default: /* event */
- /* 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(response->response_type == GenericEvent && ((xcb_ge_event_t *) response)->length)
- {
- xcb_ge_event_t *event = (xcb_ge_event_t *) response;
- memmove(&event->full_sequence, &event[1], event->length * 4);
- }
- _XEnq(dpy, (xEvent *) response);
- break;
- }
- free(response);
-}
-
-int _XEventsQueued(Display *dpy, int mode)
-{
- xcb_generic_reply_t *response;
- if(dpy->flags & XlibDisplayIOError)
- return 0;
- if(dpy->xcb->event_owner != XlibOwnsEventQueue)
- return 0;
-
- if(mode == QueuedAfterFlush)
- _XSend(dpy, NULL, 0);
- else
- check_internal_connections(dpy);
-
- /* If another thread is blocked waiting for events, then we must
- * let that thread pick up the next event. Since it blocked, we
- * can reasonably claim there are no new events right now. */
- if(!dpy->xcb->event_waiter)
- {
- while((response = poll_for_response(dpy)))
- handle_response(dpy, response, False);
- if(xcb_connection_has_error(dpy->xcb->connection))
- _XIOError(dpy);
- }
- return dpy->qlen;
-}
-
-/* _XReadEvents - Flush the output queue,
- * then read as many events as possible (but at least 1) and enqueue them
- */
-void _XReadEvents(Display *dpy)
-{
- xcb_generic_reply_t *response;
- unsigned long serial;
-
- if(dpy->flags & XlibDisplayIOError)
- return;
- _XSend(dpy, NULL, 0);
- if(dpy->xcb->event_owner != XlibOwnsEventQueue)
- return;
- check_internal_connections(dpy);
-
- serial = dpy->next_event_serial_num;
- while(serial == dpy->next_event_serial_num || dpy->qlen == 0)
- {
- if(dpy->xcb->event_waiter)
- {
- ConditionWait(dpy, dpy->xcb->event_notify);
- /* Maybe the other thread got us an event. */
- continue;
- }
-
- if(!dpy->xcb->next_event)
- {
- xcb_generic_event_t *event;
- dpy->xcb->event_waiter = 1;
- UnlockDisplay(dpy);
- event = xcb_wait_for_event(dpy->xcb->connection);
- /* It appears that classic Xlib respected user
- * locks when waking up after waiting for
- * events. However, if this thread did not have
- * any user locks, and another thread takes a
- * user lock and tries to read events, then we'd
- * deadlock. So we'll choose to let the thread
- * that got in first consume events, despite the
- * later thread's user locks. */
- InternalLockDisplay(dpy, /* ignore user locks */ 1);
- dpy->xcb->event_waiter = 0;
- ConditionBroadcast(dpy, dpy->xcb->event_notify);
- if(!event)
- _XIOError(dpy);
- dpy->xcb->next_event = event;
- }
-
- /* We've established most of the conditions for
- * poll_for_response to return non-NULL. The exceptions
- * are connection shutdown, and finding that another
- * thread is waiting for the next reply we'd like to
- * process. */
-
- response = poll_for_response(dpy);
- if(response)
- handle_response(dpy, response, False);
- else if(dpy->xcb->pending_requests->reply_waiter)
- { /* need braces around ConditionWait */
- ConditionWait(dpy, dpy->xcb->reply_notify);
- }
- else
- _XIOError(dpy);
- }
-
- /* The preceding loop established that there is no
- * event_waiter--unless we just called ConditionWait because of
- * a reply_waiter, in which case another thread may have become
- * the event_waiter while we slept unlocked. */
- if(!dpy->xcb->event_waiter)
- while((response = poll_for_response(dpy)))
- handle_response(dpy, response, False);
- if(xcb_connection_has_error(dpy->xcb->connection))
- _XIOError(dpy);
-}
-
-/*
- * _XSend - Flush the buffer and send the client data. 32 bit word aligned
- * transmission is used, if size is not 0 mod 4, extra bytes are transmitted.
- *
- * Note that the connection must not be read from once the data currently
- * in the buffer has been written.
- */
-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;
-
- if(dpy->bufptr == dpy->buffer && !size)
- return;
-
- /* 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 + 1; sequence <= dpy->request; ++sequence)
- append_pending_request(dpy, sequence);
- }
- requests = dpy->request - dpy->xcb->last_flushed;
- dpy->xcb->last_flushed = dpy->request;
-
- 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);
- }
-
- if(xcb_writev(c, vec, 3, requests) < 0)
- _XIOError(dpy);
- dpy->bufptr = dpy->buffer;
- dpy->last_req = (char *) &dummy_request;
-
- check_internal_connections(dpy);
-
- _XSetSeqSyncFunction(dpy);
-}
-
-/*
- * _XFlush - Flush the X request buffer. If the buffer is empty, no
- * action is taken.
- */
-void _XFlush(Display *dpy)
-{
- require_socket(dpy);
- _XSend(dpy, NULL, 0);
-
- _XEventsQueued(dpy, QueuedAfterReading);
-}
-
-static const XID inval_id = ~0UL;
-
-void _XIDHandler(Display *dpy)
-{
- if (dpy->xcb->next_xid == inval_id)
- _XAllocIDs(dpy, &dpy->xcb->next_xid, 1);
-}
-
-/* _XAllocID - resource ID allocation routine. */
-XID _XAllocID(Display *dpy)
-{
- XID ret = dpy->xcb->next_xid;
- assert (ret != inval_id);
- dpy->xcb->next_xid = inval_id;
- _XSetPrivSyncFunction(dpy);
- return ret;
-}
-
-/* _XAllocIDs - multiple resource ID allocation routine. */
-void _XAllocIDs(Display *dpy, XID *ids, int count)
-{
- int i;
-#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);
-#ifdef XTHREADS
- InternalLockDisplay(dpy, /* don't skip user locks */ 0);
- if (dpy->lock)
- (*dpy->lock->user_unlock_display)(dpy);
-#endif
-}
-
-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 = NULL;
-}
-
-/*
- * _XReply - Wait for a reply packet and copy its contents into the
- * specified rep.
- * extra: number of 32-bit words expected after the reply
- * discard: should I discard data following "extra" words?
- */
-Status _XReply(Display *dpy, xReply *rep, int extra, Bool discard)
-{
- xcb_generic_error_t *error;
- xcb_connection_t *c = dpy->xcb->connection;
- char *reply;
- PendingRequest *current;
-
- if (dpy->xcb->reply_data)
- throw_extlib_fail_assert("Extra reply data still left in queue",
- xcb_xlib_extra_reply_data_left);
-
- if(dpy->flags & XlibDisplayIOError)
- return 0;
-
- _XSend(dpy, NULL, 0);
- if(dpy->xcb->pending_requests_tail && dpy->xcb->pending_requests_tail->sequence == dpy->request)
- current = dpy->xcb->pending_requests_tail;
- else
- current = append_pending_request(dpy, dpy->request);
- /* Don't let any other thread get this reply. */
- current->reply_waiter = 1;
-
- while(1)
- {
- PendingRequest *req = dpy->xcb->pending_requests;
- xcb_generic_reply_t *response;
-
- if(req != current && req->reply_waiter)
- {
- ConditionWait(dpy, dpy->xcb->reply_notify);
- /* Another thread got this reply. */
- continue;
- }
- req->reply_waiter = 1;
- UnlockDisplay(dpy);
- response = xcb_wait_for_reply(c, req->sequence, &error);
- /* Any user locks on another thread must have been taken
- * while we slept in xcb_wait_for_reply. Classic Xlib
- * ignored those user locks in this case, so we do too. */
- InternalLockDisplay(dpy, /* ignore user locks */ 1);
-
- /* We have the response we're looking for. Now, before
- * letting anyone else process this sequence number, we
- * need to process any events that should have come
- * earlier. */
-
- if(dpy->xcb->event_owner == XlibOwnsEventQueue)
- {
- xcb_generic_reply_t *event;
- /* If some thread is already waiting for events,
- * it will get the first one. That thread must
- * process that event before we can continue. */
- /* FIXME: That event might be after this reply,
- * and might never even come--or there might be
- * multiple threads trying to get events. */
- while(dpy->xcb->event_waiter)
- { /* need braces around ConditionWait */
- ConditionWait(dpy, dpy->xcb->event_notify);
- }
- while((event = poll_for_event(dpy)))
- handle_response(dpy, event, True);
- }
-
- req->reply_waiter = 0;
- ConditionBroadcast(dpy, dpy->xcb->reply_notify);
- if(XLIB_SEQUENCE_COMPARE(req->sequence, >, dpy->request)) {
- throw_thread_fail_assert("Unknown sequence number "
- "while processing reply",
- xcb_xlib_threads_sequence_lost);
- }
- dpy->last_request_read = req->sequence;
- if(!response)
- dequeue_pending_request(dpy, req);
-
- if(req == current)
- {
- reply = (char *) response;
- break;
- }
-
- if(error)
- handle_response(dpy, (xcb_generic_reply_t *) error, True);
- else if(response)
- handle_response(dpy, response, True);
- }
- check_internal_connections(dpy);
-
- if(dpy->xcb->next_event && dpy->xcb->next_event->response_type == X_Error)
- {
- xcb_generic_event_t *event = dpy->xcb->next_event;
- unsigned long event_sequence = dpy->last_request_read;
- widen(&event_sequence, event->full_sequence);
- if(event_sequence == dpy->last_request_read)
- {
- error = (xcb_generic_error_t *) event;
- dpy->xcb->next_event = NULL;
- }
- }
-
- if(error)
- {
- int ret_code;
-
- /* Xlib is evil and assumes that even errors will be
- * copied into rep. */
- memcpy(rep, error, 32);
-
- /* do not die on "no such font", "can't allocate",
- "can't grab" failures */
- switch(error->error_code)
- {
- case BadName:
- switch(error->major_code)
- {
- case X_LookupColor:
- case X_AllocNamedColor:
- free(error);
- return 0;
- }
- break;
- case BadFont:
- if(error->major_code == X_QueryFont) {
- free(error);
- return 0;
- }
- break;
- case BadAlloc:
- case BadAccess:
- free(error);
- return 0;
- }
-
- ret_code = handle_error(dpy, (xError *) error, True);
- free(error);
- return ret_code;
- }
-
- /* it's not an error, but we don't have a reply, so it's an I/O
- * error. */
- if(!reply)
- {
- _XIOError(dpy);
- return 0;
- }
-
- /* there's no error and we have a reply. */
- dpy->xcb->reply_data = reply;
- dpy->xcb->reply_consumed = sizeof(xReply) + (extra * 4);
- dpy->xcb->reply_length = sizeof(xReply);
- if(dpy->xcb->reply_data[0] == 1)
- dpy->xcb->reply_length += (((xcb_generic_reply_t *) dpy->xcb->reply_data)->length * 4);
-
- /* error: Xlib asks too much. give them what we can anyway. */
- if(dpy->xcb->reply_length < dpy->xcb->reply_consumed)
- dpy->xcb->reply_consumed = dpy->xcb->reply_length;
-
- memcpy(rep, dpy->xcb->reply_data, dpy->xcb->reply_consumed);
- _XFreeReplyData(dpy, discard);
- return 1;
-}
-
-int _XRead(Display *dpy, char *data, long size)
-{
- assert(size >= 0);
- if(size == 0)
- return 0;
- if(dpy->xcb->reply_data == NULL ||
- dpy->xcb->reply_consumed + size > dpy->xcb->reply_length)
- throw_extlib_fail_assert("Too much data requested from _XRead",
- xcb_xlib_too_much_data_requested);
- memcpy(data, dpy->xcb->reply_data + dpy->xcb->reply_consumed, size);
- dpy->xcb->reply_consumed += size;
- _XFreeReplyData(dpy, False);
- return 0;
-}
-
-/*
- * _XReadPad - Read bytes from the socket taking into account incomplete
- * reads. If the number of bytes is not 0 mod 4, read additional pad
- * bytes.
- */
-void _XReadPad(Display *dpy, char *data, long size)
-{
- _XRead(dpy, data, size);
- dpy->xcb->reply_consumed += -size & 3;
- _XFreeReplyData(dpy, False);
-}
-
-/* Read and discard "n" 8-bit bytes of data */
-void _XEatData(Display *dpy, unsigned long n)
-{
- dpy->xcb->reply_consumed += n;
- _XFreeReplyData(dpy, False);
-}
+/* 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 <config.h> +#endif + +#include "Xlibint.h" +#include "locking.h" +#include "Xprivate.h" +#include "Xxcbint.h" +#include <xcb/xcbext.h> + +#include <assert.h> +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#define xcb_fail_assert(_message, _var) { \ + unsigned int _var = 1; \ + fprintf(stderr, "[xcb] Aborting, sorry about that.\n"); \ + assert(!_var); \ +} + +#define throw_thread_fail_assert(_message, _var) { \ + fprintf(stderr, "[xcb] " _message "\n"); \ + fprintf(stderr, "[xcb] Most likely this is a multi-threaded client " \ + "and XInitThreads has not been called\n"); \ + xcb_fail_assert(_message, _var); \ +} + +/* XXX: It would probably be most useful if we stored the last-processed + * request, so we could find the offender from the message. */ +#define throw_extlib_fail_assert(_message, _var) { \ + fprintf(stderr, "[xcb] " _message "\n"); \ + fprintf(stderr, "[xcb] This is most likely caused by a broken X " \ + "extension library\n"); \ + xcb_fail_assert(_message, _var); \ +} + +static void return_socket(void *closure) +{ + Display *dpy = closure; + InternalLockDisplay(dpy, /* don't skip user locks */ 0); + _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. */ + if (sizeof(unsigned long) > sizeof(unsigned int) && + dpy->xcb->event_owner == XlibOwnsEventQueue && + (sent - dpy->last_request_read >= (UINT64_C(1) << 32))) { + throw_thread_fail_assert("Sequence number wrapped " + "beyond 32 bits while Xlib " + "did not own the socket", + xcb_xlib_seq_number_wrapped); + } + 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. + * + * This code borrowed from _XWaitForReadable. Inverse call tree: + * _XRead + * _XWaitForWritable + * _XFlush + * _XSend + * _XEventsQueued + * _XReadEvents + * _XRead[0-9]+ + * _XAllocIDs + * _XReply + * _XEatData + * _XReadPad + */ +static void check_internal_connections(Display *dpy) +{ + struct _XConnectionInfo *ilist; + fd_set r_mask; + struct timeval tv; + int result; + int highest_fd = -1; + + if(dpy->flags & XlibDisplayProcConni || !dpy->im_fd_info) + return; + + FD_ZERO(&r_mask); + for(ilist = dpy->im_fd_info; ilist; ilist = ilist->next) + { + assert(ilist->fd >= 0); + FD_SET(ilist->fd, &r_mask); + if(ilist->fd > highest_fd) + highest_fd = ilist->fd; + } + assert(highest_fd >= 0); + + tv.tv_sec = 0; + tv.tv_usec = 0; + result = select(highest_fd + 1, &r_mask, NULL, NULL, &tv); + + if(result == -1) + { + if(errno == EINTR) + return; + _XIOError(dpy); + } + + for(ilist = dpy->im_fd_info; result && ilist; ilist = ilist->next) + if(FD_ISSET(ilist->fd, &r_mask)) + { + _XProcessInternalConnection(dpy, ilist); + --result; + } +} + +static PendingRequest *append_pending_request(Display *dpy, unsigned long sequence) +{ + PendingRequest *node = malloc(sizeof(PendingRequest)); + assert(node); + node->next = NULL; + node->sequence = sequence; + node->reply_waiter = 0; + if(dpy->xcb->pending_requests_tail) + { + if (XLIB_SEQUENCE_COMPARE(dpy->xcb->pending_requests_tail->sequence, + >=, node->sequence)) + throw_thread_fail_assert("Unknown sequence number " + "while appending request", + xcb_xlib_unknown_seq_number); + if (dpy->xcb->pending_requests_tail->next != NULL) + throw_thread_fail_assert("Unknown request in queue " + "while appending request", + xcb_xlib_unknown_req_pending); + dpy->xcb->pending_requests_tail->next = node; + } + else + dpy->xcb->pending_requests = node; + dpy->xcb->pending_requests_tail = node; + return node; +} + +static void dequeue_pending_request(Display *dpy, PendingRequest *req) +{ + if (req != dpy->xcb->pending_requests) + throw_thread_fail_assert("Unknown request in queue while " + "dequeuing", + xcb_xlib_unknown_req_in_deq); + + dpy->xcb->pending_requests = req->next; + if(!dpy->xcb->pending_requests) + { + if (req != dpy->xcb->pending_requests_tail) + throw_thread_fail_assert("Unknown request in queue " + "while dequeuing", + xcb_xlib_unknown_req_in_deq); + dpy->xcb->pending_requests_tail = NULL; + } + else if (XLIB_SEQUENCE_COMPARE(req->sequence, >=, + dpy->xcb->pending_requests->sequence)) + throw_thread_fail_assert("Unknown sequence number while " + "dequeuing request", + xcb_xlib_threads_sequence_lost); + + free(req); +} + +static int handle_error(Display *dpy, xError *err, Bool in_XReply) +{ + _XExtension *ext; + int ret_code; + /* Oddly, Xlib only allows extensions to suppress errors when + * those errors were seen by _XReply. */ + if(in_XReply) + /* + * 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)) + return ret_code; + _XError(dpy, err); + return 0; +} + +/* 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); +} + +/* Thread-safety rules: + * + * At most one thread can be reading from XCB's event queue at a time. + * If you are not the current event-reading thread and you need to find + * out if an event is available, you must wait. + * + * The same rule applies for reading replies. + * + * A single thread cannot be both the the event-reading and the + * reply-reading thread at the same time. + * + * We always look at both the current event and the first pending reply + * to decide which to process next. + * + * We always process all responses in sequence-number order, which may + * mean waiting for another thread (either the event_waiter or the + * reply_waiter) to handle an earlier response before we can process or + * return a later one. If so, we wait on the corresponding condition + * variable for that thread to process the response and wake us up. + */ + +static xcb_generic_reply_t *poll_for_event(Display *dpy) +{ + /* Make sure the Display's sequence numbers are valid */ + require_socket(dpy); + + /* Precondition: This thread can safely get events from XCB. */ + assert(dpy->xcb->event_owner == XlibOwnsEventQueue && !dpy->xcb->event_waiter); + + if(!dpy->xcb->next_event) + dpy->xcb->next_event = xcb_poll_for_event(dpy->xcb->connection); + + if(dpy->xcb->next_event) + { + PendingRequest *req = dpy->xcb->pending_requests; + xcb_generic_event_t *event = dpy->xcb->next_event; + unsigned long event_sequence = dpy->last_request_read; + widen(&event_sequence, event->full_sequence); + if(!req || XLIB_SEQUENCE_COMPARE(event_sequence, <, req->sequence) + || (event->response_type != X_Error && event_sequence == req->sequence)) + { + if (XLIB_SEQUENCE_COMPARE(event_sequence, >, + dpy->request)) + { + throw_thread_fail_assert("Unknown sequence " + "number while " + "processing queue", + xcb_xlib_threads_sequence_lost); + } + dpy->last_request_read = event_sequence; + dpy->xcb->next_event = NULL; + return (xcb_generic_reply_t *) event; + } + } + return NULL; +} + +static xcb_generic_reply_t *poll_for_response(Display *dpy) +{ + void *response; + xcb_generic_error_t *error; + PendingRequest *req; + while(!(response = poll_for_event(dpy)) && + (req = dpy->xcb->pending_requests) && + !req->reply_waiter && + xcb_poll_for_reply(dpy->xcb->connection, req->sequence, &response, &error)) + { + if(XLIB_SEQUENCE_COMPARE(req->sequence, >, dpy->request)) + { + throw_thread_fail_assert("Unknown sequence number " + "while awaiting reply", + xcb_xlib_threads_sequence_lost); + } + dpy->last_request_read = req->sequence; + if(response) + break; + dequeue_pending_request(dpy, req); + if(error) + return (xcb_generic_reply_t *) error; + } + return response; +} + +static void handle_response(Display *dpy, xcb_generic_reply_t *response, Bool in_XReply) +{ + _XAsyncHandler *async, *next; + switch(response->response_type) + { + case X_Reply: + for(async = dpy->async_handlers; async; async = next) + { + next = async->next; + if(async->handler(dpy, (xReply *) response, (char *) response, sizeof(xReply) + (response->length << 2), async->data)) + break; + } + break; + + case X_Error: + handle_error(dpy, (xError *) response, in_XReply); + break; + + default: /* event */ + /* 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(response->response_type == GenericEvent && ((xcb_ge_event_t *) response)->length) + { + xcb_ge_event_t *event = (xcb_ge_event_t *) response; + memmove(&event->full_sequence, &event[1], event->length * 4); + } + _XEnq(dpy, (xEvent *) response); + break; + } + free(response); +} + +int _XEventsQueued(Display *dpy, int mode) +{ + xcb_generic_reply_t *response; + if(dpy->flags & XlibDisplayIOError) + return 0; + if(dpy->xcb->event_owner != XlibOwnsEventQueue) + return 0; + + if(mode == QueuedAfterFlush) + _XSend(dpy, NULL, 0); + else + check_internal_connections(dpy); + + /* If another thread is blocked waiting for events, then we must + * let that thread pick up the next event. Since it blocked, we + * can reasonably claim there are no new events right now. */ + if(!dpy->xcb->event_waiter) + { + while((response = poll_for_response(dpy))) + handle_response(dpy, response, False); + if(xcb_connection_has_error(dpy->xcb->connection)) + _XIOError(dpy); + } + return dpy->qlen; +} + +/* _XReadEvents - Flush the output queue, + * then read as many events as possible (but at least 1) and enqueue them + */ +void _XReadEvents(Display *dpy) +{ + xcb_generic_reply_t *response; + unsigned long serial; + + if(dpy->flags & XlibDisplayIOError) + return; + _XSend(dpy, NULL, 0); + if(dpy->xcb->event_owner != XlibOwnsEventQueue) + return; + check_internal_connections(dpy); + + serial = dpy->next_event_serial_num; + while(serial == dpy->next_event_serial_num || dpy->qlen == 0) + { + if(dpy->xcb->event_waiter) + { + ConditionWait(dpy, dpy->xcb->event_notify); + /* Maybe the other thread got us an event. */ + continue; + } + + if(!dpy->xcb->next_event) + { + xcb_generic_event_t *event; + dpy->xcb->event_waiter = 1; + UnlockDisplay(dpy); + event = xcb_wait_for_event(dpy->xcb->connection); + /* It appears that classic Xlib respected user + * locks when waking up after waiting for + * events. However, if this thread did not have + * any user locks, and another thread takes a + * user lock and tries to read events, then we'd + * deadlock. So we'll choose to let the thread + * that got in first consume events, despite the + * later thread's user locks. */ + InternalLockDisplay(dpy, /* ignore user locks */ 1); + dpy->xcb->event_waiter = 0; + ConditionBroadcast(dpy, dpy->xcb->event_notify); + if(!event) + _XIOError(dpy); + dpy->xcb->next_event = event; + } + + /* We've established most of the conditions for + * poll_for_response to return non-NULL. The exceptions + * are connection shutdown, and finding that another + * thread is waiting for the next reply we'd like to + * process. */ + + response = poll_for_response(dpy); + if(response) + handle_response(dpy, response, False); + else if(dpy->xcb->pending_requests->reply_waiter) + { /* need braces around ConditionWait */ + ConditionWait(dpy, dpy->xcb->reply_notify); + } + else + _XIOError(dpy); + } + + /* The preceding loop established that there is no + * event_waiter--unless we just called ConditionWait because of + * a reply_waiter, in which case another thread may have become + * the event_waiter while we slept unlocked. */ + if(!dpy->xcb->event_waiter) + while((response = poll_for_response(dpy))) + handle_response(dpy, response, False); + if(xcb_connection_has_error(dpy->xcb->connection)) + _XIOError(dpy); +} + +/* + * _XSend - Flush the buffer and send the client data. 32 bit word aligned + * transmission is used, if size is not 0 mod 4, extra bytes are transmitted. + * + * Note that the connection must not be read from once the data currently + * in the buffer has been written. + */ +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; + + if(dpy->bufptr == dpy->buffer && !size) + return; + + /* 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 + 1; sequence <= dpy->request; ++sequence) + append_pending_request(dpy, sequence); + } + requests = dpy->request - dpy->xcb->last_flushed; + dpy->xcb->last_flushed = dpy->request; + + vec[0].iov_base = dpy->buffer; + vec[0].iov_len = dpy->bufptr - dpy->buffer; + vec[1].iov_base = (char *)data; + vec[1].iov_len = size; + vec[2].iov_base = (char *)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); + } + + if(xcb_writev(c, vec, 3, requests) < 0) + _XIOError(dpy); + dpy->bufptr = dpy->buffer; + dpy->last_req = (char *) &dummy_request; + + check_internal_connections(dpy); + + _XSetSeqSyncFunction(dpy); +} + +/* + * _XFlush - Flush the X request buffer. If the buffer is empty, no + * action is taken. + */ +void _XFlush(Display *dpy) +{ + require_socket(dpy); + _XSend(dpy, NULL, 0); + + _XEventsQueued(dpy, QueuedAfterReading); +} + +static const XID inval_id = ~0UL; + +void _XIDHandler(Display *dpy) +{ + if (dpy->xcb->next_xid == inval_id) + _XAllocIDs(dpy, &dpy->xcb->next_xid, 1); +} + +/* _XAllocID - resource ID allocation routine. */ +XID _XAllocID(Display *dpy) +{ + XID ret = dpy->xcb->next_xid; + assert (ret != inval_id); + dpy->xcb->next_xid = inval_id; + _XSetPrivSyncFunction(dpy); + return ret; +} + +/* _XAllocIDs - multiple resource ID allocation routine. */ +void _XAllocIDs(Display *dpy, XID *ids, int count) +{ + int i; +#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); +#ifdef XTHREADS + InternalLockDisplay(dpy, /* don't skip user locks */ 0); + if (dpy->lock) + (*dpy->lock->user_unlock_display)(dpy); +#endif +} + +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 = NULL; +} + +/* + * _XReply - Wait for a reply packet and copy its contents into the + * specified rep. + * extra: number of 32-bit words expected after the reply + * discard: should I discard data following "extra" words? + */ +Status _XReply(Display *dpy, xReply *rep, int extra, Bool discard) +{ + xcb_generic_error_t *error; + xcb_connection_t *c = dpy->xcb->connection; + char *reply; + PendingRequest *current; + + if (dpy->xcb->reply_data) + throw_extlib_fail_assert("Extra reply data still left in queue", + xcb_xlib_extra_reply_data_left); + + if(dpy->flags & XlibDisplayIOError) + return 0; + + _XSend(dpy, NULL, 0); + if(dpy->xcb->pending_requests_tail && dpy->xcb->pending_requests_tail->sequence == dpy->request) + current = dpy->xcb->pending_requests_tail; + else + current = append_pending_request(dpy, dpy->request); + /* Don't let any other thread get this reply. */ + current->reply_waiter = 1; + + while(1) + { + PendingRequest *req = dpy->xcb->pending_requests; + xcb_generic_reply_t *response; + + if(req != current && req->reply_waiter) + { + ConditionWait(dpy, dpy->xcb->reply_notify); + /* Another thread got this reply. */ + continue; + } + req->reply_waiter = 1; + UnlockDisplay(dpy); + response = xcb_wait_for_reply(c, req->sequence, &error); + /* Any user locks on another thread must have been taken + * while we slept in xcb_wait_for_reply. Classic Xlib + * ignored those user locks in this case, so we do too. */ + InternalLockDisplay(dpy, /* ignore user locks */ 1); + + /* We have the response we're looking for. Now, before + * letting anyone else process this sequence number, we + * need to process any events that should have come + * earlier. */ + + if(dpy->xcb->event_owner == XlibOwnsEventQueue) + { + xcb_generic_reply_t *event; + /* If some thread is already waiting for events, + * it will get the first one. That thread must + * process that event before we can continue. */ + /* FIXME: That event might be after this reply, + * and might never even come--or there might be + * multiple threads trying to get events. */ + while(dpy->xcb->event_waiter) + { /* need braces around ConditionWait */ + ConditionWait(dpy, dpy->xcb->event_notify); + } + while((event = poll_for_event(dpy))) + handle_response(dpy, event, True); + } + + req->reply_waiter = 0; + ConditionBroadcast(dpy, dpy->xcb->reply_notify); + if(XLIB_SEQUENCE_COMPARE(req->sequence, >, dpy->request)) { + throw_thread_fail_assert("Unknown sequence number " + "while processing reply", + xcb_xlib_threads_sequence_lost); + } + dpy->last_request_read = req->sequence; + if(!response) + dequeue_pending_request(dpy, req); + + if(req == current) + { + reply = (char *) response; + break; + } + + if(error) + handle_response(dpy, (xcb_generic_reply_t *) error, True); + else if(response) + handle_response(dpy, response, True); + } + check_internal_connections(dpy); + + if(dpy->xcb->next_event && dpy->xcb->next_event->response_type == X_Error) + { + xcb_generic_event_t *event = dpy->xcb->next_event; + unsigned long event_sequence = dpy->last_request_read; + widen(&event_sequence, event->full_sequence); + if(event_sequence == dpy->last_request_read) + { + error = (xcb_generic_error_t *) event; + dpy->xcb->next_event = NULL; + } + } + + if(error) + { + int ret_code; + + /* Xlib is evil and assumes that even errors will be + * copied into rep. */ + memcpy(rep, error, 32); + + /* do not die on "no such font", "can't allocate", + "can't grab" failures */ + switch(error->error_code) + { + case BadName: + switch(error->major_code) + { + case X_LookupColor: + case X_AllocNamedColor: + free(error); + return 0; + } + break; + case BadFont: + if(error->major_code == X_QueryFont) { + free(error); + return 0; + } + break; + case BadAlloc: + case BadAccess: + free(error); + return 0; + } + + ret_code = handle_error(dpy, (xError *) error, True); + free(error); + return ret_code; + } + + /* it's not an error, but we don't have a reply, so it's an I/O + * error. */ + if(!reply) + { + _XIOError(dpy); + return 0; + } + + /* there's no error and we have a reply. */ + dpy->xcb->reply_data = reply; + dpy->xcb->reply_consumed = sizeof(xReply) + (extra * 4); + dpy->xcb->reply_length = sizeof(xReply); + if(dpy->xcb->reply_data[0] == 1) + dpy->xcb->reply_length += (((xcb_generic_reply_t *) dpy->xcb->reply_data)->length * 4); + + /* error: Xlib asks too much. give them what we can anyway. */ + if(dpy->xcb->reply_length < dpy->xcb->reply_consumed) + dpy->xcb->reply_consumed = dpy->xcb->reply_length; + + memcpy(rep, dpy->xcb->reply_data, dpy->xcb->reply_consumed); + _XFreeReplyData(dpy, discard); + return 1; +} + +int _XRead(Display *dpy, char *data, long size) +{ + assert(size >= 0); + if(size == 0) + return 0; + if(dpy->xcb->reply_data == NULL || + dpy->xcb->reply_consumed + size > dpy->xcb->reply_length) + throw_extlib_fail_assert("Too much data requested from _XRead", + xcb_xlib_too_much_data_requested); + memcpy(data, dpy->xcb->reply_data + dpy->xcb->reply_consumed, size); + dpy->xcb->reply_consumed += size; + _XFreeReplyData(dpy, False); + return 0; +} + +/* + * _XReadPad - Read bytes from the socket taking into account incomplete + * reads. If the number of bytes is not 0 mod 4, read additional pad + * bytes. + */ +void _XReadPad(Display *dpy, char *data, long size) +{ + _XRead(dpy, data, size); + dpy->xcb->reply_consumed += -size & 3; + _XFreeReplyData(dpy, False); +} + +/* Read and discard "n" 8-bit bytes of data */ +void _XEatData(Display *dpy, unsigned long n) +{ + dpy->xcb->reply_consumed += n; + _XFreeReplyData(dpy, False); +} |