def _add_fields(fields): read_code.append('_unpacked = unpack_from_stream("=%s", stream)' % fmt) build_fields = [] for idx, field in enumerate(fields): # try if we can get a modifier modifier = get_modifier(field) value = modifier % ('_unpacked[%d]' % idx) read_code.append(template('self.$fieldname = $value', fieldname=prefix_if_needed(field.field_name), value=value )) if modifier != '%s': build_fields.append('get_internal(self.%s)' % prefix_if_needed(field.field_name)) else: build_fields.append('self.%s' % prefix_if_needed(field.field_name)) cls.add_instance_attribute(prefix_if_needed(field.field_name), '') # TODO: description build_code.append('stream.write(pack("=%s", %s))' % (fmt, ', '.join(build_fields)))
def py_enum(self, name): ''' Exported function that handles enum declarations. ''' if is_ignored(name): return cls = PyClass(strip_ns(name)) count = 0 for (enam, evalue) in self.values: cls.new_attribute(prefix_if_needed(enam), evalue if evalue != '' else count) count += 1 ALL[cls.name] = cls cls.is_enum = True
def get_field_by_name(fields, name): for field in fields: if prefix_if_needed(field.field_name) == name: return field raise KeyError('No field named "%s" found!' % name)
def request_helper(self, name, void, regular): ''' Declares a request function. ''' # Four stunningly confusing possibilities here: # # Void Non-void # ------------------------------ # "req" "req" # 0 flag CHECKED flag Normal Mode # void_cookie req_cookie # ------------------------------ # "req_checked" "req_unchecked" # CHECKED flag 0 flag Abnormal Mode # void_cookie req_cookie # ------------------------------ reqinfo = get_request_info(strip_ns(name)) defaults = reqinfo.get('defaults', {}) # Whether we are _checked or _unchecked checked = void and not regular unchecked = not void and not regular # What kind of cookie we return func_cookie = 'ooxcb.VoidCookie' if void else self.py_cookie_name # What flag is passed to xcb_request func_flags = checked or (not void and regular) # What our function name is func_name = self.py_request_name if checked: func_name = self.py_checked_name if unchecked: func_name = self.py_unchecked_name # now pythonize the name ... if not 'name' in reqinfo: func_name = pythonize_name(func_name) else: func_name = reqinfo['name'] if checked: func_name += '_checked' if unchecked: func_name += '_unchecked' param_fields = [] wire_fields = [] optional_param_fields = [] meth = PyMethod(func_name) # now decide to which class that stuff belongs to, and # determinate the `self` parameter subject_field = None # Check for a subject parameter, use extension class as fallback if 'subject' in reqinfo: # yep, there's a subject. so, try to get the py class of the # subject. or, maybe it's givin explicitly! subject_field = get_field_by_name(self.fields, reqinfo['subject']) clsname = reqinfo.get('class', None) if not clsname: cls = WRAPPERS[subject_field.py_type] else: cls = ALL[clsname] else: clsname = reqinfo.get('class', None) if clsname is None: cls = EXTCLS else: cls = ALL[clsname] cls.add_member(meth) # get the argument / field names. Only needed for the length field shortcut. all_field_names = [prefix_if_needed(field.field_name) for field in self.fields] if 'arguments' in reqinfo: all_field_names = reqinfo for field in self.fields: if field.wire: wire_fields.append(field) if field is subject_field: # It's `self`. skippit. continue if field.visible: # The field should appear as a call parameter f = prefix_if_needed(field.field_name) if f in defaults: # interface provides with a default value f += '=' + str(defaults[f]) optional_param_fields.append(f) else: if (f.endswith('_len') and f[:-len('_len')] in all_field_names): # It's most likely a length field. We don't have # to include those in the arguments list, the length # can be got from the list itself. meth.code.append('%s = len(%s)' % (f, f[:-len('_len')])) else: param_fields.append(f) if 'arguments' in reqinfo: param_fields = reqinfo['arguments'] optional_param_fields = [] meth.arguments.extend(param_fields) meth.arguments.extend(optional_param_fields) if 'precode' in reqinfo: meth.code.extend(reqinfo['precode']) if 'initcode' in reqinfo: meth.code.extend(reqinfo['initcode']) else: # Check if we have to add some `get_internal()` somewhere for field in self.fields: if (is_wrapped(field.py_type) and field is not subject_field and field.field_name not in reqinfo.get('do_not_xize', []) and field.type.is_simple) : meth.code.append('%s = get_internal(%s)' % (field.field_name, field.field_name)) if field is subject_field: # Well, that's not really necessary, because `self` will never # be None or an int instance. meth.code.append('%s = get_internal(self)' % field.field_name) meth.code.append('buf = StringIO.StringIO()') struct = Struct() for field in wire_fields: if field.auto: struct.push_pad(field.type.size) continue if field.type.is_simple: struct.push_format(field) continue if field.type.is_pad: struct.push_pad(field.type.nmemb) continue fields, size, format = struct.flush() if size > 0: meth.code.append('buf.write(pack("=%s", %s))' % (format, \ ', '.join([prefix_if_needed(f.field_name) for f in fields]))) if field.type.is_expr: #_py(' buf.write(pack(\'%s\', %s))', field.type.py_format_str, _py_get_expr(field.type.expr)) meth.code.append('buf.write(pack("=%s", %s))' % (field.type.py_format_str, get_expr(field.type.expr, False))) elif field.type.is_pad: meth.code.append('buf.write(pack("=%sx"))' % field.type.nmemb) elif field.type.is_container: if is_ignored(strip_ns(field.type.name)): meth.code.append('for elt in %s:' % prefix_if_needed(field.field_name)) meth.code.append(INDENT) meth.code.append('buf.write(pack("=%s", *elt))' % field.type.py_format_str) meth.code.append(DEDENT) else: meth.code.append('%s.build(buf)' % prefix_if_needed(field.field_name)) elif field.type.is_list and field.type.member.is_simple: meth.code.append('buf.write(make_array(%s, "%s"))' % \ (prefix_if_needed(field.field_name), field.type.member.py_format_str)) else: # TODO: should we really add that? hmmm ... if field.field_type[-1] == 'CHAR2B': # we don't need a struct for CHAR2B. We'll just `.encode`. meth.code.append('buf.write(%s.encode("utf-16be"))' % \ prefix_if_needed(field.field_name)) else: if is_ignored(strip_ns(field.type.name)): meth.code.append('for elt in %s:' % (prefix_if_needed(field.field_name))) meth.code.append(INDENT) meth.code.append('buf.write(pack("=%s", *elt))' % field.type.member.py_format_str) meth.code.append(DEDENT) elif is_wrapped(strip_ns(field.type.name)): meth.code.extend([ 'for elt in %s:' % prefix_if_needed(field.field_name), INDENT, 'elt.build(buf)', DEDENT]) else: meth.code.append('%s.build(buf)' % prefix_if_needed(field.field_name)) fields, size, format = struct.flush() if size > 0: meth.code.append('buf.write(pack("=%s", %s))' % (format, ', '.join( [prefix_if_needed(f.field_name) for f in fields]))) meth.code.append('return self.conn.%s.send_request(ooxcb.Request(self.conn, buf.getvalue(), %s, %s, %s), \\' % \ (NAMESPACE.header, self.opcode, void, func_flags)) meth.code.append(INDENT) meth.code.append('%s()%s' % (func_cookie, ')' if void else ',')) if not void: meth.code.append('%s)' % self.py_reply_name) meth.code.append(DEDENT)
def py_union(self, name): ''' Exported function that handles union declarations. ''' setup_type(self, name) cls = PyClass(self.py_type) cls.base = 'ooxcb.Union' init = cls.new_method('__init__') if self.fixed_size(): init.arguments += ['conn'] init.code.append('ooxcb.Union.__init__(self, conn)') else: init.arguments += ['conn'] init.code.append('ooxcb.Union.__init__(self, conn)') read = cls.new_method('read') read.arguments.append('stream') build = cls.new_method('build') build.arguments.append('stream') read.code.append('count = 0') read.code.append('root = stream.tell()') build.code.append('root = stream.tell()') kw = 'if' # the first iteration has an if! for field in self.fields: # TODO: is it possible to have wrapped objects in unions? if yes, what to do? # TODO: we should check against None. What if we send the int 0 in an union? build.code.append('%s self.%s:' % (kw, prefix_if_needed(field.field_name))) build.code.append(INDENT) if field.type.is_simple: read.code.append('self.%s = unpack_from_stream("=%s", stream)' % \ (prefix_if_needed(field.field_name), field.type.py_format_str)) read.code.append('count = max(count, %s)', field.type.size) read.code.append('stream.seek(root)') build.code.append( 'stream.write(pack("=%s", %s))' % (field.py_format_str, prefix_if_needed(field.field_name)) ) # add a simple default value. init.code.append('self.%s = None' % (prefix_if_needed(field.field_name))) elif field.type.is_list: read.code.append('self.%s = ooxcb.List(self.conn, stream, %s, %s, %s)' % \ (prefix_if_needed(field.field_name), get_expr(field.type.expr), field.py_listtype, field.py_listsize)) read.code.append('count = max(count, self.%s.size)' % prefix_if_needed(field.field_name)) read.code.append('stream.seek(root)') build.code.append( 'build_list(self.conn, stream, self.%s, %s)' % (prefix_if_needed(field.field_name), field.py_listtype) ) init.code.append('self.%s = []' % (prefix_if_needed(field.field_name))) elif field.type.is_container and field.type.fixed_size(): read.code.append('self.%s = %s.create_from_stream(self.conn, stream)' % (prefix_if_needed(field.field_name), field.py_type, field.type.size)) read.code.append('count = max(count, %s)' % field.type.size) read.code.append('stream.seek(root)') build.code.append('self.%s.build(stream)' % prefix_if_needed(field.field_name)) init.code.append('self.%s = None' % (prefix_if_needed(field.field_name))) else: read.code.append('self.%s = %s.create_from_stream(self.conn, stream)' % (prefix_if_needed(field.field_name), field.py_type)) read.code.append('count = max(count, self.%s.size)' % prefix_if_needed(field.field_name)) read.code.append('stream.seek(root)') build.code.append('self.%s.build(stream)' % prefix_if_needed(field.field_name)) init.code.append('self.%s = None' % (prefix_if_needed(field.field_name))) kw = 'elif' # all further iterations have an elif. build.code.append(DEDENT) # using this dirty stuff to get the total length # of the union. TODO: what for not-fixed-size unions? assert self.fixed_size() size = self.fields[0].type.py_format_len build.code.extend(['else:', INDENT, 'raise ooxcb.XcbException("No value set in the union!")', DEDENT, # check if the union has the correct size. if not, append dummy bytes. 'if stream.tell() - root < %d:' % size, INDENT, 'stream.write(pack("=" + "x" * (%d - (stream.tell() - root))))' % size, DEDENT, ]) if not self.fixed_size(): read.code.append('ooxcb._resize_obj(self, count)') ALL[cls.name] = cls WRAPPERS[strip_ns(name)] = cls
def py_complex(self, name, cls): m_init = cls.get_member_by_name('__init__') init_code = m_init.code m_read = cls.new_method('read') m_read.arguments.append('stream') read_code = m_read.code m_build = cls.new_method('build') m_build.arguments.append('stream') build_code = m_build.code def _add_fields(fields): read_code.append('_unpacked = unpack_from_stream("=%s", stream)' % fmt) build_fields = [] for idx, field in enumerate(fields): # try if we can get a modifier modifier = get_modifier(field) value = modifier % ('_unpacked[%d]' % idx) read_code.append(template('self.$fieldname = $value', fieldname=prefix_if_needed(field.field_name), value=value )) if modifier != '%s': build_fields.append('get_internal(self.%s)' % prefix_if_needed(field.field_name)) else: build_fields.append('self.%s' % prefix_if_needed(field.field_name)) cls.add_instance_attribute(prefix_if_needed(field.field_name), '') # TODO: description build_code.append('stream.write(pack("=%s", %s))' % (fmt, ', '.join(build_fields))) need_alignment = False # because of that address storing, we'll only be able to read # from a MemStream. That's sad. But the address of the struct # seems to be needed by some replys, e.g. GetKeyboardMappingReply, # to access `self.length`. read_code.extend(['self._address = stream.address', 'root = stream.tell()']) # Here we store the index of the `root = stream.tell()` line to be able # to remove obsolete calls later. needs_root = False build_code.append('count = 0') # prework to pad to the correct size if cls.base == 'ooxcb.Event': build_code.append('root = stream.tell()') struct = Struct() for field in self.fields: # This hack is ugly, but it seems to be required for valid send_event stuff. # Normally, `response_type` is set automatically, but it isn't if the # event is part of a send_event request. We have to set it explicitly then # to avoid `BadValue` errors. I hope that doesn't have any side effects. if (field.field_name == 'response_type' and isinstance(self, xcbgen.xtypes.Event)): init_code.append('self.response_type = %s' % self.opcodes[name]) struct.push_format(field) continue if field.auto: struct.push_pad(field.type.size) continue if field.type.is_simple: struct.push_format(field) # add a simple default value (needs to be changed by the user, of course) init_code.append('self.%s = None' % (prefix_if_needed(field.field_name))) cls.add_instance_attribute(prefix_if_needed(field.field_name), '') # TODO: description continue if field.type.is_pad: struct.push_pad(field.type.nmemb) continue fields, size, fmt = struct.flush() if fields: _add_fields(fields) if size > 0: if not fields: # if no fields got added, the pad would get lost. add it manually then. read_code.append(template('stream.seek($size, 1)', size=size)) build_code.append(template('count += $size', size=size)) if need_alignment: read_code.append('stream.seek(ooxcb.type_pad(%d, stream.tell() - root), 1)' % align_size(field)) needs_root = True # need to add pad for `build`? # build_code.append(r'stream.write("\0" * ooxcb.type_pad(%d, count)' % align_size(field)) need_alignment = True if field.type.is_list: if field.type.member.py_type == 'void': # It is a void list. The problem about void lists is: # we don't exactly know if it's 8, 16 or 32 bit per item. # Fortunately, there seems to be an complex type # attribute called `self.format` present which has the # value 8, 16 or 32. So, we'll use this value # to get the type of the list members. # That should work for the GetPropertyReply in xproto, # but it might not work for other stuff. TODO? It's not nice. # # If `self.format` is 0 (happens for GetPropertyReply # if we try to access a non-existent property), # we use "B" (which is an unsigned byte) as a fallback. lread_code = ('ooxcb.List(self.conn, stream, %s, SIZES.get(self.format, "B"), self.format // 8)' % \ (get_expr(field.type.expr))) else: lread_code = ('ooxcb.List(self.conn, stream, %s, %s, %d)' % \ (get_expr(field.type.expr), field.py_listtype, field.py_listsize)) if field.py_type == 'char': # convert a list of chars to strings lread_code = '%s.to_string()' % lread_code elif field.py_type in INTERFACE.get('ResourceClasses', []): # is a resource. wrap them. lread_code = '[%s for w in %s]' % (get_modifier(field) % 'w', lread_code) elif field.py_type == 'ATOM': # TODO: hey, to have this hardcoded is not cool! lread_code = 'map(self.conn.atoms.get_by_id, %s)' % lread_code read_code.append('self.%s = %s' % (prefix_if_needed(field.field_name), lread_code)) cls.add_instance_attribute(prefix_if_needed(field.field_name), '') # TODO: description # TODO: add the lazy length property setter ... # e.g. `self.cmaps_length` is set to `len(self.colormaps)`. # The problem is: the field type expr isn't always a simple # expression, it also can be "(self.keycodes_per_modifier * 5)" - # how should we solve that? build_code.append('build_list(self.conn, stream, self.%s, %s)' % ( prefix_if_needed(field.field_name), field.py_listtype)) init_code.append('self.%s = []' % (prefix_if_needed(field.field_name))) elif field.type.is_container and field.type.fixed_size(): read_code.append('self.%s = %s.create_from_stream(self.conn, stream)' % (prefix_if_needed(field.field_name), get_wrapped(field.py_type))) cls.add_instance_attribute(prefix_if_needed(field.field_name), '') # TODO: description build_code.append('self.%s.build(stream)' % prefix_if_needed(field.field_name)) init_code.append('self.%s = None' % (prefix_if_needed(field.field_name))) else: read_code.append('self.%s = %s.create_from_stream(self.conn, stream)' % (prefix_if_needed(field.field_name), get_wrapped(field.py_type))) cls.add_instance_attribute(prefix_if_needed(field.field_name), '') # TODO: description build_code.append('self.%s.build(stream)' % prefix_if_needed(field.field_name)) init_code.append('self.%s = None' % (prefix_if_needed(field.field_name))) fields, size, fmt = struct.flush() if fields: if need_alignment: read_code.append('stream.seek(ooxcb.type_pad(4, stream.tell() - root), 1)') needs_root = True _add_fields(fields) if (not self.fixed_size() and cls.base == 'ooxcb.Struct'): # only do that for variable-length structs. # However, the check above is very nasty. needs_root = True # Events have a fixed size of 32 bytes. Here we pad them to the correct size # in the build code.TODO: this solution is nasty, but at least it works. if cls.base == 'ooxcb.Event': build_code.append(r'stream.write("\0" * (32 - (stream.tell() - root)))') if not needs_root: read_code.remove('root = stream.tell()')