c_client.py: generate manpages

Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
Michael Stapelberg 2011-11-27 10:38:26 +00:00 committed by Julien Danjou
parent 57a62e99b1
commit ea71d7d7e3
5 changed files with 827 additions and 6 deletions

View File

@ -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)

View File

@ -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 <xcb/%s.h>\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 <xcb/%s.h>\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)

1
src/list_of_manpages.inc Normal file

File diff suppressed because one or more lines are too long

View File

@ -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 <michael+xcb at stapelberg dot de>

View File

@ -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 <michael+xcb at stapelberg dot de>