aboutsummaryrefslogtreecommitdiff
path: root/libxcb/src
diff options
context:
space:
mode:
Diffstat (limited to 'libxcb/src')
-rw-r--r--libxcb/src/c_client.py103
-rw-r--r--libxcb/src/xcb_in.c1
-rw-r--r--libxcb/src/xcb_out.c85
-rw-r--r--libxcb/src/xcbext.h77
4 files changed, 198 insertions, 68 deletions
diff --git a/libxcb/src/c_client.py b/libxcb/src/c_client.py
index e55fc3c6b..70f842933 100644
--- a/libxcb/src/c_client.py
+++ b/libxcb/src/c_client.py
@@ -629,8 +629,7 @@ def _c_helper_resolve_field_names (prefix):
all_fields = {}
tmp_prefix = []
# look for fields in the remaining containers
- for idx, p in enumerate(prefix):
- name, sep, obj = p
+ for idx, (name, sep, obj) in enumerate(prefix):
if ''==sep:
# sep can be preset in prefix, if not, make a sensible guess
sep = '.' if (obj.is_switch or obj.is_case_or_bitcase) else '->'
@@ -1033,7 +1032,7 @@ def _c_serialize_helper_fields_fixed_size(context, self, field,
if not self.is_case_or_bitcase:
code_lines.append('%s /* %s.%s */' % (space, self.c_type, field.c_field_name))
else:
- scoped_name = [p[2].c_type if idx==0 else p[0] for idx, p in enumerate(prefix)]
+ scoped_name = [obj.c_type if idx==0 else name for idx, (name, _, obj) in enumerate(prefix)]
typename = ".".join(scoped_name)
code_lines.append('%s /* %s.%s */' % (space, typename, field.c_field_name))
@@ -1327,7 +1326,7 @@ def _c_serialize(context, self):
for p in params:
typespec, pointerspec, field_name = p
spacing = ' '*(maxtypelen-len(typespec)-len(pointerspec))
- param_str.append("%s%s%s %s%s /**< */" % (indent, typespec, spacing, pointerspec, field_name))
+ param_str.append("%s%s%s %s%s" % (indent, typespec, spacing, pointerspec, field_name))
# insert function name
param_str[0] = "%s (%s" % (func_name, param_str[0].strip())
param_str = ["%s," % x for x in param_str]
@@ -1520,9 +1519,9 @@ def _c_iterator(self, name):
_h(' * @brief %s', self.c_iterator_type)
_h(' **/')
_h('typedef struct %s {', self.c_iterator_type)
- _h(' %s *data; /**< */', self.c_type)
- _h(' int%s rem; /**< */', ' ' * (len(self.c_type) - 2))
- _h(' int%s index; /**< */', ' ' * (len(self.c_type) - 2))
+ _h(' %s *data;', self.c_type)
+ _h(' int%s rem;', ' ' * (len(self.c_type) - 2))
+ _h(' int%s index;', ' ' * (len(self.c_type) - 2))
# add additional params of the type "self" as fields to the iterator struct
# so that they can be passed to the sizeof-function by the iterator's next-function
params = _c_get_additional_type_params(self)
@@ -1546,8 +1545,8 @@ def _c_iterator(self, name):
_h(' */')
_c('')
_hc('void')
- _h('%s (%s *i /**< */);', self.c_next_name, self.c_iterator_type)
- _c('%s (%s *i /**< */)', self.c_next_name, self.c_iterator_type)
+ _h('%s (%s *i);', self.c_next_name, self.c_iterator_type)
+ _c('%s (%s *i)', self.c_next_name, self.c_iterator_type)
_c('{')
if not self.fixed_size():
@@ -1592,8 +1591,8 @@ def _c_iterator(self, name):
_h(' */')
_c('')
_hc('xcb_generic_iterator_t')
- _h('%s (%s i /**< */);', self.c_end_name, self.c_iterator_type)
- _c('%s (%s i /**< */)', self.c_end_name, self.c_iterator_type)
+ _h('%s (%s i);', self.c_end_name, self.c_iterator_type)
+ _c('%s (%s i)', self.c_end_name, self.c_iterator_type)
_c('{')
_c(' xcb_generic_iterator_t ret;')
@@ -1749,8 +1748,8 @@ def _c_accessors_field(self, field):
if field.type.is_simple:
_hc('')
_hc('%s', field.c_field_type)
- _h('%s (const %s *R /**< */);', field.c_accessor_name, c_type)
- _c('%s (const %s *R /**< */)', field.c_accessor_name, c_type)
+ _h('%s (const %s *R);', field.c_accessor_name, c_type)
+ _c('%s (const %s *R)', field.c_accessor_name, c_type)
_c('{')
if field.prev_varsized_field is None:
_c(' return (%s *) (R + 1);', field.c_field_type)
@@ -1767,8 +1766,8 @@ def _c_accessors_field(self, field):
return_type = '%s *' % field.c_field_type
_hc(return_type)
- _h('%s (const %s *R /**< */);', field.c_accessor_name, c_type)
- _c('%s (const %s *R /**< */)', field.c_accessor_name, c_type)
+ _h('%s (const %s *R);', field.c_accessor_name, c_type)
+ _c('%s (const %s *R)', field.c_accessor_name, c_type)
_c('{')
if field.prev_varsized_field is None:
_c(' return (%s) (R + 1);', return_type)
@@ -1867,7 +1866,7 @@ def _c_accessors_list(self, field):
if len(additional_params) == 0:
return ''
else:
- return (',\n' + indent).join([''] + ['%s %s /**< */' % p for p in additional_params])
+ return (',\n' + indent).join([''] + ['%s %s' % p for p in additional_params])
_h_setlevel(1)
_c_setlevel(1)
@@ -1876,8 +1875,8 @@ def _c_accessors_list(self, field):
_hc('')
_hc('%s *', field.c_field_type)
- _h('%s (%s /**< */);', field.c_accessor_name, params[idx][0])
- _c('%s (%s /**< */)', field.c_accessor_name, params[idx][0])
+ _h('%s (%s);', field.c_accessor_name, params[idx][0])
+ _c('%s (%s)', field.c_accessor_name, params[idx][0])
_c('{')
if switch_obj is not None:
@@ -1902,12 +1901,12 @@ def _c_accessors_list(self, field):
spacing = ' '*(len(field.c_length_name)+2)
add_param_str = additional_params_to_str(spacing)
if switch_obj is not None:
- _hc('%s (const %s *R /**< */,', field.c_length_name, R_obj.c_type)
- _h('%sconst %s *S /**< */%s);', spacing, S_obj.c_type, add_param_str)
- _c('%sconst %s *S /**< */%s)', spacing, S_obj.c_type, add_param_str)
+ _hc('%s (const %s *R,', field.c_length_name, R_obj.c_type)
+ _h('%sconst %s *S%s);', spacing, S_obj.c_type, add_param_str)
+ _c('%sconst %s *S%s)', spacing, S_obj.c_type, add_param_str)
else:
- _h('%s (const %s *R /**< */%s);', field.c_length_name, c_type, add_param_str)
- _c('%s (const %s *R /**< */%s)', field.c_length_name, c_type, add_param_str)
+ _h('%s (const %s *R%s);', field.c_length_name, c_type, add_param_str)
+ _c('%s (const %s *R%s)', field.c_length_name, c_type, add_param_str)
_c('{')
length = _c_accessor_get_expr(field.type.expr, fields)
_c(' return %s;', length)
@@ -1919,12 +1918,12 @@ def _c_accessors_list(self, field):
spacing = ' '*(len(field.c_end_name)+2)
add_param_str = additional_params_to_str(spacing)
if switch_obj is not None:
- _hc('%s (const %s *R /**< */,', field.c_end_name, R_obj.c_type)
- _h('%sconst %s *S /**< */%s);', spacing, S_obj.c_type, add_param_str)
- _c('%sconst %s *S /**< */%s)', spacing, S_obj.c_type, add_param_str)
+ _hc('%s (const %s *R,', field.c_end_name, R_obj.c_type)
+ _h('%sconst %s *S%s);', spacing, S_obj.c_type, add_param_str)
+ _c('%sconst %s *S%s)', spacing, S_obj.c_type, add_param_str)
else:
- _h('%s (const %s *R /**< */%s);', field.c_end_name, c_type, add_param_str)
- _c('%s (const %s *R /**< */%s)', field.c_end_name, c_type, add_param_str)
+ _h('%s (const %s *R%s);', field.c_end_name, c_type, add_param_str)
+ _c('%s (const %s *R%s)', field.c_end_name, c_type, add_param_str)
_c('{')
_c(' xcb_generic_iterator_t i;')
@@ -1952,12 +1951,12 @@ def _c_accessors_list(self, field):
spacing = ' '*(len(field.c_iterator_name)+2)
add_param_str = additional_params_to_str(spacing)
if switch_obj is not None:
- _hc('%s (const %s *R /**< */,', field.c_iterator_name, R_obj.c_type)
- _h('%sconst %s *S /**< */%s);', spacing, S_obj.c_type, add_param_str)
- _c('%sconst %s *S /**< */%s)', spacing, S_obj.c_type, add_param_str)
+ _hc('%s (const %s *R,', field.c_iterator_name, R_obj.c_type)
+ _h('%sconst %s *S%s);', spacing, S_obj.c_type, add_param_str)
+ _c('%sconst %s *S%s)', spacing, S_obj.c_type, add_param_str)
else:
- _h('%s (const %s *R /**< */%s);', field.c_iterator_name, c_type, add_param_str)
- _c('%s (const %s *R /**< */%s)', field.c_iterator_name, c_type, add_param_str)
+ _h('%s (const %s *R%s);', field.c_iterator_name, c_type, add_param_str)
+ _c('%s (const %s *R%s)', field.c_iterator_name, c_type, add_param_str)
_c('{')
_c(' %s i;', field.c_iterator_type)
@@ -2063,10 +2062,10 @@ def _c_complex(self, force_packed = False):
# necessary for unserialize to work
(self.is_switch and field.type.is_switch)):
spacing = ' ' * (maxtypelen - len(field.c_field_type))
- _h('%s %s%s %s%s; /**< */', space, field.c_field_type, spacing, field.c_field_name, field.c_subscript)
+ _h('%s %s%s %s%s;', space, field.c_field_type, spacing, field.c_field_name, field.c_subscript)
else:
spacing = ' ' * (maxtypelen - (len(field.c_field_type) + 1))
- _h('%s %s%s *%s%s; /**< */', space, field.c_field_type, spacing, field.c_field_name, field.c_subscript)
+ _h('%s %s%s *%s%s;', space, field.c_field_type, spacing, field.c_field_name, field.c_subscript)
if not self.is_switch:
for field in struct_fields:
@@ -2243,9 +2242,9 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
spacing = ' ' * (maxtypelen - len('xcb_connection_t'))
comma = ',' if len(param_fields) else ');'
- _h('%s (xcb_connection_t%s *c /**< */%s', func_name, spacing, comma)
+ _h('%s (xcb_connection_t%s *c%s', func_name, spacing, comma)
comma = ',' if len(param_fields) else ')'
- _c('%s (xcb_connection_t%s *c /**< */%s', func_name, spacing, comma)
+ _c('%s (xcb_connection_t%s *c%s', func_name, spacing, comma)
func_spacing = ' ' * (len(func_name) + 2)
count = len(param_fields)
@@ -2258,10 +2257,10 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
c_pointer = '*'
spacing = ' ' * (maxtypelen - len(c_field_const_type))
comma = ',' if count else ');'
- _h('%s%s%s %s%s /**< */%s', func_spacing, c_field_const_type,
+ _h('%s%s%s %s%s%s', func_spacing, c_field_const_type,
spacing, c_pointer, field.c_field_name, comma)
comma = ',' if count else ')'
- _c('%s%s%s %s%s /**< */%s', func_spacing, c_field_const_type,
+ _c('%s%s%s %s%s%s', func_spacing, c_field_const_type,
spacing, c_pointer, field.c_field_name, comma)
count = 2
@@ -2291,13 +2290,16 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
_c(' void *xcb_aux = 0;')
- for idx, f in enumerate(serial_fields):
+ for idx, _ in enumerate(serial_fields):
if aux:
_c(' void *xcb_aux%d = 0;' % (idx))
if list_with_var_size_elems:
_c(' unsigned int i;')
_c(' unsigned int xcb_tmp_len;')
_c(' char *xcb_tmp;')
+ num_fds = len([field for field in param_fields if field.isfd])
+ if num_fds > 0:
+ _c(' int fds[%d];' % (num_fds))
_c('')
# fixed size fields
@@ -2398,11 +2400,16 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
# no padding necessary - _serialize() keeps track of padding automatically
_c('')
+ fd_index = 0
for field in param_fields:
if field.isfd:
- _c(' xcb_send_fd(c, %s);', field.c_field_name)
+ _c(' fds[%d] = %s;', fd_index, field.c_field_name)
+ fd_index = fd_index + 1
- _c(' xcb_ret.sequence = xcb_send_request(c, %s, xcb_parts + 2, &xcb_req);', func_flags)
+ if num_fds == 0:
+ _c(' xcb_ret.sequence = xcb_send_request(c, %s, xcb_parts + 2, &xcb_req);', func_flags)
+ else:
+ _c(' xcb_ret.sequence = xcb_send_request_with_fds(c, %s, xcb_parts + 2, &xcb_req, %d, fds);', func_flags, num_fds)
# free dyn. all. data, if any
for f in free_calls:
@@ -2457,10 +2464,10 @@ def _c_reply(self, name):
_h(' */')
_c('')
_hc('%s *', self.c_reply_type)
- _hc('%s (xcb_connection_t%s *c /**< */,', self.c_reply_name, spacing1)
+ _hc('%s (xcb_connection_t%s *c,', self.c_reply_name, spacing1)
_hc('%s%s cookie /**< */,', spacing3, self.c_cookie_type)
- _h('%sxcb_generic_error_t%s **e /**< */);', spacing3, spacing2)
- _c('%sxcb_generic_error_t%s **e /**< */)', spacing3, spacing2)
+ _h('%sxcb_generic_error_t%s **e);', spacing3, spacing2)
+ _c('%sxcb_generic_error_t%s **e)', spacing3, spacing2)
_c('{')
if len(unserialize_fields)>0:
@@ -2515,8 +2522,8 @@ def _c_reply_fds(self, name):
_c('')
_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)
+ _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)
@@ -2543,7 +2550,7 @@ def _c_cookie(self, name):
_h(' * @brief %s', self.c_cookie_type)
_h(' **/')
_h('typedef struct %s {', self.c_cookie_type)
- _h(' unsigned int sequence; /**< */')
+ _h(' unsigned int sequence;')
_h('} %s;', self.c_cookie_type)
def _man_request(self, name, void, aux):
diff --git a/libxcb/src/xcb_in.c b/libxcb/src/xcb_in.c
index 623a0a803..322bed816 100644
--- a/libxcb/src/xcb_in.c
+++ b/libxcb/src/xcb_in.c
@@ -761,6 +761,7 @@ xcb_generic_event_t *xcb_wait_for_special_event(xcb_connection_t *c,
if(!_xcb_conn_wait(c, &se->special_event_cond, 0, 0))
break;
+ _xcb_in_wake_up_next_reader(c);
pthread_mutex_unlock(&c->iolock);
return event;
}
diff --git a/libxcb/src/xcb_out.c b/libxcb/src/xcb_out.c
index 8cc5be868..3601a5fa4 100644
--- a/libxcb/src/xcb_out.c
+++ b/libxcb/src/xcb_out.c
@@ -177,15 +177,59 @@ uint32_t xcb_get_maximum_request_length(xcb_connection_t *c)
return c->out.maximum_request_length.value;
}
-uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *req)
+static void close_fds(int *fds, unsigned int num_fds)
+{
+ for (unsigned int index = 0; index < num_fds; index++)
+ close(fds[index]);
+}
+
+static void send_fds(xcb_connection_t *c, int *fds, unsigned int num_fds)
+{
+#if HAVE_SENDMSG
+ /* Calling _xcb_out_flush_to() can drop the iolock and wait on a condition
+ * variable if another thread is currently writing (c->out.writing > 0).
+ * This call waits for writers to be done and thus _xcb_out_flush_to() will
+ * do the work itself (in which case we are a writer and
+ * prepare_socket_request() will wait for us to be done if another threads
+ * tries to send fds, too). Thanks to this, we can atomically write out FDs.
+ */
+ prepare_socket_request(c);
+
+ while (num_fds > 0) {
+ while (c->out.out_fd.nfd == XCB_MAX_PASS_FD && !c->has_error) {
+ /* XXX: if c->out.writing > 0, this releases the iolock and
+ * potentially allows other threads to interfere with their own fds.
+ */
+ _xcb_out_flush_to(c, c->out.request);
+
+ if (c->out.out_fd.nfd == XCB_MAX_PASS_FD) {
+ /* We need some request to send FDs with */
+ _xcb_out_send_sync(c);
+ }
+ }
+ if (c->has_error)
+ break;
+
+ c->out.out_fd.fd[c->out.out_fd.nfd++] = fds[0];
+ fds++;
+ num_fds--;
+ }
+#endif
+ close_fds(fds, num_fds);
+}
+
+uint64_t xcb_send_request_with_fds64(xcb_connection_t *c, int flags, struct iovec *vector,
+ const xcb_protocol_request_t *req, unsigned int num_fds, int *fds)
{
uint64_t request;
uint32_t prefix[2];
int veclen = req->count;
enum workarounds workaround = WORKAROUND_NONE;
- if(c->has_error)
+ if(c->has_error) {
+ close_fds(fds, num_fds);
return 0;
+ }
assert(c != 0);
assert(vector != 0);
@@ -204,6 +248,7 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, req->ext);
if(!(extension && extension->present))
{
+ close_fds(fds, num_fds);
_xcb_conn_shutdown(c, XCB_CONN_CLOSED_EXT_NOTSUPPORTED);
return 0;
}
@@ -234,6 +279,7 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
}
else if(longlen > xcb_get_maximum_request_length(c))
{
+ close_fds(fds, num_fds);
_xcb_conn_shutdown(c, XCB_CONN_CLOSED_REQ_LEN_EXCEED);
return 0; /* server can't take this; maybe need BIGREQUESTS? */
}
@@ -264,6 +310,11 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
/* get a sequence number and arrange for delivery. */
pthread_mutex_lock(&c->iolock);
+ /* send FDs before establishing a good request number, because this might
+ * call send_sync(), too
+ */
+ send_fds(c, fds, num_fds);
+
prepare_socket_request(c);
/* send GetInputFocus (sync_req) when 64k-2 requests have been sent without
@@ -272,7 +323,7 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
* applications see sequence 0 as that is used to indicate
* an error in sending the request
*/
-
+
while ((req->isvoid && c->out.request == c->in.request_expected + (1 << 16) - 2) ||
(unsigned int) (c->out.request + 1) == 0)
{
@@ -287,6 +338,18 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
}
/* request number are actually uint64_t internally but keep API compat with unsigned int */
+unsigned int xcb_send_request_with_fds(xcb_connection_t *c, int flags, struct iovec *vector,
+ const xcb_protocol_request_t *req, unsigned int num_fds, int *fds)
+{
+ return xcb_send_request_with_fds64(c, flags, vector, req, num_fds, fds);
+}
+
+uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *req)
+{
+ return xcb_send_request_with_fds64(c, flags, vector, req, 0, NULL);
+}
+
+/* request number are actually uint64_t internally but keep API compat with unsigned int */
unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *req)
{
return xcb_send_request64(c, flags, vector, req);
@@ -295,19 +358,15 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect
void
xcb_send_fd(xcb_connection_t *c, int fd)
{
-#if HAVE_SENDMSG
- if (c->has_error)
+ int fds[1] = { fd };
+
+ if (c->has_error) {
+ close(fd);
return;
- pthread_mutex_lock(&c->iolock);
- while (c->out.out_fd.nfd == XCB_MAX_PASS_FD) {
- _xcb_out_flush_to(c, c->out.request);
- if (c->has_error)
- break;
}
- if (!c->has_error)
- c->out.out_fd.fd[c->out.out_fd.nfd++] = fd;
+ pthread_mutex_lock(&c->iolock);
+ send_fds(c, &fds[0], 1);
pthread_mutex_unlock(&c->iolock);
-#endif
}
int xcb_take_socket(xcb_connection_t *c, void (*return_socket)(void *closure), void *closure, int flags, uint64_t *sent)
diff --git a/libxcb/src/xcbext.h b/libxcb/src/xcbext.h
index b2575f7e3..44d789eee 100644
--- a/libxcb/src/xcbext.h
+++ b/libxcb/src/xcbext.h
@@ -68,13 +68,13 @@ enum xcb_send_request_flags_t {
*
* This function sends a new request to the X server. The data of the request is
* given as an array of @c iovecs in the @p vector argument. The length of that
- * array and the neccessary management information are given in the @p request
+ * array and the necessary management information are given in the @p request
* argument.
*
* When this function returns, the request might or might not be sent already.
* Use xcb_flush() to make sure that it really was sent.
*
- * Please note that this function is not the prefered way for sending requests.
+ * Please note that this function is not the preferred way for sending requests.
* It's better to use the generated wrapper functions.
*
* Please note that xcb might use index -1 and -2 of the @p vector array internally,
@@ -83,6 +83,37 @@ enum xcb_send_request_flags_t {
unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *request);
/**
+ * @brief Send a request to the server.
+ * @param c: The connection to the X server.
+ * @param flags: A combination of flags from the xcb_send_request_flags_t enumeration.
+ * @param vector: Data to send; must have two iovecs before start for internal use.
+ * @param request: Information about the request to be sent.
+ * @param num_fds: Number of additional file descriptors to send to the server
+ * @param fds: Additional file descriptors that should be send to the server.
+ * @return The request's sequence number on success, 0 otherwise.
+ *
+ * This function sends a new request to the X server. The data of the request is
+ * given as an array of @c iovecs in the @p vector argument. The length of that
+ * array and the necessary management information are given in the @p request
+ * argument.
+ *
+ * If @p num_fds is non-zero, @p fds points to an array of file descriptors that
+ * will be sent to the X server along with this request. After this function
+ * returns, all file descriptors sent are owned by xcb and will be closed
+ * eventually.
+ *
+ * When this function returns, the request might or might not be sent already.
+ * Use xcb_flush() to make sure that it really was sent.
+ *
+ * Please note that this function is not the preferred way for sending requests.
+ *
+ * Please note that xcb might use index -1 and -2 of the @p vector array internally,
+ * so they must be valid!
+ */
+unsigned int xcb_send_request_with_fds(xcb_connection_t *c, int flags, struct iovec *vector,
+ const xcb_protocol_request_t *request, unsigned int num_fds, int *fds);
+
+/**
* @brief Send a request to the server, with 64-bit sequence number returned.
* @param c: The connection to the X server.
* @param flags: A combination of flags from the xcb_send_request_flags_t enumeration.
@@ -92,13 +123,13 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect
*
* This function sends a new request to the X server. The data of the request is
* given as an array of @c iovecs in the @p vector argument. The length of that
- * array and the neccessary management information are given in the @p request
+ * array and the necessary management information are given in the @p request
* argument.
*
* When this function returns, the request might or might not be sent already.
* Use xcb_flush() to make sure that it really was sent.
*
- * Please note that this function is not the prefered way for sending requests.
+ * Please note that this function is not the preferred way for sending requests.
* It's better to use the generated wrapper functions.
*
* Please note that xcb might use index -1 and -2 of the @p vector array internally,
@@ -107,6 +138,38 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect
uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *request);
/**
+ * @brief Send a request to the server, with 64-bit sequence number returned.
+ * @param c: The connection to the X server.
+ * @param flags: A combination of flags from the xcb_send_request_flags_t enumeration.
+ * @param vector: Data to send; must have two iovecs before start for internal use.
+ * @param request: Information about the request to be sent.
+ * @param num_fds: Number of additional file descriptors to send to the server
+ * @param fds: Additional file descriptors that should be send to the server.
+ * @return The request's sequence number on success, 0 otherwise.
+ *
+ * This function sends a new request to the X server. The data of the request is
+ * given as an array of @c iovecs in the @p vector argument. The length of that
+ * array and the necessary management information are given in the @p request
+ * argument.
+ *
+ * If @p num_fds is non-zero, @p fds points to an array of file descriptors that
+ * will be sent to the X server along with this request. After this function
+ * returns, all file descriptors sent are owned by xcb and will be closed
+ * eventually.
+ *
+ * When this function returns, the request might or might not be sent already.
+ * Use xcb_flush() to make sure that it really was sent.
+ *
+ * Please note that this function is not the preferred way for sending requests.
+ * It's better to use the generated wrapper functions.
+ *
+ * Please note that xcb might use index -1 and -2 of the @p vector array internally,
+ * so they must be valid!
+ */
+uint64_t xcb_send_request_with_fds64(xcb_connection_t *c, int flags, struct iovec *vector,
+ const xcb_protocol_request_t *request, unsigned int num_fds, int *fds);
+
+/**
* @brief Send a file descriptor to the server in the next call to xcb_send_request.
* @param c: The connection to the X server.
* @param fd: The file descriptor to send.
@@ -114,9 +177,9 @@ uint64_t xcb_send_request64(xcb_connection_t *c, int flags, struct iovec *vector
* After this function returns, the file descriptor given is owned by xcb and
* will be closed eventually.
*
- * FIXME: How the heck is this supposed to work in a thread-safe way? There is a
- * race between two threads doing xcb_send_fd(); xcb_send_request(); at the same
- * time.
+ * @deprecated This function cannot be used in a thread-safe way. Two threads
+ * that run xcb_send_fd(); xcb_send_request(); could mix up their file
+ * descriptors. Instead, xcb_send_request_with_fds() should be used.
*/
void xcb_send_fd(xcb_connection_t *c, int fd);