コード例 #1
0
ファイル: ooxcb_client.py プロジェクト: samurai-x/ooxcb
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)
コード例 #2
0
ファイル: ooxcb_client.py プロジェクト: samurai-x/ooxcb
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()')