X-Git-Url: http://git.demorecorder.com/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fc_client.py;h=99fd307b0966cfc67fd0e9d758455eba0b4b5b8b;hb=18f0afab3f0de68114fe185e89d8b25a8c072a2c;hp=26826e42fc400407377a102d69ce2f9794e5ccfa;hpb=28d39258008fcc8ced84dc6c1dd2644e2c908c87;p=free-sw%2Fxcb%2Flibxcb diff --git a/src/c_client.py b/src/c_client.py index 26826e4..99fd307 100644 --- a/src/c_client.py +++ b/src/c_client.py @@ -1,8 +1,12 @@ #!/usr/bin/env python from xml.etree.cElementTree import * from os.path import basename +from functools import reduce import getopt +import os import sys +import errno +import time import re # Jump to the bottom of this file for the main routine @@ -30,6 +34,11 @@ finished_serializers = [] finished_sizeof = [] finished_switch = [] +# keeps enum objects so that we can refer to them when generating manpages. +enums = {} + +manpaths = False + def _h(fmt, *args): ''' Writes the given line to the header file. @@ -167,12 +176,19 @@ def c_open(self): _h('') _h('#include "xcb.h"') + _c('#ifdef HAVE_CONFIG_H') + _c('#include "config.h"') + _c('#endif') _c('#include ') _c('#include ') _c('#include ') + _c('#include /* for offsetof() */') _c('#include "xcbext.h"') _c('#include "%s.h"', _ns.header) + _c('') + _c('#define ALIGNOF(type) offsetof(struct { char dummy; type member; }, member)') + if _ns.is_ext: for (n, h) in self.imports: _hc('#include "%s.h"', h) @@ -242,6 +258,8 @@ def c_enum(self, name): Exported function that handles enum declarations. ''' + enums[name] = self + tname = _t(name) if namecount[tname] > 1: tname = _t(name + ('enum',)) @@ -256,7 +274,10 @@ def c_enum(self, name): count = count - 1 equals = ' = ' if eval != '' else '' comma = ',' if count > 0 else '' - _h(' %s%s%s%s', _n(name + (enam,)).upper(), equals, eval, comma) + doc = '' + if hasattr(self, "doc") and self.doc and enam in self.doc.fields: + doc = '\n/**< %s */\n' % self.doc.fields[enam] + _h(' %s%s%s%s%s', _n(name + (enam,)).upper(), equals, eval, comma, doc) _h('} %s;', tname) @@ -282,6 +303,7 @@ def _c_type_setup(self, name, postfix): self.c_reply_name = _n(name + ('reply',)) self.c_reply_type = _t(name + ('reply',)) self.c_cookie_type = _t(name + ('cookie',)) + self.c_reply_fds_name = _n(name + ('reply_fds',)) self.need_aux = False self.need_serialize = False @@ -323,7 +345,7 @@ def _c_type_setup(self, name, postfix): field.c_field_type = _t(field.field_type) field.c_field_const_type = ('' if field.type.nmemb == 1 else 'const ') + field.c_field_type field.c_field_name = _cpp(field.field_name) - field.c_subscript = '[%d]' % field.type.nmemb if (field.type.nmemb > 1) else '' + field.c_subscript = '[%d]' % field.type.nmemb if (field.type.nmemb and field.type.nmemb > 1) else '' field.c_pointer = ' ' if field.type.nmemb == 1 else '*' # correct the c_pointer field for variable size non-list types @@ -399,8 +421,9 @@ def _c_type_setup(self, name, postfix): if self.need_sizeof: if self.c_sizeof_name not in finished_sizeof: - finished_sizeof.append(self.c_sizeof_name) - _c_serialize('sizeof', self) + if not module.namespace.is_ext or self.name[:2] == module.namespace.prefix: + finished_sizeof.append(self.c_sizeof_name) + _c_serialize('sizeof', self) # _c_type_setup() def _c_helper_absolute_name(prefix, field=None): @@ -442,7 +465,7 @@ def _c_helper_field_mapping(complex_type, prefix, flat=False): else: for f in complex_type.fields: fname = _c_helper_absolute_name(prefix, f) - if all_fields.has_key(f.field_name): + if f.field_name in all_fields: raise Exception("field name %s has been registered before" % f.field_name) all_fields[f.field_name] = (fname, f) @@ -523,7 +546,7 @@ def get_expr_fields(self): prefix.append(('', '', self)) all_fields = _c_helper_resolve_field_names (prefix) - resolved_fields_names = filter(lambda x: x in all_fields.keys(), unresolved_fields_names) + resolved_fields_names = list(filter(lambda x: x in all_fields.keys(), unresolved_fields_names)) if len(unresolved_fields_names) != len(resolved_fields_names): raise Exception("could not resolve all fields for %s" % self.name) @@ -635,7 +658,7 @@ def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'): def _c_serialize_helper_insert_padding(context, code_lines, space, postpone): code_lines.append('%s /* insert padding */' % space) - code_lines.append('%s xcb_pad = -xcb_block_len & 3;' % space) + code_lines.append('%s xcb_pad = -xcb_block_len & (xcb_align_to - 1);' % space) # code_lines.append('%s printf("automatically inserting padding: %%%%d\\n", xcb_pad);' % space) code_lines.append('%s xcb_buffer_len += xcb_block_len + xcb_pad;' % space) @@ -665,10 +688,20 @@ def _c_serialize_helper_switch(context, self, complex_name, switch_expr = _c_accessor_get_expr(self.expr, None) for b in self.bitcases: - bitcase_expr = _c_accessor_get_expr(b.type.expr, None) - code_lines.append(' if(%s & %s) {' % (switch_expr, bitcase_expr)) -# code_lines.append(' printf("switch %s: entering bitcase section %s (mask=%%%%d)...\\n", %s);' % -# (self.name[-1], b.type.name[-1], bitcase_expr)) + len_expr = len(b.type.expr) + for n, expr in enumerate(b.type.expr): + bitcase_expr = _c_accessor_get_expr(expr, None) + # only one in the + if len_expr == 1: + code_lines.append(' if(%s & %s) {' % (switch_expr, bitcase_expr)) + # multiple in the + elif n == 0: # first + code_lines.append(' if((%s & %s) ||' % (switch_expr, bitcase_expr)) + elif len_expr == (n + 1): # last + code_lines.append(' (%s & %s)) {' % (switch_expr, bitcase_expr)) + else: # between first and last + code_lines.append(' (%s & %s) ||' % (switch_expr, bitcase_expr)) + b_prefix = prefix if b.type.has_name: b_prefix = prefix + [(b.c_field_name, '.', b.type)] @@ -742,8 +775,8 @@ def _c_serialize_helper_list_field(context, self, field, param_names = [p[2] for p in params] expr_fields_names = [f.field_name for f in get_expr_fields(field.type)] - resolved = filter(lambda x: x in param_names, expr_fields_names) - unresolved = filter(lambda x: x not in param_names, expr_fields_names) + resolved = list(filter(lambda x: x in param_names, expr_fields_names)) + unresolved = list(filter(lambda x: x not in param_names, expr_fields_names)) field_mapping = {} for r in resolved: @@ -756,8 +789,8 @@ def _c_serialize_helper_list_field(context, self, field, field.c_field_name) field_mapping.update(_c_helper_resolve_field_names(prefix)) - resolved += filter(lambda x: x in field_mapping, unresolved) - unresolved = filter(lambda x: x not in field_mapping, unresolved) + resolved += list(filter(lambda x: x in field_mapping, unresolved)) + unresolved = list(filter(lambda x: x not in field_mapping, unresolved)) if len(unresolved)>0: raise Exception('could not resolve the length fields required for list %s' % field.c_field_name) @@ -874,7 +907,7 @@ def _c_serialize_helper_fields_variable_size(context, self, field, # special case: intermixed fixed and variable size fields if self.var_followed_by_fixed_fields and 'unserialize' == context: - value = ' %s = xcb_tmp;' % field.c_field_name + value = ' %s = (%s *)xcb_tmp;' % (field.c_field_name, field.c_field_type) temp_vars.append(' %s *%s;' % (field.type.c_type, field.c_field_name)) # special case: switch if 'unpack' == context: @@ -924,8 +957,9 @@ def _c_serialize_helper_fields(context, self, prev_field_was_variable = False for field in self.fields: - if not ((field.wire and not field.auto) or field.visible): - continue + if not field.visible: + if not ((field.wire and not field.auto) or 'unserialize' == context): + continue # switch/bitcase: fixed size fields must be considered explicitly if field.type.fixed_size(): @@ -990,6 +1024,8 @@ def _c_serialize_helper_fields(context, self, code_lines.append('%s xcb_parts_idx++;' % space) count += 1 + code_lines.append('%s xcb_align_to = ALIGNOF(%s);' % (space, 'char' if field.c_field_type == 'void' else field.c_field_type)) + need_padding = True if self.var_followed_by_fixed_fields: need_padding = False @@ -1025,8 +1061,8 @@ def _c_serialize_helper(context, complex_type, if context in ('unserialize', 'unpack', 'sizeof') and not self.var_followed_by_fixed_fields: code_lines.append('%s xcb_block_len += sizeof(%s);' % (space, self.c_type)) code_lines.append('%s xcb_tmp += xcb_block_len;' % space) - # probably not needed - #_c_serialize_helper_insert_padding(context, code_lines, space, False) + code_lines.append('%s xcb_buffer_len += xcb_block_len;' % space) + code_lines.append('%s xcb_block_len = 0;' % space) count += _c_serialize_helper_fields(context, self, code_lines, temp_vars, @@ -1081,7 +1117,7 @@ def _c_serialize(context, self): 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 = map(lambda x: "%s," % x, param_str) + param_str = list(map(lambda x: "%s," % x, param_str)) for s in param_str[:-1]: _hc(s) _h("%s);" % param_str[-1].rstrip(',')) @@ -1097,9 +1133,11 @@ def _c_serialize(context, self): _c(' %s *xcb_out = *_buffer;', self.c_type) _c(' unsigned int xcb_out_pad = -sizeof(%s) & 3;', self.c_type) _c(' unsigned int xcb_buffer_len = sizeof(%s) + xcb_out_pad;', self.c_type) + _c(' unsigned int xcb_align_to = 0;') else: _c(' char *xcb_out = *_buffer;') _c(' unsigned int xcb_buffer_len = 0;') + _c(' unsigned int xcb_align_to = 0;') prefix = [('_aux', '->', self)] aux_ptr = 'xcb_out' @@ -1122,6 +1160,7 @@ def _c_serialize(context, self): _c(' unsigned int xcb_buffer_len = 0;') _c(' unsigned int xcb_block_len = 0;') _c(' unsigned int xcb_pad = 0;') + _c(' unsigned int xcb_align_to = 0;') elif 'sizeof' == context: param_names = [p[2] for p in params] @@ -1133,8 +1172,7 @@ def _c_serialize(context, self): return elif self.var_followed_by_fixed_fields: # special case: call _unserialize() - _c(' %s *_aux = NULL;', self.c_type) - _c(' return %s(%s, &_aux);', self.c_unserialize_name, reduce(lambda x,y: "%s, %s" % (x, y), param_names)) + _c(' return %s(%s, NULL);', self.c_unserialize_name, reduce(lambda x,y: "%s, %s" % (x, y), param_names)) _c('}') return else: @@ -1158,7 +1196,7 @@ def _c_serialize(context, self): if not (self.is_switch or self.var_followed_by_fixed_fields): # look if we have to declare an '_aux' variable at all - if len(filter(lambda x: x.find('_aux')!=-1, code_lines))>0: + if len(list(filter(lambda x: x.find('_aux')!=-1, code_lines)))>0: if not self.var_followed_by_fixed_fields: _c(' const %s *_aux = (%s *)_buffer;', self.c_type, self.c_type) else: @@ -1167,6 +1205,7 @@ def _c_serialize(context, self): _c(' unsigned int xcb_buffer_len = 0;') _c(' unsigned int xcb_block_len = 0;') _c(' unsigned int xcb_pad = 0;') + _c(' unsigned int xcb_align_to = 0;') _c('') for t in temp_vars: @@ -1179,6 +1218,12 @@ def _c_serialize(context, self): # allocate memory and copy everything into a continuous memory area # note: this is not necessary in case of unpack if context in ('serialize', 'unserialize'): + # unserialize: check for sizeof-only invocation + if 'unserialize' == context: + _c('') + _c(' if (NULL == _aux)') + _c(' return xcb_buffer_len;') + _c('') _c(' if (NULL == %s) {', aux_ptr) _c(' /* allocate memory */') @@ -1212,12 +1257,13 @@ def _c_serialize(context, self): # unserialize: assign variable size fields individually if 'unserialize' == context: - _c(' *%s = xcb_out;', aux_ptr) - _c(' xcb_tmp = (char *)(*_aux+1);') + _c(' xcb_tmp = ((char *)*_aux)+xcb_buffer_len;') + param_fields.reverse() for field in param_fields: if not field.type.fixed_size(): - _c(' memcpy(xcb_tmp, %s, %s_len);', field.c_field_name, field.c_field_name) - _c(' xcb_tmp += %s_len;', field.c_field_name) + _c(' xcb_tmp -= %s_len;', field.c_field_name) + _c(' memmove(xcb_tmp, %s, %s_len);', field.c_field_name, field.c_field_name) + _c(' *%s = xcb_out;', aux_ptr) _c('') _c(' return xcb_buffer_len;') @@ -1415,6 +1461,11 @@ def _c_accessor_get_expr(expr, field_mapping): else: return lenexp +def type_pad_type(type): + if type == 'void': + return 'char' + return type + def _c_accessors_field(self, field): ''' Declares the accessor functions for a non-list field that follows a variable-length field. @@ -1449,7 +1500,7 @@ def _c_accessors_field(self, field): else: _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) _c(' return * (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);', - field.c_field_type, field.first_field_after_varsized.type.c_type, field.prev_varsized_offset) + field.c_field_type, type_pad_type(field.first_field_after_varsized.type.c_type), field.prev_varsized_offset) _c('}') else: _hc('') @@ -1480,7 +1531,7 @@ def _c_accessors_field(self, field): else: _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) _c(' return (%s) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);', - return_type, field.first_field_after_varsized.type.c_type, field.prev_varsized_offset) + return_type, type_pad_type(field.first_field_after_varsized.type.c_type), field.prev_varsized_offset) _c('}') @@ -1567,7 +1618,7 @@ def _c_accessors_list(self, field): else: _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) _c(' return (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);', - field.c_field_type, field.first_field_after_varsized.type.c_type, field.prev_varsized_offset) + field.c_field_type, type_pad_type(field.first_field_after_varsized.type.c_type), field.prev_varsized_offset) _c('}') _hc('') @@ -1671,7 +1722,7 @@ def _c_accessors_list(self, field): else: _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) _c(' i.data = (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index));', - field.c_field_type, field.c_field_type) + field.c_field_type, type_pad_type(field.c_field_type)) if switch_obj is None: _c(' i.rem = %s;', _c_accessor_get_expr(field.type.expr, fields)) _c(' i.index = (char *) i.data - (char *) %s;', 'R' if switch_obj is None else 'S' ) @@ -1737,12 +1788,12 @@ def _c_complex(self): for field in struct_fields: length = len(field.c_field_type) # account for '*' pointer_spec - if not field.type.fixed_size(): + if not field.type.fixed_size() and not self.is_union: length += 1 maxtypelen = max(maxtypelen, length) def _c_complex_field(self, field, space=''): - if (field.type.fixed_size() or + if (field.type.fixed_size() or self.is_union or # in case of switch with switch children, don't make the field a pointer # necessary for unserialize to work (self.is_switch and field.type.is_switch)): @@ -1785,7 +1836,7 @@ def c_union(self, name): _c_complex(self) _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. ''' @@ -1814,6 +1865,12 @@ def _c_request_helper(self, name, cookie_type, void, regular, aux=False): # What flag is passed to xcb_request 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 func_ext_global = '&' + _ns.c_ext_global_name if _ns.is_ext else '0' @@ -1854,11 +1911,58 @@ def _c_request_helper(self, name, cookie_type, void, regular, aux=False): _c_setlevel(1) _h('') _h('/**') - _h(' * Delivers a request to the X server') + if hasattr(self, "doc") and self.doc: + if self.doc.brief: + _h(' * @brief ' + self.doc.brief) + else: + _h(' * No brief doc yet') + + _h(' *') _h(' * @param c The connection') + param_names = [f.c_field_name for f in param_fields] + if hasattr(self, "doc") and self.doc: + for field in param_fields: + # XXX: hard-coded until we fix xproto.xml + base_func_name = self.c_request_name if not aux else self.c_aux_name + if base_func_name == 'xcb_change_gc' and field.c_field_name == 'value_mask': + field.enum = 'GC' + elif base_func_name == 'xcb_change_window_attributes' and field.c_field_name == 'value_mask': + field.enum = 'CW' + elif base_func_name == 'xcb_create_window' and field.c_field_name == 'value_mask': + field.enum = 'CW' + if field.enum: + # XXX: why the 'xcb' prefix? + key = ('xcb', field.enum) + + tname = _t(key) + if namecount[tname] > 1: + tname = _t(key + ('enum',)) + _h(' * @param %s A bitmask of #%s values.' % (field.c_field_name, tname)) + + if self.doc and field.field_name in self.doc.fields: + desc = self.doc.fields[field.field_name] + for name in param_names: + desc = desc.replace('`%s`' % name, '\\a %s' % (name)) + desc = desc.split("\n") + desc = [line if line != '' else '\\n' for line in desc] + _h(' * @param %s %s' % (field.c_field_name, "\n * ".join(desc))) + # If there is no documentation yet, we simply don't generate an + # @param tag. Doxygen will then warn about missing documentation. + _h(' * @return A cookie') _h(' *') - _h(' * Delivers a request to the X server.') + + if hasattr(self, "doc") and self.doc: + if self.doc.description: + desc = self.doc.description + for name in param_names: + desc = desc.replace('`%s`' % name, '\\a %s' % (name)) + desc = desc.split("\n") + _h(' * ' + "\n * ".join(desc)) + else: + _h(' * No description yet') + else: + _h(' * Delivers a request to the X server.') _h(' * ') if checked: _h(' * This form can be used only if the request will not cause') @@ -2052,6 +2156,10 @@ def _c_request_helper(self, name, cookie_type, void, regular, aux=False): # no padding necessary - _serialize() keeps track of padding automatically _c(' ') + for field in param_fields: + if field.isfd: + _c(' xcb_send_fd(c, %s);', field.c_field_name) + _c(' xcb_ret.sequence = xcb_send_request(c, %s, xcb_parts + 2, &xcb_req);', func_flags) # free dyn. all. data, if any @@ -2155,6 +2263,51 @@ def _c_reply(self, name): _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): ''' Declares the opcode define for requests, events, and errors. @@ -2177,6 +2330,523 @@ def _c_cookie(self, name): _h(' unsigned int sequence; /**< */') _h('} %s;', self.c_cookie_type) +def _man_request(self, name, cookie_type, void, aux): + param_fields = [f for f in self.fields if f.visible] + + func_name = self.c_request_name if not aux else self.c_aux_name + + def create_link(linkname): + name = 'man/%s.3' % linkname + if manpaths: + sys.stdout.write(name) + f = open(name, 'w') + f.write('.so man3/%s.3' % func_name) + f.close() + + if manpaths: + sys.stdout.write('man/%s.3 ' % func_name) + # Our CWD is src/, so this will end up in src/man/ + f = open('man/%s.3' % func_name, 'w') + f.write('.TH %s 3 %s "XCB" "XCB Requests"\n' % (func_name, today)) + # Left-adjust instead of adjusting to both sides + f.write('.ad l\n') + f.write('.SH NAME\n') + brief = self.doc.brief if hasattr(self, "doc") and self.doc else '' + f.write('%s \\- %s\n' % (func_name, brief)) + f.write('.SH SYNOPSIS\n') + # Don't split words (hyphenate) + f.write('.hy 0\n') + f.write('.B #include \n' % _ns.header) + + # function prototypes + prototype = '' + count = len(param_fields) + for field in param_fields: + count = count - 1 + c_field_const_type = field.c_field_const_type + c_pointer = field.c_pointer + if c_pointer == ' ': + c_pointer = '' + if field.type.need_serialize and not aux: + c_field_const_type = "const void" + c_pointer = '*' + comma = ', ' if count else ');' + prototype += '%s\\ %s\\fI%s\\fP%s' % (c_field_const_type, c_pointer, field.c_field_name, comma) + + f.write('.SS Request function\n') + f.write('.HP\n') + base_func_name = self.c_request_name if not aux else self.c_aux_name + f.write('%s \\fB%s\\fP(xcb_connection_t\\ *\\fIconn\\fP, %s\n' % (cookie_type, base_func_name, prototype)) + create_link('%s_%s' % (base_func_name, ('checked' if void else 'unchecked'))) + if not void: + f.write('.PP\n') + f.write('.SS Reply datastructure\n') + f.write('.nf\n') + f.write('.sp\n') + f.write('typedef %s %s {\n' % (self.reply.c_container, self.reply.c_type)) + struct_fields = [] + maxtypelen = 0 + + for field in self.reply.fields: + if not field.type.fixed_size() and not self.is_switch and not self.is_union: + continue + if field.wire: + struct_fields.append(field) + + for field in struct_fields: + length = len(field.c_field_type) + # account for '*' pointer_spec + if not field.type.fixed_size(): + length += 1 + maxtypelen = max(maxtypelen, length) + + def _c_complex_field(self, field, space=''): + if (field.type.fixed_size() or + # in case of switch with switch children, don't make the field a pointer + # necessary for unserialize to work + (self.is_switch and field.type.is_switch)): + spacing = ' ' * (maxtypelen - len(field.c_field_type)) + f.write('%s %s%s \\fI%s\\fP%s;\n' % (space, field.c_field_type, spacing, field.c_field_name, field.c_subscript)) + else: + spacing = ' ' * (maxtypelen - (len(field.c_field_type) + 1)) + f.write('ELSE %s = %s\n' % (field.c_field_type, field.c_field_name)) + #_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: + _c_complex_field(self, field) + else: + for b in self.bitcases: + space = '' + if b.type.has_name: + space = ' ' + for field in b.type.fields: + _c_complex_field(self, field, space) + if b.type.has_name: + print >> sys.stderr, 'ERROR: New unhandled documentation case' + pass + + f.write('} \\fB%s\\fP;\n' % self.reply.c_type) + f.write('.fi\n') + + f.write('.SS Reply function\n') + f.write('.HP\n') + f.write(('%s *\\fB%s\\fP(xcb_connection_t\\ *\\fIconn\\fP, %s\\ ' + '\\fIcookie\\fP, xcb_generic_error_t\\ **\\fIe\\fP);\n') % + (self.c_reply_type, self.c_reply_name, self.c_cookie_type)) + create_link('%s' % self.c_reply_name) + + has_accessors = False + for field in self.reply.fields: + if field.type.is_list and not field.type.fixed_size(): + has_accessors = True + elif field.prev_varsized_field is not None or not field.type.fixed_size(): + has_accessors = True + + if has_accessors: + f.write('.SS Reply accessors\n') + + def _c_accessors_field(self, field): + ''' + Declares the accessor functions for a non-list field that follows a variable-length field. + ''' + c_type = self.c_type + + # special case: switch + switch_obj = self if self.is_switch else None + if self.is_bitcase: + switch_obj = self.parents[-1] + if switch_obj is not None: + c_type = switch_obj.c_type + + if field.type.is_simple: + f.write('%s %s (const %s *reply)\n' % (field.c_field_type, field.c_accessor_name, c_type)) + create_link('%s' % field.c_accessor_name) + else: + f.write('%s *%s (const %s *reply)\n' % (field.c_field_type, field.c_accessor_name, c_type)) + create_link('%s' % field.c_accessor_name) + + def _c_accessors_list(self, field): + ''' + Declares the accessor functions for a list field. + Declares a direct-accessor function only if the list members are fixed size. + Declares length and get-iterator functions always. + ''' + list = field.type + c_type = self.reply.c_type + + # special case: switch + # in case of switch, 2 params have to be supplied to certain accessor functions: + # 1. the anchestor object (request or reply) + # 2. the (anchestor) switch object + # the reason is that switch is either a child of a request/reply or nested in another switch, + # so whenever we need to access a length field, we might need to refer to some anchestor type + switch_obj = self if self.is_switch else None + if self.is_bitcase: + switch_obj = self.parents[-1] + if switch_obj is not None: + c_type = switch_obj.c_type + + params = [] + fields = {} + parents = self.parents if hasattr(self, 'parents') else [self] + # 'R': parents[0] is always the 'toplevel' container type + params.append(('const %s *\\fIreply\\fP' % parents[0].c_type, parents[0])) + fields.update(_c_helper_field_mapping(parents[0], [('R', '->', parents[0])], flat=True)) + # auxiliary object for 'R' parameters + R_obj = parents[0] + + if switch_obj is not None: + # now look where the fields are defined that are needed to evaluate + # the switch expr, and store the parent objects in accessor_params and + # the fields in switch_fields + + # 'S': name for the 'toplevel' switch + toplevel_switch = parents[1] + params.append(('const %s *S' % toplevel_switch.c_type, toplevel_switch)) + fields.update(_c_helper_field_mapping(toplevel_switch, [('S', '->', toplevel_switch)], flat=True)) + + # initialize prefix for everything "below" S + prefix_str = '/* %s */ S' % toplevel_switch.name[-1] + prefix = [(prefix_str, '->', toplevel_switch)] + + # look for fields in the remaining containers + for p in parents[2:] + [self]: + # the separator between parent and child is always '.' here, + # because of nested switch statements + if not p.is_bitcase or (p.is_bitcase and p.has_name): + prefix.append((p.name[-1], '.', p)) + fields.update(_c_helper_field_mapping(p, prefix, flat=True)) + + # auxiliary object for 'S' parameter + S_obj = parents[1] + + if list.member.fixed_size(): + idx = 1 if switch_obj is not None else 0 + f.write('.HP\n') + f.write('%s *\\fB%s\\fP(%s);\n' % + (field.c_field_type, field.c_accessor_name, params[idx][0])) + create_link('%s' % field.c_accessor_name) + + f.write('.HP\n') + f.write('int \\fB%s\\fP(const %s *\\fIreply\\fP);\n' % + (field.c_length_name, c_type)) + create_link('%s' % field.c_length_name) + + if field.type.member.is_simple: + f.write('.HP\n') + f.write('xcb_generic_iterator_t \\fB%s\\fP(const %s *\\fIreply\\fP);\n' % + (field.c_end_name, c_type)) + create_link('%s' % field.c_end_name) + else: + f.write('.HP\n') + f.write('%s \\fB%s\\fP(const %s *\\fIreply\\fP);\n' % + (field.c_iterator_type, field.c_iterator_name, + c_type)) + create_link('%s' % field.c_iterator_name) + + for field in self.reply.fields: + if field.type.is_list and not field.type.fixed_size(): + _c_accessors_list(self, field) + elif field.prev_varsized_field is not None or not field.type.fixed_size(): + _c_accessors_field(self, field) + + + f.write('.br\n') + # Re-enable hyphenation and adjusting to both sides + f.write('.hy 1\n') + + # argument reference + f.write('.SH REQUEST ARGUMENTS\n') + f.write('.IP \\fI%s\\fP 1i\n' % 'conn') + f.write('The XCB connection to X11.\n') + for field in param_fields: + f.write('.IP \\fI%s\\fP 1i\n' % (field.c_field_name)) + printed_enum = False + # XXX: hard-coded until we fix xproto.xml + if base_func_name == 'xcb_change_gc' and field.c_field_name == 'value_mask': + field.enum = 'GC' + elif base_func_name == 'xcb_change_window_attributes' and field.c_field_name == 'value_mask': + field.enum = 'CW' + elif base_func_name == 'xcb_create_window' and field.c_field_name == 'value_mask': + field.enum = 'CW' + if hasattr(field, "enum") and field.enum: + # XXX: why the 'xcb' prefix? + key = ('xcb', field.enum) + if key in enums: + f.write('One of the following values:\n') + f.write('.RS 1i\n') + enum = enums[key] + count = len(enum.values) + for (enam, eval) in enum.values: + count = count - 1 + f.write('.IP \\fI%s\\fP 1i\n' % (_n(key + (enam,)).upper())) + if hasattr(enum, "doc") and enum.doc and enam in enum.doc.fields: + desc = re.sub(r'`([^`]+)`', r'\\fI\1\\fP', enum.doc.fields[enam]) + f.write('%s\n' % desc) + else: + f.write('TODO: NOT YET DOCUMENTED.\n') + f.write('.RE\n') + f.write('.RS 1i\n') + printed_enum = True + + if hasattr(self, "doc") and self.doc and field.field_name in self.doc.fields: + desc = self.doc.fields[field.field_name] + desc = re.sub(r'`([^`]+)`', r'\\fI\1\\fP', desc) + if printed_enum: + f.write('\n') + f.write('%s\n' % desc) + else: + f.write('TODO: NOT YET DOCUMENTED.\n') + if printed_enum: + f.write('.RE\n') + + # Reply reference + if not void: + f.write('.SH REPLY FIELDS\n') + # These fields are present in every reply: + f.write('.IP \\fI%s\\fP 1i\n' % 'response_type') + f.write(('The type of this reply, in this case \\fI%s\\fP. This field ' + 'is also present in the \\fIxcb_generic_reply_t\\fP and can ' + 'be used to tell replies apart from each other.\n') % + _n(self.reply.name).upper()) + f.write('.IP \\fI%s\\fP 1i\n' % 'sequence') + f.write('The sequence number of the last request processed by the X11 server.\n') + f.write('.IP \\fI%s\\fP 1i\n' % 'length') + f.write('The length of the reply, in words (a word is 4 bytes).\n') + for field in self.reply.fields: + if (field.c_field_name in frozenset(['response_type', 'sequence', 'length']) or + field.c_field_name.startswith('pad')): + continue + + if field.type.is_list and not field.type.fixed_size(): + continue + elif field.prev_varsized_field is not None or not field.type.fixed_size(): + continue + f.write('.IP \\fI%s\\fP 1i\n' % (field.c_field_name)) + printed_enum = False + if hasattr(field, "enum") and field.enum: + # XXX: why the 'xcb' prefix? + key = ('xcb', field.enum) + if key in enums: + f.write('One of the following values:\n') + f.write('.RS 1i\n') + enum = enums[key] + count = len(enum.values) + for (enam, eval) in enum.values: + count = count - 1 + f.write('.IP \\fI%s\\fP 1i\n' % (_n(key + (enam,)).upper())) + if enum.doc and enam in enum.doc.fields: + desc = re.sub(r'`([^`]+)`', r'\\fI\1\\fP', enum.doc.fields[enam]) + f.write('%s\n' % desc) + else: + f.write('TODO: NOT YET DOCUMENTED.\n') + f.write('.RE\n') + f.write('.RS 1i\n') + printed_enum = True + + if hasattr(self.reply, "doc") and self.reply.doc and field.field_name in self.reply.doc.fields: + desc = self.reply.doc.fields[field.field_name] + desc = re.sub(r'`([^`]+)`', r'\\fI\1\\fP', desc) + if printed_enum: + f.write('\n') + f.write('%s\n' % desc) + else: + f.write('TODO: NOT YET DOCUMENTED.\n') + if printed_enum: + f.write('.RE\n') + + + + # text description + f.write('.SH DESCRIPTION\n') + if hasattr(self, "doc") and self.doc and self.doc.description: + desc = self.doc.description + desc = re.sub(r'`([^`]+)`', r'\\fI\1\\fP', desc) + lines = desc.split('\n') + f.write('\n'.join(lines) + '\n') + + f.write('.SH RETURN VALUE\n') + if void: + f.write(('Returns an \\fIxcb_void_cookie_t\\fP. Errors (if any) ' + 'have to be handled in the event loop.\n\nIf you want to ' + 'handle errors directly with \\fIxcb_request_check\\fP ' + 'instead, use \\fI%s_checked\\fP. See ' + '\\fBxcb-requests(3)\\fP for details.\n') % (base_func_name)) + else: + f.write(('Returns an \\fI%s\\fP. Errors have to be handled when ' + 'calling the reply function \\fI%s\\fP.\n\nIf you want to ' + 'handle errors in the event loop instead, use ' + '\\fI%s_unchecked\\fP. See \\fBxcb-requests(3)\\fP for ' + 'details.\n') % + (cookie_type, self.c_reply_name, base_func_name)) + f.write('.SH ERRORS\n') + if hasattr(self, "doc") and self.doc: + for errtype, errtext in self.doc.errors.items(): + f.write('.IP \\fI%s\\fP 1i\n' % (_t(('xcb', errtype, 'error')))) + errtext = re.sub(r'`([^`]+)`', r'\\fI\1\\fP', errtext) + f.write('%s\n' % (errtext)) + if not hasattr(self, "doc") or not self.doc or len(self.doc.errors) == 0: + f.write('This request does never generate any errors.\n') + if hasattr(self, "doc") and self.doc and self.doc.example: + f.write('.SH EXAMPLE\n') + f.write('.nf\n') + f.write('.sp\n') + lines = self.doc.example.split('\n') + f.write('\n'.join(lines) + '\n') + f.write('.fi\n') + f.write('.SH SEE ALSO\n') + if hasattr(self, "doc") and self.doc: + see = ['.BR %s (3)' % 'xcb-requests'] + if self.doc.example: + see.append('.BR %s (3)' % 'xcb-examples') + for seename, seetype in self.doc.see.items(): + if seetype == 'program': + see.append('.BR %s (1)' % seename) + elif seetype == 'event': + see.append('.BR %s (3)' % _t(('xcb', seename, 'event'))) + elif seetype == 'request': + see.append('.BR %s (3)' % _n(('xcb', seename))) + elif seetype == 'function': + see.append('.BR %s (3)' % seename) + else: + see.append('TODO: %s (type %s)' % (seename, seetype)) + f.write(',\n'.join(see) + '\n') + f.write('.SH AUTHOR\n') + f.write('Generated from %s.xml. Contact xcb@lists.freedesktop.org for corrections and improvements.\n' % _ns.header) + f.close() + +def _man_event(self, name): + if manpaths: + sys.stdout.write('man/%s.3 ' % self.c_type) + # Our CWD is src/, so this will end up in src/man/ + f = open('man/%s.3' % self.c_type, 'w') + f.write('.TH %s 3 %s "XCB" "XCB Events"\n' % (self.c_type, today)) + # Left-adjust instead of adjusting to both sides + f.write('.ad l\n') + f.write('.SH NAME\n') + brief = self.doc.brief if hasattr(self, "doc") and self.doc else '' + f.write('%s \\- %s\n' % (self.c_type, brief)) + f.write('.SH SYNOPSIS\n') + # Don't split words (hyphenate) + f.write('.hy 0\n') + f.write('.B #include \n' % _ns.header) + + f.write('.PP\n') + f.write('.SS Event datastructure\n') + f.write('.nf\n') + f.write('.sp\n') + f.write('typedef %s %s {\n' % (self.c_container, self.c_type)) + struct_fields = [] + maxtypelen = 0 + + for field in self.fields: + if not field.type.fixed_size() and not self.is_switch and not self.is_union: + continue + if field.wire: + struct_fields.append(field) + + for field in struct_fields: + length = len(field.c_field_type) + # account for '*' pointer_spec + if not field.type.fixed_size(): + length += 1 + maxtypelen = max(maxtypelen, length) + + def _c_complex_field(self, field, space=''): + if (field.type.fixed_size() or + # in case of switch with switch children, don't make the field a pointer + # necessary for unserialize to work + (self.is_switch and field.type.is_switch)): + spacing = ' ' * (maxtypelen - len(field.c_field_type)) + f.write('%s %s%s \\fI%s\\fP%s;\n' % (space, field.c_field_type, spacing, field.c_field_name, field.c_subscript)) + else: + print >> sys.stderr, 'ERROR: New unhandled documentation case' + + if not self.is_switch: + for field in struct_fields: + _c_complex_field(self, field) + else: + for b in self.bitcases: + space = '' + if b.type.has_name: + space = ' ' + for field in b.type.fields: + _c_complex_field(self, field, space) + if b.type.has_name: + print >> sys.stderr, 'ERROR: New unhandled documentation case' + pass + + f.write('} \\fB%s\\fP;\n' % self.c_type) + f.write('.fi\n') + + + f.write('.br\n') + # Re-enable hyphenation and adjusting to both sides + f.write('.hy 1\n') + + # argument reference + f.write('.SH EVENT FIELDS\n') + f.write('.IP \\fI%s\\fP 1i\n' % 'response_type') + f.write(('The type of this event, in this case \\fI%s\\fP. This field is ' + 'also present in the \\fIxcb_generic_event_t\\fP and can be used ' + 'to tell events apart from each other.\n') % _n(name).upper()) + f.write('.IP \\fI%s\\fP 1i\n' % 'sequence') + f.write('The sequence number of the last request processed by the X11 server.\n') + + if not self.is_switch: + for field in struct_fields: + # Skip the fields which every event has, we already documented + # them (see above). + if field.c_field_name in ('response_type', 'sequence'): + continue + if isinstance(field.type, PadType): + continue + f.write('.IP \\fI%s\\fP 1i\n' % (field.c_field_name)) + if hasattr(self, "doc") and self.doc and field.field_name in self.doc.fields: + desc = self.doc.fields[field.field_name] + desc = re.sub(r'`([^`]+)`', r'\\fI\1\\fP', desc) + f.write('%s\n' % desc) + else: + f.write('NOT YET DOCUMENTED.\n') + + # text description + f.write('.SH DESCRIPTION\n') + if hasattr(self, "doc") and self.doc and self.doc.description: + desc = self.doc.description + desc = re.sub(r'`([^`]+)`', r'\\fI\1\\fP', desc) + lines = desc.split('\n') + f.write('\n'.join(lines) + '\n') + + if hasattr(self, "doc") and self.doc and self.doc.example: + f.write('.SH EXAMPLE\n') + f.write('.nf\n') + f.write('.sp\n') + lines = self.doc.example.split('\n') + f.write('\n'.join(lines) + '\n') + f.write('.fi\n') + f.write('.SH SEE ALSO\n') + if hasattr(self, "doc") and self.doc: + see = ['.BR %s (3)' % 'xcb_generic_event_t'] + if self.doc.example: + see.append('.BR %s (3)' % 'xcb-examples') + for seename, seetype in self.doc.see.items(): + if seetype == 'program': + see.append('.BR %s (1)' % seename) + elif seetype == 'event': + see.append('.BR %s (3)' % _t(('xcb', seename, 'event'))) + elif seetype == 'request': + see.append('.BR %s (3)' % _n(('xcb', seename))) + elif seetype == 'function': + see.append('.BR %s (3)' % seename) + else: + see.append('TODO: %s (type %s)' % (seename, seetype)) + f.write(',\n'.join(see) + '\n') + f.write('.SH AUTHOR\n') + f.write('Generated from %s.xml. Contact xcb@lists.freedesktop.org for corrections and improvements.\n' % _ns.header) + f.close() + + def c_request(self, name): ''' Exported function that handles request declarations. @@ -2198,14 +2868,17 @@ def c_request(self, name): # Reply structure definition _c_complex(self.reply) # Request prototypes - _c_request_helper(self, name, self.c_cookie_type, False, True) - _c_request_helper(self, name, self.c_cookie_type, False, False) + has_fds = _c_reply_has_fds(self.reply) + _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: - _c_request_helper(self, name, self.c_cookie_type, False, True, True) - _c_request_helper(self, name, self.c_cookie_type, False, False, 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, has_fds) # Reply accessors _c_accessors(self.reply, name + ('reply',), name) _c_reply(self, name) + if has_fds: + _c_reply_fds(self, name) else: # Request prototypes _c_request_helper(self, name, 'xcb_void_cookie_t', True, False) @@ -2214,11 +2887,32 @@ def c_request(self, name): _c_request_helper(self, name, 'xcb_void_cookie_t', True, False, True) _c_request_helper(self, name, 'xcb_void_cookie_t', True, True, True) + # We generate the manpage afterwards because _c_type_setup has been called. + # TODO: what about aux helpers? + cookie_type = self.c_cookie_type if self.reply else 'xcb_void_cookie_t' + _man_request(self, name, cookie_type, not self.reply, False) def c_event(self, name): ''' Exported function that handles event declarations. ''' + + # The generic event structure xcb_ge_event_t has the full_sequence field + # at the 32byte boundary. That's why we've to inject this field into GE + # events while generating the structure for them. Otherwise we would read + # garbage (the internal full_sequence) when accessing normal event fields + # there. + if hasattr(self, 'is_ge_event') and self.is_ge_event and self.name == name: + event_size = 0 + for field in self.fields: + if field.type.size != None and field.type.nmemb != None: + event_size += field.type.size * field.type.nmemb + if event_size == 32: + full_sequence = Field(tcard32, tcard32.name, 'full_sequence', False, True, True) + idx = self.fields.index(field) + self.fields.insert(idx + 1, full_sequence) + break + _c_type_setup(self, name, ('event',)) # Opcode define @@ -2232,6 +2926,8 @@ def c_event(self, name): _h('') _h('typedef %s %s;', _t(self.name + ('event',)), _t(name + ('event',))) + _man_event(self, name) + def c_error(self, name): ''' Exported function that handles error declarations. @@ -2268,29 +2964,42 @@ output = {'open' : c_open, # Check for the argument that specifies path to the xcbgen python package. try: - opts, args = getopt.getopt(sys.argv[1:], 'p:') -except getopt.GetoptError, err: - print str(err) - print 'Usage: c_client.py [-p path] file.xml' + opts, args = getopt.getopt(sys.argv[1:], 'p:m') +except getopt.GetoptError as err: + print(err) + print('Usage: c_client.py [-p path] file.xml') sys.exit(1) for (opt, arg) in opts: if opt == '-p': - sys.path.append(arg) + sys.path.insert(1, arg) + elif opt == '-m': + manpaths = True + sys.stdout.write('man_MANS = ') # Import the module class try: from xcbgen.state import Module + from xcbgen.xtypes import * except ImportError: - print '' - print 'Failed to load the xcbgen Python package!' - print 'Make sure that xcb/proto installed it on your Python path.' - print 'If not, you will need to create a .pth file or define $PYTHONPATH' - print 'to extend the path.' - print 'Refer to the README file in xcb/proto for more info.' - print '' + print(''' +Failed to load the xcbgen Python package! +Make sure that xcb/proto installed it on your Python path. +If not, you will need to create a .pth file or define $PYTHONPATH +to extend the path. +Refer to the README file in xcb/proto for more info. +''') raise +# Ensure the man subdirectory exists +try: + os.mkdir('man') +except OSError as e: + if e.errno != errno.EEXIST: + raise + +today = time.strftime('%Y-%m-%d', time.gmtime(os.path.getmtime(args[0]))) + # Parse the xml header module = Module(args[0], output)