Blame src/xcb_io.c

Packit 5bd3a9
/* Copyright (C) 2003-2006 Jamey Sharp, Josh Triplett
Packit 5bd3a9
 * This file is licensed under the MIT license. See the file COPYING. */
Packit 5bd3a9
Packit 5bd3a9
#ifdef HAVE_CONFIG_H
Packit 5bd3a9
#include <config.h>
Packit 5bd3a9
#endif
Packit 5bd3a9
Packit 5bd3a9
#include "Xlibint.h"
Packit 5bd3a9
#include "locking.h"
Packit 5bd3a9
#include "Xprivate.h"
Packit 5bd3a9
#include "Xxcbint.h"
Packit 5bd3a9
#include <xcb/xcbext.h>
Packit 5bd3a9
Packit 5bd3a9
#include <assert.h>
Packit 5bd3a9
#ifdef HAVE_INTTYPES_H
Packit 5bd3a9
#include <inttypes.h>
Packit 5bd3a9
#endif
Packit 5bd3a9
#include <stdio.h>
Packit 5bd3a9
#include <stdint.h>
Packit 5bd3a9
#include <stdlib.h>
Packit 5bd3a9
#include <string.h>
Packit 5bd3a9
#include <limits.h>
Packit 5bd3a9
#ifdef HAVE_SYS_SELECT_H
Packit 5bd3a9
#include <sys/select.h>
Packit 5bd3a9
#endif
Packit 5bd3a9
Packit 5bd3a9
#define xcb_fail_assert(_message, _var) { \
Packit 5bd3a9
	unsigned int _var = 1; \
Packit 5bd3a9
	fprintf(stderr, "[xcb] Aborting, sorry about that.\n"); \
Packit 5bd3a9
	assert(!_var); \
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
#define throw_thread_fail_assert(_message, _var) { \
Packit 5bd3a9
	fprintf(stderr, "[xcb] " _message "\n"); \
Packit 5bd3a9
	fprintf(stderr, "[xcb] Most likely this is a multi-threaded client " \
Packit 5bd3a9
	                "and XInitThreads has not been called\n"); \
Packit 5bd3a9
	xcb_fail_assert(_message, _var); \
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/* XXX: It would probably be most useful if we stored the last-processed
Packit 5bd3a9
 *      request, so we could find the offender from the message. */
Packit 5bd3a9
#define throw_extlib_fail_assert(_message, _var) { \
Packit 5bd3a9
	fprintf(stderr, "[xcb] " _message "\n"); \
Packit 5bd3a9
	fprintf(stderr, "[xcb] This is most likely caused by a broken X " \
Packit 5bd3a9
	                "extension library\n"); \
