1#!/usr/bin/env python3
2# Copyright (c) 2016 Remi Thebault <remi.thebault@gmail.com>
3#
4# Permission is hereby granted, free of charge, to any
5# person obtaining a copy of this software and associated
6# documentation files (the "Software"), to deal in the
7# Software without restriction, including without
8# limitation the rights to use, copy, modify, merge,
9# publish, distribute, sublicense, and/or sell copies of
10# the Software, and to permit persons to whom the Software
11# is furnished to do so, subject to the following
12# conditions:
13#
14# The above copyright notice and this permission notice
15# shall be included in all copies or substantial portions
16# of the Software.
17#
18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
19# ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
20# TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
21# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
22# SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
25# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26# DEALINGS IN THE SOFTWARE.
27'''
28script that generates rust code from xcb xml definitions
29Each invokation of this script generates one ffi file and one
30rust file for an extension or the main X Protocol.
31
32Usage: ./rs_client.py -o src xml/xproto.xml
33'''
34
35import sys
36import os
37import re
38
39
40
41class SourceFile(object):
42    '''
43    buffer to append code in various sections of a file
44    in any order
45    '''
46
47    _one_indent_level = '    '
48
49    def __init__(self):
50        self._section = 0
51        self._lines = []
52        self._indents = []
53
54    def getsection(self):
55        return self._section
56
57    def section(self, section):
58        '''
59        Set the section of the file where to append code.
60        Allows to make different sections in the file to append
61        to in any order
62        '''
63        while len(self._lines) <= section:
64            self._lines.append([])
65        while len(self._indents) <= section:
66            self._indents.append(0)
67        self._section = section
68
69    def getindent(self):
70        '''
71        returns indentation of the current section
72        '''
73        return self._indents[self._section]
74
75    def setindent(self, indent):
76        '''
77        sets indentation of the current section
78        '''
79        self._indents[self._section] = indent;
80
81    def indent_block(self):
82        class Indenter(object):
83            def __init__(self, sf):
84                self.sf = sf
85            def __enter__(self):
86                self.sf.indent()
87            def __exit__(self, type, value, traceback):
88                self.sf.unindent()
89        return Indenter(self)
90
91    def indent(self):
92        '''
93        adds one level of indentation to the current section
94        '''
95        self._indents[self._section] += 1
96
97    def unindent(self):
98        '''
99        removes one level of indentation to the current section
100        '''
101        assert self.getindent() > 0, "negative indent"
102        self._indents[self._section] -= 1
103
104    def __call__(self, fmt, *args):
105        '''
106        Append a line to the file at in its current section and
107        indentation of the current section
108        '''
109        indent = SourceFile._one_indent_level * self._indents[self._section]
110        self._lines[self._section].append(indent + (fmt % args))
111
112
113    def writeout(self, path):
114        os.makedirs(os.path.dirname(path), exist_ok=True)
115        with open(path, 'w') as f:
116            for section in self._lines:
117                for line in section:
118                    print(line.rstrip(), file=f)
119
120
121# FFI source file
122_f = SourceFile()
123
124# Rust interface file
125_r = SourceFile()
126
127# utility to add same code in both files
128def _rf(fmt, *args):
129    _f(fmt, *args)
130    _r(fmt, *args)
131
132
133_ns = None
134_ext_names = {}
135
136# global variable to keep track of serializers and
137# switch data types due to weird dependencies
138finished_serializers = []
139finished_sizeof = []
140finished_switch = []
141
142_types_uneligible_to_copy = []
143
144# current handler is used for error reporting
145current_handler = None
146
147# Keep tracks of types that have lifetime parameter
148# Initialized with types that are defined in one module and used in other modules
149types_with_lifetime = [
150    "xcb_str_iterator_t",                   # defined in xproto, used in render
151    "xcb_xv_image_format_info_iterator_t"   # defined in xv, used in xvmc
152]
153
154# link exceptions
155link_exceptions = {
156    "bigreq": "xcb",
157    "xc_misc": "xcb"
158}
159
160
161#type translation
162_ffi_type_translation = {
163    'BOOL': 'u8'
164}
165_rs_type_translation = {
166    'BOOL': 'bool'
167}
168
169# struct with only simple fields are defined as typedef to the ffi struct (issue #7)
170# this list adds exception to this behavior
171_rs_typedef_exceptions = [
172    # not strictly necessary has Setup has complex fields
173    # however intent is clear: 'xproto::Setup' MUST use StructPtr
174    'xproto::Setup'
175]
176
177# exported functions to xcbgen start by 'rs_'
178
179# starting with opening and closing
180
181def rs_open(module):
182    '''
183    Handles module open.
184    module is a xcbgen.state.Module object
185    '''
186    global _ns
187    _ns = module.namespace
188
189    linklib = "xcb"
190    if _ns.is_ext:
191        linklib = 'xcb-' + _ns.header
192        _ext_names[_ns.ext_name.lower()] = _ns.header
193        for (n, h) in module.direct_imports:
194            if h != 'xproto':
195                _ext_names[n.lower()] = h
196
197    if _ns.header in link_exceptions:
198        linklib = link_exceptions[_ns.header]
199
200    ext_id_name = _ffi_name(_ns.prefix + ('id',))
201
202    _r.section(0)
203    _f.section(0)
204    _rf('// Generated automatically from %s by rs_client.py version %s.',
205            _ns.file, os.getenv('CARGO_PKG_VERSION', 'undefined'))
206    _rf('// Do not edit!')
207    _rf('')
208
209    _f('')
210    _f('use ffi::base::*;')
211
212    if _ns.is_ext:
213        for (n, h) in module.imports:
214            _f('use ffi::%s::*;', _module_name(n))
215        _f('')
216    _f('use libc::{c_char, c_int, c_uint, c_void};')
217    _f('use std;')
218    _f('')
219
220    _f.section(1)
221    _f('')
222    _f('')
223    _f('#[link(name="%s")]', linklib)
224    _f('extern {')
225    _f.indent()
226    if _ns.is_ext:
227        _f('')
228        _f('pub static mut %s: xcb_extension_t;', ext_id_name)
229
230
231    _r('use base;')
232    if _ns.is_ext:
233        for (n, h) in module.imports:
234            _r('use %s;', _module_name(n))
235    _r('use ffi::base::*;')
236    _r('use ffi::%s::*;', _module_name(_ns.ext_name))
237    if _ns.is_ext:
238        for (n, h) in module.imports:
239            _r('use ffi::%s::*;', _module_name(n))
240    _r('use libc::{self, c_char, c_int, c_uint, c_void};')
241    _r('use std;')
242    _r('use std::iter::Iterator;')
243    _r('')
244    if _ns.is_ext:
245        _r('')
246        _r("pub fn id() -> &'static mut base::Extension {")
247        _r('    unsafe {')
248        _r('        &mut %s', ext_id_name)
249        _r('    }')
250        _r('}')
251
252    _r.section(1)
253    _r('')
254    _r('')
255
256
257
258    if _ns.is_ext:
259        _f.section(0)
260        _f('')
261        _f('pub const %s: u32 = %s;',
262                    _ffi_const_name(('xcb', _ns.ext_name, 'major', 'version')),
263                    _ns.major_version)
264        _f('pub const %s: u32 = %s;',
265                    _ffi_const_name(('xcb', _ns.ext_name, 'minor', 'version')),
266                    _ns.minor_version)
267
268        _r.section(0)
269        _r('')
270        _r('pub const MAJOR_VERSION: u32 = %s;', _ns.major_version)
271        _r('pub const MINOR_VERSION: u32 = %s;', _ns.minor_version)
272
273
274
275    EnumCodegen.build_collision_table(module)
276
277
278
279
280def rs_close(module):
281    '''
282    Handles module close.
283    module is a xcbgen.state.Module object.
284    main task is to write the files out
285    '''
286
287    _f.section(1)
288
289    _f('')
290    _f.unindent()
291    _f('} // extern')
292
293    _f.writeout(os.path.join(module.rs_srcdir, "ffi", "%s.rs" % _module_name(_ns.ext_name)))
294    _r.writeout(os.path.join(module.rs_srcdir, "%s.rs" % _module_name(_ns.ext_name)))
295
296
297
298# transformation of name tuples
299
300_cname_re = re.compile('([A-Z0-9][a-z]+|[A-Z0-9]+(?![a-z])|[a-z]+)')
301_rs_keywords = ['type', 'str', 'match', 'new']
302
303
304def _tit_split(string):
305    '''
306    splits string with '_' on each titlecase letter
307    >>> _tit_split('SomeString')
308    Some_String
309    >>> _tit_split('WINDOW')
310    WINDOW
311    '''
312    split = _cname_re.finditer(string)
313    name_parts = [match.group(0) for match in split]
314    return '_'.join(name_parts)
315
316def _tit_cap(string):
317    '''
318    capitalize each substring beggining by a titlecase letter
319    >>> _tit_cap('SomeString')
320    SomeString
321    >>> _tit_cap('WINDOW')
322    Window
323    '''
324    split = _cname_re.finditer(string)
325    name_parts = [match.group(0) for match in split]
326    name_parts = [i[0].upper() + i[1:].lower() for i in name_parts]
327    return ''.join(name_parts)
328
329
330_extension_special_cases = ['XPrint', 'XCMisc', 'BigRequests']
331
332def _module_name(name):
333    if len(name):
334        if name in _extension_special_cases:
335            return _tit_split(name).lower()
336        else:
337            return name.lower()
338    else:
339        return 'xproto'
340
341
342def _symbol(string):
343    if string in _rs_keywords:
344        string += '_'
345    return string
346
347def _upper_1st(string):
348    '''
349    return copy of string with first letter turned into upper.
350    Other letters are untouched.
351    '''
352    if len(string) == 0:
353        return ''
354    if len(string) == 1:
355        return string.upper()
356    return string[0].upper() + string[1:]
357
358def _upper_name(nametup):
359    '''
360    return a string made from a nametuple with all upper case
361    joined with underscore
362    >>> _upper_name(('xcb', 'constant', 'AwesomeValue'))
363    XCB_CONSTANT_AWESOME_VALUE
364    '''
365    return '_'.join(tuple(_tit_split(name) for name in nametup)).upper()
366
367def _cap_name(nametup):
368    '''
369    return a string made from a nametuple with joined title case
370    >>> _cap_name(('xcb', 'Type', 'Name'))
371    XcbTypeName
372    >>> _cap_name(('xcb', 'TypeName'))
373    XcbTypeName
374    >>> _cap_name(('xcb', 'TYPENAME'))
375    XcbTypename
376    '''
377    return ''.join(tuple(_upper_1st(name) for name in nametup))
378
379def _lower_name(nametup):
380    '''
381    return a string made from a nametuple with all lower case
382    joined with underscore
383    >>> _upper_name(('xcb', 'Ext', 'RequestName'))
384    xcb_ext_request_name
385    '''
386    return '_'.join(tuple(_tit_split(name) for name in nametup)).lower()
387
388
389def _ext_nametup(nametup):
390    '''
391    return the nametup with 2nd name lowered if module is an extension
392    >>> _ext_nametup(('u32',))
393    ('u32',)
394    >>> _ext_nametup(('xcb', 'XprotoType'))
395    ('xcb', 'XprotoType')
396    >>> _ext_nametup(('xcb', 'RandR', 'SuperType'))
397    ('xcb', 'randr', 'SuperType')
398    '''
399    if len(nametup) > 2 and nametup[1].lower() in _ext_names:
400        #nametup = tuple(_ext_names[name.lower()] if i == 1 else name
401        #        for (i, name) in enumerate(nametup))
402        # lowers extension to avoid '_' split with title letters
403        nametup = tuple(_module_name(name) if i == 1 else name
404                for (i, name) in enumerate(nametup))
405    return nametup
406
407def _ffi_type_name(nametup):
408    '''
409    turns the nametup into a FFI type
410    >>> _ffi_type_name(('u32',))
411    u32
412    >>> _ffi_type_name(('xcb', 'XprotoType'))
413    xcb_xproto_type_t
414    >>> _ffi_type_name(('xcb', 'RandR', 'SuperType'))
415    xcb_randr_super_type_t
416    '''
417    if len(nametup) == 1:
418        # handles SimpleType
419        if nametup[0] in _ffi_type_translation:
420            return _ffi_type_translation[nametup[0]]
421        return nametup[0]
422    return _ffi_name(nametup + ('t',))
423
424
425def _ffi_name(nametup):
426    '''
427    turns the nametup into a FFI name
428    >>> _ffi_type_name(('u32',))
429    u32
430    >>> _ffi_type_name(('xcb', 'XprotoType', 't'))
431    xcb_xproto_type_t
432    >>> _ffi_type_name(('xcb', 'RandR', 'SuperType', 't'))
433    xcb_randr_super_type_t
434    '''
435    secondIsExt = (len(nametup) > 2 and nametup[1].lower() in _ext_names)
436    nametup = _ext_nametup(nametup)
437
438    if secondIsExt:
439        return '_'.join(tuple(name if i==1 else _tit_split(name)
440                for (i, name) in enumerate(nametup))).lower()
441    else:
442        return '_'.join(tuple(_tit_split(name) for name in nametup)).lower()
443
444
445def _ffi_const_name(nametup):
446    return _ffi_name(_ext_nametup(nametup)).upper()
447
448
449def _rs_extract_module(nametup):
450    '''
451    returns the module extracted from nametup
452    along with the nametup without the module parts
453    if module is local module, an empty module is returned
454    >>> _rs_extract_module(('u32',))
455    ("", "u32")
456    >>> _rs_extract_module(('xcb', 'Type'))
457    ("", ("Type"))
458    >>> _rs_extract_module(('xcb', 'RandR', 'SuperType'))
459    ("randr::", ("SuperType"))
460    '''
461    # handles SimpleType
462    if len(nametup) == 1:
463        return ("", nametup[0])
464
465    # remove 'xcb'
466    if nametup[0].lower() == 'xcb':
467        nametup = nametup[1:]
468
469    module = ''
470    # handle extension type
471    if nametup[0].lower() in _ext_names:
472        ext = _ext_names[nametup[0].lower()]
473        if (not _ns.is_ext or
474                ext != _ns.header):
475            module = ext + '::'
476        nametup = nametup[1:]
477
478    # handle xproto type for extensions
479    else:
480        if _ns.is_ext:
481            module = 'xproto::'
482
483    return (module, nametup)
484
485
486
487def _rs_type_name(nametup):
488    '''
489    turns the nametup into a Rust type name
490    foreign rust type names include module prefix
491    >>> _rs_type_name(('u32',))
492    u32
493    >>> _rs_type_name(('xcb', 'Type'))
494    xproto::Type
495    >>> _rs_type_name(('xcb', 'RandR', 'SuperType'))
496    randr::SuperType
497    '''
498    if len(nametup) == 1:
499        if nametup[0] in _rs_type_translation:
500            return _rs_type_translation[nametup[0]]
501        return nametup[0]
502
503    (module, nametup) = _rs_extract_module(nametup)
504
505    return module + ''.join([_tit_cap(n) for n in nametup])
506
507
508def _rs_name(nametup):
509
510    (module, nametup) = _rs_extract_module(nametup)
511
512    return module + '_'.join([_tit_split(n) for n in nametup]).lower()
513
514
515def _rs_const_name(nametup):
516    return _upper_name(_rs_extract_module(nametup)[1])
517
518def _rs_field_name(string):
519    res = ''
520    for c in string:
521        if c.isupper():
522            res = res + '_' + c.lower()
523        else:
524            res = res + c
525    return res
526
527
528def _set_type_lifetime(typeobj, has_lifetime):
529    typeobj.has_lifetime = has_lifetime
530
531    # handle successive calls to _set_type_lifetime on the same object
532    def ensure_in(val):
533        if not val in types_with_lifetime:
534            types_with_lifetime.append(val)
535
536    def ensure_out(val):
537        while val in types_with_lifetime:
538            types_with_lifetime.remove(val)
539
540    if has_lifetime:
541        ensure_in(typeobj.ffi_iterator_type)
542        ensure_in(typeobj.rs_type)
543        ensure_in(typeobj.rs_iterator_type)
544    else:
545        ensure_out(typeobj.ffi_iterator_type)
546        ensure_out(typeobj.rs_type)
547        ensure_out(typeobj.rs_iterator_type)
548
549
550
551# FFI codegen functions
552
553def _ffi_type_setup(typeobj, nametup, suffix=()):
554    '''
555    Sets up all the C-related state by adding additional data fields to
556    all Field and Type objects.  Here is where we figure out most of our
557    variable and function names.
558
559    Recurses into child fields and list member types.
560    '''
561    # Do all the various names in advance
562    typeobj.ffi_type = _ffi_type_name(nametup + suffix)
563
564    typeobj.ffi_iterator_type = _ffi_type_name(nametup + ('iterator',))
565    typeobj.ffi_next_fn = _ffi_name(nametup + ('next',))
566    typeobj.ffi_end_fn = _ffi_name(nametup + ('end',))
567
568    typeobj.ffi_request_fn = _ffi_name(nametup)
569    typeobj.ffi_checked_fn = _ffi_name(nametup + ('checked',))
570    typeobj.ffi_unchecked_fn = _ffi_name(nametup + ('unchecked',))
571    typeobj.ffi_reply_fn = _ffi_name(nametup + ('reply',))
572    typeobj.ffi_reply_type = _ffi_type_name(nametup + ('reply',))
573    typeobj.ffi_cookie_type = _ffi_type_name(nametup + ('cookie',))
574    typeobj.ffi_reply_fds_fn = _ffi_name(nametup + ('reply_fds',))
575
576    typeobj.ffi_need_aux = False
577    typeobj.ffi_need_serialize = False
578    typeobj.ffi_need_sizeof = False
579
580    typeobj.ffi_aux_fn = _ffi_name(nametup + ('aux',))
581    typeobj.ffi_aux_checked_fn = _ffi_name(nametup + ('aux', 'checked'))
582    typeobj.ffi_aux_unchecked_fn = _ffi_name(nametup + ('aux', 'unchecked'))
583    typeobj.ffi_serialize_fn = _ffi_name(nametup + ('serialize',))
584    typeobj.ffi_unserialize_fn = _ffi_name(nametup + ('unserialize',))
585    typeobj.ffi_unpack_fn = _ffi_name(nametup + ('unpack',))
586    typeobj.ffi_sizeof_fn = _ffi_name(nametup + ('sizeof',))
587
588    # special case: structs where variable size fields are followed
589    # by fixed size fields
590    typeobj.ffi_var_followed_by_fixed_fields = False
591
592    if not typeobj.fixed_size():
593        if not typeobj in _types_uneligible_to_copy:
594            _types_uneligible_to_copy.append(typeobj)
595        if hasattr(typeobj, 'parents'):
596            for p in typeobj.parents:
597                _types_uneligible_to_copy.append(p)
598
599
600    if typeobj.is_container:
601
602        prev_varsized_field = None
603        prev_varsized_offset = 0
604        first_field_after_varsized = None
605
606        for field in typeobj.fields:
607            _ffi_type_setup(field.type, field.field_type, ())
608            if field.type.is_list:
609                _ffi_type_setup(field.type.member, field.field_type, ())
610                if (field.type.nmemb is None):
611                    typeobj.ffi_need_sizeof = True
612
613            field.ffi_field_type = _ffi_type_name(field.field_type)
614            field.ffi_field_name = _symbol(field.field_name)
615            field.has_subscript = (field.type.nmemb and
616                            field.type.nmemb > 1)
617            field.ffi_need_const = (field.type.nmemb != 1)
618            field.ffi_need_pointer = (field.type.nmemb != 1)
619
620            # correct the need_pointer field for variable size non-list types
621            if not field.type.fixed_size():
622                field.ffi_need_pointer = True
623            if field.type.is_list and not field.type.member.fixed_size():
624                field.ffi_need_pointer = True
625
626            if field.type.is_switch:
627                field.ffi_need_const = True
628                field.ffi_need_pointer = True
629                field.ffi_need_aux = True
630            elif not field.type.fixed_size() and not field.type.is_bitcase:
631                typeobj.ffi_need_sizeof = True
632
633            field.ffi_iterator_type = _ffi_type_name(
634                    field.field_type + ('iterator',))
635            field.ffi_iterator_fn = _ffi_name(
636                    nametup + (field.field_name, 'iterator'))
637            field.ffi_accessor_fn = _ffi_name(
638                    nametup + (field.field_name,))
639            field.ffi_length_fn = _ffi_name(
640                    nametup + (field.field_name, 'length'))
641            field.ffi_end_fn = _ffi_name(
642                    nametup + (field.field_name, 'end'))
643
644            field.prev_varsized_field = prev_varsized_field
645            field.prev_varsized_offset = prev_varsized_offset
646
647            if prev_varsized_offset == 0:
648                first_field_after_varsized = field
649            field.first_field_after_varsized = first_field_after_varsized
650
651            if field.type.fixed_size():
652                prev_varsized_offset += field.type.size
653                # special case: intermixed fixed and variable size fields
654                if (prev_varsized_field is not None and
655                        not field.type.is_pad and field.wire):
656                    if not typeobj.is_union:
657                        typeobj.ffi_need_serialize = True
658                        typeobj.ffi_var_followed_by_fixed_fields = True
659            else:
660                typeobj.last_varsized_field = field
661                prev_varsized_field = field
662                prev_varsized_offset = 0
663
664            if typeobj.ffi_var_followed_by_fixed_fields:
665                if field.type.fixed_size():
666                    field.prev_varsized_field = None
667
668    if typeobj.ffi_need_serialize:
669        # when _unserialize() is wanted, create _sizeof() as well
670        # for consistency reasons
671        typeobj.ffi_need_sizeof = True
672
673    if not typeobj.is_bitcase:
674        if typeobj.ffi_need_serialize:
675            if typeobj.ffi_serialize_fn not in finished_serializers:
676                finished_serializers.append(typeobj.ffi_serialize_fn)
677                #_ffi_serialize('serialize', typeobj)
678
679                # _unpack() and _unserialize() are only needed
680                # for special cases:
681                #   switch -> unpack
682                #   special cases -> unserialize
683                if (typeobj.is_switch or
684                        typeobj.ffi_var_followed_by_fixed_fields):
685                    pass
686                    #_ffi_serialize('unserialize', typeobj)
687
688        if typeobj.ffi_need_sizeof:
689            if typeobj.ffi_sizeof_fn not in finished_sizeof:
690                if not _ns.is_ext or typeobj.name[:2] == _ns.prefix:
691                    finished_sizeof.append(typeobj.ffi_sizeof_fn)
692                    #_ffi_serialize('sizeof', typeobj)
693
694
695
696
697def _ffi_bitcase_name(switch, bitcase):
698    assert switch.is_switch and bitcase.type.has_name
699    switch_name = _lower_name(_ext_nametup(switch.name))
700    return '_%s__%s' % (switch_name, bitcase.ffi_field_name)
701
702
703def _ffi_struct(typeobj, must_pack=False):
704    '''
705    Helper function for handling all structure types.
706    Called for structs, requests, replies, events, errors...
707    '''
708
709    struct_fields = []
710
711    for field in typeobj.fields:
712        if (not field.type.fixed_size()
713            and not typeobj.is_switch
714            and not typeobj.is_union):
715            continue
716        if field.wire:
717            struct_fields.append(field)
718
719    _f.section(0)
720    _f('')
721    _write_doc_brief_desc(_f, typeobj.doc)
722    _f('#[repr(C%s)]', ', packed' if must_pack else '')
723    _f('pub struct %s {', typeobj.ffi_type)
724    _f.indent()
725
726    maxfieldlen = 0
727    if not typeobj.is_switch:
728        for field in typeobj.fields:
729            maxfieldlen = max(maxfieldlen, len(field.ffi_field_name))
730    else:
731        for b in typeobj.bitcases:
732            if b.type.has_name:
733                maxfieldlen = max(maxfieldlen, len(b.ffi_field_name))
734            else:
735                for field in b.type.fields:
736                    maxfieldlen = max(maxfieldlen, len(field.ffi_field_name))
737
738
739
740    def _ffi_struct_field(field):
741        ftype = field.ffi_field_type
742        space = ' '* (maxfieldlen - len(field.ffi_field_name))
743        if (field.type.fixed_size() or typeobj.is_union or
744            # in case of switch with switch children,
745            # don't make the field a pointer
746            # necessary for unserialize to work
747            (typeobj.is_switch and field.type.is_switch)):
748            if field.has_subscript:
749                ftype = '[%s; %d]' % (ftype, field.type.nmemb)
750            _f('pub %s: %s%s,', field.ffi_field_name, space, ftype)
751        else:
752            assert not field.has_subscript
753            _f('pub %s: %s*mut %s,', field.ffi_field_name, space, ftype)
754
755    named_bitcases = []
756
757    if not typeobj.is_switch:
758        for field in struct_fields:
759            for d in typeobj.doc.fields[field.field_name]:
760                _f('/// %s', d)
761            _ffi_struct_field(field)
762    else:
763        for b in typeobj.bitcases:
764            if b.type.has_name:
765                named_bitcases.append(b)
766                space = ' ' * (maxfieldlen - len(b.ffi_field_name))
767                _f('pub %s: %s%s,', b.ffi_field_name, space,
768                        _ffi_bitcase_name(typeobj, b))
769            else:
770                for field in b.type.fields:
771                    _ffi_struct_field(field)
772
773    _f.unindent()
774    _f('}')
775    if not typeobj in _types_uneligible_to_copy:
776        _f('')
777        _f('impl Copy for %s {}', typeobj.ffi_type)
778        _f('impl Clone for %s {', typeobj.ffi_type)
779        _f('    fn clone(&self) -> %s { *self }', typeobj.ffi_type)
780        _f('}')
781
782    for b in named_bitcases:
783        _f('')
784        _f('#[repr(C)]')
785        _f('pub struct %s {', _ffi_bitcase_name(typeobj, b))
786        _f.indent()
787        maxfieldlen = 0
788        for field in b.type.fields:
789            maxfieldlen = max(maxfieldlen, len(field.ffi_field_name))
790        for field in b.type.fields:
791            _ffi_struct_field(field)
792        _f.unindent()
793        _f('}')
794
795
796
797def _ffi_accessors_list(typeobj, field):
798    '''
799    Declares the accessor functions for a list field.
800    Declares a direct-accessor function only if the list members
801        are fixed size.
802    Declares length and get-iterator functions always.
803    '''
804
805    list = field.type
806    ffi_type = typeobj.ffi_type
807
808    # special case: switch
809    # in case of switch, 2 params have to be supplied to certain
810    # accessor functions:
811    #   1. the anchestor object (request or reply)
812    #   2. the (anchestor) switch object
813    # the reason is that switch is either a child of a request/reply
814    # or nested in another switch,
815    # so whenever we need to access a length field, we might need to
816    # refer to some anchestor type
817    switch_obj = typeobj if typeobj.is_switch else None
818    if typeobj.is_bitcase:
819        switch_obj = typeobj.parents[-1]
820    if switch_obj is not None:
821        ffi_type = switch_obj.ffi_type
822
823    params = []
824    parents = typeobj.parents if hasattr(typeobj, 'parents') else [typeobj]
825    # 'R': parents[0] is always the 'toplevel' container type
826    params.append(('R: *const %s' % parents[0].ffi_type, parents[0]))
827    # auxiliary object for 'R' parameters
828    R_obj = parents[0]
829
830    if switch_obj is not None:
831        # now look where the fields are defined that are needed to evaluate
832        # the switch expr, and store the parent objects in accessor_params and
833        # the fields in switch_fields
834
835        # 'S': name for the 'toplevel' switch
836        toplevel_switch = parents[1]
837        params.append(('S: *const %s' % toplevel_switch.ffi_type,
838                toplevel_switch))
839
840        # auxiliary object for 'S' parameter
841        S_obj = parents[1]
842
843    _f.section(1)
844    if list.member.fixed_size():
845        idx = 1 if switch_obj is not None else 0
846        _f('')
847        _f('pub fn %s (%s)', field.ffi_accessor_fn, params[idx][0])
848        _f('        -> *mut %s;', field.ffi_field_type)
849
850    def _may_switch_fn(fn_name, return_type):
851        _f('')
852        has_lifetime = return_type in types_with_lifetime
853        lifetime = "<'a>" if has_lifetime else ""
854        if switch_obj is not None:
855            fn_start = 'pub fn %s%s (' % (fn_name, lifetime)
856            spacing = ' '*len(fn_start)
857            _f('%sR: *const %s,', fn_start, R_obj.ffi_type)
858            _f('%sS: *const %s)', spacing, S_obj.ffi_type)
859            _f('        -> %s%s;', return_type, lifetime)
860        else:
861            _f('pub fn %s%s (R: *const %s)', fn_name, lifetime, ffi_type)
862            _f('        -> %s%s;', return_type, lifetime)
863
864    _may_switch_fn(field.ffi_length_fn, 'c_int')
865
866    if field.type.member.is_simple:
867        _may_switch_fn(field.ffi_end_fn, 'xcb_generic_iterator_t')
868    else:
869        _may_switch_fn(field.ffi_iterator_fn, field.ffi_iterator_type)
870
871
872
873def _ffi_accessors_field(typeobj, field):
874    '''
875    Declares the accessor functions for a non-list field that follows
876    a variable-length field.
877    '''
878    ffi_type = typeobj.ffi_type
879
880    # special case: switch
881    switch_obj = typeobj if typeobj.is_switch else None
882    if typeobj.is_bitcase:
883        switch_obj = typeobj.parents[-1]
884    if switch_obj is not None:
885        ffi_type = switch_obj.ffi_type
886
887    _f.section(1)
888    if field.type.is_simple:
889        _f('')
890        _f('pub fn %s (R: *const %s)', field.ffi_accessor_fn, ffi_type)
891        _f('        -> %s;', field.ffi_field_type)
892    else:
893        if field.type.is_switch and switch_obj is None:
894            return_type = '*mut c_void'
895        else:
896            return_type = '*mut %s' % field.ffi_field_type
897
898        _f('')
899        _f('pub fn %s (R: *const %s)', field.ffi_accessor_fn, ffi_type)
900        _f('        -> %s;', return_type)
901
902
903def _ffi_accessors(typeobj, nametup):
904    for field in typeobj.fields:
905        if not field.type.is_pad:
906            if field.type.is_list and not field.type.fixed_size():
907                _ffi_accessors_list(typeobj, field)
908            elif (field.prev_varsized_field is not None
909                    or not field.type.fixed_size()):
910                _ffi_accessors_field(typeobj, field)
911
912
913def _ffi_iterator(typeobj, nametup):
914
915    has_lifetime = typeobj.ffi_iterator_type in types_with_lifetime
916    lifetime = "<'a>" if has_lifetime else ""
917
918    _f.section(0)
919    _f('')
920    _f('#[repr(C)]')
921    _f("pub struct %s%s {", typeobj.ffi_iterator_type, lifetime)
922    _f('    pub data:  *mut %s,', typeobj.ffi_type)
923    _f('    pub rem:   c_int,')
924    _f('    pub index: c_int,')
925    if has_lifetime:
926        _f("    _phantom:  std::marker::PhantomData<&'a %s>,", typeobj.ffi_type)
927    _f('}')
928
929    _f.section(1)
930    _f('')
931    _f('pub fn %s (i: *mut %s);', typeobj.ffi_next_fn,
932            typeobj.ffi_iterator_type)
933
934    _f('')
935    _f('pub fn %s (i: *mut %s)', typeobj.ffi_end_fn,
936            typeobj.ffi_iterator_type)
937    _f('        -> xcb_generic_iterator_t;')
938
939
940
941
942def _ffi_reply(request):
943    '''
944    Declares the function that returns the reply structure.
945    '''
946    _f.section(1)
947    _f('')
948    _f('/// the returned value must be freed by the caller using ' +
949            'libc::free().')
950    fn_start = 'pub fn %s (' % request.ffi_reply_fn
951    spacing = ' ' * len(fn_start)
952    _f('%sc:      *mut xcb_connection_t,', fn_start)
953    _f('%scookie: %s,', spacing, request.ffi_cookie_type)
954    _f('%serror:  *mut *mut xcb_generic_error_t)', spacing)
955    _f('        -> *mut %s;', request.ffi_reply_type)
956
957
958def _ffi_reply_has_fds(self):
959    for field in self.fields:
960        if field.isfd:
961            return True
962    return False
963
964
965def _ffi_reply_fds(request, name):
966    '''
967    Declares the function that returns fds related to the reply.
968    '''
969    _f.section(1)
970    _f('')
971    _f('/// the returned value must be freed by the caller using ' +
972            'libc::free().')
973    fn_start = 'pub fn %s (' % request.ffi_reply_fds_fn
974    spacing = ' ' * len(fn_start)
975    _f('%sc:     *mut xcb_connection_t,', fn_start)
976    _f('%sreply: *mut %s)', spacing, request.ffi_reply_type)
977    _f('        -> *mut c_int;')
978
979
980
981# Rust codegen function
982
983def _rs_type_setup(typeobj, nametup, suffix=()):
984    #assert typeobj.hasattr('ffi_type')
985
986    typeobj.rs_type = _rs_type_name(nametup + suffix)
987
988    if len(nametup) == 1:
989        typeobj.rs_qualified_type = typeobj.rs_type
990    else:
991        module = _ns.ext_name.lower() if _ns.is_ext else 'xproto'
992        typeobj.rs_qualified_type = '%s::%s' % (module, typeobj.rs_type)
993
994    typeobj.rs_iterator_type = _rs_type_name(nametup+('iterator',))
995    typeobj.rs_request_fn = _rs_name(nametup)
996    typeobj.rs_checked_fn = _rs_name(nametup+('checked',))
997    typeobj.rs_unchecked_fn = _rs_name(nametup+('unchecked',))
998
999    typeobj.rs_aux_fn = _rs_name(nametup+('aux',))
1000    typeobj.rs_aux_checked_fn = _rs_name(nametup+('aux', 'checked'))
1001    typeobj.rs_aux_unchecked_fn = _rs_name(nametup+('aux', 'unchecked'))
1002    typeobj.rs_reply_type = _rs_type_name(nametup + ('reply',))
1003    typeobj.rs_cookie_type = _rs_type_name(nametup + ('cookie',))
1004
1005    typeobj.rs_is_pod = False
1006
1007    if typeobj.is_container:
1008        has_complex = False
1009        for field in typeobj.fields:
1010            _rs_type_setup(field.type, field.field_type)
1011            if field.type.is_list:
1012                _rs_type_setup(field.type.member, field.field_type)
1013            field.rs_field_name = _symbol(_rs_field_name(field.field_name))
1014            field.rs_field_type = _rs_type_name(field.field_type)
1015
1016            field.rs_iterator_type = _rs_type_name(
1017                    field.field_type + ('iterator',))
1018
1019            if not field.type.is_simple and not field.type.rs_is_pod \
1020                    and not field.type.is_pad:
1021                has_complex = True
1022
1023        typeobj.rs_only_has_simple = not has_complex
1024        # we restrict POD a little
1025        typeobj.rs_is_pod = (
1026                (not has_complex) and
1027                (not typeobj.rs_qualified_type in _rs_typedef_exceptions) and
1028                (not typeobj.is_reply and not typeobj.is_union) and
1029                (not typeobj.is_switch))
1030
1031        if typeobj.rs_is_pod:
1032            _set_type_lifetime(typeobj, False)
1033
1034
1035
1036def _rs_struct(typeobj):
1037    _r.section(1)
1038    _r('')
1039    _write_doc_brief_desc(_r, typeobj.doc)
1040    if typeobj.rs_is_pod:
1041        _r('#[derive(Copy, Clone)]')
1042        _r('pub struct %s {', typeobj.rs_type)
1043        _r('    pub base: %s,', typeobj.ffi_type)
1044        _r('}')
1045    else:
1046        has_lifetime = typeobj.rs_type in types_with_lifetime
1047        lifetime1 = "<'a>" if has_lifetime else ""
1048        lifetime2 = "'a, " if has_lifetime else ""
1049
1050        _r("pub type %s%s = base::StructPtr<%s%s>;", typeobj.rs_type, lifetime1,
1051                lifetime2, typeobj.ffi_type)
1052
1053
1054def _rs_accessors(typeobj):
1055
1056    has_lifetime = typeobj.rs_type in types_with_lifetime
1057    lifetime = "<'a>" if has_lifetime else ""
1058
1059    _r.section(1)
1060    _r('')
1061    _r('impl%s %s%s {', lifetime, typeobj.rs_type, lifetime)
1062    with _r.indent_block():
1063        if typeobj.rs_is_pod:
1064            # POD structs have a new method
1065            fnstart = 'pub fn new('
1066            fnspace = ' '*len(fnstart)
1067            argfields = []
1068            for f in typeobj.fields:
1069                if not f.type.is_pad:
1070                    argfields.append(f)
1071            maxfieldlen = 0
1072            for f in typeobj.fields:
1073                maxfieldlen = max(maxfieldlen, len(f.rs_field_name))
1074            if len(argfields):
1075                eol = ',' if len(argfields) > 1 else ')'
1076                f1 = argfields[0]
1077                space1 = ' '*(maxfieldlen - len(f1.rs_field_name))
1078                _r('#[allow(unused_unsafe)]')
1079                _r('%s%s: %s%s%s', fnstart, f1.rs_field_name, space1, f1.rs_field_type, eol)
1080                for (i, f) in enumerate(argfields[1:]):
1081                    argspace = ' '*(maxfieldlen-len(f.rs_field_name))
1082                    eol = ',' if i < len(argfields)-2 else ')'
1083                    _r('%s%s: %s%s%s', fnspace, f.rs_field_name, argspace, f.rs_field_type, eol)
1084                _r('        -> %s {', typeobj.rs_type)
1085            else:
1086                _r('#[allow(unused_unsafe)]')
1087                _r('%s) -> %s {', fnstart, typeobj.rs_type)
1088
1089            with _r.indent_block():
1090                _r('unsafe {')
1091                with _r.indent_block():
1092                    _r('%s {', typeobj.rs_type)
1093                    with _r.indent_block():
1094                        _r('base: %s {', typeobj.ffi_type)
1095                        with _r.indent_block():
1096                            for f in typeobj.fields:
1097                                space = ' '*(maxfieldlen-len(f.rs_field_name))
1098                                if f.type.rs_is_pod:
1099                                    _r('%s: %sstd::mem::transmute(%s),', f.rs_field_name, space, f.rs_field_name)
1100                                elif f.type.is_pad:
1101                                    fval = '0'
1102                                    if f.has_subscript:
1103                                        fval = '[0; %d]' % f.type.nmemb
1104                                    _r('%s: %s%s,', f.rs_field_name, space, fval)
1105                                else:
1106                                    assignment = f.rs_field_name
1107                                    if f.rs_field_type == 'bool':
1108                                        assignment = 'if %s { 1 } else { 0 }' % f.rs_field_name
1109                                    _r('%s: %s%s,', f.ffi_field_name, space, assignment)
1110                        _r('}')
1111                    _r('}')
1112                _r('}')
1113            _r('}')
1114
1115        for (i, field) in enumerate(typeobj.fields):
1116            if field.visible and not field.type.is_switch:
1117                for d in typeobj.doc.fields[field.field_name]:
1118                    _r('/// %s', d)
1119                if typeobj.is_union:
1120                    _rs_union_accessor(typeobj, field)
1121                else:
1122                    _rs_accessor(typeobj, field)
1123    _r('}')
1124
1125
1126def _rs_reply_accessors(reply):
1127    '''
1128    same as _rs_accessors but handles fds special case
1129    '''
1130    has_lifetime = reply.rs_type in types_with_lifetime
1131    lifetime = "<'a>" if has_lifetime else ""
1132
1133    fd_field = None
1134    nfd_field = None
1135    for f in reply.fields:
1136        if f.rs_field_name == 'nfd':
1137            nfd_field = f
1138        if f.isfd:
1139            fd_field = f
1140
1141    reply_fields = []
1142    for f in reply.fields:
1143        if f.rs_field_name == 'nfd':
1144            # writing nfd field only if fds is not written
1145            if not fd_field or not nfd_field:
1146                reply_fields.append(f)
1147        elif not f.isfd:
1148            reply_fields.append(f)
1149
1150
1151    _r.section(1)
1152    _r('')
1153    _r('impl%s %s%s {', lifetime, reply.rs_type, lifetime)
1154    with _r.indent_block():
1155        # regular fields
1156        for field in reply_fields:
1157            if field.visible and not field.type.is_switch:
1158                _rs_accessor(reply, field)
1159
1160        # fds field if any
1161        if nfd_field and fd_field:
1162            getter = reply.request.ffi_reply_fds_fn
1163            # adding 's'
1164            fname = fd_field.rs_field_name
1165            if not fname.endswith('s'):
1166                fname += 's'
1167            _r('pub fn %s(&self, c: &base::Connection) -> &[i32] {', fname)
1168            with _r.indent_block():
1169                _r('unsafe {')
1170                with _r.indent_block():
1171                    _r('let nfd = (*self.ptr).nfd as usize;')
1172                    _r('let ptr = %s(c.get_raw_conn(), self.ptr);', getter)
1173                    _r('')
1174                    _r('std::slice::from_raw_parts(ptr, nfd)')
1175                _r('}')
1176            _r('}')
1177    _r('}')
1178
1179
1180def _rs_union_accessor(typeobj, field):
1181    if field.type.is_simple or field.type.rs_is_pod:
1182        _r('pub fn %s(&self) -> %s {', field.rs_field_name, field.rs_field_type)
1183        with _r.indent_block():
1184            _r('unsafe {')
1185            with _r.indent_block():
1186                convert = ''
1187                if field.rs_field_type == 'bool':
1188                    convert = ' != 0'
1189                _r('let _ptr = self.data.as_ptr() as *const %s;', field.rs_field_type)
1190                _r('*_ptr%s', convert)
1191            _r('}')
1192        _r('}')
1193        _r('pub fn from_%s(%s: %s) -> %s {', field.rs_field_name,
1194                field.rs_field_name, field.rs_field_type, typeobj.rs_type)
1195        with _r.indent_block():
1196            _r('unsafe {')
1197            with _r.indent_block():
1198                if field.rs_field_type == 'bool':
1199                    _r('let %s: %s = %s != 0;', field.rs_field_name,
1200                            field.ffi_field_type, field.rs_field_name)
1201                _r('let mut res = %s { data: [0; %d] };', typeobj.rs_type,
1202                        typeobj.union_num_bytes)
1203                _r('let res_ptr = res.data.as_mut_ptr() as *mut %s;', field.rs_field_type)
1204                _r('*res_ptr = %s;', field.rs_field_name)
1205                _r('res')
1206            _r('}')
1207        _r('}')
1208
1209
1210    elif field.type.is_list and field.type.fixed_size():
1211        assert (typeobj.union_num_bytes % field.type.size) == 0
1212        _r('pub fn %s(&self) -> &[%s] {',
1213                field.rs_field_name, field.rs_field_type)
1214        with _r.indent_block():
1215            _r('unsafe {')
1216            with _r.indent_block():
1217                _r('let ptr = self.data.as_ptr() as *const %s;', field.rs_field_type)
1218                _r('std::slice::from_raw_parts(ptr, %d)',
1219                        typeobj.union_num_bytes / field.type.size)
1220            _r('}')
1221        _r('}')
1222        _r('pub fn from_%s(%s: [%s; %d]) -> %s {', field.rs_field_name,
1223                field.rs_field_name, field.rs_field_type,
1224                typeobj.union_num_bytes / field.type.size,
1225                typeobj.rs_type)
1226        with _r.indent_block():
1227            _r('unsafe {')
1228            with _r.indent_block():
1229                _r('%s { data: std::mem::transmute(%s) }', typeobj.rs_type,
1230                        field.rs_field_name)
1231            _r('}')
1232        _r('}')
1233
1234
1235    elif field.type.is_container:
1236        if not field.type.rs_is_pod:
1237            _r('pub fn %s<\'a>(&\'a self) -> %s<\'a> {',
1238                    field.rs_field_name, field.rs_field_type)
1239        else:
1240            _r('pub fn %s(&self) -> %s {', field.rs_field_name, field.rs_field_type)
1241
1242        with _r.indent_block():
1243            _r('unsafe {')
1244            with _r.indent_block():
1245                if not field.type.rs_is_pod:
1246                    _r('std::mem::transmute(self)')
1247                else:
1248                    _r('let _ptr = self.data.as_ptr() as *const %s;', field.rs_field_type)
1249                    _r('*_ptr')
1250            _r('}')
1251        _r('}')
1252
1253
1254
1255def _rs_accessor(typeobj, field, disable_pod_acc=False):
1256    if field.type.is_simple or field.type.rs_is_pod:
1257        _r('pub fn %s(&self) -> %s {', field.rs_field_name,
1258                field.rs_field_type)
1259
1260        acc = '(*self.ptr)'
1261        if typeobj.rs_is_pod and not disable_pod_acc:
1262            acc = 'self.base'
1263
1264        with _r.indent_block():
1265            convert = ''
1266            if field.rs_field_type == 'bool':
1267                convert = ' != 0'
1268            _r('unsafe {')
1269            with _r.indent_block():
1270                if field.type.rs_is_pod:
1271                    _r('std::mem::transmute(%s.%s)', acc, field.ffi_field_name)
1272                else:
1273                    _r('%s.%s%s', acc, field.ffi_field_name, convert)
1274            _r('}')
1275        _r('}')
1276
1277    elif field.type.is_union:
1278        # do we already have a lifetime declared?
1279        has_lifetime = typeobj.rs_type in types_with_lifetime
1280        lifetime = "<'a>" if not has_lifetime else ""
1281        _r("pub fn %s%s(&'a self) -> &'a %s {", field.rs_field_name, lifetime,
1282                field.rs_field_type)
1283        with _r.indent_block():
1284            _r('unsafe {')
1285            with _r.indent_block():
1286                _r('&(*self.ptr).%s', field.ffi_field_name)
1287            _r('}')
1288        _r('}')
1289
1290    elif field.type.is_list and not field.type.fixed_size():
1291        if field.type.member.rs_type == 'bool':
1292            # special case for bool: we need to convert all elements into an owned vec
1293            _r('pub fn %s(&self) -> Vec<bool> {', field.rs_field_name)
1294            with _r.indent_block():
1295                _r('unsafe {')
1296                with _r.indent_block():
1297                    _r('let field = self.ptr;')
1298                    _r('let len = %s(field);', field.ffi_length_fn)
1299                    _r('let data = %s(field);', field.ffi_accessor_fn)
1300                    _r('let slice = std::slice::from_raw_parts(data, len as usize);')
1301                    _r('slice.iter().map(|el| if *el == 0 {false} else{true}).collect()')
1302                _r('}')
1303            _r('}')
1304        elif field.type.member.is_simple:
1305            field_type = field.type.member.rs_type
1306            is_template = False
1307            if field_type == 'c_char':
1308                return_type = '&str'
1309            elif field_type == 'c_void':
1310                is_template = True
1311                return_type = '&[T]'
1312            else:
1313                return_type = '&[%s]' % field_type
1314            _r('pub fn %s%s(&self) -> %s {', field.rs_field_name,
1315                    '<T>' if is_template else '', return_type)
1316            with _r.indent_block():
1317                _r('unsafe {')
1318                with _r.indent_block():
1319                    _r('let field = self.ptr;')
1320                    _r('let len = %s(field) as usize;', field.ffi_length_fn)
1321                    _r('let data = %s(field);', field.ffi_accessor_fn)
1322                    if field_type == 'c_char':
1323                        _r('let slice = ' +
1324                            'std::slice::from_raw_parts(' +
1325                                'data as *const u8, len);')
1326                        _r('// should we check what comes from X?')
1327                        _r('std::str::from_utf8_unchecked(&slice)')
1328                    elif is_template:
1329                        _r('debug_assert_eq!(len %% std::mem::size_of::<T>(), 0);')
1330                        _r('std::slice::from_raw_parts(data as *const T, ' +
1331                                'len / std::mem::size_of::<T>())')
1332                    else:
1333                        _r('std::slice::from_raw_parts(data, len)')
1334                _r('}')
1335            _r('}')
1336        else:
1337            lifetime = ""
1338            if field.rs_iterator_type in types_with_lifetime and \
1339                    typeobj.rs_type in types_with_lifetime:
1340                lifetime = "<'a>"
1341            _r('pub fn %s(&self) -> %s%s {',
1342                    field.rs_field_name, field.rs_iterator_type, lifetime)
1343            with _r.indent_block():
1344                _r('unsafe {')
1345                with _r.indent_block():
1346                    _r('%s(self.ptr)', field.ffi_iterator_fn)
1347                _r('}')
1348            _r('}')
1349            pass
1350
1351    elif field.type.is_list:
1352        _r('pub fn %s(&self) -> &[%s] {',
1353                field.rs_field_name, field.rs_field_type)
1354        with _r.indent_block():
1355            _r('unsafe {')
1356            with _r.indent_block():
1357                _r('&(*self.ptr).%s', field.ffi_field_name)
1358            _r('}')
1359        _r('}')
1360
1361    elif field.type.is_container:
1362        _r('pub fn %s(&self) -> %s {',
1363                field.rs_field_name, field.rs_field_type)
1364        with _r.indent_block():
1365            _r('unsafe {')
1366            with _r.indent_block():
1367                _r('std::mem::transmute(&(*self.ptr).%s)',
1368                        field.ffi_field_name)
1369            _r('}')
1370        _r('}')
1371
1372    elif not field.type.is_pad:
1373        raise Exception('did not treat accessor %s.%s'
1374                % (typeobj.ffi_type, field.ffi_field_name))
1375
1376
1377
1378def _rs_iterator(typeobj):
1379
1380    has_lifetime = typeobj.rs_iterator_type in types_with_lifetime
1381    lifetime1 = "<'a>" if has_lifetime else ""
1382    lifetime2 = "'a, " if has_lifetime else ""
1383    return_expr = '*data'
1384    if typeobj.rs_is_pod:
1385        return_expr = 'std::mem::transmute(*data)'
1386    elif typeobj.is_container and not typeobj.is_union:
1387        return_expr = 'std::mem::transmute(data)'
1388
1389    _r.section(1)
1390    _r('')
1391    _r("pub type %s%s = %s%s;",
1392            typeobj.rs_iterator_type, lifetime1, typeobj.ffi_iterator_type, lifetime1)
1393
1394    _r('')
1395    _r("impl%s Iterator for %s%s {", lifetime1, typeobj.rs_iterator_type, lifetime1)
1396    _r("    type Item = %s%s;", typeobj.rs_type, lifetime1)
1397    _r("    fn next(&mut self) -> std::option::Option<%s%s> {",
1398            typeobj.rs_type, lifetime1)
1399    _r('        if self.rem == 0 { None }')
1400    _r('        else {')
1401    _r('            unsafe {')
1402    _r('                let iter = self as *mut %s;',
1403            typeobj.ffi_iterator_type)
1404    _r('                let data = (*iter).data;')
1405    _r('                %s(iter);', typeobj.ffi_next_fn)
1406    _r('                Some(%s)', return_expr)
1407    _r('            }')
1408    _r('        }')
1409    _r('    }')
1410    _r('}')
1411
1412
1413
1414def _rs_reply(request):
1415
1416    _r.section(1)
1417    _r('')
1418    _r('pub type %s = base::Reply<%s>;', request.rs_reply_type, request.ffi_reply_type);
1419
1420
1421
1422
1423# Common codegen utilities
1424
1425def _prepare_doc(typeobj):
1426    # preparing doc for easier handling
1427    # each typeobj must have a doc attribute with brief, description and fields
1428
1429    def rework_phrase(phrase):
1430        # having 'unknown start of token' error by rustdoc sometimes.
1431        # This silents it
1432        # return phrase.replace('`', '')
1433        # Edit: not necessary anymore
1434        return phrase
1435
1436    if hasattr(typeobj, "doc_prepared"):
1437        assert typeobj.doc_prepared == True
1438        return
1439    if hasattr(typeobj, "doc") and typeobj.doc:
1440        if typeobj.doc.brief:
1441            typeobj.doc.brief = [rework_phrase(p) for p in typeobj.doc.brief.split('\n')]
1442        else:
1443            typeobj.doc.brief = []
1444        if typeobj.doc.description:
1445            typeobj.doc.description = [rework_phrase(p) for p in typeobj.doc.description.split('\n')]
1446        else:
1447            typeobj.doc.description = []
1448        if hasattr(typeobj, "fields"):
1449            if not hasattr(typeobj.doc, "fields"):
1450                typeobj.doc.fields = {}
1451            for f in typeobj.fields:
1452                if f.field_name in typeobj.doc.fields:
1453                    typeobj.doc.fields[f.field_name] = \
1454                            [rework_phrase(p) for p in typeobj.doc.fields[f.field_name].split('\n')]
1455                else:
1456                    typeobj.doc.fields[f.field_name] = []
1457    else:
1458        class Doc(object): pass
1459        typeobj.doc = Doc()
1460        typeobj.doc.brief = []
1461        typeobj.doc.description = []
1462        typeobj.doc.fields = {}
1463        if hasattr(typeobj, "fields"):
1464            for f in typeobj.fields:
1465                typeobj.doc.fields[f.field_name] = []
1466
1467    typeobj.doc_prepared = True
1468
1469
1470def _write_docs(sf, doclist):
1471    for s in doclist:
1472        sf('/// %s', s)
1473
1474
1475def _write_doc_brief_desc(sf, doc):
1476    _write_docs(sf, doc.brief)
1477    if len(doc.brief) and len(doc.description):
1478        sf('///')
1479    _write_docs(sf, doc.description)
1480
1481
1482class EnumCodegen(object):
1483
1484    namecount = {}
1485
1486    def build_collision_table(module):
1487        for v in module.types.values():
1488            key = _ffi_type_name(v[0])
1489            EnumCodegen.namecount[key] = (
1490                (EnumCodegen.namecount.get(key) or 0) + 1
1491            )
1492
1493
1494    def __init__(self, nametup, doc):
1495        self._nametup = nametup
1496        self._doc = doc
1497
1498        self.done_vals = {}
1499        self.unique_discriminants = []
1500        self.conflicts = []
1501        self.all_discriminants = []
1502        key = _ffi_type_name(nametup)
1503        if EnumCodegen.namecount[key] > 1:
1504            nametup = nametup + ('enum',)
1505        self.ffi_name = _ffi_type_name(nametup)
1506        self.rs_name = _rs_type_name(nametup)
1507
1508
1509    def add_discriminant(self, name, val):
1510        class Discriminant: pass
1511        d = Discriminant()
1512        #d.rs_name = name
1513        d.rs_name = _rs_const_name(self._nametup+(name,))
1514        d.ffi_name = _ffi_const_name(self._nametup+(name,))
1515        d.valstr = '0x%02x' % val
1516        d.val = val
1517        d.doc = None
1518        if self._doc and name in self._doc.fields:
1519            d.doc = self._doc.fields[name]
1520        self.all_discriminants.append(d)
1521        if val in self.done_vals:
1522            self.conflicts.append(d)
1523        else:
1524            self.done_vals[val] = d
1525            self.unique_discriminants.append(d)
1526
1527
1528    def maxlen(self, name_field):
1529        maxnamelen = 0
1530        maxvallen = 0
1531        for d in self.unique_discriminants:
1532            maxvallen = max(maxvallen, len(d.valstr))
1533            maxnamelen = max(maxnamelen, len(getattr(d, name_field)))
1534        return (maxnamelen, maxvallen)
1535
1536
1537    def write_ffi(self):
1538        (maxnamelen, maxvallen) = self.maxlen('ffi_name')
1539        type_name = self.ffi_name
1540        _f.section(0)
1541        _f('')
1542        _write_doc_brief_desc(_f, self._doc)
1543        _f('pub type %s = u32;', type_name)
1544        for d in self.all_discriminants:
1545            d_name = d.ffi_name
1546            namespace = ' ' * (maxnamelen-len(d_name))
1547            valspace = ' ' * (maxvallen-len(d.valstr))
1548            if d.doc:
1549                ddocs = d.doc.split('\n')
1550                for dd in ddocs:
1551                    _f('/// %s', dd)
1552            _f('pub const %s%s: %s =%s %s;', d_name, namespace, type_name,
1553                    valspace, d.valstr)
1554
1555    def write_rs(self):
1556        (maxnamelen, maxvallen) = self.maxlen("rs_name")
1557        _r.section(0)
1558        _r('')
1559        _write_doc_brief_desc(_r, self._doc)
1560        _r('pub type %s = u32;', self.rs_name)
1561        for d in self.all_discriminants:
1562            namespace = ' ' * (maxnamelen-len(d.rs_name))
1563            valspace = ' ' * (maxvallen-len(d.valstr))
1564            if d.doc:
1565                ddocs = d.doc.split('\n')
1566                for dd in ddocs:
1567                    _r('/// %s', dd)
1568            _r('pub const %s%s: %s =%s %s;', d.rs_name, namespace, self.rs_name,
1569                    valspace, d.valstr)
1570
1571
1572
1573
1574
1575class RequestCodegen(object):
1576
1577    def __init__(self, request):
1578        self.request = request
1579
1580        self.void = False if self.request.reply else True
1581
1582        self.ffi_cookie_type = ('xcb_void_cookie_t' if self.void
1583                else self.request.ffi_cookie_type)
1584        self.rs_cookie_type = ('base::VoidCookie' if self.void
1585                else self.request.rs_cookie_type)
1586
1587        self.visible_fields = []
1588        for field in self.request.fields:
1589            if field.visible:
1590                self.visible_fields.append(field)
1591
1592        # for, we do not filter out any visible field,
1593        # but we must find out if it is pointer, const ...
1594        self.ffi_params = []
1595        for field in self.visible_fields:
1596            self.ffi_params.append(field)
1597
1598
1599        # Rust is more complicated because of lists
1600        # here we pack lists in slices
1601
1602        # there's basically 3 cases:
1603        # 1. regular fields, passed as-is to the ffi func
1604        # 2. masked lists (such as create_window event mask)
1605        #    given to rs slice of tuple (mask, value) and unpacked
1606        #    into int and pointer to ffi func
1607        # 3. regular lists, for which a length and a pointer
1608        #    must be passed to the ffi_func. these are given to
1609        #    rs by a slice
1610
1611        # it happens to have 2 or more lists for same length field.
1612        # in this case, we will make 2 slices and runtime assert same length
1613        # eg: take a look at render::create_conical_gradient
1614
1615        rs_num_template = 0
1616        template_letters = ['T', 'U', 'V', 'W']
1617
1618        # xproto::send_event is special.
1619        # the FFI takes an event argument casted to a char*
1620        # here we are going to require an &Event<T> for the rs func
1621        self.rs_send_event = False
1622        if _ns.header == "xproto" and \
1623                self.request.rs_request_fn.startswith("send_event"):
1624            self.rs_send_event = True
1625
1626        for f in self.visible_fields:
1627            f.rs_is_slice = False
1628            f.rs_template_let = ''
1629            f.rs_lenfield = None
1630            f.rs_is_mask_slice = False
1631            f.rs_maskfield = None
1632            f.rs_skip = False
1633
1634        for (ffi_index, field) in enumerate(self.visible_fields):
1635            field.ffi_index = ffi_index
1636
1637            if self.rs_send_event and field.rs_field_name == "event":
1638                field.rs_template_let = template_letters[rs_num_template]
1639                rs_num_template += 1
1640
1641            elif field.type.is_list:
1642
1643                if field.type.expr.bitfield:
1644                    # field associated with a mask
1645                    # eg. create_window last field
1646                    field.rs_is_mask_slice = True
1647                else:
1648                    # regular list with length and ptr
1649                    field.rs_is_slice = True
1650                    if field.type.member.rs_type == 'c_void':
1651                        field.rs_template_let = template_letters[rs_num_template]
1652                        rs_num_template += 1
1653                field.rs_lenfield = field.type.expr.lenfield
1654                if not field.rs_lenfield:
1655                    len_name = field.type.expr.lenfield_name
1656                    for f in self.visible_fields:
1657                        if f.field_name == len_name:
1658                            field.rs_lenfield = f
1659                # the mask is mandatory, but not the length (eg c strings)
1660                if field.rs_is_mask_slice:
1661                    assert field.rs_lenfield
1662                if field.rs_lenfield:
1663                    field.rs_lenfield.rs_skip = True
1664
1665        self.rs_params = []
1666
1667        for field in self.visible_fields:
1668            if not field.rs_skip:
1669                self.rs_params.append(field)
1670
1671        self.rs_template = "<'a>"
1672        if rs_num_template:
1673            self.rs_template = "<'a"
1674            for i in range(rs_num_template):
1675                self.rs_template += ', ' + template_letters[i]
1676            self.rs_template += '>'
1677
1678    def ffi_func_name(self, regular, aux):
1679        checked = self.void and not regular
1680        unchecked = not self.void and not regular
1681
1682        if checked:
1683            func_name = (self.request.ffi_checked_fn if not aux else
1684                    self.request.ffi_aux_checked_fn)
1685        elif unchecked:
1686            func_name = (self.request.ffi_unchecked_fn if not aux else
1687                    self.request.ffi_aux_unchecked_fn)
1688        else:
1689            func_name = (self.request.ffi_request_fn if not aux else
1690                    self.request.ffi_aux_fn)
1691
1692        return func_name
1693
1694
1695
1696    def rs_func_name(self, regular, aux):
1697        checked = self.void and not regular
1698        unchecked = not self.void and not regular
1699
1700        if checked:
1701            func_name = (self.request.rs_checked_fn if not aux else
1702                    self.request.rs_aux_checked_fn)
1703        elif unchecked:
1704            func_name = (self.request.rs_unchecked_fn if not aux else
1705                    self.request.rs_aux_unchecked_fn)
1706        else:
1707            func_name = (self.request.rs_request_fn if not aux else
1708                    self.request.rs_aux_fn)
1709
1710        return func_name
1711
1712    def ffi_rq_type(self, field, aux):
1713        ffi_rq_type = field.ffi_field_type
1714        if field.ffi_need_pointer:
1715            pointer = '*const ' if field.ffi_need_const else '*mut '
1716            ffi_rq_type = pointer + ffi_rq_type
1717        if field.type.ffi_need_serialize and not aux:
1718            ffi_rq_type = '*const c_void'
1719        return ffi_rq_type
1720
1721
1722
1723    def write_ffi_rs(self, regular, aux=False):
1724        self.write_ffi(regular, aux)
1725        self.write_rs(regular, aux)
1726
1727
1728    def write_ffi(self, regular, aux=False):
1729
1730        ffi_func_name = self.ffi_func_name(regular, aux)
1731
1732        maxnamelen = 1
1733        for p in self.ffi_params:
1734            maxnamelen = max(maxnamelen, len(p.ffi_field_name))
1735
1736        _f.section(1)
1737        _f("")
1738        _write_doc_brief_desc(_f, self.request.doc)
1739        fn_start = "pub fn %s (" % ffi_func_name
1740        func_spacing = ' ' * len(fn_start)
1741        spacing = " " * (maxnamelen-len('c'))
1742        eol = ',' if len(self.ffi_params) else ')'
1743        _f("%sc: %s*mut xcb_connection_t%s", fn_start, spacing, eol)
1744
1745        for (i, p) in enumerate(self.ffi_params):
1746            ffi_rq_type = self.ffi_rq_type(p, aux)
1747
1748            spacing = ' '*(maxnamelen-len(p.ffi_field_name))
1749            eol = ')' if i == (len(self.ffi_params)-1) else ','
1750            _f('%s%s: %s%s%s', func_spacing, p.ffi_field_name, spacing,
1751                    ffi_rq_type, eol)
1752
1753        _f("        -> %s;", self.ffi_cookie_type)
1754
1755
1756    def write_rs(self, regular, aux=False):
1757        checked = (self.void and not regular) \
1758                or ((not self.void) and regular)
1759        rs_func_name = self.rs_func_name(regular, aux)
1760        ffi_func_name = self.ffi_func_name(regular, aux)
1761
1762        maxnamelen = len('c')
1763        for p in self.rs_params:
1764            maxnamelen = max(maxnamelen, len(p.rs_field_name))
1765
1766        let_lines = []
1767        call_params = []
1768
1769        _r.section(1)
1770        _r('')
1771        _write_doc_brief_desc(_r, self.request.doc)
1772        doc_params = False
1773        for f in self.rs_params:
1774            if len(self.request.doc.fields[f.field_name]):
1775                doc_params = True
1776                break
1777        if doc_params:
1778            _r('///')
1779            _r('/// parameters:')
1780            _r('///')
1781            _r('///   - __c__:')
1782            _r('///       The connection object to the server')
1783            for f in self.rs_params:
1784                _r('///')
1785                _r('///   - __%s__:', f.field_name)
1786                for fd in self.request.doc.fields[f.field_name]:
1787                    _r('///       %s', fd)
1788        fn_start = "pub fn %s%s(" % (rs_func_name, self.rs_template)
1789        func_spacing = ' ' * len(fn_start)
1790        eol = ',' if len(self.rs_params) else ')'
1791        spacing = ' ' * (maxnamelen-len('c'))
1792        _r("%sc%s: &'a base::Connection%s", fn_start, spacing, eol)
1793
1794        for (i, p) in enumerate(self.rs_params):
1795
1796            ffi_rq_type = self.ffi_rq_type(p, aux)
1797            rs_typestr = p.rs_field_type
1798
1799            if self.rs_send_event and p.rs_field_name == "event":
1800                rs_typestr = "&base::Event<%s>" % p.rs_template_let
1801                let_lines.append("let event_ptr = " +
1802                    "std::mem::transmute(event.ptr);")
1803                call_params.append((p.ffi_index, "event_ptr"))
1804                pass
1805            elif p.rs_is_mask_slice:
1806
1807                maskfield = p.rs_lenfield
1808                rs_typestr = '&[(%s, %s)]' % (maskfield.rs_field_type,
1809                    p.rs_field_type)
1810
1811                let_lines.append('let mut %s_copy = %s.to_vec();' %
1812                        (p.rs_field_name, p.rs_field_name))
1813                let_lines.append(('let (%s_mask, %s_vec) = ' +
1814                        'base::pack_bitfield(&mut %s_copy);') %
1815                        (p.rs_field_name, p.rs_field_name, p.rs_field_name))
1816                let_lines.append("let %s_ptr = %s_vec.as_ptr();" %
1817                        (p.rs_field_name, p.rs_field_name))
1818
1819                # adding mask field if not already done
1820                # (already done should not happen with masks)
1821                if not next((cp for cp in call_params
1822                            if cp[0] == maskfield.ffi_index), None):
1823                    call_params.append((maskfield.ffi_index, "%s_mask as %s" %
1824                        (p.rs_field_name, maskfield.ffi_field_type)))
1825
1826                # adding actual field
1827                call_params.append((p.ffi_index, '%s_ptr as %s' %
1828                        (p.rs_field_name, ffi_rq_type)))
1829
1830            elif p.rs_is_slice:
1831
1832                if p.type.member.rs_type == 'c_char':
1833                    rs_typestr = '&str'
1834                    let_lines.append('let %s = %s.as_bytes();' %
1835                            (p.rs_field_name, p.rs_field_name))
1836                elif p.type.member.rs_type == 'c_void':
1837                    rs_typestr = '&[%s]' % p.rs_template_let
1838                else:
1839                    rs_typestr = '&[%s]' % rs_typestr
1840
1841                if p.rs_lenfield:
1842                    lenfield = p.rs_lenfield
1843                    # adding len field if not already done
1844                    # (already done can happen with lists)
1845                    if not next((cp for cp in call_params
1846                            if cp[0] == lenfield.ffi_index), None):
1847                        let_lines.append('let %s_len = %s.len();' %
1848                                (p.rs_field_name, p.rs_field_name))
1849                        call_params.append((lenfield.ffi_index,
1850                                "%s_len as %s" %
1851                                (p.rs_field_name, lenfield.ffi_field_type)))
1852
1853                let_lines.append('let %s_ptr = %s.as_ptr();' %
1854                        (p.rs_field_name, p.rs_field_name))
1855                # adding actual field
1856                call_params.append((p.ffi_index, '%s_ptr as %s' %
1857                        (p.rs_field_name, ffi_rq_type)))
1858
1859            elif p.type.is_container and p.ffi_need_pointer:
1860                rs_typestr = 'std::option::Option<%s>' % rs_typestr
1861                let_lines.append('let %s_ptr = match %s {' % (p.rs_field_name,
1862                        p.rs_field_name))
1863                let_lines.append('    Some(p) => p.ptr as %s,' %
1864                        ffi_rq_type)
1865                let_lines.append('    None => std::ptr::null()')
1866                let_lines.append('};')
1867                call_params.append((p.ffi_index, '%s_ptr' % p.rs_field_name))
1868
1869            elif p.type.is_container and not p.type.rs_is_pod:
1870                call_params.append((p.ffi_index, '*(%s.ptr)' %
1871                        p.rs_field_name))
1872
1873            elif p.type.rs_is_pod:
1874                call_params.append((p.ffi_index, '%s.base' %
1875                        p.rs_field_name))
1876            else:
1877                call_params.append((p.ffi_index,
1878                        '%s as %s' % (p.rs_field_name, ffi_rq_type)))
1879
1880            spacing = ' ' * (maxnamelen-len(p.rs_field_name))
1881            eol = ',' if i < (len(self.rs_params)-1) else ')'
1882            _r('%s%s%s: %s%s', func_spacing, p.rs_field_name,
1883                    spacing, rs_typestr, eol)
1884
1885        _r("        -> %s<'a> {", self.rs_cookie_type)
1886
1887        with _r.indent_block():
1888            _r('unsafe {')
1889            with _r.indent_block():
1890                for l in let_lines:
1891                    _r(l)
1892
1893                call_start = 'let cookie = %s(' % ffi_func_name
1894                eol = ',' if len(call_params) else ');'
1895                spacing = ' ' * len(call_start)
1896                _r('%sc.get_raw_conn()%s', call_start, eol)
1897
1898                call_params.sort(key=lambda x: x[0])
1899
1900                for (i, (ffi_ind, p)) in enumerate(call_params):
1901                    eol = ',' if i < (len(call_params)-1) else ');'
1902                    _r('%s%s%s  // %d', spacing, p, eol, ffi_ind)
1903
1904                _r("%s {", self.rs_cookie_type)
1905                _r("    cookie:  cookie,")
1906                _r("    conn:    c,")
1907                _r("    checked: %s", 'true' if checked else 'false')
1908                _r("}")
1909            _r('}')
1910        _r('}')
1911
1912
1913
1914
1915def _opcode(nametup, opcode):
1916    # handle GLX with -1 opcode
1917    optype = 'u8' if int(opcode) >= 0 else 'i8'
1918
1919    ffi_name = _ffi_const_name(nametup)
1920    _f.section(0)
1921    _f('')
1922    _f('pub const %s: %s = %s;', ffi_name, optype, opcode)
1923
1924    rs_name = _rs_const_name(nametup)
1925    _r.section(1)
1926    _r('')
1927    _r('pub const %s: %s = %s;', rs_name, optype, opcode)
1928
1929
1930
1931def _cookie(request):
1932    _f.section(0)
1933    _f('')
1934    _f('#[derive(Copy, Clone)]')
1935    _f('#[repr(C)]')
1936    _f('pub struct %s {', request.ffi_cookie_type)
1937    _f('    pub(crate) sequence: c_uint')
1938    _f('}')
1939
1940    _r("impl base::CookieSeq for %s {", request.ffi_cookie_type)
1941    with _r.indent_block():
1942        _r("fn sequence(&self) -> c_uint { self.sequence }")
1943    _r("}")
1944
1945
1946
1947    _r.section(1)
1948    _r("")
1949    _r("pub type %s<'a> = base::Cookie<'a, %s>;",
1950            request.rs_cookie_type, request.ffi_cookie_type)
1951
1952    cookie = request.rs_cookie_type
1953    reply = request.rs_reply_type
1954    func = request.ffi_reply_fn
1955
1956    _r.section(1)
1957    _r('')
1958    _r("impl<'a> %s<'a> {", cookie)
1959    with _r.indent_block():
1960        _r("pub fn get_reply(self) -> Result<%s, base::GenericError> {", reply)
1961        with _r.indent_block():
1962            _r('unsafe {')
1963            with _r.indent_block():
1964                _r("if self.checked {")
1965                _r("    let mut err: *mut xcb_generic_error_t = "
1966                        + "std::ptr::null_mut();")
1967                _r("    let reply = %s {", reply)
1968                _r("        ptr: %s (self.conn.get_raw_conn(), self.cookie, &mut err)", func)
1969                _r("    };")
1970                _r("    std::mem::forget(self);")
1971                _r("    if err.is_null() { Ok (reply) }")
1972                _r("    else { Err(base::GenericError { ptr: err }) }")
1973                _r("} else {")
1974                _r("    let res = %s {", reply)
1975                _r("        ptr: %s (self.conn.get_raw_conn(), self.cookie, ", func)
1976                _r("                std::ptr::null_mut())")
1977                _r("    };")
1978                _r("    std::mem::forget(self);")
1979                _r("    Ok(res)")
1980                _r("}")
1981            _r('}')
1982        _r('}')
1983    _r('}')
1984
1985
1986
1987
1988def _must_pack_event(event, nametup):
1989    # The generic event structure xcb_ge_event_t has the full_sequence field
1990    # at the 32byte boundary. That's why we've to inject this field into GE
1991    # events while generating the structure for them. Otherwise we would read
1992    # garbage (the internal full_sequence) when accessing normal event fields
1993    # there.
1994    must_pack = False
1995    if (hasattr(event, 'is_ge_event')
1996            and event.is_ge_event
1997            and event.name == nametup):
1998        event_size = 0
1999        for field in event.fields:
2000            if field.type.size != None and field.type.nmemb != None:
2001                event_size += field.type.size * field.type.nmemb
2002            if event_size == 32:
2003                full_sequence = Field(tcard32,
2004                        tcard32.name, 'full_sequence',
2005                        False, True, True)
2006                idx = event.fields.index(field)
2007                event.fields.insert(idx + 1, full_sequence)
2008
2009                # If the event contains any 64-bit extended fields, they need
2010                # to remain aligned on a 64-bit boundary. Adding full_sequence
2011                # would normally break that; force the struct to be packed.
2012                must_pack = any(f.type.size == 8 and f.type.is_simple
2013                        for f in event.fields[(idx+1):])
2014                break
2015
2016    return must_pack
2017
2018
2019def _handle_switch(typeobj, nametup):
2020    if typeobj.is_switch and typeobj.ffi_type not in finished_switch:
2021        finished_switch.append(typeobj.ffi_type)
2022
2023        for bitcase in typeobj.bitcases:
2024            fname = _symbol(bitcase.field_name)
2025            bitcase.ffi_field_name = fname
2026            bitcase.rs_field_name = fname
2027            bitcase.nametup = (bitcase.field_type if bitcase.type.has_name
2028                    else nametup)
2029            _ffi_type_setup(bitcase.type, bitcase.nametup, ())
2030            _rs_type_setup(bitcase.type, bitcase.nametup, ())
2031
2032        _set_type_lifetime(typeobj, True)
2033
2034        _ffi_struct(typeobj)
2035        _rs_struct(typeobj)
2036
2037        for bitcase in typeobj.bitcases:
2038            _ffi_accessors(bitcase.type, bitcase.nametup)
2039            # TODO: rs accessors
2040
2041    if typeobj.is_container:
2042        for f in typeobj.fields:
2043            _prepare_doc(f.type)
2044            _handle_switch(f.type, f.field_type)
2045
2046
2047# codegen drivers
2048
2049def rs_simple(simple, nametup):
2050    '''
2051    simple is SimpleType object
2052    nametup is a name tuple
2053    '''
2054    global current_handler
2055    current_handler = ('simple:  ', nametup)
2056
2057    _prepare_doc(simple)
2058
2059    simple.has_lifetime = False
2060
2061    _ffi_type_setup(simple, nametup)
2062    _f.section(0)
2063    assert len(simple.name) == 1
2064    _f('')
2065    _write_doc_brief_desc(_f, simple.doc)
2066    _f('pub type %s = %s;', simple.ffi_type, simple.name[0])
2067    _ffi_iterator(simple, nametup)
2068
2069    _rs_type_setup(simple, nametup)
2070    _r.section(0)
2071    _r('')
2072    _write_doc_brief_desc(_r, simple.doc)
2073    _r('pub type %s = %s;', simple.rs_type, simple.ffi_type)
2074
2075
2076
2077def rs_enum(typeobj, nametup):
2078    '''
2079    typeobj is xcbgen.xtypes.Enum object
2080    nametup is a name tuple
2081    '''
2082    global current_handler
2083    current_handler = ('enum:    ', nametup)
2084
2085    _prepare_doc(typeobj)
2086
2087    ecg = EnumCodegen(nametup, typeobj.doc)
2088
2089    val = -1
2090    for (enam, eval) in typeobj.values:
2091        val = int(eval) if eval != '' else val+1
2092        ecg.add_discriminant(enam, val)
2093
2094    ecg.write_ffi()
2095    ecg.write_rs()
2096
2097
2098
2099
2100def rs_struct(struct, nametup):
2101    '''
2102    struct is Struct object
2103    nametup is a name tuple
2104    '''
2105    global current_handler
2106    current_handler = ('struct:  ', nametup)
2107
2108    _prepare_doc(struct)
2109
2110    struct.has_lifetime = True
2111
2112    _ffi_type_setup(struct, nametup)
2113    _rs_type_setup(struct, nametup)
2114    _handle_switch(struct, nametup)
2115
2116    _set_type_lifetime(struct, struct.has_lifetime)
2117
2118    _ffi_struct(struct)
2119    _ffi_accessors(struct, nametup)
2120    _ffi_iterator(struct, nametup)
2121
2122    _rs_struct(struct)
2123    _rs_accessors(struct)
2124    _rs_iterator(struct)
2125
2126
2127
2128def rs_union(union, nametup):
2129    '''
2130    union is Union object
2131    nametup is a name tuple
2132    '''
2133    global current_handler
2134    current_handler = ('union:   ', nametup)
2135
2136    _prepare_doc(union)
2137
2138    union.has_lifetime = False
2139
2140    _ffi_type_setup(union, nametup)
2141    _rs_type_setup(union, nametup)
2142
2143    biggest = 1
2144    most_aligned = 1
2145    ptr_size = 8 if sys.maxsize > 2**32 else 4
2146    for field in union.fields:
2147        fs = ptr_size
2148        fa = ptr_size
2149        if field.type.size:
2150            fs = field.type.size
2151            fa = field.type.size
2152        if field.type.nmemb:
2153            fs = fa * field.type.nmemb
2154        biggest = max(biggest, fs)
2155        most_aligned = max(most_aligned, fa)
2156
2157    assert biggest >= most_aligned
2158
2159    num_aligned = int(biggest / most_aligned)
2160    if biggest % most_aligned:
2161        num_aligned += 1
2162
2163    num_bytes = num_aligned * most_aligned
2164    union.union_num_bytes = num_bytes
2165
2166    _f.section(0)
2167    _f('')
2168    _write_doc_brief_desc(_f, union.doc)
2169    _f('// union')
2170    _f('#[repr(C)]')
2171    _f('pub struct %s {', union.ffi_type)
2172    _f('    pub data: [u8; %d]', num_bytes)
2173    _f('}')
2174
2175    _f('')
2176    _f('impl Copy for %s {}', union.ffi_type)
2177    _f('impl Clone for %s {', union.ffi_type)
2178    _f('    fn clone(&self) -> %s { *self }', union.ffi_type)
2179    _f('}')
2180
2181    _ffi_iterator(union, nametup)
2182
2183    _r.section(1)
2184    _r('')
2185    _r('pub type %s = %s;', union.rs_type, union.ffi_type)
2186    _rs_accessors(union)
2187    _rs_iterator(union)
2188
2189
2190
2191def rs_request(request, nametup):
2192    '''
2193    request is Request object
2194    nametup is a name tuple
2195    '''
2196    global current_handler
2197    current_handler = ('request: ', nametup)
2198
2199    _prepare_doc(request)
2200
2201    request.has_lifetime = False
2202
2203    _ffi_type_setup(request, nametup, ('request',))
2204    _rs_type_setup(request, nametup, ('request',))
2205    _handle_switch(request, nametup)
2206
2207    _set_type_lifetime(request, request.has_lifetime)
2208
2209    rcg = RequestCodegen(request)
2210
2211    _opcode(nametup, request.opcode)
2212    _ffi_struct(request)
2213
2214    if request.reply:
2215        _prepare_doc(request.reply)
2216        # enable getting the request from the reply
2217        request.reply.request = request
2218        request.reply.has_lifetime = False
2219
2220        _cookie(request)
2221
2222        _ffi_type_setup(request.reply, nametup, ('reply',))
2223        _rs_type_setup(request.reply, nametup, ('reply',))
2224        _handle_switch(request.reply, nametup)
2225
2226        _set_type_lifetime(request.reply, request.reply.has_lifetime)
2227
2228        _ffi_struct(request.reply)
2229        _ffi_accessors(request.reply, nametup + ('reply',))
2230        _ffi_reply(request)
2231        if _ffi_reply_has_fds(request.reply):
2232            _ffi_reply_fds(request, nametup)
2233
2234        _rs_reply(request)
2235        _rs_reply_accessors(request.reply)
2236
2237    # regular call 'request_name'
2238    rcg.write_ffi_rs(True, False)
2239    # unregular call 'request_name_checked' or 'request_name_unchecked'
2240    # depending on cookie type
2241    rcg.write_ffi_rs(False, False)
2242
2243    if request.ffi_need_aux:
2244        rcg.write_ffi_rs(True, True)
2245        rcg.write_ffi_rs(False, True)
2246
2247
2248def rs_event(event, nametup):
2249    '''
2250    event is Event object
2251    nametup is a name tuple
2252    '''
2253    global current_handler
2254    current_handler = ('event:   ', nametup)
2255
2256    must_pack = _must_pack_event(event, nametup)
2257    # _must_pack_event may insert fields,
2258    # therefore must be called before _prepare_doc
2259    _prepare_doc(event)
2260
2261    if must_pack:
2262        print('event ', nametup, ' is packed')
2263
2264    event.has_lifetime = False
2265
2266    _ffi_type_setup(event, nametup, ('event',))
2267    _rs_type_setup(event, nametup, ('event',))
2268
2269    _set_type_lifetime(event, event.has_lifetime)
2270
2271    _opcode(nametup, event.opcodes[nametup])
2272
2273    _r.section(1)
2274    _r('')
2275    _write_doc_brief_desc(_r, event.doc)
2276    _r('pub type %s = base::Event<%s>;', event.rs_type, event.ffi_type)
2277
2278    if event.name == nametup:
2279        _ffi_struct(event, must_pack)
2280
2281        accessor_fields = []
2282        for f in event.fields:
2283            if not f.visible: continue
2284            accessor_fields.append(f)
2285            if f.type.is_list or f.type.is_switch or f.type.is_bitcase:
2286                try:
2287                    accessor_fields.remove(f.type.expr.lenfield)
2288                except:
2289                    pass
2290
2291        new_params = []
2292        if len(event.opcodes) > 1:
2293            new_params.append('response_type: u8')
2294
2295        _r.section(1)
2296        _r('')
2297        _r('impl %s {', event.rs_type)
2298        with _r.indent_block():
2299            for f in accessor_fields:
2300                for fd in event.doc.fields[f.field_name]:
2301                    _r('/// %s', fd)
2302                _rs_accessor(event, f, True)
2303
2304                rs_ftype = f.rs_field_type
2305                if f.has_subscript:
2306                    rs_ftype = "[%s; %d]" % (rs_ftype, f.type.nmemb)
2307
2308                new_params.append("%s: %s" % (f.rs_field_name, rs_ftype))
2309
2310            _r('/// Constructs a new %s', event.rs_type)
2311            if len(event.opcodes) > 1:
2312                _r('/// `response_type` must be set to one of:')
2313                for opname in event.opcodes:
2314                    _r('///     - `%s`', _rs_const_name(opname))
2315            else:
2316                _r('/// `response_type` will be set automatically to %s',
2317                        _rs_const_name(nametup))
2318            fn_start = "pub fn new("
2319            fn_space = ' ' * len(fn_start)
2320            p = new_params[0] if len(new_params) else ''
2321            eol = ',' if len(new_params)>1 else ')'
2322            _r('%s%s%s', fn_start, p, eol)
2323            for (i, p) in enumerate(new_params[1:]):
2324                eol = ',' if i != len(new_params)-2 else ')'
2325                _r("%s%s%s", fn_space, p, eol)
2326
2327            _r('        -> %s {', event.rs_type)
2328            with _r.indent_block():
2329                _r('unsafe {')
2330                with _r.indent_block():
2331                    _r('let raw = libc::malloc(32 as usize) as *mut %s;',
2332                            event.ffi_type)
2333                    if len(event.opcodes) > 1:
2334                        # build list of possible opcodes
2335                        orlist = ' ||\n                    '.join(
2336                                [('response_type == %s' % _rs_const_name(opname))
2337                                    for opname in event.opcodes])
2338                        _r('assert!(%s,', orlist)
2339                        _r('        "wrong response_type supplied to %s::new");',
2340                                event.rs_type)
2341                        _r('(*raw).response_type = response_type;')
2342                    else:
2343                        _r('(*raw).response_type = %s;', _rs_const_name(nametup))
2344                    for f in event.fields:
2345                        if not f.visible: continue
2346                        if f.type.is_container and not f.type.is_union \
2347                                and not f.type.rs_is_pod:
2348                            _r('(*raw).%s = *%s.ptr;',
2349                                    f.ffi_field_name, f.rs_field_name)
2350
2351                        elif f.type.rs_is_pod:
2352                            _r('(*raw).%s = %s.base;', f.ffi_field_name,
2353                                    f.rs_field_name)
2354
2355                        else:
2356                            assignment = f.rs_field_name
2357                            if f.rs_field_type == 'bool':
2358                                assignment = ('if %s { 1 } else { 0 }' %
2359                                    f.rs_field_name)
2360                            _r('(*raw).%s = %s;', f.ffi_field_name, assignment)
2361                    _r('%s {', event.rs_type)
2362                    _r('    ptr: raw')
2363                    _r('}')
2364                _r('}')
2365            _r('}')
2366        _r('}')
2367
2368
2369    else:
2370        _f.section(0)
2371        _f('')
2372        _f('pub type %s = %s;', _ffi_type_name(nametup+('event',)),
2373                            _ffi_type_name(event.name+('event',)))
2374
2375
2376
2377def rs_error(error, nametup):
2378    '''
2379    error is Error object
2380    nametup is a name tuple
2381    '''
2382    global current_handler
2383    current_handler = ('error:   ', nametup)
2384
2385    _prepare_doc(error)
2386
2387    _ffi_type_setup(error, nametup, ('error',))
2388    _opcode(nametup, error.opcodes[nametup])
2389
2390    if error.name == nametup:
2391        _ffi_struct(error)
2392    else:
2393        _f.section(0)
2394        _f('')
2395        _f('pub type %s = %s;', _ffi_type_name(nametup+('error',)),
2396                            _ffi_type_name(error.name+('error',)))
2397
2398    _rs_type_setup(error, nametup, ('error',))
2399    _r.section(0)
2400    _r('')
2401    _r('pub struct %s {', error.rs_type)
2402    _r('    pub base: base::Error<%s>', error.ffi_type)
2403    _r('}')
2404
2405
2406def usage(program):
2407    print('Usage: {} -o SRCDIR file.xml', program, file=sys.stderr)
2408
2409
2410if __name__ == '__main__':
2411
2412    from optparse import OptionParser
2413
2414    parser = OptionParser(usage="Usage: %prog -o SRCDIR file.xml")
2415    parser.add_option('-o', '--output', dest='srcdir', metavar='SRCDIR',
2416                help='specifies rust src dir where to generate files')
2417
2418    (options, args) = parser.parse_args(sys.argv)
2419
2420    if options.srcdir == None:
2421        parser.error('-o SRCDIR is mandatory')
2422
2423    if not os.path.isdir(options.srcdir):
2424        parser.error('-o SRCDIR must be a directory')
2425
2426    if len(args) < 2:
2427        parser.error('input XML file must be supplied')
2428
2429    output = {  'open'      : rs_open,
2430                'close'     : rs_close,
2431                'simple'    : rs_simple,
2432                'enum'      : rs_enum,
2433                'struct'    : rs_struct,
2434                'union'     : rs_union,
2435                'request'   : rs_request,
2436                'event'     : rs_event,
2437                'error'     : rs_error }
2438    try:
2439        from xcbgen.state import Module
2440        from xcbgen.xtypes import *
2441    except ImportError:
2442        print('failed to load xcbgen', file=sys.stderr)
2443        raise
2444
2445    # Parse the xml header
2446    module = Module(args[1], output)
2447    module.rs_srcdir = options.srcdir
2448
2449    # Build type-registry and resolve type dependencies
2450    module.register()
2451    module.resolve()
2452
2453    # Output the code
2454    try:
2455        module.generate()
2456    except:
2457        print('error occured in handler: ', current_handler, file=sys.stderr)
2458        raise
2459