c_client.py: generate manpages
Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
parent
57a62e99b1
commit
ea71d7d7e3
|
@ -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)
|
||||
|
|
600
src/c_client.py
600
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 <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)
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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>
|
|
@ -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>
|
Loading…
Reference in New Issue