def build_callbackcode(self): func = self.typedef.type.type args = func.args.params if len(args) < 1 or hasattr( args[0].type.type, 'names') and self.lv_base_obj_pattern.match( args[0].type.type.names[0]): raise MissingConversionException( "Callback: First argument of callback function must be lv_obj_t" ) func_name = get_arg_name(func.type) return_type = type_repr(func.type) if not self.lv_callback_return_type_pattern.match(return_type): raise MissingConversionException( "Callback: Can only handle callbaks that return lv_res_t or void" ) code = (""" /* * Callback function {func_name} * {func_prototype} */ STATIC {return_type} {func_name}_callback({func_args}) {{ mp_obj_t args[{num_args}]; {build_args} mp_obj_t action = mp_to_lv_action(args[0]); mp_obj_t arg_list = mp_obj_new_list({num_args}, args); bool schedule_result = mp_sched_schedule(action, arg_list); return{return_value}; }} """.format(func_prototype=generate_c(func), func_name=func_name, return_type=return_type, func_args=', '.join([ "%s arg%s" % (type_repr(arg.type), i) for i, arg in enumerate(args) ]), num_args=len(args), build_args="\n ".join([ self.build_callback_func_arg(arg, i, func) for i, arg in enumerate(args) ]), return_value='' if return_type == 'void' else ' schedule_result? LV_RES_OK: LV_RES_INV')) # Do this after the previous statement, since that may raise a MissingConversionException, # in which case we should *not* register the callback def register_callback(arg, index, func, obj_name): return """set_action(args[0], args[{i}]); {arg} = &{func_name}_callback;""".format(i=index, arg=generate_c(arg), func_name=func_name) mp_to_lv[func_name] = register_callback return code
def get_GLOBALS_ASSIGNMENTS(self): code = '' for name, type in self.parseresult.declarations.items(): typename = type_repr(type) code += f' PyModule_AddObject(module, "{name}", Struct_fromglobal(&py{typename}_Type, &{type.declname}, sizeof({typename})));\n' return code
def gen_mp_func(self, func, obj_name): # print("/*\n{ast}\n*/").format(ast=func) args = func.type.args.params # Handle the case of a single function argument which is "void" if len(args) == 1 and type_repr(args[0].type) == "void": param_count = 0 else: param_count = len(args) return_type = type_repr(func.type.type) if return_type == "void": build_result = "" build_return_value = "mp_const_none" else: if not return_type in lv_to_mp: self.try_generate_type(return_type) if not return_type in lv_to_mp: raise MissingConversionException( "Missing convertion from %s" % return_type) build_result = "%s res = " % return_type build_return_value = lv_to_mp[return_type](func, obj_name) if callable(lv_to_mp[return_type]) else \ "%s(res)" % lv_to_mp[return_type] return (""" /* * lvgl extension definition for: * {print_func} */ STATIC mp_obj_t mp_{func}(size_t n_args, const mp_obj_t *args) {{ {build_args} {build_result}{func}({send_args}); return {build_return_value}; }} MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_{func}_obj, {count}, {count}, mp_{func}); """.format(func=func.name, print_func=generate_c(func), count=param_count, build_args="\n ".join([ self.build_mp_func_arg(arg, i, func, obj_name) for i, arg in enumerate(args) if arg.name ]), send_args=", ".join(arg.name for arg in args if arg.name), build_result=build_result, build_return_value=build_return_value))
def deref_typedef(self, typestr): ''' Given a type as string representation (e.g. lv_opa_t), recursively dereference it using the typedefs in self.parseresult ''' while True: typedef = self.parseresult.typedefs.get(typestr) if not typedef or not isinstance(typedef.type.type, c_ast.IdentifierType): return typestr typestr = type_repr(typedef.type)
def build_callback_func_arg(self, arg, index, func): arg_type = type_repr(arg.type) if not arg_type in lv_to_mp: self.bindingsgenerator.try_generate_type(arg_type) if not arg_type in lv_to_mp: raise MissingConversionException( "Callback: Missing conversion to %s" % arg_type) return lv_to_mp[arg_type](arg, index, func, obj_name) if callable(lv_to_mp[arg_type]) else \ 'args[{i}] = {convertor}(arg{i});'.format( convertor = lv_to_mp[arg_type], i = index)
def build_mp_func_arg(self, arg, index, func, obj_name): arg_type = type_repr(arg.type) if not arg_type in mp_to_lv: self.try_generate_type(arg_type) if not arg_type in mp_to_lv: raise MissingConversionException("Missing conversion to %s" % arg_type) return mp_to_lv[arg_type](arg, index, func, obj_name) if callable(mp_to_lv[arg_type]) else \ '{var} = {convertor}(args[{i}]);'.format( var = generate_c(arg), convertor = mp_to_lv[arg_type], i = index)
def deref_typedef(self, typestr): ''' Given a type as string representation (e.g. lv_opa_t), recursively dereference it using the typedefs in self.parseresult ''' while True: typedef = self.parseresult.typedefs.get(typestr) if typedef: if isinstance(typedef.type.type, c_ast.IdentifierType): typestr = type_repr(typedef.type) continue # Continue dereferencing if isinstance(typedef.type.type, c_ast.Enum): return 'int' # Enum is represented by an int return typestr
def try_generate_type(self, type): #TODO: mp_to_lv and lv_to_mp not global, but class or instance members if type in mp_to_lv: return True orig_type = type while type in self.parseresult.typedefs: type = type_repr(self.parseresult.typedefs[type].type.type) # todo: no need to add to mp_to_lv and vv if type in mp_to_lv: mp_to_lv[orig_type] = mp_to_lv[type] lv_to_mp[orig_type] = lv_to_mp[type] return False
def build_methodcode(self, method): object = self if isinstance(method, CustomMethod): return '' # Custom implementation is in lvglmodule_template.c startCode = f''' static PyObject* py{method.decl.name}(pylv_Obj *self, PyObject *args, PyObject *kwds) {{ if (check_alive(self)) return NULL; ''' paramnames = [] paramctypes = [] paramfmts = [] for param in method.decl.type.args.params: paramtype = type_repr(param.type) paramtype_derefed = self.bindingsgenerator.deref_typedef(paramtype) # For params, if the param type is e.g. "lv_obj_t*", "const lv_obj_t*" is allowed, too # However, we do not add "const lv_obj_t*" to the TYPECONV dict, since "const lv_obj_t*" would not be a valid return type try: fmt, ctype = self.TYPECONV_PARAMETER[paramtype_derefed] except KeyError: raise MissingConversionException(f'{method.decl.name}: Parameter type not found >{paramtype}< ') paramnames.append(param.name) paramctypes.append(ctype) paramfmts.append(fmt) restype = type_repr(method.decl.type.type) if restype == 'void': resfmt, resctype = None, None else: try: resfmt, resctype = self.TYPECONV_RETURN[self.bindingsgenerator.deref_typedef(restype)] except KeyError: raise MissingConversionException(f'{method.decl.name}: Return type not found >{restype}< ') # First argument should always be a reference to the object itself assert paramctypes and paramctypes[0] == 'pylv_Obj *' paramnames.pop(0) paramctypes.pop(0) paramfmts.pop(0) code = startCode kwlist = ''.join('"%s", ' % name for name in paramnames) code += f' static char *kwlist[] = {{{kwlist}NULL}};\n'; crefvarlist = '' cvarlist = '' for name, ctype, fmt in zip(paramnames, paramctypes, paramfmts): code += f' {ctype} {name};\n' if ctype == 'pylv_Obj *' : # Object, convert from Python crefvarlist += f', &pylv_obj_Type, &{name}' cvarlist += f', {name}->ref' elif fmt == 'O&': # struct crefvarlist += f', py{ctype.rstrip(" *")}_arg_converter, &{name}' cvarlist += f', {name}' else: crefvarlist += f', &{name}' cvarlist += f', {name}' code += f' if (!PyArg_ParseTupleAndKeywords(args, kwds, "{"".join(paramfmts)}", kwlist {crefvarlist})) return NULL;\n' callcode = f'{method.decl.name}(self->ref{cvarlist})' if resctype == 'pylv_Obj *': # Result of function is an lv_obj; find or create the corresponding Python # object using pyobj_from_lv helper code += f''' LVGL_LOCK lv_obj_t *result = {callcode}; LVGL_UNLOCK PyObject *retobj = pyobj_from_lv(result); return retobj; ''' elif resctype is None: code += f''' LVGL_LOCK {callcode}; LVGL_UNLOCK Py_RETURN_NONE; ''' elif resfmt == 'O&': code += f''' LVGL_LOCK {resctype} result = {callcode}; LVGL_UNLOCK return pystruct_from_lv(result); ''' else: code += f''' LVGL_LOCK {resctype} result = {callcode}; LVGL_UNLOCK ''' if resfmt == 'p': # Py_BuildValue does not support 'p' (which is supported by PyArg_ParseTuple..) code += ' if (result) {Py_RETURN_TRUE;} else {Py_RETURN_FALSE;}\n' else: code += f' return Py_BuildValue("{resfmt}", result);\n' return code + '}\n';
def getset(self): code = f'static PyGetSetDef pylv_{self.name}_getset[] = {{\n' bitfieldscode = '' for decl in self.get_flattened_decls(): assert decl.name if self.subpath: offsetcode = f'(offsetof(lv_{self.basename}, {self.subpath}{decl.name})-offsetof(lv_{self.basename}, {self.subpath[:-1]}))' else: offsetcode = f'offsetof(lv_{self.basename}, {decl.name})' if isinstance(decl.type.type, (c_ast.Struct, c_ast.Union)): getter, setter = 'struct_get_struct', 'struct_set_struct' # the closure for struct & blob is a struct of PyTypObject*, offset (w.r.t. base type), size (in bytes) closure = f'& ((struct_closure_t){{ &pylv_{self.name}_{decl.name}_Type, {offsetcode}, sizeof(((lv_{self.basename} *)0)->{self.subpath}{decl.name})}})' elif decl.bitsize is not None: # Needs a custom getter & setter function getsetname = f'struct_bitfield_{self.name}_{decl.name}' getter, setter = 'get_' + getsetname, 'set_' + getsetname closure = 'NULL' assert(self.bindingsgenerator.deref_typedef(generate_c(decl.type.type)) in ('uint8_t', 'uint16_t', 'uint32_t')) # the following only supports unsigned values for bitfields bitfieldscode += f''' static PyObject * {getter}(StructObject *self, void *closure) {{ return PyLong_FromLong(((lv_{self.basename}*)(self->data))->{self.subpath}{decl.name} ); }} static int {setter}(StructObject *self, PyObject *value, void *closure) {{ long v; if (long_to_int(value, &v, 0, {2**int(decl.bitsize.value)-1})) return -1; ((lv_{self.basename}*)(self->data))->{self.subpath}{decl.name} = v; return 0; }} ''' else: typestr = self.bindingsgenerator.deref_typedef(type_repr(decl.type)) if typestr in self.TYPES: getter, setter = self.TYPES[typestr] closure = f'(void*){offsetcode}' elif typestr.startswith('lv_') and (stripstart(typestr,'lv_') in self.bindingsgenerator.structs): getter, setter = 'struct_get_struct', 'struct_set_struct' closure = f'& ((struct_closure_t){{ &py{typestr}_Type, {offsetcode}, sizeof({typestr})}})' else: # default: blob type getter, setter = 'struct_get_struct', 'struct_set_struct' closure = f'& ((struct_closure_t){{ &Blob_Type, {offsetcode}, sizeof(((lv_{self.basename} *)0)->{self.subpath}{decl.name})}})' typedoc = generate_c(decl.type).replace('\n', ' ') + (f':{decl.bitsize.value}' if decl.bitsize else '') code += f' {{"{decl.name}", (getter) {getter}, (setter) {setter}, "{typedoc} {decl.name}", {closure}}},\n' code += ' {NULL}\n};\n' return bitfieldscode + code