[Xcb-commit] configure.ac Makefile.am README xcbgen

Eamon Walsh ewalsh at kemper.freedesktop.org
Sat Apr 19 01:55:01 PDT 2008


 Makefile.am        |    2 
 README             |   13 +
 configure.ac       |    4 
 xcbgen/Makefile.am |    3 
 xcbgen/__init__.py |    1 
 xcbgen/error.py    |    5 
 xcbgen/expr.py     |   88 +++++++++
 xcbgen/matcher.py  |  112 ++++++++++++
 xcbgen/state.py    |  166 ++++++++++++++++++
 xcbgen/types.py    |  467 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 859 insertions(+), 2 deletions(-)

New commits:
commit 7820273c4b00209d5ace8cbfdb1400728e15c158
Author: Eamon Walsh <ewalsh at tycho.nsa.gov>
Date:   Thu Apr 17 19:46:16 2008 -0400

    Add Python parser language-independent parts.

diff --git a/Makefile.am b/Makefile.am
index ed9adfb..c272c62 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS=src
+SUBDIRS = src xcbgen
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = xcb-proto.pc
diff --git a/README b/README
index 3c24331..22d7deb 100644
--- a/README
+++ b/README
@@ -14,6 +14,19 @@ the XML-XCB protocol descriptions, client-side support for a new
 extension requires only an XML description of the extension, and not a
 single line of code.
 
