diff options
Diffstat (limited to 'chromium/ui/gfx/x/gen_xproto.py')
-rw-r--r-- | chromium/ui/gfx/x/gen_xproto.py | 1124 |
1 files changed, 840 insertions, 284 deletions
diff --git a/chromium/ui/gfx/x/gen_xproto.py b/chromium/ui/gfx/x/gen_xproto.py index 4a34901012d..24a2efcc245 100644 --- a/chromium/ui/gfx/x/gen_xproto.py +++ b/chromium/ui/gfx/x/gen_xproto.py @@ -23,15 +23,15 @@ # #include "base/component_export.h" # #include "ui/gfx/x/xproto_types.h" # -# typedef struct _XDisplay XDisplay; -# # namespace x11 { # +# class Connection; +# # class COMPONENT_EXPORT(X11) XProto { # public: -# explicit XProto(XDisplay* display); +# explicit XProto(Connection* connection); # -# XDisplay* display() { return display_; } +# Connection* connection() const { return connection_; } # # struct RGB { # uint16_t red{}; @@ -54,7 +54,7 @@ # Future<QueryColorsReply> QueryColors(const QueryColorsRequest& request); # # private: -# XDisplay* display_; +# Connection* const connection_; # }; # # } // namespace x11 @@ -66,12 +66,13 @@ # #include <xcb/xcb.h> # #include <xcb/xcbext.h> # -# #include "base/logging.h" +# #include "base/notreached.h" +# #include "base/check_op.h" # #include "ui/gfx/x/xproto_internal.h" # # namespace x11 { # -# XProto::XProto(XDisplay* display) : display_(display) {} +# XProto::XProto(Connection* connection) : connection_(connection) {} # # Future<XProto::QueryColorsReply> # XProto::QueryColors( @@ -102,7 +103,7 @@ # Write(&pixels_elem, &buf); # } # -# return x11::SendRequest<XProto::QueryColorsReply>(display_, &buf); +# return x11::SendRequest<XProto::QueryColorsReply>(connection_, &buf); # } # # template<> COMPONENT_EXPORT(X11) @@ -168,6 +169,8 @@ from __future__ import print_function import argparse import collections +import functools +import itertools import os import re import sys @@ -177,61 +180,106 @@ import types # so this global is unavoidable. output = collections.defaultdict(int) -UPPER_CASE_PATTERN = re.compile(r'^[A-Z0-9_]+$') - - -def adjust_type_case(name): - if UPPER_CASE_PATTERN.match(name): - SPECIAL = { - 'ANIMCURSORELT': 'AnimationCursorElement', - 'CA': 'ChangeAlarmAttribute', - 'CHAR2B': 'Char16', - 'CHARINFO': 'CharInfo', - 'COLORITEM': 'ColorItem', - 'COLORMAP': 'ColorMap', - 'CP': 'CreatePictureAttribute', - 'CW': 'CreateWindowAttribute', - 'DAMAGE': 'DamageId', - 'DIRECTFORMAT': 'DirectFormat', - 'DOTCLOCK': 'DotClock', - 'FBCONFIG': 'FbConfig', - 'FLOAT32': 'float', - 'FLOAT64': 'double', - 'FONTPROP': 'FontProperty', - 'GC': 'GraphicsContextAttribute', - 'GCONTEXT': 'GraphicsContext', - 'GLYPHINFO': 'GlyphInfo', - 'GLYPHSET': 'GlyphSet', - 'INDEXVALUE': 'IndexValue', - 'KB': 'Keyboard', - 'KEYCODE': 'KeyCode', - 'KEYCODE32': 'KeyCode32', - 'KEYSYM': 'KeySym', - 'LINEFIX': 'LineFix', - 'OP': 'Operation', - 'PBUFFER': 'PBuffer', - 'PCONTEXT': 'PContext', - 'PICTDEPTH': 'PictDepth', - 'PICTFORMAT': 'PictFormat', - 'PICTFORMINFO': 'PictFormInfo', - 'PICTSCREEN': 'PictScreen', - 'PICTVISUAL': 'PictVisual', - 'POINTFIX': 'PointFix', - 'SEGMENT': 'SEGMENT', - 'SPANFIX': 'SpanFix', - 'SUBPICTURE': 'SubPicture', - 'SYSTEMCOUNTER': 'SystemCounter', - 'TIMECOORD': 'TimeCoord', - 'TIMESTAMP': 'TimeStamp', - 'VISUALID': 'VisualId', - 'VISUALTYPE': 'VisualType', - 'WAITCONDITION': 'WaitCondition', - } - if name in SPECIAL: - return SPECIAL[name] +RENAME = { + 'ANIMCURSORELT': 'AnimationCursorElement', + 'CA': 'ChangeAlarmAttribute', + 'CHAR2B': 'Char16', + 'CHARINFO': 'CharInfo', + 'COLORITEM': 'ColorItem', + 'COLORMAP': 'ColorMap', + 'Connection': 'RandRConnection', + 'CP': 'CreatePictureAttribute', + 'CW': 'CreateWindowAttribute', + 'DAMAGE': 'DamageId', + 'DIRECTFORMAT': 'DirectFormat', + 'DOTCLOCK': 'DotClock', + 'FBCONFIG': 'FbConfig', + 'FLOAT32': 'float', + 'FLOAT64': 'double', + 'FONTPROP': 'FontProperty', + 'GC': 'GraphicsContextAttribute', + 'GCONTEXT': 'GraphicsContext', + 'GLYPHINFO': 'GlyphInfo', + 'GLYPHSET': 'GlyphSet', + 'INDEXVALUE': 'IndexValue', + 'KB': 'Keyboard', + 'KEYCODE': 'KeyCode', + 'KEYCODE32': 'KeyCode32', + 'KEYSYM': 'KeySym', + 'LINEFIX': 'LineFix', + 'OP': 'Operation', + 'PBUFFER': 'PBuffer', + 'PCONTEXT': 'PContext', + 'PICTDEPTH': 'PictDepth', + 'PICTFORMAT': 'PictFormat', + 'PICTFORMINFO': 'PictFormInfo', + 'PICTSCREEN': 'PictScreen', + 'PICTVISUAL': 'PictVisual', + 'POINTFIX': 'PointFix', + 'SPANFIX': 'SpanFix', + 'SUBPICTURE': 'SubPicture', + 'SYSTEMCOUNTER': 'SystemCounter', + 'TIMECOORD': 'TimeCoord', + 'TIMESTAMP': 'Time', + 'VISUALID': 'VisualId', + 'VISUALTYPE': 'VisualType', + 'WAITCONDITION': 'WaitCondition', +} + +READ_SPECIAL = set([ + ('xcb', 'Setup'), +]) + +WRITE_SPECIAL = set([ + ('xcb', 'ClientMessage'), + ('xcb', 'UnmapNotify'), + ('xcb', 'SelectionNotify'), +]) + + +def adjust_type_name(name): + if name in RENAME: + return RENAME[name] + # If there's an underscore, then this is either snake case or upper case. + if '_' in name: return ''.join([ token[0].upper() + token[1:].lower() for token in name.split('_') ]) + if name.isupper(): + name = name.lower() + # Now the only possibilities are caml case and pascal case. It could also + # be snake case with a single word, but that would be same as caml case. + # To convert all of these, just capitalize the first letter. + return name[0].upper() + name[1:] + + +# Given a list of event names like ["KeyPress", "KeyRelease"], returns a name +# suitable for use as a base event like "Key". +def event_base_name(names): + # If there's only one event in this group, the "common name" is just + # the event name. + if len(names) == 1: + return names[0] + + # Handle a few special cases where the longest common prefix is empty: eg. + # EnterNotify/LeaveNotify/FocusIn/FocusOut -> Crossing. + EVENT_NAMES = [ + ('TouchBegin', 'Device'), + ('RawTouchBegin', 'RawDevice'), + ('Enter', 'Crossing'), + ('EnterNotify', 'Crossing'), + ('DeviceButtonPress', 'LegacyDevice'), + ] + for name, rename in EVENT_NAMES: + if name in names: + return rename + + # Use the longest common prefix of the event names as the base name. + name = ''.join( + chars[0] + for chars in itertools.takewhile(lambda chars: len(set(chars)) == 1, + zip(*names))) + assert name return name @@ -251,17 +299,6 @@ class Indent: self.xproto.write(self.closing_line) -class NullContext: - def __init__(self): - pass - - def __enter__(self): - pass - - def __exit__(self, exc_type, exc_value, exc_traceback): - pass - - # Make all members of |obj|, given by |fields|, visible in # the local scope while this class is alive. class ScopedFields: @@ -302,20 +339,37 @@ def safe_name(name): return name -class GenXproto: - def __init__(self, args, xcbgen): +class FileWriter: + def __init__(self): + self.indent = 0 + + # Write a line to the current file. + def write(self, line=''): + indent = self.indent if line and not line.startswith('#') else 0 + print((' ' * indent) + line, file=self.file) + + +class GenXproto(FileWriter): + def __init__(self, proto, proto_dir, gen_dir, xcbgen, all_types): + FileWriter.__init__(self) + # Command line arguments - self.args = args + self.proto = proto + self.xml_filename = os.path.join(proto_dir, '%s.xml' % proto) + self.header_file = open(os.path.join(gen_dir, '%s.h' % proto), 'w') + self.source_file = open(os.path.join(gen_dir, '%s.cc' % proto), 'w') + self.undef_file = open(os.path.join(gen_dir, '%s_undef.h' % proto), + 'w') # Top-level xcbgen python module self.xcbgen = xcbgen + # Types for every module including this one + self.all_types = all_types + # The last used UID for making unique names self.prev_id = -1 - # Current indentation level - self.indent = 0 - # Current file to write to self.file = None @@ -331,7 +385,7 @@ class GenXproto: # Map from type names to a set of types. Certain types # like enums and simple types can alias each other. - self.types = collections.defaultdict(set) + self.types = collections.defaultdict(list) # Set of names of simple types to be replaced with enums self.replace_with_enum = set() @@ -339,10 +393,11 @@ class GenXproto: # Map of enums to their underlying types self.enum_types = collections.defaultdict(set) - # Write a line to the current file. - def write(self, line=''): - indent = self.indent if line and not line.startswith('#') else 0 - print((' ' * indent) + line, file=self.file) + # Map from (XML tag, XML name) to XML element + self.module_names = {} + + # Enums that represent bit masks. + self.bitenums = [] # Geenerate an ID suitable for use in temporary variable names. def new_uid(self, ): @@ -361,57 +416,70 @@ class GenXproto: return '' def rename_type(self, t, name): - name = list(name) - for i in range(1, len(name)): - name[i] = adjust_type_case(name[i]) - name[-1] += self.type_suffix(t) - return name - - # Given an xcbgen.xtypes.Type, returns a C++-namespace-qualified - # string that looks like Input::InputClass::Key. - def qualtype(self, t, name): # Work around a bug in xcbgen: ('int') should have been ('int',) if name == 'int': name = ('int', ) - name = self.rename_type(t, name) + name = list(name) if name[0] == 'xcb': # Use namespace x11 instead of xcb. name[0] = 'x11' - # We want the non-extension X11 structures to live in a class too. - if len(name) == 2: - name[1:1] = ['XProto'] + for i in range(1, len(name)): + name[i] = adjust_type_name(name[i]) + name[-1] += self.type_suffix(t) + return name + + # Given an unqualified |name| like ('Window') and a namespace like ['x11'], + # returns a fully qualified name like ('x11', 'Window'). + def qualify_type(self, name, namespace): + if tuple(namespace + name) in self.all_types: + return namespace + name + return self.qualify_type(name, namespace[:-1]) + + # Given an xcbgen.xtypes.Type, returns a C++-namespace-qualified + # string that looks like Input::InputClass::Key. + def qualtype(self, t, name): + name = self.rename_type(t, name) # Try to avoid adding namespace qualifiers if they're not necessary. chop = 0 for t1, t2 in zip(name, self.namespace): if t1 != t2: break + if self.qualify_type(name[chop + 1:], self.namespace) != name: + break chop += 1 return '::'.join(name[chop:]) def fieldtype(self, field): - return self.qualtype(field.type, field.field_type) + return self.qualtype(field.type, + field.enum if field.enum else field.field_type) + + def switch_fields(self, switch): + fields = [] + for case in switch.bitcases: + if case.field_name: + fields.append(case) + else: + fields.extend(case.type.fields) + return fields def add_field_to_scope(self, field, obj): - if not field.visible or not field.wire: + if not field.visible or (not field.wire and not field.isfd): + return 0 + + field_name = safe_name(field.field_name) + + if field.type.is_switch: + self.write('auto& %s = %s;' % (field_name, obj)) return 0 self.scope.append(field) - field_name = safe_name(field.field_name) - # There's one case where we would have generated: - # auto& enable = enable.enable; - # To prevent a compiler error from trying to use the variable - # in its own definition, save to a temporary variable first. - if field_name == obj: - tmp_id = self.new_uid() - self.write('auto& tmp%d = %s.%s;' % (tmp_id, obj, field_name)) - self.write('auto& %s = tmp%d;' % (field_name, tmp_id)) - elif field.for_list: - self.write('%s %s;' % (self.fieldtype(field), field_name)) + if field.for_list or field.for_switch: + self.write('%s %s{};' % (self.fieldtype(field), field_name)) else: self.write('auto& %s = %s.%s;' % (field_name, obj, field_name)) @@ -432,9 +500,9 @@ class GenXproto: # Work around conflicts caused by Xlib's liberal use of macros. def undef(self, name): - print('#ifdef %s' % name, file=self.args.undeffile) - print('#undef %s' % name, file=self.args.undeffile) - print('#endif', file=self.args.undeffile) + print('#ifdef %s' % name, file=self.undef_file) + print('#undef %s' % name, file=self.undef_file) + print('#endif', file=self.undef_file) def expr(self, expr): if expr.op == 'popcount': @@ -476,16 +544,53 @@ class GenXproto: assert expr.lenfield_name return expr.lenfield_name + def get_xidunion_element(self, name): + key = ('xidunion', name[-1]) + return self.module_names.get(key, None) + + def declare_xidunion(self, xidunion, xidname): + names = [type_element.text for type_element in xidunion] + types = list(set([self.module.get_type(name) for name in names])) + assert len(types) == 1 + value_type = types[0] + value_typename = self.qualtype(value_type, value_type.name) + with Indent(self, 'struct %s {' % xidname, '};'): + self.write('%s() : value{} {}' % xidname) + self.write() + for name in names: + cpp_name = self.module.get_type_name(name) + typename = self.qualtype(value_type, cpp_name) + self.write('%s(%s value) : value{static_cast<%s>(value)} {}' % + (xidname, typename, value_typename)) + self.write( + 'operator %s() const { return static_cast<%s>(value); }' % + (typename, typename)) + self.write() + self.write('%s value{};' % value_typename) + def declare_simple(self, item, name): # The underlying type of an enum must be integral, so avoid defining # FLOAT32 or FLOAT64. Usages are renamed to float and double instead. renamed = tuple(self.rename_type(item, name)) - if name[-1] not in ('FLOAT32', 'FLOAT64' - ) and renamed not in self.replace_with_enum: - self.write( - 'enum class %s : %s {};' % - (adjust_type_case(name[-1]), self.qualtype(item, item.name))) + if (name[-1] in ('FLOAT32', 'FLOAT64') + or renamed in self.replace_with_enum): + return + elif name[-1] == 'FP1616': + # Xcbproto defines FP1616 as uint32_t instead of a struct of + # two 16-bit ints, which is how it's intended to be used. + with Indent(self, 'struct Fp1616 {', '};'): + self.write('int16_t integral;') + self.write('uint16_t frac;') self.write() + return + + xidunion = self.get_xidunion_element(name) + if xidunion: + self.declare_xidunion(xidunion, renamed[-1]) + else: + self.write('enum class %s : %s {};' % + (renamed[-1], self.qualtype(item, item.name))) + self.write() def copy_primitive(self, name): self.write('%s(&%s, &buf);' % @@ -495,28 +600,36 @@ class GenXproto: type_name = self.fieldtype(field) name = safe_name(field.field_name) + def copy_basic(): + self.write('%s %s;' % (type_name, name)) + self.copy_primitive(name) + if name in ('major_opcode', 'minor_opcode'): assert not self.is_read - is_ext = any( - [f.field_name == 'minor_opcode' for f in field.parent.fields]) - if is_ext and name == 'major_opcode': - self.write('// Caller fills in extension major opcode.') - self.write('Pad(&buf, sizeof(%s));' % type_name) + is_ext = self.module.namespace.is_ext + self.write( + '%s %s = %s;' % + (type_name, name, 'info_.major_opcode' if is_ext + and name == 'major_opcode' else field.parent[0].opcode)) + self.copy_primitive(name) + elif name == 'response_type': + if self.is_read: + copy_basic() else: - self.write('%s %s = %s;' % - (type_name, name, field.parent.opcode)) + container_type, container_name = field.parent + assert container_type.is_event + opcode = container_type.opcodes[container_name] + self.write('%s %s = %s;' % (type_name, name, opcode)) self.copy_primitive(name) - elif name in ('response_type', 'sequence', 'extension'): + elif name in ('extension', 'error_code', 'event_type'): assert self.is_read - self.write('%s %s;' % (type_name, name)) - self.copy_primitive(name) + copy_basic() elif name == 'length': if not self.is_read: self.write('// Caller fills in length for writes.') self.write('Pad(&buf, sizeof(%s));' % type_name) else: - self.write('%s %s;' % (type_name, name)) - self.copy_primitive(name) + copy_basic() else: assert field.type.is_expr assert (not isinstance(field.type, self.xcbgen.xtypes.Enum)) @@ -527,48 +640,52 @@ class GenXproto: def declare_case(self, case): assert case.type.is_case != case.type.is_bitcase - with (Indent(self, 'struct {', '} %s;' % safe_name(case.field_name)) - if case.field_name else NullContext()): - for case_field in case.type.fields: - self.declare_field(case_field) - - def copy_case(self, case, switch_var): - op = 'CaseEq' if case.type.is_case else 'BitAnd' + fields = [ + field for case_field in case.type.fields + for field in self.declare_field(case_field) + ] + if not case.field_name: + return fields + name = safe_name(case.field_name) + with Indent(self, 'struct %s_t {' % name, '};'): + for field in fields: + self.write('%s %s{};' % field) + return [(name + '_t', name)] + + def copy_case(self, case, switch_name): + op = 'CaseEq' if case.type.is_case else 'CaseAnd' condition = ' || '.join([ - '%s(%s, %s)' % (op, switch_var, self.expr(expr)) + '%s(%s_expr, %s)' % (op, switch_name, self.expr(expr)) for expr in case.type.expr ]) with Indent(self, 'if (%s) {' % condition, '}'): - with (ScopedFields(self, case.field_name, case.type.fields) - if case.field_name else NullContext()): + if case.field_name: + fields = [case] + obj = '(*%s.%s)' % (switch_name, safe_name(case.field_name)) + else: + fields = case.type.fields + obj = '*' + switch_name + for case_field in fields: + name = safe_name(case_field.field_name) + if case_field.visible and self.is_read: + self.write('%s.%s.emplace();' % (switch_name, name)) + with ScopedFields(self, obj, case.type.fields): for case_field in case.type.fields: - assert case_field.wire self.copy_field(case_field) def declare_switch(self, field): - t = field.type - name = safe_name(field.field_name) - - with Indent(self, 'struct {', '} %s;' % name): - for case in t.bitcases: - self.declare_case(case) + return [('base::Optional<%s>' % field_type, field_name) + for case in field.type.bitcases + for field_type, field_name in self.declare_case(case)] def copy_switch(self, field): t = field.type name = safe_name(field.field_name) - scope_fields = [] + self.write('auto %s_expr = %s;' % (name, self.expr(t.expr))) for case in t.bitcases: - if case.field_name: - scope_fields.append(case) - else: - scope_fields.extend(case.type.fields) - with Indent(self, '{', '}'), ScopedFields(self, name, scope_fields): - switch_var = name + '_expr' - self.write('auto %s = %s;' % (switch_var, self.expr(t.expr))) - for case in t.bitcases: - self.copy_case(case, switch_var) + self.copy_case(case, name) def declare_list(self, field): t = field.type @@ -588,7 +705,7 @@ class GenXproto: type_name = 'std::string' else: type_name = 'std::vector<%s>' % type_name - self.write('%s %s{};' % (type_name, name)) + return [(type_name, name)] def copy_list(self, field): t = field.type @@ -604,35 +721,60 @@ class GenXproto: with Indent(self, 'for (auto& %s_elem : %s) {' % (name, name), '}'): elem_name = name + '_elem' elem_type = t.member - if elem_type.is_simple or elem_type.is_union: - assert (not isinstance(elem_type, self.xcbgen.xtypes.Enum)) - self.copy_primitive(elem_name) - else: - assert elem_type.is_container - self.copy_container(elem_type, elem_name) + elem_field = self.xcbgen.expr.Field(elem_type, field.field_type, + elem_name, field.visible, + field.wire, field.auto, + field.enum, field.isfd) + elem_field.for_list = None + elem_field.for_switch = None + self.copy_field(elem_field) + + def generate_switch_var(self, field): + name = safe_name(field.field_name) + for case in field.for_switch.type.bitcases: + case_field = case if case.field_name else case.type.fields[0] + self.write('SwitchVar(%s, %s.%s.has_value(), %s, &%s);' % + (self.expr(case.type.expr[0]), + safe_name(field.for_switch.field_name), + safe_name(case_field.field_name), + 'true' if case.type.is_bitcase else 'false', name)) def declare_field(self, field): t = field.type name = safe_name(field.field_name) - if not field.wire or not field.visible or field.for_list: - return + if not field.visible or field.for_list or field.for_switch: + return [] if t.is_switch: - self.declare_switch(field) - elif t.is_list: - self.declare_list(field) - else: - self.write( - '%s %s{};' % - (self.qualtype(field.type, field.enum - if field.enum else field.field_type), name)) + return self.declare_switch(field) + if t.is_list: + return self.declare_list(field) + return [(self.fieldtype(field), name)] def copy_field(self, field): + if not field.wire and not field.isfd: + return + t = field.type + renamed = tuple(self.rename_type(field.type, field.field_type)) + if t.is_list: + t.member = self.all_types.get(renamed, t.member) + else: + t = self.all_types.get(renamed, t) name = safe_name(field.field_name) self.write('// ' + name) + + # If this is a generated field, initialize the value of the field + # variable from the given context. + if not self.is_read: + if field.for_list: + self.write('%s = %s.size();' % + (name, safe_name(field.for_list.field_name))) + if field.for_switch: + self.generate_switch_var(field) + if t.is_pad: if t.align > 1: assert t.nmemb == 1 @@ -642,11 +784,6 @@ class GenXproto: self.write('Pad(&buf, %d);' % t.nmemb) elif not field.visible: self.copy_special_field(field) - elif field.for_list: - if not self.is_read: - self.write('%s = %s.size();' % - (name, safe_name(field.for_list.field_name))) - self.copy_primitive(name) elif t.is_switch: self.copy_switch(field) elif t.is_list: @@ -656,6 +793,9 @@ class GenXproto: elif t.is_container: with Indent(self, '{', '}'): self.copy_container(t, name) + elif t.is_fd: + # TODO(https://crbug.com/1066670): Copy FDs out of band. + self.write('NOTIMPLEMENTED();') else: assert t.is_simple if field.enum: @@ -663,6 +803,8 @@ class GenXproto: else: self.copy_primitive(name) + self.write() + def declare_enum(self, enum): def declare_enum_entry(name, value): name = safe_name(name) @@ -672,7 +814,7 @@ class GenXproto: self.undef(enum.name[-1]) with Indent( self, 'enum class %s : %s {' % - (adjust_type_case(enum.name[-1]), self.enum_types[enum.name][0] + (adjust_type_name(enum.name[-1]), self.enum_types[enum.name][0] if enum.name in self.enum_types else 'int'), '};'): bitnames = set([name for name, _ in enum.bits]) for name, value in enum.values: @@ -686,7 +828,7 @@ class GenXproto: # The size of enum types may be different depending on the # context, so they should always be casted to the contextual # underlying type before calling Read() or Write(). - underlying_type = self.fieldtype(field) + underlying_type = self.qualtype(field.type, field.type.name) tmp_name = 'tmp%d' % self.new_uid() real_name = safe_name(field.field_name) self.write('%s %s;' % (underlying_type, tmp_name)) @@ -699,30 +841,84 @@ class GenXproto: self.write('%s = static_cast<%s>(%s);' % (real_name, enum_type, tmp_name)) - def declare_container(self, struct): - name = struct.name[-1] + self.type_suffix(struct) + def declare_fields(self, fields): + for field in fields: + for field_type_name in self.declare_field(field): + self.write('%s %s{};' % field_type_name) + + def declare_event(self, event, name): + event_name = name[-1] + 'Event' + self.undef(event_name) + with Indent(self, 'struct %s {' % adjust_type_name(event_name), '};'): + self.write('static constexpr int type_id = %d;' % event.type_id) + if len(event.opcodes) == 1: + self.write('static constexpr uint8_t opcode = %s;' % + event.opcodes[name]) + else: + with Indent(self, 'enum Opcode {', '} opcode{};'): + items = [(int(x), y) + for (y, x) in event.enum_opcodes.items()] + for opcode, opname in sorted(items): + self.undef(opname) + self.write('%s = %s,' % (opname, opcode)) + self.write('bool send_event{};') + self.declare_fields(event.fields) + self.write() + + def declare_container(self, struct, struct_name): + name = struct_name[-1] + self.type_suffix(struct) self.undef(name) - with Indent(self, 'struct %s {' % adjust_type_case(name), '};'): - for field in struct.fields: - self.declare_field(field) + with Indent(self, 'struct %s {' % adjust_type_name(name), '};'): + self.declare_fields(struct.fields) self.write() def copy_container(self, struct, name): assert not struct.is_union with ScopedFields(self, name, struct.fields): for field in struct.fields: - if field.wire: - self.copy_field(field) - self.write() + self.copy_field(field) + + def read_special_container(self, struct, name): + self.namespace = ['x11'] + name = self.qualtype(struct, name) + self.write('template <> COMPONENT_EXPORT(X11)') + self.write('%s Read<%s>(' % (name, name)) + with Indent(self, ' const uint8_t* buffer) {', '}'): + self.write('ReadBuffer buf{buffer, 0UL};') + self.write('%s obj;' % name) + self.write() + self.is_read = True + self.copy_container(struct, 'obj') + self.write('return obj;') + self.write() + + def write_special_container(self, struct, name): + self.namespace = ['x11'] + name = self.qualtype(struct, name) + self.write('template <> COMPONENT_EXPORT(X11)') + self.write('std::vector<uint8_t> Write<%s>(' % name) + with Indent(self, ' const %s& obj) {' % name, '}'): + self.write('WriteBuffer buf;') + self.write() + self.is_read = False + self.copy_container(struct, 'obj') + self.write('return buf;') + self.write() def declare_union(self, union): name = union.name[-1] + if union.elt.tag == 'eventstruct': + # There's only one of these in all of the protocol descriptions. + # It's just used to represent any 32-byte event for XInput. + self.write('using %s = std::array<uint8_t, 32>;' % name) + return with Indent(self, 'union %s {' % name, '};'): self.write('%s() { memset(this, 0, sizeof(*this)); }' % name) self.write() for field in union.fields: - type_name = self.fieldtype(field) - self.write('%s %s;' % (type_name, safe_name(field.field_name))) + field_type_names = self.declare_field(field) + assert len(field_type_names) == 1 + self.write('%s %s;' % field_type_names[0]) self.write( 'static_assert(std::is_trivially_copyable<%s>::value, "");' % name) self.write() @@ -730,26 +926,30 @@ class GenXproto: def declare_request(self, request): method_name = request.name[-1] request_name = method_name + 'Request' - reply_name = method_name + 'Reply' + reply_name = method_name + 'Reply' if request.reply else 'void' - self.declare_container(request) - if request.reply: - self.declare_container(request.reply) - else: - reply_name = 'void' + in_class = self.namespace == ['x11', self.class_name] - self.write('using %sResponse = Response<%s>;' % - (method_name, reply_name)) - self.write() + if not in_class or self.module.namespace.is_ext: + self.declare_container(request, request.name) + if request.reply: + self.declare_container(request.reply, request.reply.name) - self.write('Future<%s> %s(' % (reply_name, method_name)) - self.write(' const %s& request);' % request_name) - self.write() + self.write('using %sResponse = Response<%s>;' % + (method_name, reply_name)) + self.write() + + if in_class: + self.write('Future<%s> %s(' % (reply_name, method_name)) + self.write(' const %s& request);' % request_name) + self.write() def define_request(self, request): method_name = '%s::%s' % (self.class_name, request.name[-1]) - request_name = method_name + 'Request' - reply_name = method_name + 'Reply' + prefix = (method_name + if self.module.namespace.is_ext else request.name[-1]) + request_name = prefix + 'Request' + reply_name = prefix + 'Reply' reply = request.reply if not reply: @@ -758,6 +958,12 @@ class GenXproto: self.write('Future<%s>' % reply_name) self.write('%s(' % method_name) with Indent(self, ' const %s& request) {' % request_name, '}'): + cond = '!connection_->Ready()' + if self.module.namespace.is_ext: + cond += ' || !present()' + self.write('if (%s)' % cond) + self.write(' return {};') + self.write() self.namespace = ['x11', self.class_name] self.write('WriteBuffer buf;') self.write() @@ -765,7 +971,7 @@ class GenXproto: self.copy_container(request, 'request') self.write('Align(&buf, 4);') self.write() - self.write('return x11::SendRequest<%s>(display_, &buf);' % + self.write('return x11::SendRequest<%s>(connection_, &buf);' % reply_name) self.write() @@ -789,14 +995,38 @@ class GenXproto: self.write('return reply;') self.write() + def define_event(self, event, name): + self.namespace = ['x11'] + name = self.qualtype(event, name) + self.write('template <> COMPONENT_EXPORT(X11)') + self.write('void ReadEvent<%s>(' % name) + with Indent(self, ' %s* event_, const uint8_t* buffer) {' % name, + '}'): + self.write('ReadBuffer buf{buffer, 0UL};') + self.write() + self.is_read = True + self.copy_container(event, '(*event_)') + self.write() + + def define_type(self, item, name): + if name in READ_SPECIAL: + self.read_special_container(item, name) + if name in WRITE_SPECIAL: + self.write_special_container(item, name) + if isinstance(item, self.xcbgen.xtypes.Request): + self.define_request(item) + elif item.is_event: + self.define_event(item, name) + def declare_type(self, item, name): if item.is_union: self.declare_union(item) elif isinstance(item, self.xcbgen.xtypes.Request): self.declare_request(item) + elif item.is_event: + self.declare_event(item, name) elif item.is_container: - item.name = name - self.declare_container(item) + self.declare_container(item, name) elif isinstance(item, self.xcbgen.xtypes.Enum): self.declare_enum(item) else: @@ -823,72 +1053,141 @@ class GenXproto: if enums: assert len(enums) == 1 enum = enums[0] - field.enum = self.module.get_type(enum).name if enums else None + field.enum = self.module.get_type(enum).name self.enum_types[enum].add(field.type.name) + else: + field.enum = None def resolve_type(self, t, name): renamed = tuple(self.rename_type(t, name)) - if t in self.types[renamed]: - return - self.types[renamed].add(t) + assert renamed[0] == 'x11' + assert t not in self.types[renamed] + self.types[renamed].append(t) + self.all_types[renamed] = t + + if isinstance(t, self.xcbgen.xtypes.Enum): + self.bitenums.append((t, name)) if not t.is_container: return - if t.is_switch: - fields = {} - for case in t.bitcases: - if case.field_name: - fields[case.field_name] = case - else: - for field in case.type.fields: - fields[field.field_name] = field - else: - fields = {field.field_name: field for field in t.fields} + fields = { + field.field_name: field + for field in (self.switch_fields(t) if t.is_switch else t.fields) + } self.resolve_element(t.elt, fields) for field in fields.values(): - field.parent = t - field.for_list = None + if field.field_name == 'sequence': + field.visible = True + field.parent = (t, name) + # |for_list| and |for_switch| may have already been set when + # processing other fields in this structure. + field.for_list = getattr(field, 'for_list', None) + field.for_switch = getattr(field, 'for_switch', None) + + for is_type, for_type in ((field.type.is_list, 'for_list'), + (field.type.is_switch, 'for_switch')): + if not is_type: + continue + expr = field.type.expr + field_name = expr.lenfield_name + if (expr.op in (None, 'calculate_len') + and field_name in fields): + setattr(fields[field_name], for_type, field) + if field.type.is_switch or field.type.is_case_or_bitcase: self.resolve_type(field.type, field.field_type) - elif field.type.is_list: - self.resolve_type(field.type.member, field.type.member.name) - expr = field.type.expr - if not expr.op and expr.lenfield_name in fields: - fields[expr.lenfield_name].for_list = field - else: - self.resolve_type(field.type, field.type.name) if isinstance(t, self.xcbgen.xtypes.Request) and t.reply: self.resolve_type(t.reply, t.reply.name) + # Multiple event names may map to the same underlying event. For these + # cases, we want to avoid duplicating the event structure. Instead, put + # all of these events under one structure with an additional opcode field + # to indicate the type of event. + def uniquify_events(self): + # Manually merge some events in XInput. These groups of 8 events have + # idential structure, and are merged as XIDeviceEvent in Xlib. To avoid + # duplication, and to ease the transition from Xlib to XProto, we merge + # the events here too. + # TODO(thomasanderson): We should avoid adding workarounds for xcbproto. + # Instead, the protocol files should be modified directly. However, + # some of the changes we want to make change the API, so the changes + # should be made in a fork in //third_party rather than upstreamed. + MERGE = [ + ([ + 'KeyPress', 'KeyRelease', 'ButtonPress', 'ButtonRelease', + 'Motion', 'TouchBegin', 'TouchUpdate', 'TouchEnd' + ], []), + ([ + 'RawKeyPress', 'RawKeyRelease', 'RawButtonPress', + 'RawButtonRelease', 'RawMotion', 'RawTouchBegin', + 'RawTouchUpdate', 'RawTouchEnd' + ], []), + ] + for i, (name, t) in enumerate(self.module.all): + if t.is_event and name[1] == 'Input': + for names, event in MERGE: + if name[-1] in names: + if event: + event[0].opcodes.update(t.opcodes) + self.module.all[i] = name, event[0] + else: + event.append(t) + + types = [] + events = set() + for name, t in self.module.all: + if not t.is_event or len(t.opcodes) == 1: + types.append((name, t)) + continue + + renamed = tuple(self.rename_type(t, name)) + self.all_types[renamed] = t + if t in events: + continue + events.add(t) + + names = [name[-1] for name in t.opcodes.keys()] + name = name[:-1] + (event_base_name(names), ) + types.append((name, t)) + + t.enum_opcodes = {} + for opname in t.opcodes: + opcode = t.opcodes[opname] + opname = opname[-1] + if opname.startswith(name[-1]): + opname = opname[len(name[-1]):] + t.enum_opcodes[opname] = opcode + self.module.all = types + # Perform preprocessing like renaming, reordering, and adding additional # data fields. def resolve(self): - for name, t in self.module.all: + self.class_name = (adjust_type_name(self.module.namespace.ext_name) + if self.module.namespace.is_ext else 'XProto') + + self.uniquify_events() + + for i, (name, t) in enumerate(self.module.all): + # Work around a name conflict: the type ScreenSaver has the same + # name as the extension, so rename the type. + if name == ('xcb', 'ScreenSaver'): + name = ('xcb', 'ScreenSaverMode') + t.name = name + self.module.all[i] = (name, t) self.resolve_type(t, name) - to_delete = [] - for enum in self.enum_types: - types = self.enum_types[enum] + for enum, types in list(self.enum_types.items()): if len(types) == 1: self.enum_types[enum] = list(types)[0] else: - to_delete.append(enum) - for x in to_delete: - del self.enum_types[x] + del self.enum_types[enum] for t in self.types: - # Lots of fields have types like uint8_t. Ignore these. - if len(t) == 1: - continue - l = list(self.types[t]) - # For some reason, FDs always have distint types so they appear - # duplicated in the set. If the set contains only FDs, then bail. - if all(x.is_fd for x in l): - continue + l = self.types[t] if len(l) == 1: continue @@ -908,26 +1207,34 @@ class GenXproto: self.replace_with_enum.add(t) self.enum_types[enum.name] = simple.name + for node in self.module.namespace.root: + if 'name' in node.attrib: + key = (node.tag, node.attrib['name']) + assert key not in self.module_names + self.module_names[key] = node + # The order of types in xcbproto's xml files are inconsistent, so sort - # them in the order {type aliases, enums, structs, requests/replies}. - def type_order_priority(item): + # them in the order {type aliases, enums, xidunions, structs, + # requests/replies}. + def type_order_priority(module_type): + name, item = module_type if item.is_simple: - return 0 + return 2 if self.get_xidunion_element(name) else 0 if isinstance(item, self.xcbgen.xtypes.Enum): return 1 if isinstance(item, self.xcbgen.xtypes.Request): - return 3 - return 2 + return 4 + return 3 - def cmp((_1, item1), (_2, item2)): - return type_order_priority(item1) - type_order_priority(item2) + def cmp(type1, type2): + return type_order_priority(type1) - type_order_priority(type2) # sort() is guaranteed to be stable. - self.module.all.sort(cmp=cmp) + self.module.all.sort(key=functools.cmp_to_key(cmp)) def gen_header(self): - self.file = self.args.headerfile - include_guard = self.args.headerfile.name.replace('/', '_').replace( + self.file = self.header_file + include_guard = self.header_file.name.replace('/', '_').replace( '.', '_').upper() + '_' self.write('#ifndef ' + include_guard) self.write('#define ' + include_guard) @@ -939,37 +1246,86 @@ class GenXproto: self.write('#include <vector>') self.write() self.write('#include "base/component_export.h"') + self.write('#include "base/optional.h"') self.write('#include "ui/gfx/x/xproto_types.h"') - for direct_import in self.module.direct_imports: + imports = set(self.module.direct_imports) + if self.module.namespace.is_ext: + imports.add(('xproto', 'xproto')) + for direct_import in sorted(list(imports)): self.write('#include "%s.h"' % direct_import[-1]) self.write('#include "%s_undef.h"' % self.module.namespace.header) self.write() - self.write('typedef struct _XDisplay XDisplay;') - self.write() self.write('namespace x11 {') self.write() + self.write('class Connection;') + self.write() + + self.namespace = ['x11'] + if not self.module.namespace.is_ext: + for (name, item) in self.module.all: + self.declare_type(item, name) name = self.class_name self.undef(name) with Indent(self, 'class COMPONENT_EXPORT(X11) %s {' % name, '};'): self.namespace = ['x11', self.class_name] self.write('public:') - self.write('explicit %s(XDisplay* display);' % name) + if self.module.namespace.is_ext: + self.write('static constexpr unsigned major_version = %s;' % + self.module.namespace.major_version) + self.write('static constexpr unsigned minor_version = %s;' % + self.module.namespace.minor_version) + self.write() + self.write(name + '(Connection* connection,') + self.write(' const x11::QueryExtensionReply& info);') + self.write() + with Indent(self, 'uint8_t present() const {', '}'): + self.write('return info_.present;') + with Indent(self, 'uint8_t major_opcode() const {', '}'): + self.write('return info_.major_opcode;') + with Indent(self, 'uint8_t first_event() const {', '}'): + self.write('return info_.first_event;') + with Indent(self, 'uint8_t first_error() const {', '}'): + self.write('return info_.first_error;') + else: + self.write('explicit %s(Connection* connection);' % name) self.write() - self.write('XDisplay* display() { return display_; }') + self.write( + 'Connection* connection() const { return connection_; }') self.write() for (name, item) in self.module.all: - self.declare_type(item, name) + if self.module.namespace.is_ext: + self.declare_type(item, name) + elif isinstance(item, self.xcbgen.xtypes.Request): + self.declare_request(item) self.write('private:') - self.write('XDisplay* const display_;') + self.write('x11::Connection* const connection_;') + if self.module.namespace.is_ext: + self.write('x11::QueryExtensionReply info_{};') self.write() self.write('} // namespace x11') self.write() + self.namespace = [] + + def binop(op, name): + self.write('inline constexpr %s operator%s(' % (name, op)) + with Indent(self, ' {0} l, {0} r)'.format(name) + ' {', '}'): + self.write('using T = std::underlying_type_t<%s>;' % name) + self.write('return static_cast<%s>(' % name) + self.write(' static_cast<T>(l) %s static_cast<T>(r));' % op) + self.write() + + for enum, name in self.bitenums: + name = self.qualtype(enum, name) + binop('|', name) + binop('&', name) + + self.write() self.write('#endif // ' + include_guard) def gen_source(self): - self.file = self.args.sourcefile + self.file = self.source_file self.write('#include "%s.h"' % self.module.namespace.header) self.write() self.write('#include <xcb/xcb.h>') @@ -980,46 +1336,246 @@ class GenXproto: self.write() self.write('namespace x11 {') self.write() - name = self.class_name - self.write('%s::%s(XDisplay* display) : display_(display) {}' % - (name, name)) + ctor = '%s::%s' % (self.class_name, self.class_name) + if self.module.namespace.is_ext: + self.write(ctor + '(x11::Connection* connection,') + self.write(' const x11::QueryExtensionReply& info)') + self.write(' : connection_(connection), info_(info) {}') + else: + self.write(ctor + + '(Connection* connection) : connection_(connection) {}') self.write() for (name, item) in self.module.all: - if isinstance(item, self.xcbgen.xtypes.Request): - self.define_request(item) + self.define_type(item, name) self.write('} // namespace x11') - def generate(self): - self.module = self.xcbgen.state.Module(self.args.xmlfile.name, None) + def parse(self): + self.module = self.xcbgen.state.Module(self.xml_filename, None) self.module.register() self.module.resolve() - self.resolve() - self.class_name = (adjust_type_case(self.module.namespace.ext_name) - if self.module.namespace.is_ext else 'XProto') + def generate(self): self.gen_header() self.gen_source() +class GenExtensionManager(FileWriter): + def __init__(self, gen_dir, genprotos): + FileWriter.__init__(self) + + self.gen_dir = gen_dir + self.genprotos = genprotos + self.extensions = [ + proto for proto in genprotos if proto.module.namespace.is_ext + ] + + def gen_header(self): + self.file = open(os.path.join(self.gen_dir, 'extension_manager.h'), + 'w') + self.write('#ifndef UI_GFX_X_EXTENSION_MANAGER_H_') + self.write('#define UI_GFX_X_EXTENSION_MANAGER_H_') + self.write() + self.write('#include <memory>') + self.write() + self.write('#include "base/component_export.h"') + self.write() + self.write('// Avoid conflicts caused by the GenericEvent macro.') + self.write('#include "ui/gfx/x/ge_undef.h"') + self.write() + self.write('namespace x11 {') + self.write() + self.write('class Connection;') + self.write() + for genproto in self.genprotos: + self.write('class %s;' % genproto.class_name) + self.write() + with Indent(self, 'class COMPONENT_EXPORT(X11) ExtensionManager {', + '};'): + self.write('public:') + self.write('ExtensionManager();') + self.write('~ExtensionManager();') + self.write() + for extension in self.extensions: + name = extension.proto + self.write('%s& %s() { return *%s_; }' % + (extension.class_name, name, name)) + self.write() + self.write('protected:') + self.write('void Init(Connection* conn);') + self.write() + self.write('private:') + for extension in self.extensions: + self.write('std::unique_ptr<%s> %s_;' % + (extension.class_name, extension.proto)) + self.write() + self.write('} // namespace x11') + self.write() + self.write('#endif // UI_GFX_X_EXTENSION_MANAGER_H_') + + def gen_source(self): + self.file = open(os.path.join(self.gen_dir, 'extension_manager.cc'), + 'w') + self.write('#include "ui/gfx/x/extension_manager.h"') + self.write() + self.write('#include "ui/gfx/x/connection.h"') + self.write('#include "ui/gfx/x/xproto_internal.h"') + for genproto in self.genprotos: + self.write('#include "ui/gfx/x/%s.h"' % genproto.proto) + self.write() + self.write('namespace x11 {') + self.write() + init = 'void ExtensionManager::Init' + with Indent(self, init + '(Connection* conn) {', '}'): + for extension in self.extensions: + self.write( + 'auto %s_future = conn->QueryExtension({"%s"});' % + (extension.proto, extension.module.namespace.ext_xname)) + self.write() + for extension in self.extensions: + name = extension.proto + self.write( + '%s_ = MakeExtension<%s>(conn, std::move(%s_future));' % + (name, extension.class_name, name)) + self.write() + self.write('ExtensionManager::ExtensionManager() = default;') + self.write('ExtensionManager::~ExtensionManager() = default;') + self.write() + self.write('} // namespace x11') + + +class GenReadEvent(FileWriter): + def __init__(self, gen_dir, genprotos): + FileWriter.__init__(self) + + self.gen_dir = gen_dir + self.genprotos = genprotos + + self.events = [] + for proto in self.genprotos: + for name, item in proto.module.all: + if item.is_event: + self.events.append((name, item, proto)) + + def event_condition(self, event, typename, proto): + ext = 'conn->%s()' % proto.proto + + conds = [] + if not proto.module.namespace.is_ext: + # Core protocol event + opcode = 'evtype' + elif event.is_ge_event: + # GenericEvent extension event + conds.extend([ + 'evtype == GeGenericEvent::opcode', + '%s.present()' % ext, + 'ge->extension == %s.major_opcode()' % ext, + ]) + opcode = 'ge->event_type' + else: + # Extension event + opcode = 'evtype - %s.first_event()' % ext + conds.append('%s.present()' % ext) + + if len(event.opcodes) == 1: + conds.append('%s == %s::opcode' % (opcode, typename)) + else: + conds.append('(%s)' % ' || '.join([ + '%s == %s::%s' % (opcode, typename, opname) + for opname in event.enum_opcodes.keys() + ])) + + return ' && '.join(conds), opcode + + def gen_event(self, name, event, proto): + # We can't ever have a plain generic event. It must be a concrete + # event provided by an extension. + if name == ('xcb', 'GeGeneric'): + return + + name = [adjust_type_name(part) for part in name[1:]] + typename = '::'.join(name) + 'Event' + + cond, opcode = self.event_condition(event, typename, proto) + with Indent(self, 'if (%s) {' % cond, '}'): + self.write('event->type_id_ = %d;' % event.type_id) + with Indent(self, 'event->deleter_ = [](void* event) {', '};'): + self.write('delete reinterpret_cast<%s*>(event);' % typename) + self.write('auto* event_ = new %s;' % typename) + self.write('ReadEvent(event_, buf);') + if len(event.opcodes) > 1: + self.write('{0} = static_cast<decltype({0})>({1});'.format( + 'event_->opcode', opcode)) + self.write('event_->send_event = send_event;') + self.write('event->event_ = event_;') + self.write('return;') + self.write() + + def gen_source(self): + self.file = open(os.path.join(self.gen_dir, 'read_event.cc'), 'w') + self.write('#include "ui/gfx/x/event.h"') + self.write() + self.write('#include "ui/gfx/x/connection.h"') + for genproto in self.genprotos: + self.write('#include "ui/gfx/x/%s.h"' % genproto.proto) + self.write() + self.write('namespace x11 {') + self.write() + self.write('void ReadEvent(') + args = 'Event* event, Connection* conn, const uint8_t* buf' + with Indent(self, ' %s) {' % args, '}'): + cast = 'auto* %s = reinterpret_cast<const %s*>(buf);' + self.write(cast % ('ev', 'xcb_generic_event_t')) + self.write(cast % ('ge', 'xcb_ge_generic_event_t')) + self.write('auto evtype = ev->response_type & ~kSendEventMask;') + self.write('bool send_event = ev->response_type & kSendEventMask;') + self.write() + for name, event, proto in self.events: + self.gen_event(name, event, proto) + self.write('NOTREACHED();') + self.write() + self.write('} // namespace x11') + + def main(): parser = argparse.ArgumentParser() - parser.add_argument('xmlfile', type=argparse.FileType('r')) - parser.add_argument('undeffile', type=argparse.FileType('w')) - parser.add_argument('headerfile', type=argparse.FileType('w')) - parser.add_argument('sourcefile', type=argparse.FileType('w')) - parser.add_argument('--sysroot') + parser.add_argument('xcbproto_dir', type=str) + parser.add_argument('gen_dir', type=str) + parser.add_argument('protos', type=str, nargs='*') args = parser.parse_args() - if args.sysroot: - path = os.path.join(args.sysroot, 'usr', 'lib', 'python2.7', - 'dist-packages') - sys.path.insert(1, path) - + sys.path.insert(1, args.xcbproto_dir) import xcbgen.xtypes import xcbgen.state - generator = GenXproto(args, xcbgen) - generator.generate() + all_types = {} + proto_src_dir = os.path.join(args.xcbproto_dir, 'src') + genprotos = [ + GenXproto(proto, proto_src_dir, args.gen_dir, xcbgen, all_types) + for proto in args.protos + ] + for genproto in genprotos: + genproto.parse() + for genproto in genprotos: + genproto.resolve() + + # Give each event a unique type ID. This is used by x11::Event to + # implement downcasting for events. + type_id = 1 + for proto in genprotos: + for _, item in proto.module.all: + if item.is_event: + item.type_id = type_id + type_id += 1 + + for genproto in genprotos: + genproto.generate() + + gen_extension_manager = GenExtensionManager(args.gen_dir, genprotos) + gen_extension_manager.gen_header() + gen_extension_manager.gen_source() + + gen_read_event = GenReadEvent(args.gen_dir, genprotos) + gen_read_event.gen_source() return 0 |