Add support for receiving fds in replies
Requests signal which replies will have fds, and the replies report how many fds they expect in byte 1. Signed-off-by: Keith Packard <keithp@keithp.com> Reviewed-By: Uli Schlachter <psychon@znc.in>
This commit is contained in:
parent
aa6ac19ff4
commit
bbe7f95e07
|
@ -303,6 +303,7 @@ def _c_type_setup(self, name, postfix):
|
||||||
self.c_reply_name = _n(name + ('reply',))
|
self.c_reply_name = _n(name + ('reply',))
|
||||||
self.c_reply_type = _t(name + ('reply',))
|
self.c_reply_type = _t(name + ('reply',))
|
||||||
self.c_cookie_type = _t(name + ('cookie',))
|
self.c_cookie_type = _t(name + ('cookie',))
|
||||||
|
self.c_reply_fds_name = _n(name + ('reply_fds',))
|
||||||
|
|
||||||
self.need_aux = False
|
self.need_aux = False
|
||||||
self.need_serialize = False
|
self.need_serialize = False
|
||||||
|
@ -1835,7 +1836,7 @@ def c_union(self, name):
|
||||||
_c_complex(self)
|
_c_complex(self)
|
||||||
_c_iterator(self, name)
|
_c_iterator(self, name)
|
||||||
|
|
||||||
def _c_request_helper(self, name, cookie_type, void, regular, aux=False):
|
def _c_request_helper(self, name, cookie_type, void, regular, aux=False, reply_fds=False):
|
||||||
'''
|
'''
|
||||||
Declares a request function.
|
Declares a request function.
|
||||||
'''
|
'''
|
||||||
|
@ -1864,6 +1865,12 @@ def _c_request_helper(self, name, cookie_type, void, regular, aux=False):
|
||||||
# What flag is passed to xcb_request
|
# What flag is passed to xcb_request
|
||||||
func_flags = '0' if (void and regular) or (not void and not regular) else 'XCB_REQUEST_CHECKED'
|
func_flags = '0' if (void and regular) or (not void and not regular) else 'XCB_REQUEST_CHECKED'
|
||||||
|
|
||||||
|
if reply_fds:
|
||||||
|
if func_flags == '0':
|
||||||
|
func_flags = 'XCB_REQUEST_REPLY_FDS'
|
||||||
|
else:
|
||||||
|
func_flags = func_flags + '|XCB_REQUEST_REPLY_FDS'
|
||||||
|
|
||||||
# Global extension id variable or NULL for xproto
|
# Global extension id variable or NULL for xproto
|
||||||
func_ext_global = '&' + _ns.c_ext_global_name if _ns.is_ext else '0'
|
func_ext_global = '&' + _ns.c_ext_global_name if _ns.is_ext else '0'
|
||||||
|
|
||||||
|
@ -2256,6 +2263,51 @@ def _c_reply(self, name):
|
||||||
|
|
||||||
_c('}')
|
_c('}')
|
||||||
|
|
||||||
|
def _c_reply_has_fds(self):
|
||||||
|
for field in self.fields:
|
||||||
|
if field.isfd:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _c_reply_fds(self, name):
|
||||||
|
'''
|
||||||
|
Declares the function that returns fds related to the reply.
|
||||||
|
'''
|
||||||
|
spacing1 = ' ' * (len(self.c_reply_type) - len('xcb_connection_t'))
|
||||||
|
spacing3 = ' ' * (len(self.c_reply_fds_name) + 2)
|
||||||
|
_h('')
|
||||||
|
_h('/**')
|
||||||
|
_h(' * Return the reply fds')
|
||||||
|
_h(' * @param c The connection')
|
||||||
|
_h(' * @param reply The reply')
|
||||||
|
_h(' *')
|
||||||
|
_h(' * Returns the array of reply fds of the request asked by')
|
||||||
|
_h(' * ')
|
||||||
|
_h(' * The returned value must be freed by the caller using free().')
|
||||||
|
_h(' */')
|
||||||
|
_c('')
|
||||||
|
_hc('')
|
||||||
|
_hc('/*****************************************************************************')
|
||||||
|
_hc(' **')
|
||||||
|
_hc(' ** int * %s', self.c_reply_fds_name)
|
||||||
|
_hc(' ** ')
|
||||||
|
_hc(' ** @param xcb_connection_t%s *c', spacing1)
|
||||||
|
_hc(' ** @param %s *reply', self.c_reply_type)
|
||||||
|
_hc(' ** @returns int *')
|
||||||
|
_hc(' **')
|
||||||
|
_hc(' *****************************************************************************/')
|
||||||
|
_hc(' ')
|
||||||
|
_hc('int *')
|
||||||
|
_hc('%s (xcb_connection_t%s *c /**< */,', self.c_reply_fds_name, spacing1)
|
||||||
|
_h('%s%s *reply /**< */);', spacing3, self.c_reply_type)
|
||||||
|
_c('%s%s *reply /**< */)', spacing3, self.c_reply_type)
|
||||||
|
_c('{')
|
||||||
|
|
||||||
|
_c(' return xcb_get_reply_fds(c, reply, sizeof(%s) + 4 * reply->length);', self.c_reply_type)
|
||||||
|
|
||||||
|
_c('}')
|
||||||
|
|
||||||
|
|
||||||
def _c_opcode(name, opcode):
|
def _c_opcode(name, opcode):
|
||||||
'''
|
'''
|
||||||
Declares the opcode define for requests, events, and errors.
|
Declares the opcode define for requests, events, and errors.
|
||||||
|
@ -2816,14 +2868,17 @@ def c_request(self, name):
|
||||||
# Reply structure definition
|
# Reply structure definition
|
||||||
_c_complex(self.reply)
|
_c_complex(self.reply)
|
||||||
# Request prototypes
|
# Request prototypes
|
||||||
_c_request_helper(self, name, self.c_cookie_type, False, True)
|
has_fds = _c_reply_has_fds(self.reply)
|
||||||
_c_request_helper(self, name, self.c_cookie_type, False, False)
|
_c_request_helper(self, name, self.c_cookie_type, False, True, False, has_fds)
|
||||||
|
_c_request_helper(self, name, self.c_cookie_type, False, False, False, has_fds)
|
||||||
if self.need_aux:
|
if self.need_aux:
|
||||||
_c_request_helper(self, name, self.c_cookie_type, False, True, True)
|
_c_request_helper(self, name, self.c_cookie_type, False, True, True, has_fds)
|
||||||
_c_request_helper(self, name, self.c_cookie_type, False, False, True)
|
_c_request_helper(self, name, self.c_cookie_type, False, False, True, has_fds)
|
||||||
# Reply accessors
|
# Reply accessors
|
||||||
_c_accessors(self.reply, name + ('reply',), name)
|
_c_accessors(self.reply, name + ('reply',), name)
|
||||||
_c_reply(self, name)
|
_c_reply(self, name)
|
||||||
|
if has_fds:
|
||||||
|
_c_reply_fds(self, name)
|
||||||
else:
|
else:
|
||||||
# Request prototypes
|
# Request prototypes
|
||||||
_c_request_helper(self, name, 'xcb_void_cookie_t', True, False)
|
_c_request_helper(self, name, 'xcb_void_cookie_t', True, False)
|
||||||
|
|
224
src/xcb_in.c
224
src/xcb_in.c
|
@ -90,11 +90,26 @@ static void remove_finished_readers(reader_list **prev_reader, uint64_t complete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_SENDMSG
|
||||||
|
static int read_fds(xcb_connection_t *c, int *fds, int nfd)
|
||||||
|
{
|
||||||
|
int *ifds = &c->in.in_fd.fd[c->in.in_fd.ifd];
|
||||||
|
int infd = c->in.in_fd.nfd - c->in.in_fd.ifd;
|
||||||
|
|
||||||
|
if (nfd > infd)
|
||||||
|
return 0;
|
||||||
|
memcpy(fds, ifds, nfd * sizeof (int));
|
||||||
|
c->in.in_fd.ifd += nfd;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int read_packet(xcb_connection_t *c)
|
static int read_packet(xcb_connection_t *c)
|
||||||
{
|
{
|
||||||
xcb_generic_reply_t genrep;
|
xcb_generic_reply_t genrep;
|
||||||
uint64_t length = 32;
|
uint64_t length = 32;
|
||||||
uint64_t eventlength = 0; /* length after first 32 bytes for GenericEvents */
|
uint64_t eventlength = 0; /* length after first 32 bytes for GenericEvents */
|
||||||
|
int nfd = 0; /* Number of file descriptors attached to the reply */
|
||||||
uint64_t bufsize;
|
uint64_t bufsize;
|
||||||
void *buf;
|
void *buf;
|
||||||
pending_reply *pend = 0;
|
pending_reply *pend = 0;
|
||||||
|
@ -164,13 +179,18 @@ static int read_packet(xcb_connection_t *c)
|
||||||
genrep.length = p[2] * p[3] * 2;
|
genrep.length = p[2] * p[3] * 2;
|
||||||
}
|
}
|
||||||
length += genrep.length * 4;
|
length += genrep.length * 4;
|
||||||
|
|
||||||
|
/* XXX a bit of a hack -- we "know" that all FD replys place
|
||||||
|
* the number of fds in the pad0 byte */
|
||||||
|
if (pend && pend->flags & XCB_REQUEST_REPLY_FDS)
|
||||||
|
nfd = genrep.pad0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XGE events may have sizes > 32 */
|
/* XGE events may have sizes > 32 */
|
||||||
if ((genrep.response_type & 0x7f) == XCB_XGE_EVENT)
|
if ((genrep.response_type & 0x7f) == XCB_XGE_EVENT)
|
||||||
eventlength = genrep.length * 4;
|
eventlength = genrep.length * 4;
|
||||||
|
|
||||||
bufsize = length + eventlength +
|
bufsize = length + eventlength + nfd * sizeof(int) +
|
||||||
(genrep.response_type == XCB_REPLY ? 0 : sizeof(uint32_t));
|
(genrep.response_type == XCB_REPLY ? 0 : sizeof(uint32_t));
|
||||||
if (bufsize < INT32_MAX)
|
if (bufsize < INT32_MAX)
|
||||||
buf = malloc((size_t) bufsize);
|
buf = malloc((size_t) bufsize);
|
||||||
|
@ -198,6 +218,17 @@ static int read_packet(xcb_connection_t *c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_SENDMSG
|
||||||
|
if (nfd)
|
||||||
|
{
|
||||||
|
if (!read_fds(c, (int *) &((char *) buf)[length], nfd))
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if(pend && (pend->flags & XCB_REQUEST_DISCARD_REPLY))
|
if(pend && (pend->flags & XCB_REQUEST_DISCARD_REPLY))
|
||||||
{
|
{
|
||||||
free(buf);
|
free(buf);
|
||||||
|
@ -431,6 +462,11 @@ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int *xcb_get_reply_fds(xcb_connection_t *c, void *reply, size_t reply_size)
|
||||||
|
{
|
||||||
|
return (int *) (&((char *) reply)[reply_size]);
|
||||||
|
}
|
||||||
|
|
||||||
static void insert_pending_discard(xcb_connection_t *c, pending_reply **prev_next, uint64_t seq)
|
static void insert_pending_discard(xcb_connection_t *c, pending_reply **prev_next, uint64_t seq)
|
||||||
{
|
{
|
||||||
pending_reply *pend;
|
pending_reply *pend;
|
||||||
|
@ -577,6 +613,120 @@ xcb_generic_error_t *xcb_request_check(xcb_connection_t *c, xcb_void_cookie_t co
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static xcb_generic_event_t *get_special_event(xcb_connection_t *c,
|
||||||
|
xcb_special_event_t *se)
|
||||||
|
{
|
||||||
|
xcb_generic_event_t *event = NULL;
|
||||||
|
struct event_list *events;
|
||||||
|
|
||||||
|
if ((events = se->events) != NULL) {
|
||||||
|
event = events->event;
|
||||||
|
if (!(se->events = events->next))
|
||||||
|
se->events_tail = &se->events;
|
||||||
|
free (events);
|
||||||
|
}
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_generic_event_t *xcb_poll_for_special_event(xcb_connection_t *c,
|
||||||
|
xcb_special_event_t *se)
|
||||||
|
{
|
||||||
|
xcb_generic_event_t *event;
|
||||||
|
|
||||||
|
if(c->has_error)
|
||||||
|
return 0;
|
||||||
|
pthread_mutex_lock(&c->iolock);
|
||||||
|
event = get_special_event(c, se);
|
||||||
|
pthread_mutex_unlock(&c->iolock);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_generic_event_t *xcb_wait_for_special_event(xcb_connection_t *c,
|
||||||
|
xcb_special_event_t *se)
|
||||||
|
{
|
||||||
|
xcb_generic_event_t *event;
|
||||||
|
|
||||||
|
if(c->has_error)
|
||||||
|
return 0;
|
||||||
|
pthread_mutex_lock(&c->iolock);
|
||||||
|
|
||||||
|
/* get_special_event returns 0 on empty list. */
|
||||||
|
while(!(event = get_special_event(c, se)))
|
||||||
|
if(!_xcb_conn_wait(c, &se->special_event_cond, 0, 0))
|
||||||
|
break;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&c->iolock);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_special_event_t *
|
||||||
|
xcb_register_for_special_xge(xcb_connection_t *c,
|
||||||
|
uint8_t extension,
|
||||||
|
uint32_t eid,
|
||||||
|
uint32_t *stamp)
|
||||||
|
{
|
||||||
|
xcb_special_event_t *se;
|
||||||
|
|
||||||
|
if(c->has_error)
|
||||||
|
return NULL;
|
||||||
|
pthread_mutex_lock(&c->iolock);
|
||||||
|
for (se = c->in.special_events; se; se = se->next) {
|
||||||
|
if (se->extension == extension &&
|
||||||
|
se->eid == eid) {
|
||||||
|
pthread_mutex_unlock(&c->iolock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
se = calloc(1, sizeof(xcb_special_event_t));
|
||||||
|
if (!se) {
|
||||||
|
pthread_mutex_unlock(&c->iolock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
se->extension = extension;
|
||||||
|
se->eid = eid;
|
||||||
|
|
||||||
|
se->events = NULL;
|
||||||
|
se->events_tail = &se->events;
|
||||||
|
se->stamp = stamp;
|
||||||
|
|
||||||
|
pthread_cond_init(&se->special_event_cond, 0);
|
||||||
|
|
||||||
|
se->next = c->in.special_events;
|
||||||
|
c->in.special_events = se;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&c->iolock);
|
||||||
|
return se;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
xcb_unregister_for_special_event(xcb_connection_t *c,
|
||||||
|
xcb_special_event_t *se)
|
||||||
|
{
|
||||||
|
xcb_special_event_t *s, **prev;
|
||||||
|
struct event_list *events, *next;
|
||||||
|
|
||||||
|
if (c->has_error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&c->iolock);
|
||||||
|
|
||||||
|
for (prev = &c->in.special_events; (s = *prev) != NULL; prev = &(s->next)) {
|
||||||
|
if (s == se) {
|
||||||
|
*prev = se->next;
|
||||||
|
for (events = se->events; events; events = next) {
|
||||||
|
next = events->next;
|
||||||
|
free (events->event);
|
||||||
|
free (events);
|
||||||
|
}
|
||||||
|
pthread_cond_destroy(&se->special_event_cond);
|
||||||
|
free (se);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&c->iolock);
|
||||||
|
}
|
||||||
|
|
||||||
/* Private interface */
|
/* Private interface */
|
||||||
|
|
||||||
int _xcb_in_init(_xcb_in *in)
|
int _xcb_in_init(_xcb_in *in)
|
||||||
|
@ -665,11 +815,79 @@ void _xcb_in_replies_done(xcb_connection_t *c)
|
||||||
|
|
||||||
int _xcb_in_read(xcb_connection_t *c)
|
int _xcb_in_read(xcb_connection_t *c)
|
||||||
{
|
{
|
||||||
int n = recv(c->fd, c->in.queue + c->in.queue_len, sizeof(c->in.queue) - c->in.queue_len, 0);
|
int n;
|
||||||
if(n > 0)
|
|
||||||
|
#if HAVE_SENDMSG
|
||||||
|
struct iovec iov = {
|
||||||
|
.iov_base = c->in.queue + c->in.queue_len,
|
||||||
|
.iov_len = sizeof(c->in.queue) - c->in.queue_len,
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
struct cmsghdr cmsghdr;
|
||||||
|
int fd[XCB_MAX_PASS_FD];
|
||||||
|
} fds;
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_name = NULL,
|
||||||
|
.msg_namelen = 0,
|
||||||
|
.msg_iov = &iov,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
.msg_control = &fds,
|
||||||
|
.msg_controllen = sizeof (struct cmsghdr) + sizeof(int) * (XCB_MAX_PASS_FD - c->in.in_fd.nfd),
|
||||||
|
};
|
||||||
|
n = recvmsg(c->fd, &msg, 0);
|
||||||
|
|
||||||
|
/* Check for truncation errors. Only MSG_CTRUNC is
|
||||||
|
* probably possible here, which would indicate that
|
||||||
|
* the sender tried to transmit more than XCB_MAX_PASS_FD
|
||||||
|
* file descriptors.
|
||||||
|
*/
|
||||||
|
if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
|
||||||
|
_xcb_conn_shutdown(c, XCB_CONN_CLOSED_FDPASSING_FAILED);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
n = recv(c->fd, c->in.queue + c->in.queue_len, sizeof(c->in.queue) - c->in.queue_len, 0);
|
||||||
|
#endif
|
||||||
|
if(n > 0) {
|
||||||
|
#if HAVE_SENDMSG
|
||||||
|
if (msg.msg_controllen > sizeof (struct cmsghdr))
|
||||||
|
{
|
||||||
|
if (fds.cmsghdr.cmsg_level == SOL_SOCKET &&
|
||||||
|
fds.cmsghdr.cmsg_type == SCM_RIGHTS)
|
||||||
|
{
|
||||||
|
int nfd = (msg.msg_controllen - sizeof (struct cmsghdr)) / sizeof (int);
|
||||||
|
memmove(&c->in.in_fd.fd[c->in.in_fd.nfd],
|
||||||
|
fds.fd,
|
||||||
|
nfd);
|
||||||
|
c->in.in_fd.nfd += nfd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
c->in.queue_len += n;
|
c->in.queue_len += n;
|
||||||
|
}
|
||||||
while(read_packet(c))
|
while(read_packet(c))
|
||||||
/* empty */;
|
/* empty */;
|
||||||
|
#if HAVE_SENDMSG
|
||||||
|
if (c->in.in_fd.nfd) {
|
||||||
|
c->in.in_fd.nfd -= c->in.in_fd.ifd;
|
||||||
|
memmove(&c->in.in_fd.fd[0],
|
||||||
|
&c->in.in_fd.fd[c->in.in_fd.ifd],
|
||||||
|
c->in.in_fd.nfd * sizeof (int));
|
||||||
|
c->in.in_fd.ifd = 0;
|
||||||
|
|
||||||
|
/* If we have any left-over file descriptors after emptying
|
||||||
|
* the input buffer, then the server sent some that we weren't
|
||||||
|
* expecting. Close them and mark the connection as broken;
|
||||||
|
*/
|
||||||
|
if (c->in.queue_len == 0 && c->in.in_fd.nfd != 0) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < c->in.in_fd.nfd; i++)
|
||||||
|
close(c->in.in_fd.fd[i]);
|
||||||
|
_xcb_conn_shutdown(c, XCB_CONN_CLOSED_FDPASSING_FAILED);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if((n > 0) || (n < 0 && errno == EAGAIN))
|
if((n > 0) || (n < 0 && errno == EAGAIN))
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -54,7 +54,8 @@ typedef struct {
|
||||||
enum xcb_send_request_flags_t {
|
enum xcb_send_request_flags_t {
|
||||||
XCB_REQUEST_CHECKED = 1 << 0,
|
XCB_REQUEST_CHECKED = 1 << 0,
|
||||||
XCB_REQUEST_RAW = 1 << 1,
|
XCB_REQUEST_RAW = 1 << 1,
|
||||||
XCB_REQUEST_DISCARD_REPLY = 1 << 2
|
XCB_REQUEST_DISCARD_REPLY = 1 << 2,
|
||||||
|
XCB_REQUEST_REPLY_FDS = 1 << 3
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *request);
|
unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *request);
|
||||||
|
@ -90,6 +91,7 @@ int xcb_writev(xcb_connection_t *c, struct iovec *vector, int count, uint64_t re
|
||||||
|
|
||||||
void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e);
|
void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e);
|
||||||
int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error);
|
int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error);
|
||||||
|
int *xcb_get_reply_fds(xcb_connection_t *c, void *reply, size_t replylen);
|
||||||
|
|
||||||
|
|
||||||
/* xcb_util.c */
|
/* xcb_util.c */
|
||||||
|
|
|
@ -92,6 +92,7 @@ typedef struct _xcb_fd {
|
||||||
struct cmsghdr cmsghdr;
|
struct cmsghdr cmsghdr;
|
||||||
int fd[XCB_MAX_PASS_FD];
|
int fd[XCB_MAX_PASS_FD];
|
||||||
int nfd;
|
int nfd;
|
||||||
|
int ifd;
|
||||||
} _xcb_fd;
|
} _xcb_fd;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -151,6 +152,9 @@ typedef struct _xcb_in {
|
||||||
|
|
||||||
struct pending_reply *pending_replies;
|
struct pending_reply *pending_replies;
|
||||||
struct pending_reply **pending_replies_tail;
|
struct pending_reply **pending_replies_tail;
|
||||||
|
#if HAVE_SENDMSG
|
||||||
|
_xcb_fd in_fd;
|
||||||
|
#endif
|
||||||
} _xcb_in;
|
} _xcb_in;
|
||||||
|
|
||||||
int _xcb_in_init(_xcb_in *in);
|
int _xcb_in_init(_xcb_in *in);
|
||||||
|
|
Loading…
Reference in New Issue