+Python libraries: xcb-proto also contains language-independent Python
+libraries that are used to parse an XML description and create objects
+used by Python code generators in individual language bindings.  These
+libraries are installed into $(prefix)/lib/pythonX.X/site-packages.  If
+this location is not on your system's Python path, scripts that import
+them will fail with import errors.  In this case you must add the
+install location to your Python path by creating a file with a `.pth'
+extension in a directory that _is_ on the Python path, and put the
+path to the install location in that file.  For example, on my system
+there is a file named 'local.pth' in /usr/lib/python2.5/site-packages,
+which contains '/usr/local/lib/python2.5/site-packages'.  Note that 
+this is only necessary on machines where XCB is being built. 
+
 Please report any issues you find to the freedesktop.org bug tracker,
 at:
 
diff --git a/configure.ac b/configure.ac
index 124d3fa..193fff0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -14,7 +14,9 @@ if test "$XMLLINT" = "no"; then
    AC_MSG_WARN([xmllint not found; unable to validate against schema.])
 fi
 
+AM_PATH_PYTHON([2.5])
+
 xcbincludedir='${datadir}/xcb'
 AC_SUBST(xcbincludedir)
 
-AC_OUTPUT([Makefile src/Makefile xcb-proto.pc])
+AC_OUTPUT([Makefile src/Makefile xcbgen/Makefile xcb-proto.pc])
diff --git a/xcbgen/Makefile.am b/xcbgen/Makefile.am
new file mode 100644
index 0000000..44632fa
--- /dev/null
+++ b/xcbgen/Makefile.am
@@ -0,0 +1,3 @@
+pkgpythondir = $(pythondir)/xcbgen
+
+pkgpython_PYTHON = __init__.py error.py expr.py matcher.py state.py types.py
diff --git a/xcbgen/__init__.py b/xcbgen/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/xcbgen/__init__.py
@@ -0,0 +1 @@
+
diff --git a/xcbgen/error.py b/xcbgen/error.py
new file mode 100644
index 0000000..bbcd501
--- /dev/null
+++ b/xcbgen/error.py
@@ -0,0 +1,5 @@
+class ResolveException(Exception):
+    '''
+    Gets thrown when a type doesn't resolve in the XML.
+    '''
+    pass
diff --git a/xcbgen/expr.py b/xcbgen/expr.py
new file mode 100644
index 0000000..522e17d
--- /dev/null
+++ b/xcbgen/expr.py
@@ -0,0 +1,88 @@
+'''
+This module contains helper classes for structure fields and length expressions.
+'''
+class Field(object):
+    '''
+    Represents a field of a structure.
+
+    type is the datatype object for the field.
+    field_type is the name of the type (string tuple)
+    field_name is the name of the structure field.
+    visible is true iff the field should be in the request API.
+    wire is true iff the field should be in the request structure.
+    auto is true iff the field is on the wire but not in the request API (e.g. opcode)
+    '''
+    def __init__(self, type, field_type, field_name, visible, wire, auto):
+        self.type = type
+        self.field_type = field_type
+        self.field_name = field_name
+        self.visible = visible
+        self.wire = wire
+        self.auto = auto
+
+
+class Expression(object):
+    '''
+    Represents a mathematical expression for a list length or exprfield.
+
+    Public fields:
+    op is the operation (text +,*,/,<<) or None.
+    lhs and rhs are the sub-Expressions if op is set.
+    lenfield_name is the name of the length field, or None for request lists.
+    lenfield is the Field object for the length field, or None.
+    bitfield is True if the length field is a bitmask instead of a number.
+    nmemb is the fixed size (value)of the expression, or None
+    '''
+    def __init__(self, elt, parent):
+        self.parent = parent
+
+        self.nmemb = None
+
+        self.lenfield_name = None
+        self.lenfield_type = None
+        self.lenfield = None
+        self.lenwire = False
+        self.bitfield = False
+
+        self.op = None
+        self.lhs = None
+        self.rhs = None
+
+        if elt.tag == 'list':
+            # List going into a request, which has no length field (inferred by server)
+            self.lenfield_name = elt.get('name') + '_len'
+            self.lenfield_type = 'CARD32'
+
+        elif elt.tag == 'fieldref':
+            # Standard list with a fieldref
+            self.lenfield_name = elt.text
+
+        elif elt.tag == 'valueparam':
+            # Value-mask.  The length bitmask is described by attributes.
+            self.lenfield_name = elt.get('value-mask-name')
+            self.lenfield_type = elt.get('value-mask-type')
+            self.lenwire = True
+            self.bitfield = True
+
+        elif elt.tag == 'op':
+            # Op field.  Need to recurse.
+            self.op = elt.get('op')
+            self.lhs = Expression(list(elt)[0], parent)
+            self.rhs = Expression(list(elt)[1], parent)
+
+            # Hopefully we don't have two separate length fields...
+            self.lenfield_name = self.lhs.lenfield_name
+            if self.lenfield_name == None:
+                self.lenfield_name = self.rhs.lenfield_name
+
+        elif elt.tag == 'value':
+            # Constant expression
+            self.nmemb = int(elt.text)
+
+        else:
+            # Notreached
+            raise Exception('XXX')
+
+
+    def fixed_size(self):
+        return self.nmemb != None
diff --git a/xcbgen/matcher.py b/xcbgen/matcher.py
new file mode 100644
index 0000000..b2d9b8b
--- /dev/null
+++ b/xcbgen/matcher.py
@@ -0,0 +1,112 @@
+'''
+XML parser.  One function for each top-level element in the schema.
+
+Most functions just declare a new object and add it to the module.
+For typedefs, eventcopies, xidtypes, and other aliases though,
+we do not create a new type object, we just record the existing one under a new name.
+'''
+
+from os.path import join
+from xml.etree.cElementTree import parse
+
+import state
+from types import *
+
+def import_(node, module, namespace):
+    '''
+    For imports, we load the file, create a new namespace object,
+    execute recursively, then record the import (for header files, etc.)
+    '''
+    new_file = join(namespace.dir, '%s.xml' % node.text)
+    new_root = parse(new_file).getroot()
+    new_namespace = state.Namespace(new_file)
+    execute(module, new_namespace)
+    if not module.has_import(node.text):
+        module.add_import(node.text, new_namespace)
+
+def typedef(node, module, namespace):
+    id = node.get('newname')
+    name = namespace.prefix + (id,)
+    type = module.get_type(node.get('oldname'))
+    module.add_type(id, namespace.ns, name, type)
+
+def xidtype(node, module, namespace):
+    id = node.get('name')
+    name = namespace.prefix + (id,)
+    type = module.get_type('CARD32')
+    module.add_type(id, namespace.ns, name, type)
+
+def xidunion(node, module, namespace):
+    id = node.get('name')
+    name = namespace.prefix + (id,)
+    type = module.get_type('CARD32')
+    module.add_type(id, namespace.ns, name, type)
+
+def enum(node, module, namespace):
+    id = node.get('name')
+    name = namespace.prefix + (id,)
+    type = Enum(name, node)
+    module.add_type(id, namespace.ns, name, type)
+
+def struct(node, module, namespace):
+    id = node.get('name')
+    name = namespace.prefix + (id,)
+    type = Struct(name, node)
+    module.add_type(id, namespace.ns, name, type)
+
+def union(node, module, namespace):
+    id = node.get('name')
+    name = namespace.prefix + (id,)
+    type = Union(name, node)
+    module.add_type(id, namespace.ns, name, type)
+
+def request(node, module, namespace):
+    id = node.get('name')
+    name = namespace.prefix + (id,)
+    type = Request(name, node)
+    module.add_request(id, name, type)
+
+def event(node, module, namespace):
+    id = node.get('name')
+    name = namespace.prefix + (id,)
+    event = Event(name, node)
+    event.add_opcode(node.get('number'), name, True)
+    module.add_event(id, name, event)
+
+def eventcopy(node, module, namespace):
+    id = node.get('name')
+    name = namespace.prefix + (id,)
+    event = module.get_event(node.get('ref'))
+    event.add_opcode(node.get('number'), name, False)
+    module.add_event(id, name, event)
+
+def error(node, module, namespace):
+    id = node.get('name')
+    name = namespace.prefix + (id,)
+    error = Error(name, node)
+    error.add_opcode(node.get('number'), name, True)
+    module.add_error(id, name, error)
+
+def errorcopy(node, module, namespace):
+    id = node.get('name')
+    name = namespace.prefix + (id,)
+    error = module.get_error(node.get('ref'))
+    error.add_opcode(node.get('number'), name, False)
+    module.add_error(id, name, error)
+
+funcs = {'import' : import_,
+         'typedef' : typedef,
+         'xidtype' : xidtype,
+         'xidunion' : xidunion,
+         'enum' : enum,
+         'struct' : struct,
+         'union' : union,
+         'request' : request,
+         'event' : event,
+         'eventcopy' : eventcopy,
+         'error' : error,
+         'errorcopy' : errorcopy}
+
+def execute(module, namespace):
+    for elt in list(namespace.root):
+        funcs[elt.tag](elt, module, namespace)
diff --git a/xcbgen/state.py b/xcbgen/state.py
new file mode 100644
index 0000000..59b5608
--- /dev/null
+++ b/xcbgen/state.py
@@ -0,0 +1,166 @@
+'''
+This module contains the namespace class and the singleton module class.
+'''
+from os.path import dirname, basename
+from xml.etree.cElementTree import parse
+
+import matcher
+from error import *
+from types import *
+
+import __main__
+
+class Namespace(object):
+    '''
+    Contains the naming information for an extension.
+
+    Public fields:
+
+    header is the header attribute ("header file" name).
+    is_ext is true for extensions, false for xproto.
+    major_version and minor_version are extension version info.
+    ext_xname is the X extension name string.
+    ext_name is the XCB extension name prefix.
+    '''
+    def __init__(self, filename):
+        # Path info
+        self.path = filename
+        self.dir = dirname(filename)
+        self.file = basename(filename)
+
+        # Parse XML
+        self.root = parse(filename).getroot()
+        self.header = self.root.get('header')
+        self.ns = self.header + ':'
+        
+        # Get root element attributes
+        if self.root.get('extension-xname', False): 
+            self.is_ext = True
+            self.major_version = self.root.get('major-version')
+            self.minor_version = self.root.get('minor-version')
+            self.ext_xname = self.root.get('extension-xname')
+            self.ext_name = self.root.get('extension-name')
+            self.prefix = ('xcb', self.ext_name)
+        else:
+            self.is_ext = False
+            self.ext_name = ''
+            self.prefix = ('xcb',)
+
+
+class Module(object):
+    '''
+    This is the grand, encompassing class that represents an entire XCB specification.
+    Only gets instantiated once, in the main() routine.
+
+    Don't need to worry about this much except to declare it and to get the namespace.
+
+    Public fields:
+    namespace contains the namespace info for the spec.
+    '''
+    open = __main__.output['open']
+    close = __main__.output['close']
+
+    def __init__(self, filename, output):
+        self.namespace = Namespace(filename)
+        self.output = output
+
+        self.imports = []
+        self.types = {}
+        self.events = {}
+        self.errors = {}
+        self.all = []
+
+        # Register some common types
+        self.add_type('CARD8', '', ('uint8_t',), tcard8)
+        self.add_type('CARD16', '', ('uint16_t',), tcard16)
+        self.add_type('CARD32', '', ('uint32_t',), tcard32)
+        self.add_type('INT8', '', ('int8_t',), tint8)
+        self.add_type('INT16', '', ('int16_t',), tint16)
+        self.add_type('INT32', '', ('int32_t',), tint32)
+        self.add_type('BYTE', '', ('uint8_t',), tcard8)
+        self.add_type('BOOL', '', ('uint8_t',), tcard8)
+        self.add_type('char', '', ('char',), tchar)
+        self.add_type('float', '', ('float',), tfloat)
+        self.add_type('double', '', ('double',), tdouble)
+        self.add_type('void', '', ('void',), tcard8)
+
+    # This goes out and parses the rest of the XML
+    def register(self):
+        matcher.execute(self, self.namespace)
+
+    # Recursively resolve all types
+    def resolve(self):
+        for (name, item) in self.all:
+            item.resolve(self)
+
+    # Call all the output methods
+    def generate(self):
+        self.open()
+
+        for (name, item) in self.all:
+            item.out(name)
+
+        self.close()
+
+    # Keeps track of what's been imported so far.
+    def add_import(self, name, namespace):
+        self.imports.append((name, namespace.header))
+
+    def has_import(self, name):
+        for (name_, header) in self.imports:
+            if name_ == name:
+                return True
+        return False
+
+    # Keeps track of non-request/event/error datatypes
+    def add_type(self, id, ns, name, item):
+        key = ns + id
+        if key in self.types:
+            return
+        self.types[key] = (name, item)
+        if name[:-1] == self.namespace.prefix:
+            self.all.append((name, item))
+
+    def get_type_impl(self, id, idx):
+        key = id
+        if key in self.types:
+            return self.types[key][idx]
+
+        key = self.namespace.ns + id
+        if key in self.types:
+            return self.types[key][idx]
+
+        for key in self.types.keys():
+            if key.rpartition(':')[2] == id:
+                return self.types[key][idx]
+
+        raise ResolveException('Type %s not found' % id)
+
+    def get_type(self, id):
+        return self.get_type_impl(id, 1)
+
+    def get_type_name(self, id):
+        return self.get_type_impl(id, 0)
+
+    # Keeps track of request datatypes
+    def add_request(self, id, name, item):
+        if name[:-1] == self.namespace.prefix:
+            self.all.append((name, item))
+
+    # Keeps track of event datatypes
+    def add_event(self, id, name, item):
+        self.events[id] = (name, item)
+        if name[:-1] == self.namespace.prefix:
+            self.all.append((name, item))
+
+    def get_event(self, id):
+        return self.events[id][1]
+
+    # Keeps track of error datatypes
+    def add_error(self, id, name, item):
+        self.errors[id] = (name, item)
+        if name[:-1] == self.namespace.prefix:
+            self.all.append((name, item))
+
+    def get_error(self, id):
+        return self.errors[id][1]
diff --git a/xcbgen/types.py b/xcbgen/types.py
new file mode 100644
index 0000000..a84420f
--- /dev/null
+++ b/xcbgen/types.py
@@ -0,0 +1,467 @@
+'''
+This module contains the classes which represent XCB data types.
+'''
+from expr import Field, Expression
+import __main__
+
+class Type(object):
+    '''
+    Abstract base class for all XCB data types.
+    Contains default fields, and some abstract methods.
+    '''
+    def __init__(self, name):
+        '''
+        Default structure initializer.  Sets up default fields.
+
+        Public fields:
+        name is a tuple of strings specifying the full type name.
+        size is the size of the datatype in bytes, or None if variable-sized.
+        nmemb is 1 for non-list types, None for variable-sized lists, otherwise number of elts.
+        booleans for identifying subclasses, because I can't figure out isinstance().
+        '''
+        self.name = name
+        self.size = None
+        self.nmemb = None
+        self.resolved = False
+
+        # Screw isinstance().
+        self.is_list = False
+        self.is_expr = False
+        self.is_container = False
+        self.is_reply = False
+        self.is_union = False
+        self.is_pad = False
+
+    def resolve(self, module):
+        '''
+        Abstract method for resolving a type.
+        This should make sure any referenced types are already declared.
+        '''
+        raise Exception('abstract resolve method not overridden!')
+
+    def out(self, name):
+        '''
+        Abstract method for outputting code.
+        These are declared in the language-specific modules, and
+        there must be a dictionary containing them declared when this module is imported!
+        '''
+        raise Exception('abstract out method not overridden!')
+
+    def fixed_size(self):
+        '''
+        Abstract method for determining if the data type is fixed-size.
+        '''
+        raise Exception('abstract fixed_size method not overridden!')
+
+    def make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto):
+        '''
+        Default method for making a data type a member of a structure.
+        Extend this if the data type needs to add an additional length field or something.
+
+        module is the global module object.
+        complex_type is the structure object.
+        see Field for the meaning of the other parameters.
+        '''
+        new_field = Field(self, field_type, field_name, visible, wire, auto)
+
+        # We dump the _placeholder_byte if any fields are added.
+        for (idx, field) in enumerate(complex_type.fields):
+            if field == _placeholder_byte:
+                complex_type.fields[idx] = new_field
+                return
+
+        complex_type.fields.append(new_field)
+
+class SimpleType(Type):
+    '''
+    Derived class which represents a cardinal type like CARD32 or char.
+    Any type which is typedef'ed to cardinal will be one of these.
+
+    Public fields added:
+    none
+    '''
+    def __init__(self, name, size):
+        Type.__init__(self, name)
+        self.size = size
+        self.nmemb = 1
+
+    def resolve(self, module):
+        self.resolved = True
+
+    def fixed_size(self):
+        return True
+
+    out = __main__.output['simple']
+
+
+# Cardinal datatype globals.  See module __init__ method.
+tcard8 = SimpleType(('uint8_t',), 1)
+tcard16 = SimpleType(('uint16_t',), 2)
+tcard32 = SimpleType(('uint32_t',), 4)
+tint8 =  SimpleType(('int8_t',), 1)
+tint16 = SimpleType(('int16_t',), 2)
+tint32 = SimpleType(('int32_t',), 4)
+tchar =  SimpleType(('char',), 1)
+tfloat = SimpleType(('float',), 4)
+tdouble = SimpleType(('double',), 8)
+
+
+class Enum(SimpleType):
+    '''
+    Derived class which represents an enum.  Fixed-size.
+
+    Public fields added:
+    values contains a list of (name, value) tuples.  value is empty, or a number.
+    '''
+    def __init__(self, name, elt):
+        SimpleType.__init__(self, name, 4)
+        self.values = []
+        for item in list(elt):
+            # First check if we're using a default value
+            if len(list(item)) == 0:
+                self.values.append((item.get('name'), ''))
+                continue
+
+            # An explicit value or bit was specified.
+            value = list(item)[0]
+            if value.tag == 'value':
+                self.values.append((item.get('name'), value.text))
+            elif value.tag == 'bit':
+                # XXX replace this with a simple number, please.
+                self.values.append((item.get('name'), '(1 << %s)' % value.text))
+
+    def resolve(self, module):
+        self.resolved = True
+
+    def fixed_size(self):
+        return True
+
+    out = __main__.output['enum']
+
+
+class ListType(Type):
+    '''
+    Derived class which represents a list of some other datatype.  Fixed- or variable-sized.
+
+    Public fields added:
+    member is the datatype of the list elements.
+    parent is the structure type containing the list.
+    expr is an Expression object containing the length information, for variable-sized lists.
+    '''
+    def __init__(self, elt, member, parent):
+        Type.__init__(self, member.name)
+        self.is_list = True
+        self.member = member
+        self.parent = parent
+
+        if elt.tag == 'list':
+            elts = list(elt)
+            self.expr = Expression(elts[0] if len(elts) else elt, self)
+        elif elt.tag == 'valueparam':
+            self.expr = Expression(elt, self)
+
+        self.size = member.size if member.fixed_size() else None
+        self.nmemb = self.expr.nmemb if self.expr.fixed_size() else None
+
+    def make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto):
+        if not self.fixed_size():
+            # We need a length field.
+            # Ask our Expression object for it's name, type, and whether it's on the wire.
+            lenfid = self.expr.lenfield_type
+            lenfield_name = self.expr.lenfield_name
+            lenwire = self.expr.lenwire
+            needlen = True
+
+            # See if the length field is already in the structure.
+            for field in self.parent.fields:
+                if field.field_name == lenfield_name:
+                    needlen = False
+
+            # It isn't, so we need to add it to the structure ourself.
+            if needlen:
+                type = module.get_type(lenfid)
+                lenfield_type = module.get_type_name(lenfid)
+                type.make_member_of(module, complex_type, lenfield_type, lenfield_name, True, lenwire, False)
+
+        # Add ourself to the structure by calling our original method.
+        Type.make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto)
+
+    def resolve(self, module):
+        if self.resolved:
+            return
+        self.member.resolve(module)
+
+        # Find my length field again.  We need the actual Field object in the expr.
+        # This is needed because we might have added it ourself above.
+        if not self.fixed_size():
+            for field in self.parent.fields:
+                if field.field_name == self.expr.lenfield_name and field.wire:
+                    self.expr.lenfield = field
+                    break
+            
+        self.resolved = True
+
+    def fixed_size(self):
+        return self.member.fixed_size() and self.expr.fixed_size()
+
+class ExprType(Type):
+    '''
+    Derived class which represents an exprfield.  Fixed size.
+
+    Public fields added:
+    expr is an Expression object containing the value of the field.
+    '''
+    def __init__(self, elt, member, parent):
+        Type.__init__(self, member.name)
+        self.is_expr = True
+        self.member = member
+        self.parent = parent
+
+        self.expr = Expression(list(elt)[0], self)
+
+        self.size = member.size
+        self.nmemb = 1
+
+    def resolve(self, module):
+        if self.resolved:
+            return
+        self.member.resolve(module)
+        self.resolved = True
+
+    def fixed_size(self):
+        return True
+
+class PadType(Type):
+    '''
+    Derived class which represents a padding field.
+    '''
+    def __init__(self, elt):
+        Type.__init__(self, tcard8.name)
+        self.is_pad = True
+        self.size = 1
+        self.nmemb = 1 if (elt == None) else int(elt.get('bytes'))
+
+    def resolve(self, module):
+        self.resolved = True
+
+    def fixed_size(self):
+        return True
+
+    
+class ComplexType(Type):
+    '''
+    Derived class which represents a structure.  Base type for all structure types.
+
+    Public fields added:
+    fields is an array of Field objects describing the structure fields.
+    '''
+    def __init__(self, name, elt):
+        Type.__init__(self, name)
+        self.is_container = True
+        self.elt = elt
+        self.fields = []
+        self.nmemb = 1
+        self.size = 0
+
+    def resolve(self, module):
+        if self.resolved:
+            return
+        pads = 0
+
+        # Resolve all of our field datatypes.
+        for child in list(self.elt):
+            if child.tag == 'pad':
+                field_name = 'pad' + str(pads)
+                fkey = 'CARD8'
+                type = PadType(child)
+                pads = pads + 1
+                visible = False
+            elif child.tag == 'field':
+                field_name = child.get('name')
+                fkey = child.get('type')
+                type = module.get_type(fkey)
+                visible = True
+            elif child.tag == 'exprfield':
+                field_name = child.get('name')
+                fkey = child.get('type')
+                type = ExprType(child, module.get_type(fkey), self)
+                visible = False
+            elif child.tag == 'list':
+                field_name = child.get('name')
+                fkey = child.get('type')
+                type = ListType(child, module.get_type(fkey), self)
+                visible = True
+            elif child.tag == 'valueparam':
+                field_name = child.get('value-list-name')
+                fkey = 'CARD32'
+                type = ListType(child, module.get_type(fkey), self)
+                visible = True
+            else:
+                # Hit this on Reply
+                continue 
+
+            # Get the full type name for the field
+            field_type = module.get_type_name(fkey)
+            # Add the field to ourself
+            type.make_member_of(module, self, field_type, field_name, visible, True, False)
+            # Recursively resolve the type (could be another structure, list)
+            type.resolve(module)
+
+        self.calc_size() # Figure out how big we are
+        self.resolved = True
+
+    def calc_size(self):
+        self.size = 0
+        for m in self.fields:
+            if not m.wire:
+                continue
+            if m.type.fixed_size():
+                self.size = self.size + m.type.size
+            else:
+                self.size = None
+                break
+
+    def fixed_size(self):
+        for m in self.fields:
+            if not m.type.fixed_size():
+                return False
+        return True
+
+
+class Struct(ComplexType):
+    '''
+    Derived class representing a struct data type.
+    '''
+    out = __main__.output['struct']
+
+
+class Union(ComplexType):
+    '''
+    Derived class representing a union data type.
+    '''
+    def __init__(self, name, elt):
+        ComplexType.__init__(self, name, elt)
+        self.is_union = True
+
+    out = __main__.output['union']
+
+
+class Reply(ComplexType):
+    '''
+    Derived class representing a reply.  Only found as a field of Request.
+    '''
+    def __init__(self, name, elt):
+        ComplexType.__init__(self, name, elt)
+        self.is_reply = True
+
+    def resolve(self, module):
+        if self.resolved:
+            return
+        # Add the automatic protocol fields
+        self.fields.append(Field(tcard8, tcard8.name, 'response_type', False, True, True))
+        self.fields.append(_placeholder_byte)
+        self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True))
+        self.fields.append(Field(tcard32, tcard32.name, 'length', False, True, True))
+        ComplexType.resolve(self, module)
+        
+
+class Request(ComplexType):
+    '''
+    Derived class representing a request.
+
+    Public fields added:
+    reply contains the reply datatype or None for void requests.
+    opcode contains the request number.
+    '''
+    def __init__(self, name, elt):
+        ComplexType.__init__(self, name, elt)
+        self.reply = None
+        self.opcode = elt.get('opcode')
+
+        for child in list(elt):
+            if child.tag == 'reply':
+                self.reply = Reply(name, child)
+
+    def resolve(self, module):
+        if self.resolved:
+            return
+        # Add the automatic protocol fields
+        if module.namespace.is_ext:
+            self.fields.append(Field(tcard8, tcard8.name, 'major_opcode', False, True, True))
+            self.fields.append(Field(tcard8, tcard8.name, 'minor_opcode', False, True, True))
+            self.fields.append(Field(tcard16, tcard16.name, 'length', False, True, True))
+            ComplexType.resolve(self, module)
+        else:
+            self.fields.append(Field(tcard8, tcard8.name, 'major_opcode', False, True, True))
+            self.fields.append(_placeholder_byte)
+            self.fields.append(Field(tcard16, tcard16.name, 'length', False, True, True))
+            ComplexType.resolve(self, module)
+
+        if self.reply:
+            self.reply.resolve(module)
+
+    out = __main__.output['request']
+
+
+class Event(ComplexType):
+    '''
+    Derived class representing an event data type.
+
+    Public fields added:
+    opcodes is a dictionary of name -> opcode number, for eventcopies.
+    '''
+    def __init__(self, name, elt):
+        ComplexType.__init__(self, name, elt)
+        self.opcodes = {}
+
+        tmp = elt.get('no-sequence-number')
+        self.has_seq = (tmp == None or tmp.lower() == 'false' or tmp == '0')
+            
+    def add_opcode(self, opcode, name, main):
+        self.opcodes[name] = opcode
+        if main:
+            self.name = name
+
+    def resolve(self, module):
+        if self.resolved:
+            return
+
+        # Add the automatic protocol fields
+        self.fields.append(Field(tcard8, tcard8.name, 'response_type', False, True, True))
+        if self.has_seq:
+            self.fields.append(_placeholder_byte)
+            self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True))
+        ComplexType.resolve(self, module)
+
+    out = __main__.output['event']
+
+
+class Error(ComplexType):
+    '''
+    Derived class representing an error data type.
+
+    Public fields added:
+    opcodes is a dictionary of name -> opcode number, for errorcopies.
+    '''
+    def __init__(self, name, elt):
+        ComplexType.__init__(self, name, elt)
+        self.opcodes = {}
+
+    def add_opcode(self, opcode, name, main):
+        self.opcodes[name] = opcode
+        if main:
+            self.name = name
+
+    def resolve(self, module):
+        if self.resolved:
+            return
+
+        # Add the automatic protocol fields
+        self.fields.append(Field(tcard8, tcard8.name, 'response_type', False, True, True))
+        self.fields.append(Field(tcard8, tcard8.name, 'error_code', False, True, True))
+        self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True))
+        ComplexType.resolve(self, module)
+
+    out = __main__.output['error']
+
+_placeholder_byte = Field(PadType(None), tcard8.name, 'pad0', False, True, False)


More information about the xcb-commit mailing list