Packit 5bd3a9
	xcb_fail_assert(_message, _var); \
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
static void return_socket(void *closure)
Packit 5bd3a9
{
Packit 5bd3a9
	Display *dpy = closure;
Packit 5bd3a9
	InternalLockDisplay(dpy, /* don't skip user locks */ 0);
Packit 5bd3a9
	_XSend(dpy, NULL, 0);
Packit 5bd3a9
	dpy->bufmax = dpy->buffer;
Packit 5bd3a9
	UnlockDisplay(dpy);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
static void require_socket(Display *dpy)
Packit 5bd3a9
{
Packit 5bd3a9
	if(dpy->bufmax == dpy->buffer)
Packit 5bd3a9
	{
Packit 5bd3a9
		uint64_t sent;
Packit 5bd3a9
		int flags = 0;
Packit 5bd3a9
		/* if we don't own the event queue, we have to ask XCB
Packit 5bd3a9
		 * to set our errors aside for us. */
Packit 5bd3a9
		if(dpy->xcb->event_owner != XlibOwnsEventQueue)
Packit 5bd3a9
			flags = XCB_REQUEST_CHECKED;
Packit 5bd3a9
		if(!xcb_take_socket(dpy->xcb->connection, return_socket, dpy,
Packit 5bd3a9
		                    flags, &sent))
Packit 5bd3a9
			_XIOError(dpy);
Packit 5bd3a9
		dpy->xcb->last_flushed = sent;
Packit 5bd3a9
		X_DPY_SET_REQUEST(dpy, sent);
Packit 5bd3a9
		dpy->bufmax = dpy->xcb->real_bufmax;
Packit 5bd3a9
	}
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/* Call internal connection callbacks for any fds that are currently
Packit 5bd3a9
 * ready to read. This function will not block unless one of the
Packit 5bd3a9
 * callbacks blocks.
Packit 5bd3a9
 *
Packit 5bd3a9
 * This code borrowed from _XWaitForReadable. Inverse call tree:
Packit 5bd3a9
 * _XRead
Packit 5bd3a9
 *  _XWaitForWritable
Packit 5bd3a9
 *   _XFlush
Packit 5bd3a9
 *   _XSend
Packit 5bd3a9
 *  _XEventsQueued
Packit 5bd3a9
 *  _XReadEvents
Packit 5bd3a9
 *  _XRead[0-9]+
Packit 5bd3a9
 *   _XAllocIDs
Packit 5bd3a9
 *  _XReply
Packit 5bd3a9
 *  _XEatData
Packit 5bd3a9
 * _XReadPad
Packit 5bd3a9
 */
Packit 5bd3a9
static void check_internal_connections(Display *dpy)
Packit 5bd3a9
{
Packit 5bd3a9
	struct _XConnectionInfo *ilist;
Packit 5bd3a9
	fd_set r_mask;
Packit 5bd3a9
	struct timeval tv;
Packit 5bd3a9
	int result;
Packit 5bd3a9
	int highest_fd = -1;
Packit 5bd3a9
Packit 5bd3a9
	if(dpy->flags & XlibDisplayProcConni || !dpy->im_fd_info)
Packit 5bd3a9
		return;
Packit 5bd3a9
Packit 5bd3a9
	FD_ZERO(&r_mask);
Packit 5bd3a9
	for(ilist = dpy->im_fd_info; ilist; ilist = ilist->next)
Packit 5bd3a9
	{
Packit 5bd3a9
		assert(ilist->fd >= 0);
Packit 5bd3a9
		FD_SET(ilist->fd, &r_mask);
Packit 5bd3a9
		if(ilist->fd > highest_fd)
Packit 5bd3a9
			highest_fd = ilist->fd;
Packit 5bd3a9
	}
Packit 5bd3a9
	assert(highest_fd >= 0);
Packit 5bd3a9
Packit 5bd3a9
	tv.tv_sec = 0;
Packit 5bd3a9
	tv.tv_usec = 0;
Packit 5bd3a9
	result = select(highest_fd + 1, &r_mask, NULL, NULL, &tv;;
Packit 5bd3a9
Packit 5bd3a9
	if(result == -1)
Packit 5bd3a9
	{
Packit 5bd3a9
		if(errno == EINTR)
Packit 5bd3a9
			return;
Packit 5bd3a9
		_XIOError(dpy);
Packit 5bd3a9
	}
Packit 5bd3a9
Packit 5bd3a9
	for(ilist = dpy->im_fd_info; result && ilist; ilist = ilist->next)
Packit 5bd3a9
		if(FD_ISSET(ilist->fd, &r_mask))
Packit 5bd3a9
		{
Packit 5bd3a9
			_XProcessInternalConnection(dpy, ilist);
Packit 5bd3a9
			--result;
Packit 5bd3a9
		}
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
static PendingRequest *append_pending_request(Display *dpy, uint64_t sequence)
Packit 5bd3a9
{
Packit 5bd3a9
	PendingRequest *node = malloc(sizeof(PendingRequest));
Packit 5bd3a9
	assert(node);
Packit 5bd3a9
	node->next = NULL;
Packit 5bd3a9
	node->sequence = sequence;
Packit 5bd3a9
	node->reply_waiter = 0;
Packit 5bd3a9
	if(dpy->xcb->pending_requests_tail)
Packit 5bd3a9
	{
Packit 5bd3a9
		if (XLIB_SEQUENCE_COMPARE(dpy->xcb->pending_requests_tail->sequence,
Packit 5bd3a9
		                          >=, node->sequence))
Packit 5bd3a9
			throw_thread_fail_assert("Unknown sequence number "
Packit 5bd3a9
			                         "while appending request",
Packit 5bd3a9
			                         xcb_xlib_unknown_seq_number);
Packit 5bd3a9
		if (dpy->xcb->pending_requests_tail->next != NULL)
Packit 5bd3a9
			throw_thread_fail_assert("Unknown request in queue "
Packit 5bd3a9
			                         "while appending request",
Packit 5bd3a9
			                         xcb_xlib_unknown_req_pending);
Packit 5bd3a9
		dpy->xcb->pending_requests_tail->next = node;
Packit 5bd3a9
	}
Packit 5bd3a9
	else
Packit 5bd3a9
		dpy->xcb->pending_requests = node;
Packit 5bd3a9
	dpy->xcb->pending_requests_tail = node;
Packit 5bd3a9
	return node;
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
static void dequeue_pending_request(Display *dpy, PendingRequest *req)
Packit 5bd3a9
{
Packit 5bd3a9
	if (req != dpy->xcb->pending_requests)
Packit 5bd3a9
		throw_thread_fail_assert("Unknown request in queue while "
Packit 5bd3a9
		                         "dequeuing",
Packit 5bd3a9
		                         xcb_xlib_unknown_req_in_deq);
Packit 5bd3a9
Packit 5bd3a9
	dpy->xcb->pending_requests = req->next;
Packit 5bd3a9
	if(!dpy->xcb->pending_requests)
Packit 5bd3a9
	{
Packit 5bd3a9
		if (req != dpy->xcb->pending_requests_tail)
Packit 5bd3a9
			throw_thread_fail_assert("Unknown request in queue "
Packit 5bd3a9
			                         "while dequeuing",
Packit 5bd3a9
			                         xcb_xlib_unknown_req_in_deq);
Packit 5bd3a9
		dpy->xcb->pending_requests_tail = NULL;
Packit 5bd3a9
	}
Packit 5bd3a9
	else if (XLIB_SEQUENCE_COMPARE(req->sequence, >=,
Packit 5bd3a9
	                               dpy->xcb->pending_requests->sequence))
Packit 5bd3a9
		throw_thread_fail_assert("Unknown sequence number while "
Packit 5bd3a9
		                         "dequeuing request",
Packit 5bd3a9
		                         xcb_xlib_threads_sequence_lost);
Packit 5bd3a9
Packit 5bd3a9
	free(req);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
static int handle_error(Display *dpy, xError *err, Bool in_XReply)
Packit 5bd3a9
{
Packit 5bd3a9
	_XExtension *ext;
Packit 5bd3a9
	int ret_code;
Packit 5bd3a9
	/* Oddly, Xlib only allows extensions to suppress errors when
Packit 5bd3a9
	 * those errors were seen by _XReply. */
Packit 5bd3a9
	if(in_XReply)
Packit 5bd3a9
		/*
Packit 5bd3a9
		 * we better see if there is an extension who may
Packit 5bd3a9
		 * want to suppress the error.
Packit 5bd3a9
		 */
Packit 5bd3a9
		for(ext = dpy->ext_procs; ext; ext = ext->next)
Packit 5bd3a9
			if(ext->error && (*ext->error)(dpy, err, &ext->codes, &ret_code))
Packit 5bd3a9
				return ret_code;
Packit 5bd3a9
	_XError(dpy, err);
Packit 5bd3a9
	return 0;
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/* Widen a 32-bit sequence number into a 64bit (uint64_t) sequence number.
Packit 5bd3a9
 */
Packit 5bd3a9
static void widen(uint64_t *wide, unsigned int narrow)
Packit 5bd3a9
{
Packit Service b6c31d
	*wide += (int32_t) (narrow - *wide);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/* Thread-safety rules:
Packit 5bd3a9
 *
Packit 5bd3a9
 * At most one thread can be reading from XCB's event queue at a time.
Packit 5bd3a9
 * If you are not the current event-reading thread and you need to find
Packit 5bd3a9
 * out if an event is available, you must wait.
Packit 5bd3a9
 *
Packit 5bd3a9
 * The same rule applies for reading replies.
Packit 5bd3a9
 *
Packit 5bd3a9
 * A single thread cannot be both the the event-reading and the
Packit 5bd3a9
 * reply-reading thread at the same time.
Packit 5bd3a9
 *
Packit 5bd3a9
 * We always look at both the current event and the first pending reply
Packit 5bd3a9
 * to decide which to process next.
Packit 5bd3a9
 *
Packit 5bd3a9
 * We always process all responses in sequence-number order, which may
Packit 5bd3a9
 * mean waiting for another thread (either the event_waiter or the
Packit 5bd3a9
 * reply_waiter) to handle an earlier response before we can process or
Packit 5bd3a9
 * return a later one. If so, we wait on the corresponding condition
Packit 5bd3a9
 * variable for that thread to process the response and wake us up.
Packit 5bd3a9
 */
Packit 5bd3a9
Packit 5bd3a9
static xcb_generic_reply_t *poll_for_event(Display *dpy, Bool queued_only)
Packit 5bd3a9
{
Packit 5bd3a9
	/* Make sure the Display's sequence numbers are valid */
Packit 5bd3a9
	require_socket(dpy);
Packit 5bd3a9
Packit 5bd3a9
	/* Precondition: This thread can safely get events from XCB. */
Packit 5bd3a9
	assert(dpy->xcb->event_owner == XlibOwnsEventQueue && !dpy->xcb->event_waiter);
Packit 5bd3a9
Packit 5bd3a9
	if(!dpy->xcb->next_event) {
Packit 5bd3a9
		if(queued_only)
Packit 5bd3a9
			dpy->xcb->next_event = xcb_poll_for_queued_event(dpy->xcb->connection);
Packit 5bd3a9
		else
Packit 5bd3a9
			dpy->xcb->next_event = xcb_poll_for_event(dpy->xcb->connection);
Packit 5bd3a9
	}
Packit 5bd3a9
Packit 5bd3a9
	if(dpy->xcb->next_event)
Packit 5bd3a9
	{
Packit 5bd3a9
		PendingRequest *req = dpy->xcb->pending_requests;
Packit 5bd3a9
		xcb_generic_event_t *event = dpy->xcb->next_event;
Packit 5bd3a9
		uint64_t event_sequence = X_DPY_GET_LAST_REQUEST_READ(dpy);
Packit 5bd3a9
		widen(&event_sequence, event->full_sequence);
Packit 5bd3a9
		if(!req || XLIB_SEQUENCE_COMPARE(event_sequence, <, req->sequence)
Packit 5bd3a9
		        || (event->response_type != X_Error && event_sequence == req->sequence))
Packit 5bd3a9
		{
Packit 5bd3a9
			uint64_t request = X_DPY_GET_REQUEST(dpy);
Packit 5bd3a9
			if (XLIB_SEQUENCE_COMPARE(event_sequence, >, request))
Packit 5bd3a9
			{
Packit 5bd3a9
				throw_thread_fail_assert("Unknown sequence "
Packit 5bd3a9
				                         "number while "
Packit 5bd3a9
							 "processing queue",
Packit 5bd3a9
				                xcb_xlib_threads_sequence_lost);
Packit 5bd3a9
			}
Packit 5bd3a9
			X_DPY_SET_LAST_REQUEST_READ(dpy, event_sequence);
Packit 5bd3a9
			dpy->xcb->next_event = NULL;
Packit 5bd3a9
			return (xcb_generic_reply_t *) event;
Packit 5bd3a9
		}
Packit 5bd3a9
	}
Packit 5bd3a9
	return NULL;
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
static xcb_generic_reply_t *poll_for_response(Display *dpy)
Packit 5bd3a9
{
Packit 5bd3a9
	void *response;
Packit 5bd3a9
	xcb_generic_error_t *error;
Packit 5bd3a9
	PendingRequest *req;
Packit 5bd3a9
	while(!(response = poll_for_event(dpy, False)) &&
Packit 5bd3a9
	      (req = dpy->xcb->pending_requests) &&
Packit 5bd3a9
	      !req->reply_waiter)
Packit 5bd3a9
	{
Packit 5bd3a9
		uint64_t request;
Packit 5bd3a9
Packit 5bd3a9
		if(!xcb_poll_for_reply64(dpy->xcb->connection, req->sequence,
Packit 5bd3a9
					 &response, &error)) {
Packit 5bd3a9
			/* xcb_poll_for_reply64 may have read events even if
Packit 5bd3a9
			 * there is no reply. */
Packit 5bd3a9
			response = poll_for_event(dpy, True);
Packit 5bd3a9
			break;
Packit 5bd3a9
		}
Packit 5bd3a9
Packit 5bd3a9
		request = X_DPY_GET_REQUEST(dpy);
Packit 5bd3a9
		if(XLIB_SEQUENCE_COMPARE(req->sequence, >, request))
Packit 5bd3a9
		{
Packit 5bd3a9
			throw_thread_fail_assert("Unknown sequence number "
Packit 5bd3a9
			                         "while awaiting reply",
Packit 5bd3a9
			                        xcb_xlib_threads_sequence_lost);
Packit 5bd3a9
		}
Packit 5bd3a9
		X_DPY_SET_LAST_REQUEST_READ(dpy, req->sequence);
Packit 5bd3a9
		if(response)
Packit 5bd3a9
			break;
Packit 5bd3a9
		dequeue_pending_request(dpy, req);
Packit 5bd3a9
		if(error)
Packit 5bd3a9
			return (xcb_generic_reply_t *) error;
Packit 5bd3a9
	}
Packit 5bd3a9
	return response;
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
static void handle_response(Display *dpy, xcb_generic_reply_t *response, Bool in_XReply)
Packit 5bd3a9
{
Packit 5bd3a9
	_XAsyncHandler *async, *next;
Packit 5bd3a9
	switch(response->response_type)
Packit 5bd3a9
	{
Packit 5bd3a9
	case X_Reply:
Packit 5bd3a9
		for(async = dpy->async_handlers; async; async = next)
Packit 5bd3a9
		{
Packit 5bd3a9
			next = async->next;
Packit 5bd3a9
			if(async->handler(dpy, (xReply *) response, (char *) response, sizeof(xReply) + (response->length << 2), async->data))
Packit 5bd3a9
				break;
Packit 5bd3a9
		}
Packit 5bd3a9
		break;
Packit 5bd3a9
Packit 5bd3a9
	case X_Error:
Packit 5bd3a9
		handle_error(dpy, (xError *) response, in_XReply);
Packit 5bd3a9
		break;
Packit 5bd3a9
Packit 5bd3a9
	default: /* event */
Packit 5bd3a9
		/* GenericEvents may be > 32 bytes. In this case, the
Packit 5bd3a9
		 * event struct is trailed by the additional bytes. the
Packit 5bd3a9
		 * xcb_generic_event_t struct uses 4 bytes for internal
Packit 5bd3a9
		 * numbering, so we need to shift the trailing data to
Packit 5bd3a9
		 * be after the first 32 bytes. */
Packit 5bd3a9
		if(response->response_type == GenericEvent && ((xcb_ge_event_t *) response)->length)
Packit 5bd3a9
		{
Packit 5bd3a9
			xcb_ge_event_t *event = (xcb_ge_event_t *) response;
Packit 5bd3a9
			memmove(&event->full_sequence, &event[1], event->length * 4);
Packit 5bd3a9
		}
Packit 5bd3a9
		_XEnq(dpy, (xEvent *) response);
Packit 5bd3a9
		break;
Packit 5bd3a9
	}
Packit 5bd3a9
	free(response);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
int _XEventsQueued(Display *dpy, int mode)
Packit 5bd3a9
{
Packit 5bd3a9
	xcb_generic_reply_t *response;
Packit 5bd3a9
	if(dpy->flags & XlibDisplayIOError)
Packit 5bd3a9
		return 0;
Packit 5bd3a9
	if(dpy->xcb->event_owner != XlibOwnsEventQueue)
Packit 5bd3a9
		return 0;
Packit 5bd3a9
Packit 5bd3a9
	if(mode == QueuedAfterFlush)
Packit 5bd3a9
		_XSend(dpy, NULL, 0);
Packit 5bd3a9
	else
Packit 5bd3a9
		check_internal_connections(dpy);
Packit 5bd3a9
Packit 5bd3a9
	/* If another thread is blocked waiting for events, then we must
Packit 5bd3a9
	 * let that thread pick up the next event. Since it blocked, we
Packit 5bd3a9
	 * can reasonably claim there are no new events right now. */
Packit 5bd3a9
	if(!dpy->xcb->event_waiter)
Packit 5bd3a9
	{
Packit 5bd3a9
		while((response = poll_for_response(dpy)))
Packit 5bd3a9
			handle_response(dpy, response, False);
Packit 5bd3a9
		if(xcb_connection_has_error(dpy->xcb->connection))
Packit 5bd3a9
			_XIOError(dpy);
Packit 5bd3a9
	}
Packit 5bd3a9
	return dpy->qlen;
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/* _XReadEvents - Flush the output queue,
Packit 5bd3a9
 * then read as many events as possible (but at least 1) and enqueue them
Packit 5bd3a9
 */
Packit 5bd3a9
void _XReadEvents(Display *dpy)
Packit 5bd3a9
{
Packit 5bd3a9
	xcb_generic_reply_t *response;
Packit 5bd3a9
	unsigned long serial;
Packit 5bd3a9
Packit 5bd3a9
	if(dpy->flags & XlibDisplayIOError)
Packit 5bd3a9
		return;
Packit 5bd3a9
	_XSend(dpy, NULL, 0);
Packit 5bd3a9
	if(dpy->xcb->event_owner != XlibOwnsEventQueue)
Packit 5bd3a9
		return;
Packit 5bd3a9
	check_internal_connections(dpy);
Packit 5bd3a9
Packit 5bd3a9
	serial = dpy->next_event_serial_num;
Packit 5bd3a9
	while(serial == dpy->next_event_serial_num || dpy->qlen == 0)
Packit 5bd3a9
	{
Packit 5bd3a9
		if(dpy->xcb->event_waiter)
Packit 5bd3a9
		{
Packit 5bd3a9
			ConditionWait(dpy, dpy->xcb->event_notify);
Packit 5bd3a9
			/* Maybe the other thread got us an event. */
Packit 5bd3a9
			continue;
Packit 5bd3a9
		}
Packit 5bd3a9
Packit 5bd3a9
		if(!dpy->xcb->next_event)
Packit 5bd3a9
		{
Packit 5bd3a9
			xcb_generic_event_t *event;
Packit 5bd3a9
			dpy->xcb->event_waiter = 1;
Packit 5bd3a9
			UnlockDisplay(dpy);
Packit 5bd3a9
			event = xcb_wait_for_event(dpy->xcb->connection);
Packit 5bd3a9
			/* It appears that classic Xlib respected user
Packit 5bd3a9
			 * locks when waking up after waiting for
Packit 5bd3a9
			 * events. However, if this thread did not have
Packit 5bd3a9
			 * any user locks, and another thread takes a
Packit 5bd3a9
			 * user lock and tries to read events, then we'd
Packit 5bd3a9
			 * deadlock. So we'll choose to let the thread
Packit 5bd3a9
			 * that got in first consume events, despite the
Packit 5bd3a9
			 * later thread's user locks. */
Packit 5bd3a9
			InternalLockDisplay(dpy, /* ignore user locks */ 1);
Packit 5bd3a9
			dpy->xcb->event_waiter = 0;
Packit 5bd3a9
			ConditionBroadcast(dpy, dpy->xcb->event_notify);
Packit 5bd3a9
			if(!event)
Packit 5bd3a9
				_XIOError(dpy);
Packit 5bd3a9
			dpy->xcb->next_event = event;
Packit 5bd3a9
		}
Packit 5bd3a9
Packit 5bd3a9
		/* We've established most of the conditions for
Packit 5bd3a9
		 * poll_for_response to return non-NULL. The exceptions
Packit 5bd3a9
		 * are connection shutdown, and finding that another
Packit 5bd3a9
		 * thread is waiting for the next reply we'd like to
Packit 5bd3a9
		 * process. */
Packit 5bd3a9
Packit 5bd3a9
		response = poll_for_response(dpy);
Packit 5bd3a9
		if(response)
Packit 5bd3a9
			handle_response(dpy, response, False);
Packit 5bd3a9
		else if(dpy->xcb->pending_requests->reply_waiter)
Packit 5bd3a9
		{ /* need braces around ConditionWait */
Packit 5bd3a9
			ConditionWait(dpy, dpy->xcb->reply_notify);
Packit 5bd3a9
		}
Packit 5bd3a9
		else
Packit 5bd3a9
			_XIOError(dpy);
Packit 5bd3a9
	}
Packit 5bd3a9
Packit 5bd3a9
	/* The preceding loop established that there is no
Packit 5bd3a9
	 * event_waiter--unless we just called ConditionWait because of
Packit 5bd3a9
	 * a reply_waiter, in which case another thread may have become
Packit 5bd3a9
	 * the event_waiter while we slept unlocked. */
Packit 5bd3a9
	if(!dpy->xcb->event_waiter)
Packit 5bd3a9
		while((response = poll_for_response(dpy)))
Packit 5bd3a9
			handle_response(dpy, response, False);
Packit 5bd3a9
	if(xcb_connection_has_error(dpy->xcb->connection))
Packit 5bd3a9
		_XIOError(dpy);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/*
Packit 5bd3a9
 * _XSend - Flush the buffer and send the client data. 32 bit word aligned
Packit 5bd3a9
 * transmission is used, if size is not 0 mod 4, extra bytes are transmitted.
Packit 5bd3a9
 *
Packit 5bd3a9
 * Note that the connection must not be read from once the data currently
Packit 5bd3a9
 * in the buffer has been written.
Packit 5bd3a9
 */
Packit 5bd3a9
void _XSend(Display *dpy, const char *data, long size)
Packit 5bd3a9
{
Packit 5bd3a9
	static const xReq dummy_request;
Packit 5bd3a9
	static char const pad[3];
Packit 5bd3a9
	struct iovec vec[3];
Packit 5bd3a9
	uint64_t requests;
Packit 5bd3a9
	uint64_t dpy_request;
Packit 5bd3a9
	_XExtension *ext;
Packit 5bd3a9
	xcb_connection_t *c = dpy->xcb->connection;
Packit 5bd3a9
	if(dpy->flags & XlibDisplayIOError)
Packit 5bd3a9
		return;
Packit 5bd3a9
Packit 5bd3a9
	if(dpy->bufptr == dpy->buffer && !size)
Packit 5bd3a9
		return;
Packit 5bd3a9
Packit 5bd3a9
	/* append_pending_request does not alter the dpy request number
Packit 5bd3a9
	 * therefore we can get it outside of the loop and the if
Packit 5bd3a9
	 */
Packit 5bd3a9
	dpy_request = X_DPY_GET_REQUEST(dpy);
Packit 5bd3a9
	/* iff we asked XCB to set aside errors, we must pick those up
Packit 5bd3a9
	 * eventually. iff there are async handlers, we may have just
Packit 5bd3a9
	 * issued requests that will generate replies. in either case,
Packit 5bd3a9
	 * we need to remember to check later. */
Packit 5bd3a9
	if(dpy->xcb->event_owner != XlibOwnsEventQueue || dpy->async_handlers)
Packit 5bd3a9
	{
Packit 5bd3a9
		uint64_t sequence;
Packit 5bd3a9
		for(sequence = dpy->xcb->last_flushed + 1; sequence <= dpy_request; ++sequence)
Packit 5bd3a9
			append_pending_request(dpy, sequence);
Packit 5bd3a9
	}
Packit 5bd3a9
	requests = dpy_request - dpy->xcb->last_flushed;
Packit 5bd3a9
	dpy->xcb->last_flushed = dpy_request;
Packit 5bd3a9
Packit 5bd3a9
	vec[0].iov_base = dpy->buffer;
Packit 5bd3a9
	vec[0].iov_len = dpy->bufptr - dpy->buffer;
Packit 5bd3a9
	vec[1].iov_base = (char *)data;
Packit 5bd3a9
	vec[1].iov_len = size;
Packit 5bd3a9
	vec[2].iov_base = (char *)pad;
Packit 5bd3a9
	vec[2].iov_len = -size & 3;
Packit 5bd3a9
Packit 5bd3a9
	for(ext = dpy->flushes; ext; ext = ext->next_flush)
Packit 5bd3a9
	{
Packit 5bd3a9
		int i;
Packit 5bd3a9
		for(i = 0; i < 3; ++i)
Packit 5bd3a9
			if(vec[i].iov_len)
Packit 5bd3a9
				ext->before_flush(dpy, &ext->codes, vec[i].iov_base, vec[i].iov_len);
Packit 5bd3a9
	}
Packit 5bd3a9
Packit 5bd3a9
	if(xcb_writev(c, vec, 3, requests) < 0)
Packit 5bd3a9
		_XIOError(dpy);
Packit 5bd3a9
	dpy->bufptr = dpy->buffer;
Packit 5bd3a9
	dpy->last_req = (char *) &dummy_request;
Packit 5bd3a9
Packit 5bd3a9
	check_internal_connections(dpy);
Packit 5bd3a9
Packit 5bd3a9
	_XSetSeqSyncFunction(dpy);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/*
Packit 5bd3a9
 * _XFlush - Flush the X request buffer.  If the buffer is empty, no
Packit 5bd3a9
 * action is taken.
Packit 5bd3a9
 */
Packit 5bd3a9
void _XFlush(Display *dpy)
Packit 5bd3a9
{
Packit 5bd3a9
	require_socket(dpy);
Packit 5bd3a9
	_XSend(dpy, NULL, 0);
Packit 5bd3a9
Packit 5bd3a9
	_XEventsQueued(dpy, QueuedAfterReading);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
static const XID inval_id = ~0UL;
Packit 5bd3a9
Packit 5bd3a9
void _XIDHandler(Display *dpy)
Packit 5bd3a9
{
Packit 5bd3a9
	if (dpy->xcb->next_xid == inval_id)
Packit 5bd3a9
		_XAllocIDs(dpy, &dpy->xcb->next_xid, 1);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/* _XAllocID - resource ID allocation routine. */
Packit 5bd3a9
XID _XAllocID(Display *dpy)
Packit 5bd3a9
{
Packit 5bd3a9
	XID ret = dpy->xcb->next_xid;
Packit 5bd3a9
	assert (ret != inval_id);
Packit 5bd3a9
	dpy->xcb->next_xid = inval_id;
Packit 5bd3a9
	_XSetPrivSyncFunction(dpy);
Packit 5bd3a9
	return ret;
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/* _XAllocIDs - multiple resource ID allocation routine. */
Packit 5bd3a9
void _XAllocIDs(Display *dpy, XID *ids, int count)
Packit 5bd3a9
{
Packit 5bd3a9
	int i;
Packit 5bd3a9
#ifdef XTHREADS
Packit 5bd3a9
	if (dpy->lock)
Packit 5bd3a9
		(*dpy->lock->user_lock_display)(dpy);
Packit 5bd3a9
	UnlockDisplay(dpy);
Packit 5bd3a9
#endif
Packit 5bd3a9
	for (i = 0; i < count; i++)
Packit 5bd3a9
		ids[i] = xcb_generate_id(dpy->xcb->connection);
Packit 5bd3a9
#ifdef XTHREADS
Packit 5bd3a9
	InternalLockDisplay(dpy, /* don't skip user locks */ 0);
Packit 5bd3a9
	if (dpy->lock)
Packit 5bd3a9
		(*dpy->lock->user_unlock_display)(dpy);
Packit 5bd3a9
#endif
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
static void _XFreeReplyData(Display *dpy, Bool force)
Packit 5bd3a9
{
Packit 5bd3a9
	if(!force && dpy->xcb->reply_consumed < dpy->xcb->reply_length)
Packit 5bd3a9
		return;
Packit 5bd3a9
	free(dpy->xcb->reply_data);
Packit 5bd3a9
	dpy->xcb->reply_data = NULL;
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/*
Packit 5bd3a9
 * _XReply - Wait for a reply packet and copy its contents into the
Packit 5bd3a9
 * specified rep.
Packit 5bd3a9
 * extra: number of 32-bit words expected after the reply
Packit 5bd3a9
 * discard: should I discard data following "extra" words?
Packit 5bd3a9
 */
Packit 5bd3a9
Status _XReply(Display *dpy, xReply *rep, int extra, Bool discard)
Packit 5bd3a9
{
Packit 5bd3a9
	xcb_generic_error_t *error;
Packit 5bd3a9
	xcb_connection_t *c = dpy->xcb->connection;
Packit 5bd3a9
	char *reply;
Packit 5bd3a9
	PendingRequest *current;
Packit 5bd3a9
	uint64_t dpy_request;
Packit 5bd3a9
Packit 5bd3a9
	if (dpy->xcb->reply_data)
Packit 5bd3a9
		throw_extlib_fail_assert("Extra reply data still left in queue",
Packit 5bd3a9
		                         xcb_xlib_extra_reply_data_left);
Packit 5bd3a9
Packit 5bd3a9
	if(dpy->flags & XlibDisplayIOError)
Packit 5bd3a9
		return 0;
Packit 5bd3a9
Packit 5bd3a9
	_XSend(dpy, NULL, 0);
Packit 5bd3a9
	dpy_request = X_DPY_GET_REQUEST(dpy);
Packit 5bd3a9
	if(dpy->xcb->pending_requests_tail
Packit 5bd3a9
	   && dpy->xcb->pending_requests_tail->sequence == dpy_request)
Packit 5bd3a9
		current = dpy->xcb->pending_requests_tail;
Packit 5bd3a9
	else
Packit 5bd3a9
		current = append_pending_request(dpy, dpy_request);
Packit 5bd3a9
	/* Don't let any other thread get this reply. */
Packit 5bd3a9
	current->reply_waiter = 1;
Packit 5bd3a9
Packit 5bd3a9
	while(1)
Packit 5bd3a9
	{
Packit 5bd3a9
		PendingRequest *req = dpy->xcb->pending_requests;
Packit 5bd3a9
		xcb_generic_reply_t *response;
Packit 5bd3a9
Packit 5bd3a9
		if(req != current && req->reply_waiter)
Packit 5bd3a9
		{
Packit 5bd3a9
			ConditionWait(dpy, dpy->xcb->reply_notify);
Packit 5bd3a9
			/* Another thread got this reply. */
Packit 5bd3a9
			continue;
Packit 5bd3a9
		}
Packit 5bd3a9
		req->reply_waiter = 1;
Packit 5bd3a9
		UnlockDisplay(dpy);
Packit 5bd3a9
		response = xcb_wait_for_reply64(c, req->sequence, &error);
Packit 5bd3a9
		/* Any user locks on another thread must have been taken
Packit 5bd3a9
		 * while we slept in xcb_wait_for_reply64. Classic Xlib
Packit 5bd3a9
		 * ignored those user locks in this case, so we do too. */
Packit 5bd3a9
		InternalLockDisplay(dpy, /* ignore user locks */ 1);
Packit 5bd3a9
Packit 5bd3a9
		/* We have the response we're looking for. Now, before
Packit 5bd3a9
		 * letting anyone else process this sequence number, we
Packit 5bd3a9
		 * need to process any events that should have come
Packit 5bd3a9
		 * earlier. */
Packit 5bd3a9
Packit 5bd3a9
		if(dpy->xcb->event_owner == XlibOwnsEventQueue)
Packit 5bd3a9
		{
Packit 5bd3a9
			xcb_generic_reply_t *event;
Packit 5bd3a9
			/* If some thread is already waiting for events,
Packit 5bd3a9
			 * it will get the first one. That thread must
Packit 5bd3a9
			 * process that event before we can continue. */
Packit 5bd3a9
			/* FIXME: That event might be after this reply,
Packit 5bd3a9
			 * and might never even come--or there might be
Packit 5bd3a9
			 * multiple threads trying to get events. */
Packit 5bd3a9
			while(dpy->xcb->event_waiter)
Packit 5bd3a9
			{ /* need braces around ConditionWait */
Packit 5bd3a9
				ConditionWait(dpy, dpy->xcb->event_notify);
Packit 5bd3a9
			}
Packit 5bd3a9
			while((event = poll_for_event(dpy, True)))
Packit 5bd3a9
				handle_response(dpy, event, True);
Packit 5bd3a9
		}
Packit 5bd3a9
Packit 5bd3a9
		req->reply_waiter = 0;
Packit 5bd3a9
		ConditionBroadcast(dpy, dpy->xcb->reply_notify);
Packit 5bd3a9
		dpy_request = X_DPY_GET_REQUEST(dpy);
Packit 5bd3a9
		if(XLIB_SEQUENCE_COMPARE(req->sequence, >, dpy_request)) {
Packit 5bd3a9
			throw_thread_fail_assert("Unknown sequence number "
Packit 5bd3a9
			                         "while processing reply",
Packit 5bd3a9
			                        xcb_xlib_threads_sequence_lost);
Packit 5bd3a9
		}
Packit 5bd3a9
		X_DPY_SET_LAST_REQUEST_READ(dpy, req->sequence);
Packit 5bd3a9
		if(!response)
Packit 5bd3a9
			dequeue_pending_request(dpy, req);
Packit 5bd3a9
Packit 5bd3a9
		if(req == current)
Packit 5bd3a9
		{
Packit 5bd3a9
			reply = (char *) response;
Packit 5bd3a9
			break;
Packit 5bd3a9
		}
Packit 5bd3a9
Packit 5bd3a9
		if(error)
Packit 5bd3a9
			handle_response(dpy, (xcb_generic_reply_t *) error, True);
Packit 5bd3a9
		else if(response)
Packit 5bd3a9
			handle_response(dpy, response, True);
Packit 5bd3a9
	}
Packit 5bd3a9
	check_internal_connections(dpy);
Packit 5bd3a9
Packit 5bd3a9
	if(dpy->xcb->next_event && dpy->xcb->next_event->response_type == X_Error)
Packit 5bd3a9
	{
Packit 5bd3a9
		xcb_generic_event_t *event = dpy->xcb->next_event;
Packit 5bd3a9
		uint64_t last_request_read = X_DPY_GET_LAST_REQUEST_READ(dpy);
Packit 5bd3a9
		uint64_t event_sequence = last_request_read;
Packit 5bd3a9
		widen(&event_sequence, event->full_sequence);
Packit 5bd3a9
		if(event_sequence == last_request_read)
Packit 5bd3a9
		{
Packit 5bd3a9
			error = (xcb_generic_error_t *) event;
Packit 5bd3a9
			dpy->xcb->next_event = NULL;
Packit 5bd3a9
		}
Packit 5bd3a9
	}
Packit 5bd3a9
Packit 5bd3a9
	if(error)
Packit 5bd3a9
	{
Packit 5bd3a9
		int ret_code;
Packit 5bd3a9
Packit 5bd3a9
		/* Xlib is evil and assumes that even errors will be
Packit 5bd3a9
		 * copied into rep. */
Packit 5bd3a9
		memcpy(rep, error, 32);
Packit 5bd3a9
Packit 5bd3a9
		/* do not die on "no such font", "can't allocate",
Packit 5bd3a9
		   "can't grab" failures */
Packit 5bd3a9
		switch(error->error_code)
Packit 5bd3a9
		{
Packit 5bd3a9
			case BadName:
Packit 5bd3a9
				switch(error->major_code)
Packit 5bd3a9
				{
Packit 5bd3a9
					case X_LookupColor:
Packit 5bd3a9
					case X_AllocNamedColor:
Packit 5bd3a9
						free(error);
Packit 5bd3a9
						return 0;
Packit 5bd3a9
				}
Packit 5bd3a9
				break;
Packit 5bd3a9
			case BadFont:
Packit 5bd3a9
				if(error->major_code == X_QueryFont) {
Packit 5bd3a9
					free(error);
Packit 5bd3a9
					return 0;
Packit 5bd3a9
				}
Packit 5bd3a9
				break;
Packit 5bd3a9
			case BadAlloc:
Packit 5bd3a9
			case BadAccess:
Packit 5bd3a9
				free(error);
Packit 5bd3a9
				return 0;
Packit 5bd3a9
		}
Packit 5bd3a9
Packit 5bd3a9
		ret_code = handle_error(dpy, (xError *) error, True);
Packit 5bd3a9
		free(error);
Packit 5bd3a9
		return ret_code;
Packit 5bd3a9
	}
Packit 5bd3a9
Packit 5bd3a9
	/* it's not an error, but we don't have a reply, so it's an I/O
Packit 5bd3a9
	 * error. */
Packit 5bd3a9
	if(!reply)
Packit 5bd3a9
		_XIOError(dpy);
Packit 5bd3a9
Packit 5bd3a9
	/* there's no error and we have a reply. */
Packit 5bd3a9
	dpy->xcb->reply_data = reply;
Packit 5bd3a9
	dpy->xcb->reply_consumed = sizeof(xReply) + (extra * 4);
Packit 5bd3a9
	dpy->xcb->reply_length = sizeof(xReply);
Packit 5bd3a9
	if(dpy->xcb->reply_data[0] == 1)
Packit 5bd3a9
		dpy->xcb->reply_length += (((xcb_generic_reply_t *) dpy->xcb->reply_data)->length * 4);
Packit 5bd3a9
Packit 5bd3a9
	/* error: Xlib asks too much. give them what we can anyway. */
Packit 5bd3a9
	if(dpy->xcb->reply_length < dpy->xcb->reply_consumed)
Packit 5bd3a9
		dpy->xcb->reply_consumed = dpy->xcb->reply_length;
Packit 5bd3a9
Packit 5bd3a9
	memcpy(rep, dpy->xcb->reply_data, dpy->xcb->reply_consumed);
Packit 5bd3a9
	_XFreeReplyData(dpy, discard);
Packit 5bd3a9
	return 1;
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
int _XRead(Display *dpy, char *data, long size)
Packit 5bd3a9
{
Packit 5bd3a9
	assert(size >= 0);
Packit 5bd3a9
	if(size == 0)
Packit 5bd3a9
		return 0;
Packit 5bd3a9
	if(dpy->xcb->reply_data == NULL ||
Packit 5bd3a9
	   dpy->xcb->reply_consumed + size > dpy->xcb->reply_length)
Packit 5bd3a9
		throw_extlib_fail_assert("Too much data requested from _XRead",
Packit 5bd3a9
		                         xcb_xlib_too_much_data_requested);
Packit 5bd3a9
	memcpy(data, dpy->xcb->reply_data + dpy->xcb->reply_consumed, size);
Packit 5bd3a9
	dpy->xcb->reply_consumed += size;
Packit 5bd3a9
	_XFreeReplyData(dpy, False);
Packit 5bd3a9
	return 0;
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/*
Packit 5bd3a9
 * _XReadPad - Read bytes from the socket taking into account incomplete
Packit 5bd3a9
 * reads.  If the number of bytes is not 0 mod 4, read additional pad
Packit 5bd3a9
 * bytes.
Packit 5bd3a9
 */
Packit 5bd3a9
void _XReadPad(Display *dpy, char *data, long size)
Packit 5bd3a9
{
Packit 5bd3a9
	_XRead(dpy, data, size);
Packit 5bd3a9
	dpy->xcb->reply_consumed += -size & 3;
Packit 5bd3a9
	_XFreeReplyData(dpy, False);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/* Read and discard "n" 8-bit bytes of data */
Packit 5bd3a9
void _XEatData(Display *dpy, unsigned long n)
Packit 5bd3a9
{
Packit 5bd3a9
	dpy->xcb->reply_consumed += n;
Packit 5bd3a9
	_XFreeReplyData(dpy, False);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
/*
Packit 5bd3a9
 * Read and discard "n" 32-bit words of data
Packit 5bd3a9
 * Matches the units of the length field in X protocol replies, and provides
Packit 5bd3a9
 * a single implementation of overflow checking to avoid having to replicate
Packit 5bd3a9
 * those checks in every caller.
Packit 5bd3a9
 */
Packit 5bd3a9
void _XEatDataWords(Display *dpy, unsigned long n)
Packit 5bd3a9
{
Packit 5bd3a9
	if (n < ((INT_MAX - dpy->xcb->reply_consumed) >> 2))
Packit 5bd3a9
		dpy->xcb->reply_consumed += (n << 2);
Packit 5bd3a9
	else
Packit 5bd3a9
		/* Overflow would happen, so just eat the rest of the reply */
Packit 5bd3a9
		dpy->xcb->reply_consumed = dpy->xcb->reply_length;
Packit 5bd3a9
	_XFreeReplyData(dpy, False);
Packit 5bd3a9
}
Packit 5bd3a9
Packit 5bd3a9
unsigned long
Packit 5bd3a9
_XNextRequest(Display *dpy)
Packit 5bd3a9
{
Packit 5bd3a9
    /* This will update dpy->request. The assumption is that the next thing
Packit 5bd3a9
     * that the application will do is make a request so there's little
Packit 5bd3a9
     * overhead.
Packit 5bd3a9
     */
Packit 5bd3a9
    require_socket(dpy);
Packit 5bd3a9
    return NextRequest(dpy);
Packit 5bd3a9
}