1'''
2This module contains the namespace class and the singleton module class.
3'''
4from os.path import dirname, basename
5from sys import version_info
6
7if version_info[:2] >= (3, 3):
8    from xml.etree.ElementTree import parse
9else:
10    from xml.etree.cElementTree import parse
11
12from xcbgen import matcher
13from xcbgen.error import *
14from xcbgen.xtypes import *
15
16import __main__
17
18class Namespace(object):
19    '''
20    Contains the naming information for an extension.
21
22    Public fields:
23
24    header is the header attribute ("header file" name).
25    is_ext is true for extensions, false for xproto.
26    major_version and minor_version are extension version info.
27    ext_xname is the X extension name string.
28    ext_name is the XCB extension name prefix.
29    '''
30    def __init__(self, filename):
31        # Path info
32        self.path = filename
33        self.dir = dirname(filename)
34        self.file = basename(filename)
35
36        # Parse XML
37        self.root = parse(filename).getroot()
38        self.header = self.root.get('header')
39        self.ns = self.header + ':'
40
41        # Events
42        self.events = {}
43
44        # Get root element attributes
45        if self.root.get('extension-xname', False):
46            self.is_ext = True
47            self.major_version = self.root.get('major-version')
48            self.minor_version = self.root.get('minor-version')
49            self.ext_xname = self.root.get('extension-xname')
50            self.ext_name = self.root.get('extension-name')
51            self.prefix = ('xcb', self.ext_name)
52        else:
53            self.is_ext = False
54            self.ext_name = ''
55            self.prefix = ('xcb',)
56
57    def add_event(self, id, name, item):
58        self.events[id] = (name, item)
59
60    def get_event_by_opcode(self, opcode, is_ge_event):
61        for id, (name, event) in self.events.items():
62            if event.is_ge_event == is_ge_event:
63                opcode_specific_name = event.get_name_for_opcode( opcode )
64                if opcode_specific_name is not None:
65                    return (opcode_specific_name, event)
66        return None
67
68
69class Module(object):
70    '''
71    This is the grand, encompassing class that represents an entire XCB specification.
72    Only gets instantiated once, in the main() routine.
73
74    Don't need to worry about this much except to declare it and to get the namespace.
75
76    Public fields:
77    namespace contains the namespace info for the spec.
78    '''
79    open = __main__.output['open']
80    close = __main__.output['close']
81
82    def __init__(self, filename, output):
83        self.namespace = Namespace(filename)
84        self.output = output
85
86        self.imports = []
87        self.direct_imports = []
88        self.import_level = 0
89        self.types = {}
90        self.events = {}
91        self.errors = {}
92        self.all = []
93
94        # dict of namespaces by ext_name
95        self.namespaces = {}
96        # enter the main namespace here
97        self.namespaces[self.namespace.ext_name] = self.namespace
98
99        # Register some common types
100        self.add_type('CARD8', '', ('uint8_t',), tcard8)
101        self.add_type('CARD16', '', ('uint16_t',), tcard16)
102        self.add_type('CARD32', '', ('uint32_t',), tcard32)
103        self.add_type('CARD64', '', ('uint64_t',), tcard64)
104        self.add_type('INT8', '', ('int8_t',), tint8)
105        self.add_type('INT16', '', ('int16_t',), tint16)
106        self.add_type('INT32', '', ('int32_t',), tint32)
107        self.add_type('INT64', '', ('int64_t',), tint64)
108        self.add_type('BYTE', '', ('uint8_t',), tbyte)
109        self.add_type('BOOL', '', ('uint8_t',), tbool)
110        self.add_type('char', '', ('char',), tchar)
111        self.add_type('float', '', ('float',), tfloat)
112        self.add_type('double', '', ('double',), tdouble)
113        self.add_type('void', '', ('void',), tvoid)
114
115    # This goes out and parses the rest of the XML
116    def register(self):
117        matcher.execute(self, self.namespace)
118
119    # Recursively resolve all types
120    def resolve(self):
121        self.add_events_to_namespaces()
122        for (name, item) in self.all:
123            self.pads = 0
124            item.resolve(self)
125
126    # Call all the output methods
127    def generate(self):
128        self.open()
129
130        for (name, item) in self.all:
131            item.out(name)
132
133        self.close()
134
135    # Keeps track of what's been imported so far.
136    def add_import(self, name, namespace):
137        if self.import_level == 0:
138            self.direct_imports.append((name, namespace.header))
139        self.imports.append((name, namespace.header))
140        self.namespaces[namespace.ext_name] = namespace
141
142    def has_import(self, name):
143        for (name_, header) in self.imports:
144            if name_ == name:
145                return True
146        return False
147
148    # Keeps track of non-request/event/error datatypes
149    def add_type(self, id, ns, name, item):
150        key = ns + id
151        if key in self.types:
152            return
153        self.types[key] = (name, item)
154        if name[:-1] == self.namespace.prefix:
155            self.all.append((name, item))
156
157    def get_type_impl(self, id, idx):
158        key = id
159        if key in self.types:
160            return self.types[key][idx]
161
162        key = self.namespace.ns + id
163        if key in self.types:
164            return self.types[key][idx]
165
166        for key in self.types.keys():
167            if key.rpartition(':')[2] == id:
168                return self.types[key][idx]
169
170        raise ResolveException('Type %s not found' % id)
171
172    def get_type(self, id):
173        return self.get_type_impl(id, 1)
174
175    def get_type_name(self, id):
176        return self.get_type_impl(id, 0)
177
178    def get_namespace(self, ext_name):
179        return self.namespaces[ext_name]
180
181    # Keeps track of request datatypes
182    def add_request(self, id, name, item):
183        if name[:-1] == self.namespace.prefix:
184            self.all.append((name, item))
185
186    # Keeps track of event datatypes
187    def add_event(self, id, name, item):
188        self.events[id] = (name, item)
189        if name[:-1] == self.namespace.prefix:
190            self.all.append((name, item))
191
192
193    def add_events_to_namespaces(self):
194        # add to its namespace object
195        for id, (name,item) in self.events.items():
196            if name[:-1] == ('xcb',):
197                # core event
198                namespace_name = ''
199            else:
200                # extension event
201                namespace_name = name[-2]
202
203            namespace = self.namespaces[namespace_name]
204
205            if namespace is not None:
206                namespace.add_event(id, name, item)
207
208
209    def get_event(self, id):
210        return self.events[id][1]
211
212    # Keeps track of error datatypes
213    def add_error(self, id, name, item):
214        self.errors[id] = (name, item)
215        if name[:-1] == self.namespace.prefix:
216            self.all.append((name, item))
217
218    def get_error(self, id):
219        return self.errors[id][1]
220