/* Copyright (C) 2003-2004 Jamey Sharp. * This file is licensed under the MIT license. See the file COPYING. */ #include "Xlibint.h" #include "xclint.h" #include #include #include #include static void _XProcessInternalConnection(Display *dpy, struct _XConnectionInfo *info_list) { dpy->flags |= XlibDisplayProcConni; UnlockDisplay(dpy); (*info_list->read_callback) (dpy, info_list->fd, info_list->call_data); LockDisplay(dpy); dpy->flags &= ~XlibDisplayProcConni; } /* XProcessInternalConnection * Call the _XInternalConnectionProc registered by _XRegisterInternalConnection * for this fd. * The Display is NOT locked during the call. */ void XProcessInternalConnection(Display* dpy, int fd) { struct _XConnectionInfo *info_list; LockDisplay(dpy); for(info_list = dpy->im_fd_info; info_list; info_list = info_list->next) if(info_list->fd == fd) { _XProcessInternalConnection(dpy, info_list); break; } UnlockDisplay(dpy); } /* 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 void handle_event(Display *dpy, XCBGenericEvent *e) { if(!e) _XIOError(dpy); if(e->response_type == X_Error) _XError(dpy, (xError *) e); else _XEnq(dpy, (xEvent *) e); free(e); } void XSetEventQueueOwner(Display *dpy, enum XEventQueueOwner owner) { dpy->xcl->event_owner = owner; } int _XEventsQueued(Display *dpy, int mode) { XCBConnection *c; XCBGenericEvent *e; int ret; if(dpy->xcl->event_owner != XlibOwnsEventQueue) return 0; c = XCBConnectionOfDisplay(dpy); if(mode == QueuedAfterFlush) _XSend(dpy, 0, 0); else check_internal_connections(dpy); while((e = XCBPollForEvent(c, &ret))) handle_event(dpy, e); if(ret) _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) { _XSend(dpy, 0, 0); if(dpy->xcl->event_owner != XlibOwnsEventQueue) return; handle_event(dpy, XCBWaitForEvent(XCBConnectionOfDisplay(dpy))); _XEventsQueued(dpy, QueuedAfterReading); } /* * _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) { XCBConnection *c = XCBConnectionOfDisplay(dpy); assert(!dpy->xcl->request_extra); dpy->xcl->request_extra = data; dpy->xcl->request_extra_size = size; /* give dpy->buffer to XCB */ _XPutXCBBuffer(dpy); if(XCBFlush(c) < 0) _XIOError(dpy); /* get a new dpy->buffer */ _XGetXCBBuffer(dpy); 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 _XPutXCBBuffer. */ } /* * _XFlush - Flush the X request buffer. If the buffer is empty, no * action is taken. */ void _XFlush(Display *dpy) { _XSend(dpy, 0, 0); _XEventsQueued(dpy, QueuedAfterReading); } /* _XAllocID - resource ID allocation routine. */ XID _XAllocID(Display *dpy) { return XCBGenerateID(XCBConnectionOfDisplay(dpy)); } /* _XAllocIDs - multiple resource ID allocation routine. */ void _XAllocIDs(Display *dpy, XID *ids, int count) { int i; for (i = 0; i < count; i++) ids[i] = XAllocID(dpy); } /* * The hard part about this is that we only get 16 bits from a reply. * We have three values that will march along, with the following invariant: * dpy->last_request_read <= rep->sequenceNumber <= dpy->request * We have to keep * dpy->request - dpy->last_request_read < 2^16 * or else we won't know for sure what value to use in events. We do this * by forcing syncs when we get close. */ unsigned long _XSetLastRequestRead(Display *dpy, xGenericReply *rep) { unsigned long newseq; unsigned int xcb_seqnum = XCBGetRequestRead(XCBConnectionOfDisplay(dpy)); /* * KeymapNotify has no sequence number, but is always guaranteed * to immediately follow another event, except when generated via * SendEvent (hmmm). */ if ((rep->type & 0x7f) == KeymapNotify) return(dpy->last_request_read); newseq = (xcb_seqnum & ~((unsigned long)0xffff)) | rep->sequenceNumber; /* We're always trailing XCB's processing of responses here: * when we see a response, it's always one that XCB has already * counted in its sequence number stream. So we ensure that the * 32-bit sequence number that we pick is, in fact, less than or * equal to the last thing XCB processed, taking wrap into * account. */ if (newseq > xcb_seqnum) newseq -= 0x10000; assert_sequence_less(newseq, dpy->request); dpy->last_request_read = newseq; assert_sequence_less(dpy->last_request_read, xcb_seqnum); return(newseq); } static void _XFreeReplyData(Display *dpy, Bool force) { if(!force && dpy->xcl->reply_consumed < dpy->xcl->reply_length) return; free(dpy->xcl->reply_data); dpy->xcl->reply_data = 0; } /* * _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) { XCBGenericError *error; XCBConnection *c = XCBConnectionOfDisplay(dpy); char *reply; assert(!dpy->xcl->reply_data); UnlockDisplay(dpy); /* release buffer if UnlockDisplay didn't already */ _XPutXCBBufferIf(dpy, _XBufferLocked); reply = XCBWaitForReply(c, dpy->request, &error); /* re-acquire buffer if LockDisplay won't otherwise */ _XGetXCBBufferIf(dpy, _XBufferLocked); LockDisplay(dpy); if(error) { _XExtension *ext; xError *err = (xError *) 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(err->errorCode) { case BadName: switch(err->majorCode) { case X_LookupColor: case X_AllocNamedColor: return 0; } break; case BadFont: if(err->majorCode == X_QueryFont) return 0; break; case BadAlloc: case BadAccess: 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)) return ret_code; _XError(dpy, (xError *) error); return 0; } /* 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->xcl->reply_data = reply; dpy->xcl->reply_consumed = sizeof(xReply) + (extra * 4); dpy->xcl->reply_length = sizeof(xReply); if(dpy->xcl->reply_data[0] == 1) dpy->xcl->reply_length += (((XCBGenericRep *) dpy->xcl->reply_data)->length * 4); /* error: Xlib asks too much. give them what we can anyway. */ if(dpy->xcl->reply_length < dpy->xcl->reply_consumed) dpy->xcl->reply_consumed = dpy->xcl->reply_length; memcpy(rep, dpy->xcl->reply_data, dpy->xcl->reply_consumed); _XFreeReplyData(dpy, discard); _XEventsQueued(dpy, QueuedAfterReading); return 1; } int _XRead(Display *dpy, char *data, long size) { assert(size >= 0); if(size == 0) return 0; assert(dpy->xcl->reply_data != 0); assert(dpy->xcl->reply_consumed + size <= dpy->xcl->reply_length); memcpy(data, dpy->xcl->reply_data + dpy->xcl->reply_consumed, size); dpy->xcl->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->xcl->reply_consumed += XCL_PAD(size); _XFreeReplyData(dpy, False); } /* Read and discard "n" 8-bit bytes of data */ void _XEatData(Display *dpy, unsigned long n) { dpy->xcl->reply_consumed += n; _XFreeReplyData(dpy, False); }