From ea71d7d7e3f5d8189b80747678e9ca9a417b1d37 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 27 Nov 2011 10:38:26 +0000 Subject: [PATCH] c_client.py: generate manpages Signed-off-by: Julien Danjou --- src/Makefile.am | 8 +- src/c_client.py | 600 +++++++++++++++++++++++++++++++++- src/list_of_manpages.inc | 1 + src/static-man/xcb-examples.3 | 59 ++++ src/static-man/xcb-requests.3 | 165 ++++++++++ 5 files changed, 827 insertions(+), 6 deletions(-) create mode 100644 src/list_of_manpages.inc create mode 100644 src/static-man/xcb-examples.3 create mode 100644 src/static-man/xcb-requests.3 diff --git a/src/Makefile.am b/src/Makefile.am index 950de5c..5bcb461 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -225,8 +225,12 @@ endif nodist_xcbinclude_HEADERS = $(EXTHEADERS) noinst_HEADERS = xcbint.h -BUILT_SOURCES = $(EXTSOURCES) -CLEANFILES = $(EXTSOURCES) $(EXTHEADERS) +BUILT_SOURCES = $(EXTSOURCES) $(man_MANS) +CLEANFILES = $(EXTSOURCES) $(EXTHEADERS) $(man_MANS) + +include $(srcdir)/list_of_manpages.inc $(EXTSOURCES): c_client.py $(PYTHON) $(srcdir)/c_client.py -p $(XCBPROTO_XCBPYTHONDIR) $(XCBPROTO_XCBINCLUDEDIR)/$(@:.c=.xml) + +$(man_MANS): $(EXTSOURCES) diff --git a/src/c_client.py b/src/c_client.py index ad3ea22..8382bb5 100644 --- a/src/c_client.py +++ b/src/c_client.py @@ -3,7 +3,9 @@ from xml.etree.cElementTree import * from os.path import basename from functools import reduce import getopt +import os import sys +import time import re # Jump to the bottom of this file for the main routine @@ -31,6 +33,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. @@ -247,6 +254,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',)) @@ -261,7 +270,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 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) @@ -1878,11 +1890,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 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 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 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') @@ -2201,6 +2260,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 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 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 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 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 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 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 self.doc: + for errtype, errtext in self.doc.errors.iteritems(): + 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 self.doc or len(self.doc.errors) == 0: + f.write('This request does never generate any errors.\n') + if 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 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.iteritems(): + 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 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 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 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 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 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.iteritems(): + 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. @@ -2238,6 +2814,10 @@ 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): ''' @@ -2256,6 +2836,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. @@ -2292,7 +2874,7 @@ 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:') + 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') @@ -2301,10 +2883,14 @@ except getopt.GetoptError as err: for (opt, arg) in opts: if opt == '-p': 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(''' Failed to load the xcbgen Python package! @@ -2315,6 +2901,12 @@ Refer to the README file in xcb/proto for more info. ''') raise +# Ensure the man subdirectory exists +if not os.path.exists('man'): + os.mkdir('man') + +today = time.strftime('%Y-%m-%d', time.gmtime(os.path.getmtime(args[0]))) + # Parse the xml header module = Module(args[0], output) diff --git a/src/list_of_manpages.inc b/src/list_of_manpages.inc new file mode 100644 index 0000000..6f06303 --- /dev/null +++ b/src/list_of_manpages.inc @@ -0,0 +1 @@ +man_MANS = static-man/xcb-examples.3 static-man/xcb-requests.3 man/xcb_create_window.3 man/xcb_create_window_checked.3 man/xcb_change_window_attributes.3 man/xcb_change_window_attributes_checked.3 man/xcb_get_window_attributes.3 man/xcb_get_window_attributes_unchecked.3 man/xcb_get_window_attributes_reply.3 man/xcb_destroy_window.3 man/xcb_destroy_window_checked.3 man/xcb_destroy_subwindows.3 man/xcb_destroy_subwindows_checked.3 man/xcb_change_save_set.3 man/xcb_change_save_set_checked.3 man/xcb_reparent_window.3 man/xcb_reparent_window_checked.3 man/xcb_map_window.3 man/xcb_map_window_checked.3 man/xcb_map_subwindows.3 man/xcb_map_subwindows_checked.3 man/xcb_unmap_window.3 man/xcb_unmap_window_checked.3 man/xcb_unmap_subwindows.3 man/xcb_unmap_subwindows_checked.3 man/xcb_configure_window.3 man/xcb_configure_window_checked.3 man/xcb_circulate_window.3 man/xcb_circulate_window_checked.3 man/xcb_get_geometry.3 man/xcb_get_geometry_unchecked.3 man/xcb_get_geometry_reply.3 man/xcb_query_tree.3 man/xcb_query_tree_unchecked.3 man/xcb_query_tree_reply.3 man/xcb_query_tree_children.3 man/xcb_query_tree_children_length.3 man/xcb_query_tree_children_end.3 man/xcb_intern_atom.3 man/xcb_intern_atom_unchecked.3 man/xcb_intern_atom_reply.3 man/xcb_get_atom_name.3 man/xcb_get_atom_name_unchecked.3 man/xcb_get_atom_name_reply.3 man/xcb_get_atom_name_name.3 man/xcb_get_atom_name_name_length.3 man/xcb_get_atom_name_name_end.3 man/xcb_change_property.3 man/xcb_change_property_checked.3 man/xcb_delete_property.3 man/xcb_delete_property_checked.3 man/xcb_get_property.3 man/xcb_get_property_unchecked.3 man/xcb_get_property_reply.3 man/xcb_get_property_value.3 man/xcb_get_property_value_length.3 man/xcb_get_property_value_end.3 man/xcb_list_properties.3 man/xcb_list_properties_unchecked.3 man/xcb_list_properties_reply.3 man/xcb_list_properties_atoms.3 man/xcb_list_properties_atoms_length.3 man/xcb_list_properties_atoms_end.3 man/xcb_set_selection_owner.3 man/xcb_set_selection_owner_checked.3 man/xcb_get_selection_owner.3 man/xcb_get_selection_owner_unchecked.3 man/xcb_get_selection_owner_reply.3 man/xcb_convert_selection.3 man/xcb_convert_selection_checked.3 man/xcb_send_event.3 man/xcb_send_event_checked.3 man/xcb_grab_pointer.3 man/xcb_grab_pointer_unchecked.3 man/xcb_grab_pointer_reply.3 man/xcb_ungrab_pointer.3 man/xcb_ungrab_pointer_checked.3 man/xcb_grab_button.3 man/xcb_grab_button_checked.3 man/xcb_ungrab_button.3 man/xcb_ungrab_button_checked.3 man/xcb_change_active_pointer_grab.3 man/xcb_change_active_pointer_grab_checked.3 man/xcb_grab_keyboard.3 man/xcb_grab_keyboard_unchecked.3 man/xcb_grab_keyboard_reply.3 man/xcb_ungrab_keyboard.3 man/xcb_ungrab_keyboard_checked.3 man/xcb_grab_key.3 man/xcb_grab_key_checked.3 man/xcb_ungrab_key.3 man/xcb_ungrab_key_checked.3 man/xcb_allow_events.3 man/xcb_allow_events_checked.3 man/xcb_grab_server.3 man/xcb_grab_server_checked.3 man/xcb_ungrab_server.3 man/xcb_ungrab_server_checked.3 man/xcb_query_pointer.3 man/xcb_query_pointer_unchecked.3 man/xcb_query_pointer_reply.3 man/xcb_get_motion_events.3 man/xcb_get_motion_events_unchecked.3 man/xcb_get_motion_events_reply.3 man/xcb_get_motion_events_events.3 man/xcb_get_motion_events_events_length.3 man/xcb_get_motion_events_events_iterator.3 man/xcb_translate_coordinates.3 man/xcb_translate_coordinates_unchecked.3 man/xcb_translate_coordinates_reply.3 man/xcb_warp_pointer.3 man/xcb_warp_pointer_checked.3 man/xcb_set_input_focus.3 man/xcb_set_input_focus_checked.3 man/xcb_get_input_focus.3 man/xcb_get_input_focus_unchecked.3 man/xcb_get_input_focus_reply.3 man/xcb_query_keymap.3 man/xcb_query_keymap_unchecked.3 man/xcb_query_keymap_reply.3 man/xcb_open_font.3 man/xcb_open_font_checked.3 man/xcb_close_font.3 man/xcb_close_font_checked.3 man/xcb_query_font.3 man/xcb_query_font_unchecked.3 man/xcb_query_font_reply.3 man/xcb_query_font_properties.3 man/xcb_query_font_properties_length.3 man/xcb_query_font_properties_iterator.3 man/xcb_query_font_char_infos.3 man/xcb_query_font_char_infos_length.3 man/xcb_query_font_char_infos_iterator.3 man/xcb_query_text_extents.3 man/xcb_query_text_extents_unchecked.3 man/xcb_query_text_extents_reply.3 man/xcb_list_fonts.3 man/xcb_list_fonts_unchecked.3 man/xcb_list_fonts_reply.3 man/xcb_list_fonts_names_length.3 man/xcb_list_fonts_names_iterator.3 man/xcb_list_fonts_with_info.3 man/xcb_list_fonts_with_info_unchecked.3 man/xcb_list_fonts_with_info_reply.3 man/xcb_list_fonts_with_info_properties.3 man/xcb_list_fonts_with_info_properties_length.3 man/xcb_list_fonts_with_info_properties_iterator.3 man/xcb_list_fonts_with_info_name.3 man/xcb_list_fonts_with_info_name_length.3 man/xcb_list_fonts_with_info_name_end.3 man/xcb_set_font_path.3 man/xcb_set_font_path_checked.3 man/xcb_get_font_path.3 man/xcb_get_font_path_unchecked.3 man/xcb_get_font_path_reply.3 man/xcb_get_font_path_path_length.3 man/xcb_get_font_path_path_iterator.3 man/xcb_create_pixmap.3 man/xcb_create_pixmap_checked.3 man/xcb_free_pixmap.3 man/xcb_free_pixmap_checked.3 man/xcb_create_gc.3 man/xcb_create_gc_checked.3 man/xcb_change_gc.3 man/xcb_change_gc_checked.3 man/xcb_copy_gc.3 man/xcb_copy_gc_checked.3 man/xcb_set_dashes.3 man/xcb_set_dashes_checked.3 man/xcb_set_clip_rectangles.3 man/xcb_set_clip_rectangles_checked.3 man/xcb_free_gc.3 man/xcb_free_gc_checked.3 man/xcb_clear_area.3 man/xcb_clear_area_checked.3 man/xcb_copy_area.3 man/xcb_copy_area_checked.3 man/xcb_copy_plane.3 man/xcb_copy_plane_checked.3 man/xcb_poly_point.3 man/xcb_poly_point_checked.3 man/xcb_poly_line.3 man/xcb_poly_line_checked.3 man/xcb_poly_segment.3 man/xcb_poly_segment_checked.3 man/xcb_poly_rectangle.3 man/xcb_poly_rectangle_checked.3 man/xcb_poly_arc.3 man/xcb_poly_arc_checked.3 man/xcb_fill_poly.3 man/xcb_fill_poly_checked.3 man/xcb_poly_fill_rectangle.3 man/xcb_poly_fill_rectangle_checked.3 man/xcb_poly_fill_arc.3 man/xcb_poly_fill_arc_checked.3 man/xcb_put_image.3 man/xcb_put_image_checked.3 man/xcb_get_image.3 man/xcb_get_image_unchecked.3 man/xcb_get_image_reply.3 man/xcb_get_image_data.3 man/xcb_get_image_data_length.3 man/xcb_get_image_data_end.3 man/xcb_poly_text_8.3 man/xcb_poly_text_8_checked.3 man/xcb_poly_text_16.3 man/xcb_poly_text_16_checked.3 man/xcb_image_text_8.3 man/xcb_image_text_8_checked.3 man/xcb_image_text_16.3 man/xcb_image_text_16_checked.3 man/xcb_create_colormap.3 man/xcb_create_colormap_checked.3 man/xcb_free_colormap.3 man/xcb_free_colormap_checked.3 man/xcb_copy_colormap_and_free.3 man/xcb_copy_colormap_and_free_checked.3 man/xcb_install_colormap.3 man/xcb_install_colormap_checked.3 man/xcb_uninstall_colormap.3 man/xcb_uninstall_colormap_checked.3 man/xcb_list_installed_colormaps.3 man/xcb_list_installed_colormaps_unchecked.3 man/xcb_list_installed_colormaps_reply.3 man/xcb_list_installed_colormaps_cmaps.3 man/xcb_list_installed_colormaps_cmaps_length.3 man/xcb_list_installed_colormaps_cmaps_end.3 man/xcb_alloc_color.3 man/xcb_alloc_color_unchecked.3 man/xcb_alloc_color_reply.3 man/xcb_alloc_named_color.3 man/xcb_alloc_named_color_unchecked.3 man/xcb_alloc_named_color_reply.3 man/xcb_alloc_color_cells.3 man/xcb_alloc_color_cells_unchecked.3 man/xcb_alloc_color_cells_reply.3 man/xcb_alloc_color_cells_pixels.3 man/xcb_alloc_color_cells_pixels_length.3 man/xcb_alloc_color_cells_pixels_end.3 man/xcb_alloc_color_cells_masks.3 man/xcb_alloc_color_cells_masks_length.3 man/xcb_alloc_color_cells_masks_end.3 man/xcb_alloc_color_planes.3 man/xcb_alloc_color_planes_unchecked.3 man/xcb_alloc_color_planes_reply.3 man/xcb_alloc_color_planes_pixels.3 man/xcb_alloc_color_planes_pixels_length.3 man/xcb_alloc_color_planes_pixels_end.3 man/xcb_free_colors.3 man/xcb_free_colors_checked.3 man/xcb_store_colors.3 man/xcb_store_colors_checked.3 man/xcb_store_named_color.3 man/xcb_store_named_color_checked.3 man/xcb_query_colors.3 man/xcb_query_colors_unchecked.3 man/xcb_query_colors_reply.3 man/xcb_query_colors_colors.3 man/xcb_query_colors_colors_length.3 man/xcb_query_colors_colors_iterator.3 man/xcb_lookup_color.3 man/xcb_lookup_color_unchecked.3 man/xcb_lookup_color_reply.3 man/xcb_create_cursor.3 man/xcb_create_cursor_checked.3 man/xcb_create_glyph_cursor.3 man/xcb_create_glyph_cursor_checked.3 man/xcb_free_cursor.3 man/xcb_free_cursor_checked.3 man/xcb_recolor_cursor.3 man/xcb_recolor_cursor_checked.3 man/xcb_query_best_size.3 man/xcb_query_best_size_unchecked.3 man/xcb_query_best_size_reply.3 man/xcb_query_extension.3 man/xcb_query_extension_unchecked.3 man/xcb_query_extension_reply.3 man/xcb_list_extensions.3 man/xcb_list_extensions_unchecked.3 man/xcb_list_extensions_reply.3 man/xcb_list_extensions_names_length.3 man/xcb_list_extensions_names_iterator.3 man/xcb_change_keyboard_mapping.3 man/xcb_change_keyboard_mapping_checked.3 man/xcb_get_keyboard_mapping.3 man/xcb_get_keyboard_mapping_unchecked.3 man/xcb_get_keyboard_mapping_reply.3 man/xcb_get_keyboard_mapping_keysyms.3 man/xcb_get_keyboard_mapping_keysyms_length.3 man/xcb_get_keyboard_mapping_keysyms_end.3 man/xcb_change_keyboard_control.3 man/xcb_change_keyboard_control_checked.3 man/xcb_get_keyboard_control.3 man/xcb_get_keyboard_control_unchecked.3 man/xcb_get_keyboard_control_reply.3 man/xcb_bell.3 man/xcb_bell_checked.3 man/xcb_change_pointer_control.3 man/xcb_change_pointer_control_checked.3 man/xcb_get_pointer_control.3 man/xcb_get_pointer_control_unchecked.3 man/xcb_get_pointer_control_reply.3 man/xcb_set_screen_saver.3 man/xcb_set_screen_saver_checked.3 man/xcb_get_screen_saver.3 man/xcb_get_screen_saver_unchecked.3 man/xcb_get_screen_saver_reply.3 man/xcb_change_hosts.3 man/xcb_change_hosts_checked.3 man/xcb_list_hosts.3 man/xcb_list_hosts_unchecked.3 man/xcb_list_hosts_reply.3 man/xcb_list_hosts_hosts_length.3 man/xcb_list_hosts_hosts_iterator.3 man/xcb_set_access_control.3 man/xcb_set_access_control_checked.3 man/xcb_set_close_down_mode.3 man/xcb_set_close_down_mode_checked.3 man/xcb_kill_client.3 man/xcb_kill_client_checked.3 man/xcb_rotate_properties.3 man/xcb_rotate_properties_checked.3 man/xcb_force_screen_saver.3 man/xcb_force_screen_saver_checked.3 man/xcb_set_pointer_mapping.3 man/xcb_set_pointer_mapping_unchecked.3 man/xcb_set_pointer_mapping_reply.3 man/xcb_get_pointer_mapping.3 man/xcb_get_pointer_mapping_unchecked.3 man/xcb_get_pointer_mapping_reply.3 man/xcb_get_pointer_mapping_map.3 man/xcb_get_pointer_mapping_map_length.3 man/xcb_get_pointer_mapping_map_end.3 man/xcb_set_modifier_mapping.3 man/xcb_set_modifier_mapping_unchecked.3 man/xcb_set_modifier_mapping_reply.3 man/xcb_get_modifier_mapping.3 man/xcb_get_modifier_mapping_unchecked.3 man/xcb_get_modifier_mapping_reply.3 man/xcb_get_modifier_mapping_keycodes.3 man/xcb_get_modifier_mapping_keycodes_length.3 man/xcb_get_modifier_mapping_keycodes_end.3 man/xcb_no_operation.3 man/xcb_no_operation_checked.3 diff --git a/src/static-man/xcb-examples.3 b/src/static-man/xcb-examples.3 new file mode 100644 index 0000000..291af37 --- /dev/null +++ b/src/static-man/xcb-examples.3 @@ -0,0 +1,59 @@ +.TH xcb-examples 3 2011-12-11 "XCB" "XCB examples" +.ad l +.SH NAME +xcb-examples \- manpage examples +.SH DESCRIPTION +Many of the XCB manpages contain example code. These examples intend to explain +how to use one particular part of XCB. They almost never represent a standalone +(or even useful) program - X11 programs are relatively involved and +thus beyond the scope of a manpage example. + +.SH ENVIRONMENT + +Every example assumes you have an \fIxcb_connection\fP and possibly other +variables at hand. For illustrating how \fIxcb_get_property\fP works, you need +the window of which you want to get the property, for example. To make it clear +that these variables are your responsibility, these examples consist of a +single function which takes the necessary variables as parameters. + +.SH FLUSHING + +Flushing means calling \fIxcb_flush\fP to clear the XCB-internal write buffer +and send all pending requests to the X11 server. You don't explicitly need to +flush before using a reply function (like \fIxcb_query_pointer_reply\fP), but +you do need to flush before entering the event loop of your program. + +There are only two cases when XCB flushes by itself. The first case is when +its write buffer becomes full, the second case is when you are asking for +the reply of a request which wasn't flushed out yet (like +\fIxcb_query_pointer_reply\fP). This last point also includes +xcb_request_check(). Please note that waiting for an event does \fBNOT\fP +flush. + +Examples generally include the \fIxcb_flush\fP call where appropriate (for +example after setting a property). Therefore, including these functions and +calling them in your application should just work. However, you might get +better results when flushing outside of the function, depending on the +architecture of your program. + +.SH COMPILATION + +If an example does not compile (without warnings) when using \fI-std=c99\fP, +that is considered a documentation bug. Similarly, not handling errors or +leaking memory is also considered a documentation bug. Please inform us about +it on xcb@lists.freedesktop.org. + +.SH CODING STYLE + +Every example uses 4 spaces for indention. + +Comments are in asterisks, like /* this */. + +No line is longer than 80 characters (including indention). + +.SH SEE ALSO +.BR xcb_connect (3), +.BR xcb_get_property (3), +.BR xcb_flush (3) +.SH AUTHOR +Michael Stapelberg diff --git a/src/static-man/xcb-requests.3 b/src/static-man/xcb-requests.3 new file mode 100644 index 0000000..278bcff --- /dev/null +++ b/src/static-man/xcb-requests.3 @@ -0,0 +1,165 @@ +.TH xcb-requests 3 2011-12-11 "XCB" "XCB examples" +.ad l +.SH NAME +xcb-requests \- about request manpages +.SH DESCRIPTION +Every request in X11, like \fIMapWindow\fP, corresponds to a number of +functions and data structures in XCB. For \fIMapWindow\fP, XCB provides the +function \fIxcb_map_window\fP, which fills the \fIxcb_map_window_request_t\fP +data structure and writes that to the X11 connection. Since the \fIMapWindow\fP +request does not have a reply, this is the most simple case. + +.SH REPLIES + +Many requests have replies. For each reply, XCB provides at least a +corresponding data structure and a function to return a pointer to a filled +data structure. Let's take the \fIInternAtom\fP request as an example: XCB +provides the \fIxcb_intern_atom_reply_t\fP data structure and +\fIxcb_intern_atom_reply\fP function. For replies which are more complex (for +example lists, such as in \fIxcb_list_fonts\fP), accessor functions are +provided. + +.SH COOKIES + +XCB returns a cookie for each request you send. This is an XCB-specific data +structure containing the sequence number with which the request was sent to the +X11 server. To get any reply, you have to provide that cookie (so that XCB +knows which of the waiting replies you want). Here is an example to illustrate +the use of cookies: + +.nf +.sp +void my_example(xcb_connection *conn) { + xcb_intern_atom_cookie_t cookie; + xcb_intern_atom_reply_t *reply; + + cookie = xcb_intern_atom(conn, 0, strlen("_NET_WM_NAME"), "_NET_WM_NAME"); + /* ... do other work here if possible ... */ + if ((reply = xcb_intern_atom_reply(conn, cookie, NULL))) { + printf("The _NET_WM_NAME atom has ID %u\n", reply->atom); + } + free(reply); +} +.fi + +.SH CHECKED VS. UNCHECKED + +The checked and unchecked suffixes for functions determine which kind of error +handling is used for this specific request. + +For requests which have no reply (for example \fIxcb_map_window\fP), errors +will be delivered to the event loop (you will receive an X11 event of type 0 +when calling \fIxcb_poll_for_event\fP). +If you want to explicitly check for errors in a blocking fashion, call the +_checked version of the function (for example \fIxcb_map_window_checked\fP) and +use \fIxcb_request_check\fP. + +For requests which have a reply (for example \fIxcb_intern_atom\fP), errors +will be checked when calling the reply function. To get errors in the event +loop instead, use the _unchecked version of the function (for example +\fIxcb_intern_atom_unchecked\fP). + +Here is an example which illustrates the four different ways of handling errors: + +.nf +.sp +/* + * Request without a reply, handling errors in the event loop (default) + * + */ +void my_example(xcb_connection *conn, xcb_window_t window) { + /* This is a request without a reply. Errors will be delivered to the event + * loop. Getting an error to xcb_map_window most likely is a bug in our + * program, so we don't need to check for that in a blocking way. */ + xcb_map_window(conn, window); + + /* ... of course your event loop would not be in the same function ... */ + while ((event = xcb_wait_for_event(conn)) != NULL) { + if (event->response_type == 0) { + fprintf("Received X11 error %d\\n", error->error_code); + free(event); + continue; + } + + /* ... handle a normal event ... */ + } +} + +/* + * Request without a reply, handling errors directly + * + */ +void my_example(xcb_connection *conn, xcb_window_t deco, xcb_window_t window) { + /* A reparenting window manager wants to know whether a new window was + * successfully reparented. If not (because the window got destroyed + * already, for example), it does not make sense to map an empty window + * decoration at all, so we need to know this right now. */ + xcb_void_cookie_t cookie = xcb_reparent_window_checked(conn, window, + deco, 0, 0); + xcb_generic_error_t *error; + if ((error = xcb_request_check(conn, cookie))) { + fprintf(stderr, "Could not reparent the window\\n"); + free(error); + return; + } + + /* ... do window manager stuff here ... */ +} + +/* + * Request with a reply, handling errors directly (default) + * + */ +void my_example(xcb_connection *conn, xcb_window_t window) { + xcb_intern_atom_cookie_t cookie; + xcb_intern_atom_reply_t *reply; + xcb_generic_error_t *error; + + cookie = xcb_intern_atom(c, 0, strlen("_NET_WM_NAME"), "_NET_WM_NAME"); + /* ... do other work here if possible ... */ + if ((reply = xcb_intern_atom_reply(c, cookie, &error))) { + printf("The _NET_WM_NAME atom has ID %u\n", reply->atom); + free(reply); + } else { + fprintf(stderr, "X11 Error %d\\n", error->error_code); + free(error); + } +} + +/* + * Request with a reply, handling errors in the event loop + * + */ +void my_example(xcb_connection *conn, xcb_window_t window) { + xcb_intern_atom_cookie_t cookie; + xcb_intern_atom_reply_t *reply; + + cookie = xcb_intern_atom_unchecked(c, 0, strlen("_NET_WM_NAME"), + "_NET_WM_NAME"); + /* ... do other work here if possible ... */ + if ((reply = xcb_intern_atom_reply(c, cookie, NULL))) { + printf("The _NET_WM_NAME atom has ID %u\n", reply->atom); + free(reply); + } + + /* ... of course your event loop would not be in the same function ... */ + while ((event = xcb_wait_for_event(conn)) != NULL) { + if (event->response_type == 0) { + fprintf("Received X11 error %d\\n", error->error_code); + free(event); + continue; + } + + /* ... handle a normal event ... */ + } +} +.fi + +.SH SEE ALSO +.BR xcb_map_window (3), +.BR xcb_intern_atom (3), +.BR xcb_list_fonts (3), +.BR xcb_poll_for_event (3), +.BR xcb_request_check (3) +.SH AUTHOR +Michael Stapelberg -- 2.34.1