From b51bf02972c80f3c833ae522a432ab09667d0b99 Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Thu, 25 Apr 2024 22:34:52 +0100 Subject: [PATCH 01/11] gen: update to MicroPython v1.23.x --- gen/gen_mpy.py | 2360 +++++++++++++++++++++++++++++++----------------- lvgl | 2 +- 2 files changed, 1537 insertions(+), 825 deletions(-) diff --git a/gen/gen_mpy.py b/gen/gen_mpy.py index 0470caa92..22756548e 100644 --- a/gen/gen_mpy.py +++ b/gen/gen_mpy.py @@ -10,33 +10,36 @@ from __future__ import print_function import collections import sys -import struct import copy -from itertools import chain from functools import lru_cache import json import os + def memoize(func): @lru_cache(maxsize=1000000) def memoized(*args, **kwargs): return func(*args, **kwargs) + return memoized + def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) + # from pudb.remote import set_trace # set_trace(term_size=(180, 50)) from sys import argv from argparse import ArgumentParser -import subprocess, re +import subprocess +import re from os.path import dirname, abspath from os.path import commonprefix script_path = dirname(abspath(__file__)) -sys.path.insert(0, '%s/../pycparser' % script_path) +sys.path.insert(0, "%s/../pycparser" % script_path) from pycparser import c_parser, c_ast, c_generator # @@ -44,14 +47,63 @@ def eprint(*args, **kwargs): # argParser = ArgumentParser() -argParser.add_argument('-I', '--include', dest='include', help='Preprocesor include path', metavar='', action='append') -argParser.add_argument('-D', '--define', dest='define', help='Define preprocessor macro', metavar='', action='append') -argParser.add_argument('-E', '--external-preprocessing', dest='ep', help='Prevent preprocessing. Assume input file is already preprocessed', metavar='', action='store') -argParser.add_argument('-J', '--lvgl-json', dest='json', help='Provde a JSON from the LVGL JSON generator for missing information', metavar='', action='store') -argParser.add_argument('-M', '--module_name', dest='module_name', help='Module name', metavar='', action='store') -argParser.add_argument('-MP', '--module_prefix', dest='module_prefix', help='Module prefix that starts every function name', metavar='', action='store') -argParser.add_argument('-MD', '--metadata', dest='metadata', help='Optional file to emit metadata (introspection)', metavar='', action='store') -argParser.add_argument('input', nargs='+') +argParser.add_argument( + "-I", + "--include", + dest="include", + help="Preprocesor include path", + metavar="", + action="append", +) +argParser.add_argument( + "-D", + "--define", + dest="define", + help="Define preprocessor macro", + metavar="", + action="append", +) +argParser.add_argument( + "-E", + "--external-preprocessing", + dest="ep", + help="Prevent preprocessing. Assume input file is already preprocessed", + metavar="", + action="store", +) +argParser.add_argument( + "-J", + "--lvgl-json", + dest="json", + help="Provde a JSON from the LVGL JSON generator for missing information", + metavar="", + action="store", +) +argParser.add_argument( + "-M", + "--module_name", + dest="module_name", + help="Module name", + metavar="", + action="store", +) +argParser.add_argument( + "-MP", + "--module_prefix", + dest="module_prefix", + help="Module prefix that starts every function name", + metavar="", + action="store", +) +argParser.add_argument( + "-MD", + "--metadata", + dest="metadata", + help="Optional file to emit metadata (introspection)", + metavar="", + action="store", +) +argParser.add_argument("input", nargs="+") argParser.set_defaults(include=[], define=[], ep=None, json=None, input=[]) args = argParser.parse_args() @@ -63,24 +115,28 @@ def eprint(*args, **kwargs): # if not args.ep: - pp_cmd = 'gcc -E -std=c99 -DPYCPARSER {macros} {include} {input} {first_input}'.format( - input=' '.join('-include %s' % inp for inp in args.input), - first_input= '%s' % args.input[0], - macros = ' '.join('-D%s' % define for define in args.define), - include=' '.join('-I %s' % inc for inc in args.include)) + pp_cmd = ( + "gcc -E -std=c99 -DPYCPARSER {macros} {include} {input} {first_input}".format( + input=" ".join("-include %s" % inp for inp in args.input), + first_input="%s" % args.input[0], + macros=" ".join("-D%s" % define for define in args.define), + include=" ".join("-I %s" % inc for inc in args.include), + ) + ) s = subprocess.check_output(pp_cmd.split()).decode() else: - pp_cmd = 'Preprocessing was disabled.' - s = '' - with open(args.ep, 'r') as f: + pp_cmd = "Preprocessing was disabled." + s = "" + with open(args.ep, "r") as f: s += f.read() # # AST parsing helper functions # + @memoize def remove_declname(ast): - if hasattr(ast, 'declname'): + if hasattr(ast, "declname"): ast.declname = None if isinstance(ast, tuple): remove_declname(ast[1]) @@ -89,10 +145,11 @@ def remove_declname(ast): child = ast.children()[i] remove_declname(child) + @memoize def add_default_declname(ast, name): - if hasattr(ast, 'declname'): - if (ast.declname == None): + if hasattr(ast, "declname"): + if ast.declname == None: ast.declname = name if isinstance(ast, tuple): add_default_declname(ast[1], name) @@ -101,29 +158,36 @@ def add_default_declname(ast, name): child = ast.children()[i] add_default_declname(child, name) + @memoize def convert_array_to_ptr(ast): - if hasattr(ast, 'type') and isinstance(ast.type, c_ast.ArrayDecl): - ast.type = c_ast.PtrDecl(ast.type.quals if hasattr(ast.type, 'quals') else [], ast.type.type) + if hasattr(ast, "type") and isinstance(ast.type, c_ast.ArrayDecl): + ast.type = c_ast.PtrDecl( + ast.type.quals if hasattr(ast.type, "quals") else [], ast.type.type + ) if isinstance(ast, tuple): return convert_array_to_ptr(ast[1]) for i, c1 in enumerate(ast.children()): child = ast.children()[i] convert_array_to_ptr(child) + @memoize def remove_quals(ast): - if hasattr(ast,'quals'): + if hasattr(ast, "quals"): ast.quals = [] - if hasattr(ast,'dim_quals'): + if hasattr(ast, "dim_quals"): ast.dim_quals = [] if isinstance(ast, tuple): return remove_quals(ast[1]) for i, c1 in enumerate(ast.children()): child = ast.children()[i] - if not isinstance(child, c_ast.FuncDecl): # Don't remove quals which change function prototype + if not isinstance( + child, c_ast.FuncDecl + ): # Don't remove quals which change function prototype remove_quals(child) + @memoize def remove_explicit_struct(ast): if isinstance(ast, c_ast.TypeDecl) and isinstance(ast.type, c_ast.Struct): @@ -131,7 +195,9 @@ def remove_explicit_struct(ast): # eprint('--> replace %s by %s in:\n%s' % (explicit_struct_name, explicit_structs[explicit_struct_name] if explicit_struct_name in explicit_structs else '???', ast)) if explicit_struct_name: if explicit_struct_name in explicit_structs: - ast.type = c_ast.IdentifierType([explicit_structs[explicit_struct_name]]) + ast.type = c_ast.IdentifierType( + [explicit_structs[explicit_struct_name]] + ) elif explicit_struct_name in structs: ast.type = c_ast.IdentifierType([explicit_struct_name]) if isinstance(ast, tuple): @@ -140,16 +206,19 @@ def remove_explicit_struct(ast): child = ast.children()[i] remove_explicit_struct(child) + @memoize def get_type(arg, **kwargs): if isinstance(arg, str): return arg - remove_quals_arg = 'remove_quals' in kwargs and kwargs['remove_quals'] + remove_quals_arg = "remove_quals" in kwargs and kwargs["remove_quals"] arg_ast = copy.deepcopy(arg) remove_explicit_struct(arg_ast) - if remove_quals_arg: remove_quals(arg_ast) + if remove_quals_arg: + remove_quals(arg_ast) return gen.visit(arg_ast) + @memoize def get_name(type): if isinstance(type, c_ast.Decl): @@ -171,15 +240,20 @@ def get_name(type): else: return gen.visit(type) + @memoize def remove_arg_names(ast): if isinstance(ast, c_ast.TypeDecl): ast.declname = None remove_arg_names(ast.type) - elif isinstance(ast, c_ast.Decl): remove_arg_names(ast.type) - elif isinstance(ast, c_ast.FuncDecl): remove_arg_names(ast.args) + elif isinstance(ast, c_ast.Decl): + remove_arg_names(ast.type) + elif isinstance(ast, c_ast.FuncDecl): + remove_arg_names(ast.args) elif isinstance(ast, c_ast.ParamList): - for param in ast.params: remove_arg_names(param) + for param in ast.params: + remove_arg_names(param) + # Create a function prototype AST from a function AST @memoize @@ -187,104 +261,170 @@ def function_prototype(func): bare_func = copy.deepcopy(func) remove_declname(bare_func) - ptr_decl = c_ast.PtrDecl( - quals=[], - type=bare_func.type) + ptr_decl = c_ast.PtrDecl(quals=[], type=bare_func.type) - func_proto = c_ast.Typename( - name=None, - quals=[], - align=[], - type=ptr_decl) + func_proto = c_ast.Typename(name=None, quals=[], align=[], type=ptr_decl) return func_proto + + # # module specific text patterns # IGNORECASE and "lower" are used to match both function and enum names # -base_obj_name = 'obj' -base_obj_type = '%s_%s_t' % (module_prefix, base_obj_name) -lv_ext_pattern = re.compile('^{prefix}_([^_]+)_ext_t'.format(prefix=module_prefix)) -lv_obj_pattern = re.compile('^{prefix}_([^_]+)'.format(prefix=module_prefix), re.IGNORECASE) -lv_func_pattern = re.compile('^{prefix}_(.+)'.format(prefix=module_prefix), re.IGNORECASE) -create_obj_pattern = re.compile('^{prefix}_(.+)_create$'.format(prefix=module_prefix)) -lv_method_pattern = re.compile('^{prefix}_[^_]+_(.+)'.format(prefix=module_prefix), re.IGNORECASE) -lv_base_obj_pattern = re.compile('^(struct _){{0,1}}{prefix}_{base_name}_t( [*]){{0,1}}'.format(prefix=module_prefix, base_name = base_obj_name)) -lv_str_enum_pattern = re.compile('^_?{prefix}_STR_(.+)'.format(prefix=module_prefix.upper())) -lv_callback_type_pattern = re.compile('({prefix}_){{0,1}}(.+)_cb(_t){{0,1}}'.format(prefix=module_prefix)) -lv_global_callback_pattern = re.compile('.*g_cb_t') -lv_func_returns_array = re.compile('.*_array$') -lv_enum_name_pattern = re.compile('^(ENUM_){{0,1}}({prefix}_){{0,1}}(.*)'.format(prefix=module_prefix.upper())) +base_obj_name = "obj" +base_obj_type = "%s_%s_t" % (module_prefix, base_obj_name) +lv_ext_pattern = re.compile("^{prefix}_([^_]+)_ext_t".format(prefix=module_prefix)) +lv_obj_pattern = re.compile( + "^{prefix}_([^_]+)".format(prefix=module_prefix), re.IGNORECASE +) +lv_func_pattern = re.compile( + "^{prefix}_(.+)".format(prefix=module_prefix), re.IGNORECASE +) +create_obj_pattern = re.compile("^{prefix}_(.+)_create$".format(prefix=module_prefix)) +lv_method_pattern = re.compile( + "^{prefix}_[^_]+_(.+)".format(prefix=module_prefix), re.IGNORECASE +) +lv_base_obj_pattern = re.compile( + "^(struct _){{0,1}}{prefix}_{base_name}_t( [*]){{0,1}}".format( + prefix=module_prefix, base_name=base_obj_name + ) +) +lv_str_enum_pattern = re.compile( + "^_?{prefix}_STR_(.+)".format(prefix=module_prefix.upper()) +) +lv_callback_type_pattern = re.compile( + "({prefix}_){{0,1}}(.+)_cb(_t){{0,1}}".format(prefix=module_prefix) +) +lv_global_callback_pattern = re.compile(".*g_cb_t") +lv_func_returns_array = re.compile(".*_array$") +lv_enum_name_pattern = re.compile( + "^(ENUM_){{0,1}}({prefix}_){{0,1}}(.*)".format(prefix=module_prefix.upper()) +) + # Prevent identifier names which are Python reserved words (add underscore in such case) -def sanitize(id, kwlist = - ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', - 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', - 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']): +def sanitize( + id, + kwlist=[ + "False", + "None", + "True", + "and", + "as", + "assert", + "break", + "class", + "continue", + "def", + "del", + "elif", + "else", + "except", + "finally", + "for", + "from", + "global", + "if", + "import", + "in", + "is", + "lambda", + "nonlocal", + "not", + "or", + "pass", + "raise", + "return", + "try", + "while", + "with", + "yield", + ], +): if id in kwlist: result = "_%s" % id else: result = id result = result.strip() - result = result.replace(' ','_') - result = result.replace('*','_ptr') + result = result.replace(" ", "_") + result = result.replace("*", "_ptr") return result + @memoize def simplify_identifier(id): match_result = lv_func_pattern.match(id) return match_result.group(1) if match_result else id + def obj_name_from_ext_name(ext_name): return lv_ext_pattern.match(ext_name).group(1) + def obj_name_from_func_name(func_name): return lv_obj_pattern.match(func_name).group(1) + def ctor_name_from_obj_name(obj_name): - return '{prefix}_{obj}_create'.format(prefix=module_prefix, obj=obj_name) + return "{prefix}_{obj}_create".format(prefix=module_prefix, obj=obj_name) + def is_method_of(func_name, obj_name): - return func_name.lower().startswith('{prefix}_{obj}_'.format(prefix=module_prefix, obj=obj_name).lower()) + return func_name.lower().startswith( + "{prefix}_{obj}_".format(prefix=module_prefix, obj=obj_name).lower() + ) + def method_name_from_func_name(func_name): res = lv_method_pattern.match(func_name).group(1) - return res if res != "del" else "delete" # del is a reserved name, don't use it + return res if res != "del" else "delete" # del is a reserved name, don't use it + def get_enum_name(enum): match_result = lv_enum_name_pattern.match(enum) return match_result.group(3) if match_result else enum + def str_enum_to_str(str_enum): res = lv_str_enum_pattern.match(str_enum).group(1) - return ('%s_' % module_prefix.upper()) + res + return ("%s_" % module_prefix.upper()) + res + def is_obj_ctor(func): # ctor name must match pattern - if not create_obj_pattern.match(func.name): return False + if not create_obj_pattern.match(func.name): + return False # ctor must return a base_obj type - if not lv_base_obj_pattern.match(get_type(func.type.type, remove_quals=True)): return False + if not lv_base_obj_pattern.match(get_type(func.type.type, remove_quals=True)): + return False # ctor must receive (at least) one base obj parameters args = func.type.args.params - if len(args) < 1: return False - if not lv_base_obj_pattern.match(get_type(args[0].type, remove_quals=True)): return False + if len(args) < 1: + return False + if not lv_base_obj_pattern.match(get_type(args[0].type, remove_quals=True)): + return False return True + def is_global_callback(arg_type): arg_type_str = get_name(arg_type.type) # print('/* --> is_global_callback %s: %s */' % (lv_global_callback_pattern.match(arg_type_str), arg_type_str)) result = lv_global_callback_pattern.match(arg_type_str) return result + # # Initialization, data structures, helper functions # + # We consider union as a struct, for simplicity def is_struct(type): return isinstance(type, c_ast.Struct) or isinstance(type, c_ast.Union) + obj_metadata = collections.OrderedDict() func_metadata = collections.OrderedDict() callback_metadata = collections.OrderedDict() @@ -293,7 +433,7 @@ def is_struct(type): parser = c_parser.CParser() gen = c_generator.CGenerator() -ast = parser.parse(s, filename='') +ast = parser.parse(s, filename="") if args.json is not None: with open(args.json, "r") as f: @@ -316,10 +456,10 @@ def is_struct(type): for item in ast.ext[:]: # Locate a forward declaration if ( - isinstance(item, c_ast.Decl) and - item.name is None and - isinstance(item.type, c_ast.Struct) and - item.type.name is not None + isinstance(item, c_ast.Decl) + and item.name is None + and isinstance(item.type, c_ast.Struct) + and item.type.name is not None ): # check to see if there are no fields , and of not store the structure # as a foward declaration. If it does have fields then build a single @@ -341,13 +481,13 @@ def is_struct(type): # and the last is another structure that has fields. So we need to capture # all 3 parts to build a single object that represents a structure. elif ( - isinstance(item, c_ast.Typedef) and - isinstance(item.type, c_ast.TypeDecl) and - item.name and - item.type.declname and - item.name == item.type.declname and - isinstance(item.type.type, c_ast.Struct) and - item.type.type.decls is None + isinstance(item, c_ast.Typedef) + and isinstance(item.type, c_ast.TypeDecl) + and item.name + and item.type.declname + and item.name == item.type.declname + and isinstance(item.type.type, c_ast.Struct) + and item.type.type.decls is None ): if item.type.type.name in forward_struct_decls: forward_struct_decls[item.type.type.name].append(item) @@ -357,7 +497,9 @@ def is_struct(type): # Types and structs -typedefs = [x.type for x in ast.ext if isinstance(x, c_ast.Typedef)] # and not (hasattr(x.type, 'declname') and lv_base_obj_pattern.match(x.type.declname))] +typedefs = [ + x.type for x in ast.ext if isinstance(x, c_ast.Typedef) +] # and not (hasattr(x.type, 'declname') and lv_base_obj_pattern.match(x.type.declname))] # print('/* %s */' % str(typedefs)) synonym = {} for t in typedefs: @@ -370,21 +512,37 @@ def is_struct(type): synonym[t.declname] = t.type.name # eprint('%s === struct %s' % (t.declname, t.type.name)) struct_typedefs = [typedef for typedef in typedefs if is_struct(typedef.type)] -structs_without_typedef = collections.OrderedDict((decl.type.name, decl.type) for decl in ast.ext if hasattr(decl, 'type') and is_struct(decl.type)) +structs_without_typedef = collections.OrderedDict( + (decl.type.name, decl.type) + for decl in ast.ext + if hasattr(decl, "type") and is_struct(decl.type) +) # for typedefs that referenced to a forward declaration struct, replace it with the real definition. for typedef in struct_typedefs: - if typedef.type.decls is None: # None means it's a forward declaration + if typedef.type.decls is None: # None means it's a forward declaration struct_name = typedef.type.name # check if it's found in `structs_without_typedef`. It actually has the typedef. Replace type with it. if typedef.type.name in structs_without_typedef: typedef.type = structs_without_typedef[struct_name] -structs = collections.OrderedDict((typedef.declname, typedef.type) for typedef in struct_typedefs if typedef.declname and typedef.type.decls) # and not lv_base_obj_pattern.match(typedef.declname)) -structs.update(structs_without_typedef) # This is for struct without typedef -explicit_structs = collections.OrderedDict((typedef.type.name, typedef.declname) for typedef in struct_typedefs if typedef.type.name) # and not lv_base_obj_pattern.match(typedef.type.name)) -opaque_structs = collections.OrderedDict((typedef.declname, c_ast.Struct(name=typedef.declname, decls=[])) for typedef in typedefs if isinstance(typedef.type, c_ast.Struct) and typedef.type.decls == None) -structs.update({k:v for k,v in opaque_structs.items() if k not in structs}) +structs = collections.OrderedDict( + (typedef.declname, typedef.type) + for typedef in struct_typedefs + if typedef.declname and typedef.type.decls +) # and not lv_base_obj_pattern.match(typedef.declname)) +structs.update(structs_without_typedef) # This is for struct without typedef +explicit_structs = collections.OrderedDict( + (typedef.type.name, typedef.declname) + for typedef in struct_typedefs + if typedef.type.name +) # and not lv_base_obj_pattern.match(typedef.type.name)) +opaque_structs = collections.OrderedDict( + (typedef.declname, c_ast.Struct(name=typedef.declname, decls=[])) + for typedef in typedefs + if isinstance(typedef.type, c_ast.Struct) and typedef.type.decls == None +) +structs.update({k: v for k, v in opaque_structs.items() if k not in structs}) # print('/* --> opaque structs len = %d */' % len(opaque_structs)) # print('/* --> opaque structs %s */' % ',\n'.join([struct_name for struct_name in opaque_structs])) # print('/* --> structs:\n%s */' % ',\n'.join(sorted(str(structs[struct_name]) for struct_name in structs if struct_name))) @@ -395,9 +553,15 @@ def is_struct(type): # Functions and objects func_defs = [x.decl for x in ast.ext if isinstance(x, c_ast.FuncDef)] -func_decls = [x for x in ast.ext if isinstance(x, c_ast.Decl) and isinstance(x.type, c_ast.FuncDecl)] +func_decls = [ + x + for x in ast.ext + if isinstance(x, c_ast.Decl) and isinstance(x.type, c_ast.FuncDecl) +] all_funcs = func_defs + func_decls -funcs = [f for f in all_funcs if not f.name.startswith('_')] # functions that start with underscore are usually internal +funcs = [ + f for f in all_funcs if not f.name.startswith("_") +] # functions that start with underscore are usually internal # eprint('... %s' % ',\n'.join(sorted('%s' % func.name for func in funcs))) obj_ctors = [func for func in funcs if is_obj_ctor(func)] # eprint('CTORS(%d): %s' % (len(obj_ctors), ', '.join(sorted('%s' % ctor.name for ctor in obj_ctors)))) @@ -405,25 +569,36 @@ def is_struct(type): funcs.remove(obj_ctor) obj_names = [create_obj_pattern.match(ctor.name).group(1) for ctor in obj_ctors] + def has_ctor(obj_name): return ctor_name_from_obj_name(obj_name) in [ctor.name for ctor in obj_ctors] + def get_ctor(obj_name): global obj_ctors - return next(ctor for ctor in obj_ctors if ctor.name == ctor_name_from_obj_name(obj_name)) + return next( + ctor for ctor in obj_ctors if ctor.name == ctor_name_from_obj_name(obj_name) + ) + def get_methods(obj_name): global funcs - return [func for func in funcs \ - if is_method_of(func.name,obj_name) and \ - (not func.name == ctor_name_from_obj_name(obj_name))] + return [ + func + for func in funcs + if is_method_of(func.name, obj_name) + and (not func.name == ctor_name_from_obj_name(obj_name)) + ] + @memoize def noncommon_part(member_name, stem_name): common_part = commonprefix([member_name, stem_name]) n = len(common_part) - 1 - while n > 0 and member_name[n] != '_': n-=1 - return member_name[n+1:] + while n > 0 and member_name[n] != "_": + n -= 1 + return member_name[n + 1 :] + @memoize def get_first_arg(func): @@ -435,6 +610,7 @@ def get_first_arg(func): return None return func.type.args.params[0].type + @memoize def get_first_arg_type(func): first_arg = get_first_arg(func) @@ -442,10 +618,12 @@ def get_first_arg_type(func): return None if not first_arg.type: return None - return get_type(first_arg.type, remove_quals = True) + return get_type(first_arg.type, remove_quals=True) + def get_base_struct_name(struct_name): - return struct_name[:-2] if struct_name.endswith('_t') else struct_name + return struct_name[:-2] if struct_name.endswith("_t") else struct_name + # "struct function" starts with struct name (without _t), and their first argument is a pointer to the struct # Need also to take into account struct functions of aliases of current struct. @@ -462,34 +640,55 @@ def get_struct_functions(struct_name): # print("/* get_struct_functions: func=%s, struct=%s, noncommon part=%s */" % (simplify_identifier(func.name), simplify_identifier(struct_name), # noncommon_part(simplify_identifier(func.name), simplify_identifier(struct_name)))) - reverse_aliases = [alias for alias in struct_aliases if struct_aliases[alias] == struct_name] + reverse_aliases = [ + alias for alias in struct_aliases if struct_aliases[alias] == struct_name + ] + + return ( + [ + func + for func in funcs + if noncommon_part( + simplify_identifier(func.name), simplify_identifier(struct_name) + ) + != simplify_identifier(func.name) + and get_first_arg_type(func) == struct_name + ] + if (struct_name in structs or len(reverse_aliases) > 0) + else [] + ) + ( + get_struct_functions(struct_aliases[struct_name]) + if struct_name in struct_aliases + else [] + ) - return ([func for func in funcs \ - if noncommon_part(simplify_identifier(func.name), simplify_identifier(struct_name)) != simplify_identifier(func.name) \ - and get_first_arg_type(func) == struct_name] if (struct_name in structs or len(reverse_aliases) > 0) else []) + \ - (get_struct_functions(struct_aliases[struct_name]) if struct_name in struct_aliases else []) @memoize def is_struct_function(func): return func in get_struct_functions(get_first_arg_type(func)) + # is_static_member returns true if function does not receive the obj as the first argument # and the object is not a struct function + @memoize def is_static_member(func, obj_type=base_obj_type): first_arg = get_first_arg(func) # print("/* %s : get_first_arg = %s */" % (get_name(func), first_arg)) if first_arg: if isinstance(first_arg, c_ast.ArrayDecl): - return True # Arrays cannot have non static members + return True # Arrays cannot have non static members if is_struct_function(func): return False first_arg_type = get_first_arg_type(func) return (first_arg_type == None) or (first_arg_type != obj_type) + # All object should inherit directly from base_obj, and not according to lv_ext, as disccussed on https://github.com/littlevgl/lv_binding_micropython/issues/19 -parent_obj_names = {child_name: base_obj_name for child_name in obj_names if child_name != base_obj_name} +parent_obj_names = { + child_name: base_obj_name for child_name in obj_names if child_name != base_obj_name +} parent_obj_names[base_obj_name] = None # Populate inheritance hierarchy according to lv_ext structures @@ -504,39 +703,61 @@ def is_static_member(func, obj_type=base_obj_type): # Parse Enums -enum_defs = [x for x in ast.ext if hasattr(x,'type') and isinstance(x.type, c_ast.Enum)] -enum_defs += [x.type for x in ast.ext if hasattr(x, 'type') and hasattr(x.type, 'type') and isinstance(x.type, c_ast.TypeDecl) and isinstance(x.type.type, c_ast.Enum)] +enum_defs = [ + x for x in ast.ext if hasattr(x, "type") and isinstance(x.type, c_ast.Enum) +] +enum_defs += [ + x.type + for x in ast.ext + if hasattr(x, "type") + and hasattr(x.type, "type") + and isinstance(x.type, c_ast.TypeDecl) + and isinstance(x.type.type, c_ast.Enum) +] # Enum member access functions. + def get_enum_members(obj_name): global enums - if not obj_name in enums: + if obj_name not in enums: return [] return [enum_member_name for enum_member_name, value in enums[obj_name].items()] + def get_enum_member_name(enum_member): if enum_member[0].isdigit(): - enum_member = '_' + enum_member # needs to be a valid attribute name + enum_member = "_" + enum_member # needs to be a valid attribute name return enum_member + def get_enum_value(obj_name, enum_member): return enums[obj_name][enum_member] + # eprint(enums) # parse function pointers -func_typedefs = collections.OrderedDict((t.name, t) for t in ast.ext if isinstance(t, c_ast.Typedef) and isinstance(t.type, c_ast.PtrDecl) and isinstance(t.type.type, c_ast.FuncDecl)) +func_typedefs = collections.OrderedDict( + (t.name, t) + for t in ast.ext + if isinstance(t, c_ast.Typedef) + and isinstance(t.type, c_ast.PtrDecl) + and isinstance(t.type.type, c_ast.FuncDecl) +) # Global blobs -blobs = collections.OrderedDict((decl.name, decl.type.type) for decl in ast.ext \ - if isinstance(decl, c_ast.Decl) \ - and 'extern' in decl.storage \ - and hasattr(decl, 'type') \ - and isinstance(decl.type, c_ast.TypeDecl) - and not decl.name.startswith('_')) - -blobs['_nesting'] = parser.parse('extern int _nesting;').ext[0].type.type +blobs = collections.OrderedDict( + (decl.name, decl.type.type) + for decl in ast.ext + if isinstance(decl, c_ast.Decl) + and "extern" in decl.storage + and hasattr(decl, "type") + and isinstance(decl.type, c_ast.TypeDecl) + and not decl.name.startswith("_") +) + +blobs["_nesting"] = parser.parse("extern int _nesting;").ext[0].type.type int_constants = [] @@ -544,125 +765,127 @@ def get_enum_value(obj_name, enum_member): # Type convertors # + class MissingConversionException(ValueError): pass + mp_to_lv = { - 'mp_obj_t' : '(mp_obj_t)', - 'va_list' : None, - 'void *' : 'mp_to_ptr', - 'const uint8_t *' : 'mp_to_ptr', - 'const void *' : 'mp_to_ptr', - 'bool' : 'mp_obj_is_true', - 'char *' : '(char*)convert_from_str', - 'char **' : 'mp_write_ptr_C_Pointer', - 'const char *' : 'convert_from_str', - 'const char **' : 'mp_write_ptr_C_Pointer', - '%s_obj_t *'% module_prefix : 'mp_to_lv', - 'uint8_t' : '(uint8_t)mp_obj_get_int', - 'uint16_t' : '(uint16_t)mp_obj_get_int', - 'uint32_t' : '(uint32_t)mp_obj_get_int', - 'uint64_t' : '(uint64_t)mp_obj_get_ull', - 'unsigned' : '(unsigned)mp_obj_get_int', - 'unsigned int' : '(unsigned int)mp_obj_get_int', - 'unsigned char' : '(unsigned char)mp_obj_get_int', - 'unsigned short' : '(unsigned short)mp_obj_get_int', - 'unsigned long' : '(unsigned long)mp_obj_get_int', - 'unsigned long int' : '(unsigned long int)mp_obj_get_int', - 'unsigned long long' : '(unsigned long long)mp_obj_get_ull', - 'unsigned long long int' : '(unsigned long long int)mp_obj_get_ull', - 'int8_t' : '(int8_t)mp_obj_get_int', - 'int16_t' : '(int16_t)mp_obj_get_int', - 'int32_t' : '(int32_t)mp_obj_get_int', - 'int64_t' : '(int64_t)mp_obj_get_ull', - 'size_t' : '(size_t)mp_obj_get_int', - 'int' : '(int)mp_obj_get_int', - 'char' : '(char)mp_obj_get_int', - 'short' : '(short)mp_obj_get_int', - 'long' : '(long)mp_obj_get_int', - 'long int' : '(long int)mp_obj_get_int', - 'long long' : '(long long)mp_obj_get_ull', - 'long long int' : '(long long int)mp_obj_get_ull', - 'float' : '(float)mp_obj_get_float', + "mp_obj_t": "(mp_obj_t)", + "va_list": None, + "void *": "mp_to_ptr", + "const uint8_t *": "mp_to_ptr", + "const void *": "mp_to_ptr", + "bool": "mp_obj_is_true", + "char *": "(char*)convert_from_str", + "char **": "mp_write_ptr_C_Pointer", + "const char *": "convert_from_str", + "const char **": "mp_write_ptr_C_Pointer", + "%s_obj_t *" % module_prefix: "mp_to_lv", + "uint8_t": "(uint8_t)mp_obj_get_int", + "uint16_t": "(uint16_t)mp_obj_get_int", + "uint32_t": "(uint32_t)mp_obj_get_int", + "uint64_t": "(uint64_t)mp_obj_get_ull", + "unsigned": "(unsigned)mp_obj_get_int", + "unsigned int": "(unsigned int)mp_obj_get_int", + "unsigned char": "(unsigned char)mp_obj_get_int", + "unsigned short": "(unsigned short)mp_obj_get_int", + "unsigned long": "(unsigned long)mp_obj_get_int", + "unsigned long int": "(unsigned long int)mp_obj_get_int", + "unsigned long long": "(unsigned long long)mp_obj_get_ull", + "unsigned long long int": "(unsigned long long int)mp_obj_get_ull", + "int8_t": "(int8_t)mp_obj_get_int", + "int16_t": "(int16_t)mp_obj_get_int", + "int32_t": "(int32_t)mp_obj_get_int", + "int64_t": "(int64_t)mp_obj_get_ull", + "size_t": "(size_t)mp_obj_get_int", + "int": "(int)mp_obj_get_int", + "char": "(char)mp_obj_get_int", + "short": "(short)mp_obj_get_int", + "long": "(long)mp_obj_get_int", + "long int": "(long int)mp_obj_get_int", + "long long": "(long long)mp_obj_get_ull", + "long long int": "(long long int)mp_obj_get_ull", + "float": "(float)mp_obj_get_float", } lv_to_mp = { - 'mp_obj_t' : '(mp_obj_t)', - 'va_list' : None, - 'void *' : 'ptr_to_mp', - 'const uint8_t *' : 'ptr_to_mp', - 'const void *' : 'ptr_to_mp', - 'bool' : 'convert_to_bool', - 'char *' : 'convert_to_str', - 'char **' : 'mp_read_ptr_C_Pointer', - 'const char *' : 'convert_to_str', - 'const char **' : 'mp_read_ptr_C_Pointer', - '%s_obj_t *'% module_prefix : 'lv_to_mp', - 'uint8_t' : 'mp_obj_new_int_from_uint', - 'uint16_t' : 'mp_obj_new_int_from_uint', - 'uint32_t' : 'mp_obj_new_int_from_uint', - 'uint64_t' : 'mp_obj_new_int_from_ull', - 'unsigned' : 'mp_obj_new_int_from_uint', - 'unsigned int' : 'mp_obj_new_int_from_uint', - 'unsigned char' : 'mp_obj_new_int_from_uint', - 'unsigned short' : 'mp_obj_new_int_from_uint', - 'unsigned long' : 'mp_obj_new_int_from_uint', - 'unsigned long int' : 'mp_obj_new_int_from_uint', - 'unsigned long long' : 'mp_obj_new_int_from_ull', - 'unsigned long long int' : 'mp_obj_new_int_from_ull', - 'int8_t' : 'mp_obj_new_int', - 'int16_t' : 'mp_obj_new_int', - 'int32_t' : 'mp_obj_new_int', - 'int64_t' : 'mp_obj_new_int_from_ll', - 'size_t' : 'mp_obj_new_int_from_uint', - 'int' : 'mp_obj_new_int', - 'char' : 'mp_obj_new_int', - 'short' : 'mp_obj_new_int', - 'long' : 'mp_obj_new_int', - 'long int' : 'mp_obj_new_int', - 'long long' : 'mp_obj_new_int_from_ll', - 'long long int' : 'mp_obj_new_int_from_ll', - 'float' : 'mp_obj_new_float', + "mp_obj_t": "(mp_obj_t)", + "va_list": None, + "void *": "ptr_to_mp", + "const uint8_t *": "ptr_to_mp", + "const void *": "ptr_to_mp", + "bool": "convert_to_bool", + "char *": "convert_to_str", + "char **": "mp_read_ptr_C_Pointer", + "const char *": "convert_to_str", + "const char **": "mp_read_ptr_C_Pointer", + "%s_obj_t *" % module_prefix: "lv_to_mp", + "uint8_t": "mp_obj_new_int_from_uint", + "uint16_t": "mp_obj_new_int_from_uint", + "uint32_t": "mp_obj_new_int_from_uint", + "uint64_t": "mp_obj_new_int_from_ull", + "unsigned": "mp_obj_new_int_from_uint", + "unsigned int": "mp_obj_new_int_from_uint", + "unsigned char": "mp_obj_new_int_from_uint", + "unsigned short": "mp_obj_new_int_from_uint", + "unsigned long": "mp_obj_new_int_from_uint", + "unsigned long int": "mp_obj_new_int_from_uint", + "unsigned long long": "mp_obj_new_int_from_ull", + "unsigned long long int": "mp_obj_new_int_from_ull", + "int8_t": "mp_obj_new_int", + "int16_t": "mp_obj_new_int", + "int32_t": "mp_obj_new_int", + "int64_t": "mp_obj_new_int_from_ll", + "size_t": "mp_obj_new_int_from_uint", + "int": "mp_obj_new_int", + "char": "mp_obj_new_int", + "short": "mp_obj_new_int", + "long": "mp_obj_new_int", + "long int": "mp_obj_new_int", + "long long": "mp_obj_new_int_from_ll", + "long long int": "mp_obj_new_int_from_ll", + "float": "mp_obj_new_float", } lv_mp_type = { - 'mp_obj_t' : '%s*' % base_obj_type, - 'va_list' : None, - 'void *' : 'void*', - 'const uint8_t *' : 'void*', - 'const void *' : 'void*', - 'bool' : 'bool', - 'char *' : 'char*', - 'char **' : 'char**', - 'const char *' : 'char*', - 'const char **' : 'char**', - '%s_obj_t *'% module_prefix : '%s*' % base_obj_type, - 'uint8_t' : 'int', - 'uint16_t' : 'int', - 'uint32_t' : 'int', - 'uint64_t' : 'int', - 'unsigned' : 'int', - 'unsigned int' : 'int', - 'unsigned char' : 'int', - 'unsigned short' : 'int', - 'unsigned long' : 'int', - 'unsigned long int' : 'int', - 'unsigned long long' : 'int', - 'unsigned long long int' : 'int', - 'int8_t' : 'int', - 'int16_t' : 'int', - 'int32_t' : 'int', - 'int64_t' : 'int', - 'size_t' : 'int', - 'int' : 'int', - 'char' : 'int', - 'short' : 'int', - 'long' : 'int', - 'long int' : 'int', - 'long long' : 'int', - 'long long int' : 'int', - 'void' : None, - 'float' : 'float', + "mp_obj_t": "%s*" % base_obj_type, + "va_list": None, + "void *": "void*", + "const uint8_t *": "void*", + "const void *": "void*", + "bool": "bool", + "char *": "char*", + "char **": "char**", + "const char *": "char*", + "const char **": "char**", + "%s_obj_t *" % module_prefix: "%s*" % base_obj_type, + "uint8_t": "int", + "uint16_t": "int", + "uint32_t": "int", + "uint64_t": "int", + "unsigned": "int", + "unsigned int": "int", + "unsigned char": "int", + "unsigned short": "int", + "unsigned long": "int", + "unsigned long int": "int", + "unsigned long long": "int", + "unsigned long long int": "int", + "int8_t": "int", + "int16_t": "int", + "int32_t": "int", + "int64_t": "int", + "size_t": "int", + "int": "int", + "char": "int", + "short": "int", + "long": "int", + "long int": "int", + "long long": "int", + "long long int": "int", + "void": None, + "float": "float", } lv_to_mp_byref = {} @@ -672,60 +895,57 @@ class MissingConversionException(ValueError): # These types would be converted automatically to/from array type. # Supported array (pointer) types are signed/unsigned int: 8bit, 16bit, 32bit and 64bit. + def register_int_ptr_type(convertor, *types): for ptr_type in types: - for qualified_ptr_type in [ptr_type, 'const '+ptr_type]: - mp_to_lv[qualified_ptr_type] = 'mp_array_to_%s' % (convertor) - lv_to_mp[qualified_ptr_type] = 'mp_array_from_%s' % (convertor) - lv_mp_type[qualified_ptr_type] = 'void*' + for qualified_ptr_type in [ptr_type, "const " + ptr_type]: + mp_to_lv[qualified_ptr_type] = "mp_array_to_%s" % (convertor) + lv_to_mp[qualified_ptr_type] = "mp_array_from_%s" % (convertor) + lv_mp_type[qualified_ptr_type] = "void*" + -register_int_ptr_type('u8ptr', - 'unsigned char *', - 'uint8_t *') +register_int_ptr_type("u8ptr", "unsigned char *", "uint8_t *") # char* is considered as string, not int ptr! -''' +""" register_int_ptr_type('i8ptr', 'char *', 'int8_t *') -''' - -register_int_ptr_type('u16ptr', - 'unsigned short *', - 'uint16_t *') - -register_int_ptr_type('i16ptr', - 'short *', - 'int16_t *') - -register_int_ptr_type('u32ptr', - 'uint32_t *', - 'unsigned *', - 'unsigned int *', - 'unsigned long *', - 'unsigned long int *', - 'size_t *') - -register_int_ptr_type('i32ptr', - 'int32_t *', - 'signed *', - 'signed int *', - 'signed long *', - 'signed long int *', - 'long *', - 'long int *', - 'int *') - -register_int_ptr_type('u64ptr', - 'int64_t *', - 'signed long long *', - 'long long *', - 'long long int *') - -register_int_ptr_type('i64ptr', - 'uint64_t *', - 'unsigned long long *', - 'unsigned long long int *') +""" + +register_int_ptr_type("u16ptr", "unsigned short *", "uint16_t *") + +register_int_ptr_type("i16ptr", "short *", "int16_t *") + +register_int_ptr_type( + "u32ptr", + "uint32_t *", + "unsigned *", + "unsigned int *", + "unsigned long *", + "unsigned long int *", + "size_t *", +) + +register_int_ptr_type( + "i32ptr", + "int32_t *", + "signed *", + "signed int *", + "signed long *", + "signed long int *", + "long *", + "long int *", + "int *", +) + +register_int_ptr_type( + "u64ptr", "int64_t *", "signed long long *", "long long *", "long long int *" +) + +register_int_ptr_type( + "i64ptr", "uint64_t *", "unsigned long long *", "unsigned long long int *" +) # @@ -734,17 +954,18 @@ def register_int_ptr_type(convertor, *types): headers = args.input for header in headers: - if 'lvgl.h' in header: + if "lvgl.h" in header: path, _ = os.path.split(header) - if path and path != 'lvgl.h': - path = os.path.join(path, 'src', 'lvgl_private.h') + if path and path != "lvgl.h": + path = os.path.join(path, "src", "lvgl_private.h") else: - path = 'src/lvgl_private.h' - + path = "src/lvgl_private.h" + headers.append(path) break -print (""" +print( + """ /* * Auto-Generated file, DO NOT EDIT! * @@ -778,18 +999,23 @@ def register_int_ptr_type(convertor, *types): {lv_headers} """.format( - module_name = module_name, - cmd_line=' '.join(argv), + module_name=module_name, + cmd_line=" ".join(argv), pp_cmd=pp_cmd, - objs=", ".join(['%s(%s)' % (objname, parent_obj_names[objname]) for objname in obj_names]), - lv_headers='\n'.join('#include "%s"' % header for header in headers))) + objs=", ".join( + ["%s(%s)" % (objname, parent_obj_names[objname]) for objname in obj_names] + ), + lv_headers="\n".join('#include "%s"' % header for header in headers), + ) +) # # Enable objects, if supported # if len(obj_names) > 0: - print(""" + print( + """ #define LV_OBJ_T {obj_type} typedef struct mp_lv_obj_type_t {{ @@ -797,25 +1023,24 @@ def register_int_ptr_type(convertor, *types): const mp_obj_type_t *mp_obj_type; }} mp_lv_obj_type_t; -STATIC const mp_lv_obj_type_t mp_lv_{base_obj}_type; -STATIC const mp_lv_obj_type_t *mp_lv_obj_types[]; +static const mp_lv_obj_type_t mp_lv_{base_obj}_type; +static const mp_lv_obj_type_t *mp_lv_obj_types[]; -STATIC inline const mp_obj_type_t *get_BaseObj_type() +static inline const mp_obj_type_t *get_BaseObj_type() {{ return mp_lv_{base_obj}_type.mp_obj_type; }} MP_DEFINE_EXCEPTION(LvReferenceError, Exception) - """.format( - obj_type = base_obj_type, - base_obj = base_obj_name - )) + """.format(obj_type=base_obj_type, base_obj=base_obj_name) + ) # # Emit Mpy helper functions # -print(""" +print( + """ /* * Helper functions */ @@ -839,28 +1064,28 @@ def register_int_ptr_type(convertor, *types): void *lv_fun; } mp_lv_obj_fun_builtin_var_t; -STATIC mp_obj_t lv_fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); -STATIC mp_int_t mp_func_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); +static mp_obj_t lv_fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); +static mp_int_t mp_func_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); -GENMPY_UNUSED STATIC MP_DEFINE_CONST_OBJ_TYPE( +GENMPY_UNUSED static MP_DEFINE_CONST_OBJ_TYPE( mp_lv_type_fun_builtin_var, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, call, lv_fun_builtin_var_call, - unary_op, mp_generic_unary_op, + unary_op, mp_obj_int_unary_op, buffer, mp_func_get_buffer ); -GENMPY_UNUSED STATIC MP_DEFINE_CONST_OBJ_TYPE( +GENMPY_UNUSED static MP_DEFINE_CONST_OBJ_TYPE( mp_lv_type_fun_builtin_static_var, MP_QSTR_function, MP_TYPE_FLAG_BUILTIN_FUN, call, lv_fun_builtin_var_call, - unary_op, mp_generic_unary_op, + unary_op, mp_obj_int_unary_op, buffer, mp_func_get_buffer ); -STATIC mp_obj_t lv_fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +static mp_obj_t lv_fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { assert(MP_OBJ_IS_TYPE(self_in, &mp_lv_type_fun_builtin_var) || MP_OBJ_IS_TYPE(self_in, &mp_lv_type_fun_builtin_static_var)); mp_lv_obj_fun_builtin_var_t *self = MP_OBJ_TO_PTR(self_in); @@ -868,7 +1093,7 @@ def register_int_ptr_type(convertor, *types): return self->mp_fun(n_args, args, self->lv_fun); } -STATIC mp_int_t mp_func_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { +static mp_int_t mp_func_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { (void)flags; assert(MP_OBJ_IS_TYPE(self_in, &mp_lv_type_fun_builtin_var) || MP_OBJ_IS_TYPE(self_in, &mp_lv_type_fun_builtin_static_var)); @@ -896,17 +1121,17 @@ def register_int_ptr_type(convertor, *types): void *data; } mp_lv_struct_t; -STATIC const mp_lv_struct_t mp_lv_null_obj; +static const mp_lv_struct_t mp_lv_null_obj; #ifdef LV_OBJ_T -STATIC mp_int_t mp_lv_obj_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); +static mp_int_t mp_lv_obj_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); #else -STATIC mp_int_t mp_lv_obj_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags){ return 0; } +static mp_int_t mp_lv_obj_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags){ return 0; } #endif -STATIC mp_int_t mp_blob_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); +static mp_int_t mp_blob_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); -STATIC mp_obj_t get_native_obj(mp_obj_t mp_obj) +static mp_obj_t get_native_obj(mp_obj_t mp_obj) { if (!MP_OBJ_IS_OBJ(mp_obj)) return mp_obj; const mp_obj_type_t *native_type = ((mp_obj_base_t*)mp_obj)->type; @@ -920,15 +1145,15 @@ def register_int_ptr_type(convertor, *types): return mp_obj_cast_to_native_base(mp_obj, MP_OBJ_FROM_PTR(native_type)); } -STATIC mp_obj_t dict_to_struct(mp_obj_t dict, const mp_obj_type_t *type); +static mp_obj_t dict_to_struct(mp_obj_t dict, const mp_obj_type_t *type); -STATIC mp_obj_t make_new_lv_struct( +static mp_obj_t make_new_lv_struct( const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); -STATIC mp_obj_t cast(mp_obj_t mp_obj, const mp_obj_type_t *mp_type) +static mp_obj_t cast(mp_obj_t mp_obj, const mp_obj_type_t *mp_type) { mp_obj_t res = NULL; if (mp_obj == mp_const_none && MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_type, make_new) == &make_new_lv_struct) { @@ -962,7 +1187,7 @@ def register_int_ptr_type(convertor, *types): LV_OBJ_T *callbacks; } mp_lv_obj_t; -STATIC inline LV_OBJ_T *mp_to_lv(mp_obj_t mp_obj) +static inline LV_OBJ_T *mp_to_lv(mp_obj_t mp_obj) { if (mp_obj == NULL || mp_obj == mp_const_none) return NULL; mp_obj_t native_obj = get_native_obj(mp_obj); @@ -977,7 +1202,7 @@ def register_int_ptr_type(convertor, *types): return mp_lv_obj->lv_obj; } -STATIC inline LV_OBJ_T *mp_get_callbacks(mp_obj_t mp_obj) +static inline LV_OBJ_T *mp_get_callbacks(mp_obj_t mp_obj) { if (mp_obj == NULL || mp_obj == mp_const_none) return NULL; mp_lv_obj_t *mp_lv_obj = MP_OBJ_TO_PTR(get_native_obj(mp_obj)); @@ -989,9 +1214,9 @@ def register_int_ptr_type(convertor, *types): return mp_lv_obj->callbacks; } -STATIC inline const mp_obj_type_t *get_BaseObj_type(); +static inline const mp_obj_type_t *get_BaseObj_type(); -STATIC void mp_lv_delete_cb(lv_event_t * e) +static void mp_lv_delete_cb(lv_event_t * e) { LV_OBJ_T *lv_obj = e->current_target; if (lv_obj){ @@ -1002,7 +1227,7 @@ def register_int_ptr_type(convertor, *types): } } -STATIC inline mp_obj_t lv_to_mp(LV_OBJ_T *lv_obj) +static inline mp_obj_t lv_to_mp(LV_OBJ_T *lv_obj) { if (lv_obj == NULL) return mp_const_none; mp_lv_obj_t *self = (mp_lv_obj_t*)lv_obj->user_data; @@ -1036,9 +1261,9 @@ def register_int_ptr_type(convertor, *types): return MP_OBJ_FROM_PTR(self); } -STATIC void* mp_to_ptr(mp_obj_t self_in); +static void* mp_to_ptr(mp_obj_t self_in); -STATIC mp_obj_t cast_obj_type(const mp_obj_type_t* type, mp_obj_t obj) +static mp_obj_t cast_obj_type(const mp_obj_type_t* type, mp_obj_t obj) { mp_lv_obj_t *self = m_new_obj(mp_lv_obj_t); *self = (mp_lv_obj_t){ @@ -1050,12 +1275,12 @@ def register_int_ptr_type(convertor, *types): return MP_OBJ_FROM_PTR(self); } -STATIC mp_obj_t cast_obj(mp_obj_t type_obj, mp_obj_t obj) +static mp_obj_t cast_obj(mp_obj_t type_obj, mp_obj_t obj) { return cast_obj_type((const mp_obj_type_t *)type_obj, obj); } -STATIC mp_obj_t make_new( +static mp_obj_t make_new( const mp_lv_obj_fun_builtin_var_t *lv_obj_var, const mp_obj_type_t *type, size_t n_args, @@ -1081,10 +1306,10 @@ def register_int_ptr_type(convertor, *types): return lv_obj; } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(cast_obj_obj, cast_obj); -STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(cast_obj_class_method, MP_ROM_PTR(&cast_obj_obj)); +static MP_DEFINE_CONST_FUN_OBJ_2(cast_obj_obj, cast_obj); +static MP_DEFINE_CONST_CLASSMETHOD_OBJ(cast_obj_class_method, MP_ROM_PTR(&cast_obj_obj)); -STATIC mp_int_t mp_lv_obj_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { +static mp_int_t mp_lv_obj_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { (void)flags; mp_lv_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -1094,7 +1319,7 @@ def register_int_ptr_type(convertor, *types): return 0; } -STATIC mp_obj_t mp_lv_obj_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) +static mp_obj_t mp_lv_obj_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_lv_obj_t *lhs = MP_OBJ_TO_PTR(lhs_in); mp_lv_obj_t *rhs = MP_OBJ_TO_PTR(rhs_in); @@ -1132,17 +1357,17 @@ def register_int_ptr_type(convertor, *types): #endif -STATIC inline mp_obj_t convert_to_bool(bool b) +static inline mp_obj_t convert_to_bool(bool b) { return b? mp_const_true: mp_const_false; } -STATIC inline mp_obj_t convert_to_str(const char *str) +static inline mp_obj_t convert_to_str(const char *str) { return str? mp_obj_new_str(str, strlen(str)): mp_const_none; } -STATIC inline const char *convert_from_str(mp_obj_t str) +static inline const char *convert_from_str(mp_obj_t str) { if (str == NULL || str == mp_const_none) return NULL; @@ -1160,7 +1385,7 @@ def register_int_ptr_type(convertor, *types): // struct handling -STATIC mp_lv_struct_t *mp_to_lv_struct(mp_obj_t mp_obj) +static mp_lv_struct_t *mp_to_lv_struct(mp_obj_t mp_obj) { if (mp_obj == NULL || mp_obj == mp_const_none) return NULL; mp_obj_t native_obj = get_native_obj(mp_obj); @@ -1171,7 +1396,7 @@ def register_int_ptr_type(convertor, *types): return mp_lv_struct; } -STATIC inline size_t get_lv_struct_size(const mp_obj_type_t *type) +static inline size_t get_lv_struct_size(const mp_obj_type_t *type) { mp_obj_dict_t *self = MP_OBJ_TO_PTR(MP_OBJ_TYPE_GET_SLOT(type, locals_dict)); mp_map_elem_t *elem = mp_map_lookup(&self->map, MP_OBJ_NEW_QSTR(MP_QSTR___SIZE__), MP_MAP_LOOKUP); @@ -1182,7 +1407,7 @@ def register_int_ptr_type(convertor, *types): } } -STATIC mp_obj_t make_new_lv_struct( +static mp_obj_t make_new_lv_struct( const mp_obj_type_t *type, size_t n_args, size_t n_kw, @@ -1211,7 +1436,7 @@ def register_int_ptr_type(convertor, *types): return MP_OBJ_FROM_PTR(self); } -STATIC mp_obj_t lv_struct_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) +static mp_obj_t lv_struct_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_lv_struct_t *lhs = MP_OBJ_TO_PTR(lhs_in); mp_lv_struct_t *rhs = MP_OBJ_TO_PTR(rhs_in); @@ -1226,7 +1451,7 @@ def register_int_ptr_type(convertor, *types): } } -STATIC mp_obj_t lv_struct_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) +static mp_obj_t lv_struct_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_lv_struct_t *self = mp_to_lv_struct(self_in); @@ -1265,7 +1490,7 @@ def register_int_ptr_type(convertor, *types): return MP_OBJ_FROM_PTR(element_at_index); } -GENMPY_UNUSED STATIC void *copy_buffer(const void *buffer, size_t size) +GENMPY_UNUSED static void *copy_buffer(const void *buffer, size_t size) { void *new_buffer = m_malloc(size); memcpy(new_buffer, buffer, size); @@ -1274,7 +1499,7 @@ def register_int_ptr_type(convertor, *types): // Reference an existing lv struct (or part of it) -STATIC mp_obj_t lv_to_mp_struct(const mp_obj_type_t *type, void *lv_struct) +static mp_obj_t lv_to_mp_struct(const mp_obj_type_t *type, void *lv_struct) { if (lv_struct == NULL) return mp_const_none; mp_lv_struct_t *self = m_new_obj(mp_lv_struct_t); @@ -1285,7 +1510,7 @@ def register_int_ptr_type(convertor, *types): return MP_OBJ_FROM_PTR(self); } -STATIC void call_parent_methods(mp_obj_t obj, qstr attr, mp_obj_t *dest) +static void call_parent_methods(mp_obj_t obj, qstr attr, mp_obj_t *dest) { const mp_obj_type_t *type = mp_obj_get_type(obj); while (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) { @@ -1308,7 +1533,7 @@ def register_int_ptr_type(convertor, *types): // Convert dict to struct -STATIC mp_obj_t dict_to_struct(mp_obj_t dict, const mp_obj_type_t *type) +static mp_obj_t dict_to_struct(mp_obj_t dict, const mp_obj_type_t *type) { mp_obj_t mp_struct = make_new_lv_struct(type, 0, 0, NULL); mp_obj_t native_dict = cast(dict, &mp_type_dict); @@ -1330,7 +1555,7 @@ def register_int_ptr_type(convertor, *types): // Convert mp object to ptr -STATIC void* mp_to_ptr(mp_obj_t self_in) +static void* mp_to_ptr(mp_obj_t self_in) { mp_buffer_info_t buffer_info; if (self_in == NULL || self_in == mp_const_none) @@ -1374,14 +1599,14 @@ def register_int_ptr_type(convertor, *types): // Blob is a wrapper for void* -STATIC void mp_blob_print(const mp_print_t *print, +static void mp_blob_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { mp_printf(print, "Blob"); } -STATIC mp_int_t mp_blob_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { +static mp_int_t mp_blob_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { (void)flags; mp_lv_struct_t *self = MP_OBJ_TO_PTR(self_in); @@ -1391,13 +1616,13 @@ def register_int_ptr_type(convertor, *types): return 0; } -STATIC const mp_obj_fun_builtin_var_t mp_lv_dereference_obj; +static const mp_obj_fun_builtin_var_t mp_lv_dereference_obj; // Sometimes (but not always!) Blob represents a Micropython object. // In such cases it's safe to cast the Blob back to the Micropython object // cast argument is the underlying object type, and it's optional. -STATIC mp_obj_t mp_blob_cast(size_t argc, const mp_obj_t *argv) +static mp_obj_t mp_blob_cast(size_t argc, const mp_obj_t *argv) { mp_obj_t self = argv[0]; void *ptr = mp_to_ptr(self); @@ -1410,16 +1635,16 @@ def register_int_ptr_type(convertor, *types): return cast(MP_OBJ_FROM_PTR(ptr), type); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_blob_cast_obj, 1, 2, mp_blob_cast); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_blob_cast_obj, 1, 2, mp_blob_cast); -STATIC const mp_rom_map_elem_t mp_blob_locals_dict_table[] = { +static const mp_rom_map_elem_t mp_blob_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___dereference__), MP_ROM_PTR(&mp_lv_dereference_obj) }, { MP_ROM_QSTR(MP_QSTR___cast__), MP_ROM_PTR(&mp_blob_cast_obj) }, }; -STATIC MP_DEFINE_CONST_DICT(mp_blob_locals_dict, mp_blob_locals_dict_table); +static MP_DEFINE_CONST_DICT(mp_blob_locals_dict, mp_blob_locals_dict_table); -STATIC MP_DEFINE_CONST_OBJ_TYPE( +static MP_DEFINE_CONST_OBJ_TYPE( mp_blob_type, MP_QSTR_Blob, MP_TYPE_FLAG_NONE, @@ -1429,16 +1654,16 @@ def register_int_ptr_type(convertor, *types): buffer, mp_blob_get_buffer ); -STATIC const mp_lv_struct_t mp_lv_null_obj = { {&mp_blob_type}, NULL }; +static const mp_lv_struct_t mp_lv_null_obj = { {&mp_blob_type}, NULL }; -STATIC inline mp_obj_t ptr_to_mp(void *data) +static inline mp_obj_t ptr_to_mp(void *data) { return lv_to_mp_struct(&mp_blob_type, data); } // Cast pointer to struct -STATIC mp_obj_t mp_lv_cast(mp_obj_t type_obj, mp_obj_t ptr_obj) +static mp_obj_t mp_lv_cast(mp_obj_t type_obj, mp_obj_t ptr_obj) { void *ptr = mp_to_ptr(ptr_obj); if (!ptr) return mp_const_none; @@ -1452,21 +1677,21 @@ def register_int_ptr_type(convertor, *types): // Cast instance. Can be used in ISR when memory allocation is prohibited -STATIC inline mp_obj_t mp_lv_cast_instance(mp_obj_t self_in, mp_obj_t ptr_obj) +static inline mp_obj_t mp_lv_cast_instance(mp_obj_t self_in, mp_obj_t ptr_obj) { mp_lv_struct_t *self = MP_OBJ_TO_PTR(self_in); self->data = mp_to_ptr(ptr_obj); return self_in; } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_lv_cast_obj, mp_lv_cast); -STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(mp_lv_cast_class_method, MP_ROM_PTR(&mp_lv_cast_obj)); +static MP_DEFINE_CONST_FUN_OBJ_2(mp_lv_cast_obj, mp_lv_cast); +static MP_DEFINE_CONST_CLASSMETHOD_OBJ(mp_lv_cast_class_method, MP_ROM_PTR(&mp_lv_cast_obj)); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_lv_cast_instance_obj, mp_lv_cast_instance); +static MP_DEFINE_CONST_FUN_OBJ_2(mp_lv_cast_instance_obj, mp_lv_cast_instance); // Dereference a struct/blob. This allows access to the raw data the struct holds -STATIC mp_obj_t mp_lv_dereference(size_t argc, const mp_obj_t *argv) +static mp_obj_t mp_lv_dereference(size_t argc, const mp_obj_t *argv) { mp_obj_t self_in = argv[0]; mp_obj_t size_in = argc > 1? argv[1]: mp_const_none; @@ -1485,7 +1710,7 @@ def register_int_ptr_type(convertor, *types): return MP_OBJ_FROM_PTR(view); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_lv_dereference_obj, 1, 2, mp_lv_dereference); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_lv_dereference_obj, 1, 2, mp_lv_dereference); // Callback function handling // Callback is either a callable object or a pointer. If it's a callable object, set user_data to the callback. @@ -1493,7 +1718,7 @@ def register_int_ptr_type(convertor, *types): // In case of an lv_obj_t, user_data is mp_lv_obj_t which contains a member "callbacks" for that dict. // In case of a struct, user_data is a pointer to that dict directly -STATIC mp_obj_t get_callback_dict_from_user_data(void *user_data) +static mp_obj_t get_callback_dict_from_user_data(void *user_data) { if (user_data){ mp_obj_t obj = MP_OBJ_FROM_PTR(user_data); @@ -1511,7 +1736,7 @@ def register_int_ptr_type(convertor, *types): typedef void *(*mp_lv_get_user_data)(void *); typedef void (*mp_lv_set_user_data)(void *, void *); -STATIC void *mp_lv_callback(mp_obj_t mp_callback, void *lv_callback, qstr callback_name, +static void *mp_lv_callback(mp_obj_t mp_callback, void *lv_callback, qstr callback_name, void **user_data_ptr, void *containing_struct, mp_lv_get_user_data get_user_data, mp_lv_set_user_data set_user_data) { if (lv_callback && mp_obj_is_callable(mp_callback)) { @@ -1543,7 +1768,7 @@ def register_int_ptr_type(convertor, *types): // Function pointers wrapper -STATIC mp_obj_t mp_lv_funcptr(const mp_lv_obj_fun_builtin_var_t *mp_fun, void *lv_fun, void *lv_callback, qstr func_name, void *user_data) +static mp_obj_t mp_lv_funcptr(const mp_lv_obj_fun_builtin_var_t *mp_fun, void *lv_fun, void *lv_callback, qstr func_name, void *user_data) { if (lv_fun == NULL) return mp_const_none; @@ -1560,7 +1785,7 @@ def register_int_ptr_type(convertor, *types): // Missing implementation for 64bit integer conversion -STATIC unsigned long long mp_obj_get_ull(mp_obj_t obj) +static unsigned long long mp_obj_get_ull(mp_obj_t obj) { if (mp_obj_is_small_int(obj)) return MP_OBJ_SMALL_INT_VALUE(obj); @@ -1581,7 +1806,7 @@ def register_int_ptr_type(convertor, *types): bool is_signed; } mp_lv_array_t; -STATIC void mp_lv_array_print(const mp_print_t *print, +static void mp_lv_array_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -1591,7 +1816,7 @@ def register_int_ptr_type(convertor, *types): mp_printf(print, "C Array (%sint%d[])", is_signed? "": "u", element_size*8); } -STATIC mp_obj_t lv_array_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) +static mp_obj_t lv_array_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_lv_array_t *self = MP_OBJ_TO_PTR(self_in); @@ -1632,15 +1857,15 @@ def register_int_ptr_type(convertor, *types): return self_in; } -STATIC const mp_rom_map_elem_t mp_base_struct_locals_dict_table[] = { +static const mp_rom_map_elem_t mp_base_struct_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___cast__), MP_ROM_PTR(&mp_lv_cast_class_method) }, { MP_ROM_QSTR(MP_QSTR___cast_instance__), MP_ROM_PTR(&mp_lv_cast_instance_obj) }, { MP_ROM_QSTR(MP_QSTR___dereference__), MP_ROM_PTR(&mp_lv_dereference_obj) }, }; -STATIC MP_DEFINE_CONST_DICT(mp_base_struct_locals_dict, mp_base_struct_locals_dict_table); +static MP_DEFINE_CONST_DICT(mp_base_struct_locals_dict, mp_base_struct_locals_dict_table); -STATIC MP_DEFINE_CONST_OBJ_TYPE( +static MP_DEFINE_CONST_OBJ_TYPE( mp_lv_base_struct_type, MP_QSTR_Struct, MP_TYPE_FLAG_NONE, @@ -1651,7 +1876,7 @@ def register_int_ptr_type(convertor, *types): ); // TODO: provide constructor -STATIC MP_DEFINE_CONST_OBJ_TYPE( +static MP_DEFINE_CONST_OBJ_TYPE( mp_lv_array_type, MP_QSTR_C_Array, MP_TYPE_FLAG_NONE, @@ -1662,7 +1887,7 @@ def register_int_ptr_type(convertor, *types): locals_dict, &mp_base_struct_locals_dict ); -GENMPY_UNUSED STATIC mp_obj_t mp_array_from_ptr(void *lv_arr, size_t element_size, bool is_signed) +GENMPY_UNUSED static mp_obj_t mp_array_from_ptr(void *lv_arr, size_t element_size, bool is_signed) { mp_lv_array_t *self = m_new_obj(mp_lv_array_t); *self = (mp_lv_array_t){ @@ -1673,7 +1898,7 @@ def register_int_ptr_type(convertor, *types): return MP_OBJ_FROM_PTR(self); } -GENMPY_UNUSED STATIC void *mp_array_to_ptr(mp_obj_t *mp_arr, size_t element_size, GENMPY_UNUSED bool is_signed) +GENMPY_UNUSED static void *mp_array_to_ptr(mp_obj_t *mp_arr, size_t element_size, GENMPY_UNUSED bool is_signed) { if (MP_OBJ_IS_STR_OR_BYTES(mp_arr) || MP_OBJ_IS_TYPE(mp_arr, &mp_type_bytearray) || @@ -1706,11 +1931,11 @@ def register_int_ptr_type(convertor, *types): } #define MP_ARRAY_CONVERTOR(name, size, is_signed) \ -GENMPY_UNUSED STATIC mp_obj_t mp_array_from_ ## name(void *lv_arr)\ +GENMPY_UNUSED static mp_obj_t mp_array_from_ ## name(void *lv_arr)\ {\ return mp_array_from_ptr(lv_arr, size, is_signed);\ }\ -GENMPY_UNUSED STATIC void *mp_array_to_ ## name(mp_obj_t mp_arr)\ +GENMPY_UNUSED static void *mp_array_to_ ## name(mp_obj_t mp_arr)\ {\ return mp_array_to_ptr(mp_arr, size, is_signed);\ } @@ -1724,7 +1949,8 @@ def register_int_ptr_type(convertor, *types): MP_ARRAY_CONVERTOR(u64ptr, 8, false) MP_ARRAY_CONVERTOR(i64ptr, 8, true) -""") +""" +) # # Add regular enums with integer values @@ -1733,24 +1959,36 @@ def register_int_ptr_type(convertor, *types): enums = collections.OrderedDict() for enum_def in enum_defs: # Skip stdatomic.h memory_order, no bindings needed. - if isinstance(enum_def, c_ast.TypeDecl) and enum_def.declname == 'memory_order': + if isinstance(enum_def, c_ast.TypeDecl) and enum_def.declname == "memory_order": continue # eprint("--> %s" % enum_def) - while hasattr(enum_def.type, 'name') and not enum_def.type.values: - enum_def = next(e for e in enum_defs if hasattr(e.type, 'name') and e.type.name == enum_def.type.name and e.type.values) - member_names = [member.name for member in enum_def.type.values.enumerators if not member.name.startswith('_')] + while hasattr(enum_def.type, "name") and not enum_def.type.values: + enum_def = next( + e + for e in enum_defs + if hasattr(e.type, "name") + and e.type.name == enum_def.type.name + and e.type.values + ) + member_names = [ + member.name + for member in enum_def.type.values.enumerators + if not member.name.startswith("_") + ] enum_name = commonprefix(member_names) - enum_name = "_".join(enum_name.split("_")[:-1]) # remove suffix + enum_name = "_".join(enum_name.split("_")[:-1]) # remove suffix enum = collections.OrderedDict() for member in enum_def.type.values.enumerators: - if member.name.startswith('_'): + if member.name.startswith("_"): continue - member_name = member.name[len(enum_name)+1:] if len(enum_name) > 0 else member.name + member_name = ( + member.name[len(enum_name) + 1 :] if len(enum_name) > 0 else member.name + ) if member_name[0].isdigit(): - member_name = '_' + member_name - if len(enum_name) > 0 and get_enum_name(enum_name) != 'ENUM': - enum[member_name] = 'MP_ROM_INT(%s)' % member.name + member_name = "_" + member_name + if len(enum_name) > 0 and get_enum_name(enum_name) != "ENUM": + enum[member_name] = "MP_ROM_INT(%s)" % member.name else: int_constants.append(member.name) if len(enum) > 0: @@ -1761,31 +1999,39 @@ def register_int_ptr_type(convertor, *types): else: enums[enum_name] = enum -for enum in [enum for enum in enums if len(enums[enum]) == 1 and enum.startswith('ENUM')]: - int_constants.append('%s_%s' % (enum, next(iter(enums[enum])))) +for enum in [ + enum for enum in enums if len(enums[enum]) == 1 and enum.startswith("ENUM") +]: + int_constants.append("%s_%s" % (enum, next(iter(enums[enum])))) del enums[enum] # Add special string enums -print (''' +print( + """ /* * LVGL string constants */ -''') +""" +) for enum_def in enum_defs: if not enum_def.type.values: continue - member_names = [str_enum_to_str(member.name) for member in enum_def.type.values.enumerators if lv_str_enum_pattern.match(member.name)] + member_names = [ + str_enum_to_str(member.name) + for member in enum_def.type.values.enumerators + if lv_str_enum_pattern.match(member.name) + ] enum_name = commonprefix(member_names) - enum_name = "_".join(enum_name.split("_")[:-1]) # remove suffix + enum_name = "_".join(enum_name.split("_")[:-1]) # remove suffix enum = collections.OrderedDict() if enum_name: for member in enum_def.type.values.enumerators: full_name = str_enum_to_str(member.name) - member_name = full_name[len(enum_name)+1:] - print('MP_DEFINE_STR_OBJ(mp_%s, %s);' % (full_name, full_name)) - enum[member_name] = '&mp_%s' % full_name + member_name = full_name[len(enum_name) + 1 :] + print("MP_DEFINE_STR_OBJ(mp_%s, %s);" % (full_name, full_name)) + enum[member_name] = "&mp_%s" % full_name if len(enum) > 0: if enum_name in enums: enums[enum_name].update(enum) @@ -1796,24 +2042,26 @@ def register_int_ptr_type(convertor, *types): # eprint('--> enums: \n%s' % enums) - # eprint(',\n'.join(sorted('%s : %s' % (name, get_type(blobs[name])) for name in blobs))) # # Callbacks helper functions # + def decl_to_callback(decl): # print('/* decl_to_callback %s */' % decl) - if not hasattr(decl, 'type'): + if not hasattr(decl, "type"): return None - if (isinstance(decl.type, c_ast.PtrDecl) and isinstance(decl.type.type, c_ast.FuncDecl)): + if isinstance(decl.type, c_ast.PtrDecl) and isinstance( + decl.type.type, c_ast.FuncDecl + ): return (decl.name, decl.type.type) # print('/* callback: ADDED CALLBACK: %s\n%s */' % (gen.visit(decl.type.type), decl.type.type)) elif isinstance(decl.type, c_ast.FuncDecl): return (decl.name, decl.type) # print('/* callback: ADDED CALLBACK: %s\n%s */' % (gen.visit(decl.type.type), decl.type.type)) - elif isinstance(decl.type, c_ast.TypeDecl) and hasattr(decl.type.type,'names'): + elif isinstance(decl.type, c_ast.TypeDecl) and hasattr(decl.type.type, "names"): func_typedef_name = decl.type.type.names[0] while func_typedef_name in synonym: # eprint('decl_to_callback: %s --> %s' % (func_typedef_name, synonym[func_typedef_name])) @@ -1822,38 +2070,52 @@ def decl_to_callback(decl): if func_typedef_name in func_typedefs: return (decl.name, func_typedefs[func_typedef_name].type.type) # print('/* callback: ADDED CALLBACK: %s\n%s */' % (func_typedef_name, func_typedefs[func_typedef_name])) - else: return None + else: + return None + -def get_user_data_accessors(containing_struct, containing_struct_name = None): +def get_user_data_accessors(containing_struct, containing_struct_name=None): if not containing_struct_name and containing_struct and containing_struct.name: containing_struct_name = containing_struct.name if not containing_struct_name: return None, None base_struct_name = get_base_struct_name(containing_struct_name) - getter_name = base_struct_name + '_get_user_data' - setter_name = base_struct_name + '_set_user_data' + getter_name = base_struct_name + "_get_user_data" + setter_name = base_struct_name + "_set_user_data" # print('/* struct functions = %s */' % [s.name + ':' + str(len(s.type.args.params)) for s in get_struct_functions(containing_struct_name)]) # print('/* getter_name = %s */' % getter_name) struct_functions = get_struct_functions(containing_struct_name) - getters = [s for s in struct_functions if s.name == getter_name and len(s.type.args.params) == 1] - setters = [s for s in struct_functions if s.name == setter_name and len(s.type.args.params) == 2] + getters = [ + s + for s in struct_functions + if s.name == getter_name and len(s.type.args.params) == 1 + ] + setters = [ + s + for s in struct_functions + if s.name == setter_name and len(s.type.args.params) == 2 + ] if getters and setters: return getters[0], setters[0] else: return None, None -def get_user_data(func, func_name = None, containing_struct = None, containing_struct_name = None): + +def get_user_data( + func, func_name=None, containing_struct=None, containing_struct_name=None +): args = func.args.params - if not func_name: func_name = get_arg_name(func.type) + if not func_name: + func_name = get_arg_name(func.type) # print('/* --> callback: func_name = %s, args = %s */' % (func_name, repr(args))) user_data_found = False - user_data = 'None' + user_data = "None" if len(args) > 0 and isinstance(args[0].type, c_ast.PtrDecl): # if isinstance(args[0].type.type.type, c_ast.Struct): # struct_arg_type_name = args[0].type.type.type.name # PtrDecl.TypeDecl.Struct. Needed to omit 'struct' keyword. # else: # struct_arg_type_name = get_type(args[0].type.type, remove_quals = True) - struct_arg_type_name = get_type(args[0].type.type, remove_quals = True) + struct_arg_type_name = get_type(args[0].type.type, remove_quals=True) # print('/* --> get_user_data: containing_struct_name = %s, struct_arg_type_name = %s */' % (containing_struct_name, struct_arg_type_name)) if containing_struct_name and struct_arg_type_name != containing_struct_name: return None, None, None @@ -1866,14 +2128,27 @@ def get_user_data(func, func_name = None, containing_struct = None, containing_s # print('/* --> callback: %s First argument is %s */' % (gen.visit(func), struct_arg_type_name)) if containing_struct: flatten_struct_decls = flatten_struct(containing_struct.decls) - user_data = 'user_data' + user_data = "user_data" user_data_found = user_data in [decl.name for decl in flatten_struct_decls] # print('/* --> callback: user_data=%s user_data_found=%s containing_struct=%s */' % (user_data, user_data_found, containing_struct)) if not user_data_found and lvgl_json is not None: - containing_struct_j = next((struct for struct in lvgl_json["structures"] if struct["name"] == struct_arg_type_name), None) + containing_struct_j = next( + ( + struct + for struct in lvgl_json["structures"] + if struct["name"] == struct_arg_type_name + ), + None, + ) if containing_struct_j is not None: - user_data_found = any(user_data == field["name"] for field in containing_struct_j["fields"]) - return (user_data if user_data_found else None), *get_user_data_accessors(containing_struct, containing_struct_name) + user_data_found = any( + user_data == field["name"] + for field in containing_struct_j["fields"] + ) + return (user_data if user_data_found else None), *get_user_data_accessors( + containing_struct, containing_struct_name + ) + # # Generate structs when needed @@ -1884,9 +2159,11 @@ def get_user_data(func, func_name = None, containing_struct = None, containing_s struct_aliases = collections.OrderedDict() callbacks_used_on_structs = [] + def flatten_struct(struct_decls): result = [] - if not struct_decls: return result + if not struct_decls: + return result for decl in struct_decls: if is_struct(decl.type): result.extend(flatten_struct(decl.type.decls)) @@ -1894,12 +2171,14 @@ def flatten_struct(struct_decls): result.append(decl) return result + def try_generate_struct(struct_name, struct): global lv_to_mp global mp_to_lv - if struct_name in generated_structs: return None + if struct_name in generated_structs: + return None sanitized_struct_name = sanitize(struct_name) - generated_structs[struct_name] = False # Starting generating a struct + generated_structs[struct_name] = False # Starting generating a struct # print("/* Starting generating %s */" % struct_name) if struct_name in mp_to_lv: return mp_to_lv[struct_name] @@ -1919,83 +2198,175 @@ def try_generate_struct(struct_name, struct): for decl in flatten_struct_decls: # print('/* ==> decl %s: %s */' % (gen.visit(decl), decl)) converted = try_generate_type(decl.type) - type_name = get_type(decl.type, remove_quals = True) + type_name = get_type(decl.type, remove_quals=True) # print('/* --> %s: %s (%s)*/' % (decl.name, type_name, mp_to_lv[type_name] if type_name in mp_to_lv else '---')) # Handle the case of nested struct if not converted and is_struct(decl.type.type): parent_name = struct_name child_name = decl.type.declname - type_name = '%s_%s_t' % (parent_name[:-2], child_name) - print('typedef __typeof__( (({parent}*)(0))->{child} ) {new_struct};'.format( - parent = parent_name, child = child_name, new_struct = type_name)) + type_name = "%s_%s_t" % (parent_name[:-2], child_name) + print( + "typedef __typeof__( (({parent}*)(0))->{child} ) {new_struct};".format( + parent=parent_name, child=child_name, new_struct=type_name + ) + ) try_generate_struct(type_name, decl.type.type) # print('==> %s %s: %s' % (type_name, str(type_name in mp_to_lv), decl)) - if (type_name not in mp_to_lv or not mp_to_lv[type_name]) or (type_name not in lv_to_mp or not lv_to_mp[type_name]): + if (type_name not in mp_to_lv or not mp_to_lv[type_name]) or ( + type_name not in lv_to_mp or not lv_to_mp[type_name] + ): # eprint("[%s] %s or %s : %s" % (isinstance(decl.type,c_ast.PtrDecl), type_name, get_type(decl.type), decl.type)) if type_name in generated_structs: - print("/* Already started generating %s! skipping field '%s' */" % (type_name, decl.name)) + print( + "/* Already started generating %s! skipping field '%s' */" + % (type_name, decl.name) + ) continue - raise MissingConversionException('Missing conversion to %s when generating struct %s.%s' % (type_name, struct_name, get_name(decl))) + raise MissingConversionException( + "Missing conversion to %s when generating struct %s.%s" + % (type_name, struct_name, get_name(decl)) + ) mp_to_lv_convertor = mp_to_lv[type_name] - lv_to_mp_convertor = lv_to_mp_byref[type_name] if type_name in lv_to_mp_byref else lv_to_mp[type_name] + lv_to_mp_convertor = ( + lv_to_mp_byref[type_name] + if type_name in lv_to_mp_byref + else lv_to_mp[type_name] + ) - cast = '(void*)' if isinstance(decl.type, c_ast.PtrDecl) else '' # needed when field is const. casting to void overrides it + cast = ( + "(void*)" if isinstance(decl.type, c_ast.PtrDecl) else "" + ) # needed when field is const. casting to void overrides it callback = decl_to_callback(decl) if callback: # print("/* %s callback %s */" % (gen.visit(decl), callback)) - func_name, arg_type = callback - user_data, _, _ = get_user_data(arg_type, func_name = func_name, containing_struct = struct, containing_struct_name = struct_name) - if not callback in callbacks_used_on_structs: + func_name, arg_type = callback + user_data, _, _ = get_user_data( + arg_type, + func_name=func_name, + containing_struct=struct, + containing_struct_name=struct_name, + ) + if callback not in callbacks_used_on_structs: callbacks_used_on_structs.append(callback + (struct_name,)) # Emit callback forward decl. - if user_data in [user_data_decl.name for user_data_decl in flatten_struct_decls]: - full_user_data = 'data->%s' % user_data - full_user_data_ptr = '&%s' % full_user_data - lv_callback = '%s_%s_callback' % (struct_name, func_name) - print('STATIC %s %s_%s_callback(%s);' % (get_type(arg_type.type, remove_quals = False), struct_name, func_name, gen.visit(arg_type.args))) + if user_data in [ + user_data_decl.name for user_data_decl in flatten_struct_decls + ]: + full_user_data = "data->%s" % user_data + full_user_data_ptr = "&%s" % full_user_data + lv_callback = "%s_%s_callback" % (struct_name, func_name) + print( + "static %s %s_%s_callback(%s);" + % ( + get_type(arg_type.type, remove_quals=False), + struct_name, + func_name, + gen.visit(arg_type.args), + ) + ) else: - full_user_data = 'NULL' + full_user_data = "NULL" full_user_data_ptr = full_user_data - lv_callback = 'NULL' + lv_callback = "NULL" if not user_data: - gen_func_error(decl, "Missing 'user_data' as a field of the first parameter of the callback function '%s_%s_callback'" % (struct_name, func_name)) + gen_func_error( + decl, + "Missing 'user_data' as a field of the first parameter of the callback function '%s_%s_callback'" + % (struct_name, func_name), + ) else: - gen_func_error(decl, "Missing 'user_data' member in struct '%s'" % struct_name) - write_cases.append('case MP_QSTR_{field}: data->{decl_name} = {cast}mp_lv_callback(dest[1], {lv_callback} ,MP_QSTR_{struct_name}_{field}, {user_data}, NULL, NULL, NULL); break; // converting to callback {type_name}'. - format(struct_name = struct_name, field = sanitize(decl.name), decl_name = decl.name, lv_callback = lv_callback, user_data = full_user_data_ptr, type_name = type_name, cast = cast)) - read_cases.append('case MP_QSTR_{field}: dest[0] = mp_lv_funcptr(&mp_{funcptr}_mpobj, {cast}data->{decl_name}, {lv_callback} ,MP_QSTR_{struct_name}_{field}, {user_data}); break; // converting from callback {type_name}'. - format(struct_name = struct_name, field = sanitize(decl.name), decl_name = decl.name, lv_callback = lv_callback, funcptr = lv_to_mp_funcptr[type_name], user_data = full_user_data, type_name = type_name, cast = cast)) + gen_func_error( + decl, "Missing 'user_data' member in struct '%s'" % struct_name + ) + write_cases.append( + "case MP_QSTR_{field}: data->{decl_name} = {cast}mp_lv_callback(dest[1], {lv_callback} ,MP_QSTR_{struct_name}_{field}, {user_data}, NULL, NULL, NULL); break; // converting to callback {type_name}".format( + struct_name=struct_name, + field=sanitize(decl.name), + decl_name=decl.name, + lv_callback=lv_callback, + user_data=full_user_data_ptr, + type_name=type_name, + cast=cast, + ) + ) + read_cases.append( + "case MP_QSTR_{field}: dest[0] = mp_lv_funcptr(&mp_{funcptr}_mpobj, {cast}data->{decl_name}, {lv_callback} ,MP_QSTR_{struct_name}_{field}, {user_data}); break; // converting from callback {type_name}".format( + struct_name=struct_name, + field=sanitize(decl.name), + decl_name=decl.name, + lv_callback=lv_callback, + funcptr=lv_to_mp_funcptr[type_name], + user_data=full_user_data, + type_name=type_name, + cast=cast, + ) + ) else: user_data = None # Only allow write to non-const members - is_writeable = (not hasattr(decl.type, 'quals')) or 'const' not in decl.type.quals + is_writeable = ( + not hasattr(decl.type, "quals") + ) or "const" not in decl.type.quals # Arrays must be handled by memcpy, otherwise we would get "assignment to expression with array type" error if isinstance(decl.type, c_ast.ArrayDecl): - memcpy_size = 'sizeof(%s)*%s' % (gen.visit(decl.type.type), gen.visit(decl.type.dim)) + memcpy_size = "sizeof(%s)*%s" % ( + gen.visit(decl.type.type), + gen.visit(decl.type.dim), + ) if is_writeable: - write_cases.append('case MP_QSTR_{field}: memcpy((void*)&data->{decl_name}, {cast}{convertor}(dest[1]), {size}); break; // converting to {type_name}'. - format(field = sanitize(decl.name), decl_name = decl.name, convertor = mp_to_lv_convertor, type_name = type_name, cast = cast, size = memcpy_size)) - read_cases.append('case MP_QSTR_{field}: dest[0] = {convertor}({cast}data->{decl_name}); break; // converting from {type_name}'. - format(field = sanitize(decl.name), decl_name = decl.name, convertor = lv_to_mp_convertor, type_name = type_name, cast = cast)) + write_cases.append( + "case MP_QSTR_{field}: memcpy((void*)&data->{decl_name}, {cast}{convertor}(dest[1]), {size}); break; // converting to {type_name}".format( + field=sanitize(decl.name), + decl_name=decl.name, + convertor=mp_to_lv_convertor, + type_name=type_name, + cast=cast, + size=memcpy_size, + ) + ) + read_cases.append( + "case MP_QSTR_{field}: dest[0] = {convertor}({cast}data->{decl_name}); break; // converting from {type_name}".format( + field=sanitize(decl.name), + decl_name=decl.name, + convertor=lv_to_mp_convertor, + type_name=type_name, + cast=cast, + ) + ) else: if is_writeable: - write_cases.append('case MP_QSTR_{field}: data->{decl_name} = {cast}{convertor}(dest[1]); break; // converting to {type_name}'. - format(field = sanitize(decl.name), decl_name = decl.name, convertor = mp_to_lv_convertor, type_name = type_name, cast = cast)) - read_cases.append('case MP_QSTR_{field}: dest[0] = {convertor}({cast}data->{decl_name}); break; // converting from {type_name}'. - format(field = sanitize(decl.name), decl_name = decl.name, convertor = lv_to_mp_convertor, type_name = type_name, cast = cast)) - print(''' + write_cases.append( + "case MP_QSTR_{field}: data->{decl_name} = {cast}{convertor}(dest[1]); break; // converting to {type_name}".format( + field=sanitize(decl.name), + decl_name=decl.name, + convertor=mp_to_lv_convertor, + type_name=type_name, + cast=cast, + ) + ) + read_cases.append( + "case MP_QSTR_{field}: dest[0] = {convertor}({cast}data->{decl_name}); break; // converting from {type_name}".format( + field=sanitize(decl.name), + decl_name=decl.name, + convertor=lv_to_mp_convertor, + type_name=type_name, + cast=cast, + ) + ) + print( + """ /* * Struct {struct_name} */ -STATIC inline const mp_obj_type_t *get_mp_{sanitized_struct_name}_type(); +static inline const mp_obj_type_t *get_mp_{sanitized_struct_name}_type(); -STATIC inline void* mp_write_ptr_{sanitized_struct_name}(mp_obj_t self_in) +static inline void* mp_write_ptr_{sanitized_struct_name}(mp_obj_t self_in) {{ mp_lv_struct_t *self = MP_OBJ_TO_PTR(cast(self_in, get_mp_{sanitized_struct_name}_type())); return ({struct_tag}{struct_name}*)self->data; @@ -2003,7 +2374,7 @@ def try_generate_struct(struct_name, struct): #define mp_write_{sanitized_struct_name}(struct_obj) *(({struct_tag}{struct_name}*)mp_write_ptr_{sanitized_struct_name}(struct_obj)) -STATIC inline mp_obj_t mp_read_ptr_{sanitized_struct_name}(void *field) +static inline mp_obj_t mp_read_ptr_{sanitized_struct_name}(void *field) {{ return lv_to_mp_struct(get_mp_{sanitized_struct_name}_type(), field); }} @@ -2011,7 +2382,7 @@ def try_generate_struct(struct_name, struct): #define mp_read_{sanitized_struct_name}(field) mp_read_ptr_{sanitized_struct_name}(copy_buffer(&field, sizeof({struct_tag}{struct_name}))) #define mp_read_byref_{sanitized_struct_name}(field) mp_read_ptr_{sanitized_struct_name}(&field) -STATIC void mp_{sanitized_struct_name}_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) +static void mp_{sanitized_struct_name}_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {{ mp_lv_struct_t *self = MP_OBJ_TO_PTR(self_in); GENMPY_UNUSED {struct_tag}{struct_name} *data = ({struct_tag}{struct_name}*)self->data; @@ -2038,16 +2409,16 @@ def try_generate_struct(struct_name, struct): }} }} -STATIC void mp_{sanitized_struct_name}_print(const mp_print_t *print, +static void mp_{sanitized_struct_name}_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {{ mp_printf(print, "struct {struct_name}"); }} -STATIC const mp_obj_dict_t mp_{sanitized_struct_name}_locals_dict; +static const mp_obj_dict_t mp_{sanitized_struct_name}_locals_dict; -STATIC MP_DEFINE_CONST_OBJ_TYPE( +static MP_DEFINE_CONST_OBJ_TYPE( mp_{sanitized_struct_name}_type, MP_QSTR_{sanitized_struct_name}, MP_TYPE_FLAG_NONE, @@ -2061,30 +2432,32 @@ def try_generate_struct(struct_name, struct): parent, &mp_lv_base_struct_type ); -STATIC inline const mp_obj_type_t *get_mp_{sanitized_struct_name}_type() +static inline const mp_obj_type_t *get_mp_{sanitized_struct_name}_type() {{ return &mp_{sanitized_struct_name}_type; }} - '''.format( - sanitized_struct_name = sanitized_struct_name, - struct_name = struct_name, - struct_tag = 'struct ' if struct_name in structs_without_typedef.keys() else '', - write_cases = ';\n '.join(write_cases), - read_cases = ';\n '.join(read_cases), - )); - - lv_to_mp[struct_name] = 'mp_read_%s' % sanitized_struct_name - lv_to_mp_byref[struct_name] = 'mp_read_byref_%s' % sanitized_struct_name - mp_to_lv[struct_name] = 'mp_write_%s' % sanitized_struct_name - lv_to_mp['%s *' % struct_name] = 'mp_read_ptr_%s' % sanitized_struct_name - mp_to_lv['%s *' % struct_name] = 'mp_write_ptr_%s' % sanitized_struct_name - lv_to_mp['const %s *' % struct_name] = 'mp_read_ptr_%s' % sanitized_struct_name - mp_to_lv['const %s *' % struct_name] = 'mp_write_ptr_%s' % sanitized_struct_name + """.format( + sanitized_struct_name=sanitized_struct_name, + struct_name=struct_name, + struct_tag="struct " + if struct_name in structs_without_typedef.keys() + else "", + write_cases=";\n ".join(write_cases), + read_cases=";\n ".join(read_cases), + ) + ) + lv_to_mp[struct_name] = "mp_read_%s" % sanitized_struct_name + lv_to_mp_byref[struct_name] = "mp_read_byref_%s" % sanitized_struct_name + mp_to_lv[struct_name] = "mp_write_%s" % sanitized_struct_name + lv_to_mp["%s *" % struct_name] = "mp_read_ptr_%s" % sanitized_struct_name + mp_to_lv["%s *" % struct_name] = "mp_write_ptr_%s" % sanitized_struct_name + lv_to_mp["const %s *" % struct_name] = "mp_read_ptr_%s" % sanitized_struct_name + mp_to_lv["const %s *" % struct_name] = "mp_write_ptr_%s" % sanitized_struct_name lv_mp_type[struct_name] = simplify_identifier(sanitized_struct_name) - lv_mp_type['%s *' % struct_name] = simplify_identifier(sanitized_struct_name) - lv_mp_type['const %s *' % struct_name] = simplify_identifier(sanitized_struct_name) + lv_mp_type["%s *" % struct_name] = simplify_identifier(sanitized_struct_name) + lv_mp_type["const %s *" % struct_name] = simplify_identifier(sanitized_struct_name) # print('/* --> struct "%s" generated! */' % (struct_name)) - generated_structs[struct_name] = True # Completed generating a struct + generated_structs[struct_name] = True # Completed generating a struct return struct_name @@ -2092,45 +2465,54 @@ def try_generate_struct(struct_name, struct): # Generate Array Types when needed # + def try_generate_array_type(type_ast): arr_name = get_name(type_ast) if arr_name in mp_to_lv: return mp_to_lv[arr_name] # print('/* --> try_generate_array_type %s: %s */' % (arr_name, type_ast)) - dim = gen.visit(type_ast.dim) if hasattr(type_ast, 'dim') and type_ast.dim else None - element_type = get_type(type_ast.type, remove_quals = True) + dim = gen.visit(type_ast.dim) if hasattr(type_ast, "dim") and type_ast.dim else None + element_type = get_type(type_ast.type, remove_quals=True) qualified_element_type = gen.visit(type_ast.type) if element_type not in mp_to_lv or not mp_to_lv[element_type]: try_generate_type(type_ast.type) if element_type not in mp_to_lv or not mp_to_lv[element_type]: - raise MissingConversionException('Missing conversion to %s while generating array type conversion' % element_type) - type_ptr_ast = c_ast.PtrDecl( - quals=[], - type=type_ast.type) - element_type_ptr = get_type(type_ptr_ast, remove_quals = True) + raise MissingConversionException( + "Missing conversion to %s while generating array type conversion" + % element_type + ) + type_ptr_ast = c_ast.PtrDecl(quals=[], type=type_ast.type) + element_type_ptr = get_type(type_ptr_ast, remove_quals=True) qualified_element_ptr_type = gen.visit(type_ptr_ast) if element_type_ptr not in mp_to_lv or not mp_to_lv[element_type_ptr]: try_generate_type(type_ptr_ast) if element_type_ptr not in mp_to_lv or not mp_to_lv[element_type_ptr]: - raise MissingConversionException('Missing conversion to %s while generating array type conversion' % element_type_ptr) - array_convertor_suffix = arr_name.\ - replace(' ','_').\ - replace('*','ptr').\ - replace('+','plus').\ - replace('-','minus').\ - replace('[','__').\ - replace(']','__').\ - replace('(','__').\ - replace(')','__').\ - replace('/','_div_') - arr_to_c_convertor_name = 'mp_arr_to_%s' % array_convertor_suffix - arr_to_mp_convertor_name = 'mp_arr_from_%s' % array_convertor_suffix - print(((''' + raise MissingConversionException( + "Missing conversion to %s while generating array type conversion" + % element_type_ptr + ) + array_convertor_suffix = ( + arr_name.replace(" ", "_") + .replace("*", "ptr") + .replace("+", "plus") + .replace("-", "minus") + .replace("[", "__") + .replace("]", "__") + .replace("(", "__") + .replace(")", "__") + .replace("/", "_div_") + ) + arr_to_c_convertor_name = "mp_arr_to_%s" % array_convertor_suffix + arr_to_mp_convertor_name = "mp_arr_from_%s" % array_convertor_suffix + print( + ( + ( + """ /* * Array convertors for {arr_name} */ -GENMPY_UNUSED STATIC {struct_tag}{type} *{arr_to_c_convertor_name}(mp_obj_t mp_arr) +GENMPY_UNUSED static {struct_tag}{type} *{arr_to_c_convertor_name}(mp_obj_t mp_arr) {{ mp_obj_t mp_len = mp_obj_len_maybe(mp_arr); if (mp_len == MP_OBJ_NULL) return mp_to_ptr(mp_arr); @@ -2145,8 +2527,11 @@ def try_generate_array_type(type_ast): }} return ({struct_tag}{type} *)lv_arr; }} -''') + (''' -GENMPY_UNUSED STATIC mp_obj_t {arr_to_mp_convertor_name}({qualified_type} *arr) +""" + ) + + ( + """ +GENMPY_UNUSED static mp_obj_t {arr_to_mp_convertor_name}({qualified_type} *arr) {{ mp_obj_t obj_arr[{dim}]; for (size_t i=0; i<{dim}; i++){{ @@ -2154,63 +2539,77 @@ def try_generate_array_type(type_ast): }} return mp_obj_new_list({dim}, obj_arr); // TODO: return custom iterable object! }} -''' if dim else ''' -GENMPY_UNUSED STATIC mp_obj_t {arr_to_mp_convertor_name}({qualified_type} *arr) +""" + if dim + else """ +GENMPY_UNUSED static mp_obj_t {arr_to_mp_convertor_name}({qualified_type} *arr) {{ return {lv_to_mp_ptr_convertor}((void*)arr); }} -''')).format( - arr_to_c_convertor_name = arr_to_c_convertor_name, - arr_to_mp_convertor_name = arr_to_mp_convertor_name , - arr_name = arr_name, - type = element_type, - type_ptr = element_type_ptr, - struct_tag = 'struct ' if element_type in structs_without_typedef.keys() else '', - qualified_type = qualified_element_type, - qualified_ptr_type = qualified_element_ptr_type, - check_dim = '//TODO check dim!' if dim else '', - mp_to_lv_convertor = mp_to_lv[element_type], - lv_to_mp_convertor = lv_to_mp[element_type], - mp_to_lv_ptr_convertor = mp_to_lv[element_type_ptr], - lv_to_mp_ptr_convertor = lv_to_mp[element_type_ptr], - dim = dim if dim else 1, - )) +""" + ) + ).format( + arr_to_c_convertor_name=arr_to_c_convertor_name, + arr_to_mp_convertor_name=arr_to_mp_convertor_name, + arr_name=arr_name, + type=element_type, + type_ptr=element_type_ptr, + struct_tag="struct " + if element_type in structs_without_typedef.keys() + else "", + qualified_type=qualified_element_type, + qualified_ptr_type=qualified_element_ptr_type, + check_dim="//TODO check dim!" if dim else "", + mp_to_lv_convertor=mp_to_lv[element_type], + lv_to_mp_convertor=lv_to_mp[element_type], + mp_to_lv_ptr_convertor=mp_to_lv[element_type_ptr], + lv_to_mp_ptr_convertor=lv_to_mp[element_type_ptr], + dim=dim if dim else 1, + ) + ) mp_to_lv[arr_name] = arr_to_c_convertor_name - mp_to_lv['const %s' % arr_name] = arr_to_c_convertor_name + mp_to_lv["const %s" % arr_name] = arr_to_c_convertor_name lv_to_mp[arr_name] = arr_to_mp_convertor_name - lv_to_mp['const %s' % arr_name] = arr_to_mp_convertor_name + lv_to_mp["const %s" % arr_name] = arr_to_mp_convertor_name lv_mp_type[arr_name] = arr_to_c_convertor_name - lv_mp_type['const %s' % arr_name] = 'const %s' % arr_to_c_convertor_name + lv_mp_type["const %s" % arr_name] = "const %s" % arr_to_c_convertor_name return arr_to_c_convertor_name + # # Generate types from typedefs when needed # + def get_arg_name(arg): if isinstance(arg, c_ast.PtrDecl) or isinstance(arg, c_ast.FuncDecl): return get_arg_name(arg.type) - if hasattr(arg, 'declname'): return arg.declname - if hasattr(arg, 'name'): return arg.name - return 'unnamed_arg' + if hasattr(arg, "declname"): + return arg.declname + if hasattr(arg, "name"): + return arg.name + return "unnamed_arg" + # print("// Typedefs: " + ", ".join(get_arg_name(t) for t in typedefs)) + def try_generate_type(type_ast): # eprint(' --> try_generate_type %s : %s' % (get_name(type_ast), gen.visit(type_ast))) # print('/* --> try_generate_type %s: %s */' % (get_name(type_ast), type_ast)) - if isinstance(type_ast, str): raise SyntaxError('Internal error! try_generate_type argument is a string.') + if isinstance(type_ast, str): + raise SyntaxError("Internal error! try_generate_type argument is a string.") # Handle the case of a pointer if isinstance(type_ast, c_ast.TypeDecl): return try_generate_type(type_ast.type) type = get_name(type_ast) if isinstance(type_ast, c_ast.Enum): - mp_to_lv[type] = mp_to_lv['int'] - mp_to_lv['%s *' % type] = mp_to_lv['int *'] - lv_to_mp[type] = lv_to_mp['int'] - lv_to_mp['%s *' % type] = lv_to_mp['int *'] - lv_mp_type[type] = lv_mp_type['int'] - lv_mp_type['%s *' % type] = lv_mp_type['int *'] + mp_to_lv[type] = mp_to_lv["int"] + mp_to_lv["%s *" % type] = mp_to_lv["int *"] + lv_to_mp[type] = lv_to_mp["int"] + lv_to_mp["%s *" % type] = lv_to_mp["int *"] + lv_mp_type[type] = lv_mp_type["int"] + lv_mp_type["%s *" % type] = lv_mp_type["int *"] return mp_to_lv[type] if type in mp_to_lv: return mp_to_lv[type] @@ -2220,86 +2619,108 @@ def try_generate_type(type_ast): type = get_name(type_ast.type.type) ptr_type = get_type(type_ast, remove_quals=True) # print('/* --> try_generate_type IS PtrDecl!! %s: %s */' % (type, type_ast)) - if (type in structs): + if type in structs: try_generate_struct(type, structs[type]) if type in structs else None - if isinstance(type_ast.type, c_ast.TypeDecl) and isinstance(type_ast.type.type, c_ast.Struct) and (type_ast.type.type.name in structs): + if ( + isinstance(type_ast.type, c_ast.TypeDecl) + and isinstance(type_ast.type.type, c_ast.Struct) + and (type_ast.type.type.name in structs) + ): try_generate_struct(type, structs[type_ast.type.type.name]) if isinstance(type_ast.type, c_ast.FuncDecl): - if isinstance(type_ast.type.type.type, c_ast.TypeDecl): type = type_ast.type.type.type.declname + if isinstance(type_ast.type.type.type, c_ast.TypeDecl): + type = type_ast.type.type.type.declname func_ptr_name = "funcptr_%s" % type i = 1 - while func_ptr_name in generated_funcs: # Make sure func_ptr_name is unique - func_ptr_name = "funcptr_%s_%d" % (type,i) + while func_ptr_name in generated_funcs: # Make sure func_ptr_name is unique + func_ptr_name = "funcptr_%s_%d" % (type, i) i += 1 func = c_ast.Decl( - name=func_ptr_name, - quals=[], - align=[], - storage=[], - funcspec=[], - type=type_ast.type, - init=None, - bitsize=None) + name=func_ptr_name, + quals=[], + align=[], + storage=[], + funcspec=[], + type=type_ast.type, + init=None, + bitsize=None, + ) try: print("#define %s NULL\n" % func_ptr_name) gen_mp_func(func, None) - print("STATIC inline mp_obj_t mp_lv_{f}(void *func){{ return mp_lv_funcptr(&mp_{f}_mpobj, func, NULL, MP_QSTR_, NULL); }}\n".format( - f=func_ptr_name)) + print( + "static inline mp_obj_t mp_lv_{f}(void *func){{ return mp_lv_funcptr(&mp_{f}_mpobj, func, NULL, MP_QSTR_, NULL); }}\n".format( + f=func_ptr_name + ) + ) lv_to_mp_funcptr[ptr_type] = func_ptr_name # eprint("/* --> lv_to_mp_funcptr[%s] = %s */" % (ptr_type, func_ptr_name)) lv_to_mp[ptr_type] = "mp_lv_%s" % func_ptr_name - lv_mp_type[ptr_type] = 'function pointer' + lv_mp_type[ptr_type] = "function pointer" except MissingConversionException as exp: gen_func_error(func, exp) # print('/* --> PTR %s */' % ptr_type) - if not ptr_type in mp_to_lv: mp_to_lv[ptr_type] = mp_to_lv['void *'] - if not ptr_type in lv_to_mp: lv_to_mp[ptr_type] = lv_to_mp['void *'] - if not ptr_type in lv_mp_type: lv_mp_type[ptr_type] = 'void*' + if ptr_type not in mp_to_lv: + mp_to_lv[ptr_type] = mp_to_lv["void *"] + if ptr_type not in lv_to_mp: + lv_to_mp[ptr_type] = lv_to_mp["void *"] + if ptr_type not in lv_mp_type: + lv_mp_type[ptr_type] = "void*" return mp_to_lv[ptr_type] if type in structs: if try_generate_struct(type, structs[type]): return mp_to_lv[type] for new_type_ast in [x for x in typedefs if get_arg_name(x) == type]: new_type = get_type(new_type_ast, remove_quals=True) - if isinstance(new_type_ast, c_ast.TypeDecl) and isinstance(new_type_ast.type, c_ast.Struct) and not new_type_ast.type.decls: - explicit_struct_name = new_type_ast.type.name if hasattr(new_type_ast.type, 'name') else new_type_ast.type.names[0] + if ( + isinstance(new_type_ast, c_ast.TypeDecl) + and isinstance(new_type_ast.type, c_ast.Struct) + and not new_type_ast.type.decls + ): + explicit_struct_name = ( + new_type_ast.type.name + if hasattr(new_type_ast.type, "name") + else new_type_ast.type.names[0] + ) else: explicit_struct_name = new_type if type == explicit_struct_name: continue # eprint('/* --> typedef: %s --> %s (%s) */' % (type, new_type, new_type_ast)) if explicit_struct_name in structs: - if (try_generate_struct(new_type, structs[explicit_struct_name])): + if try_generate_struct(new_type, structs[explicit_struct_name]): if explicit_struct_name == new_type: struct_aliases[new_type] = type if type != new_type and try_generate_type(new_type_ast): - # eprint('/* --> try_generate_type TYPEDEF!! %s: %s */' % (type, mp_to_lv[new_type])) - mp_to_lv[type] = mp_to_lv[new_type] - type_ptr = '%s *' % type - new_type_ptr = '%s *' % new_type - if new_type_ptr in mp_to_lv: - mp_to_lv[type_ptr] = mp_to_lv[new_type_ptr] - if new_type in lv_to_mp: - lv_to_mp[type] = lv_to_mp[new_type] - lv_mp_type[type] = lv_mp_type[new_type] - if new_type in lv_to_mp_funcptr: - lv_to_mp_funcptr[type] = lv_to_mp_funcptr[new_type] - if new_type in lv_to_mp_byref: - lv_to_mp_byref[type] = lv_to_mp_byref[new_type] - if new_type_ptr in lv_to_mp: - lv_to_mp[type_ptr] = lv_to_mp[new_type_ptr] - if new_type_ptr in lv_mp_type: - lv_mp_type[type_ptr] = lv_mp_type[new_type_ptr] - # eprint('/* --> %s = (%s) */' % (type, new_type)) - return mp_to_lv[type] + # eprint('/* --> try_generate_type TYPEDEF!! %s: %s */' % (type, mp_to_lv[new_type])) + mp_to_lv[type] = mp_to_lv[new_type] + type_ptr = "%s *" % type + new_type_ptr = "%s *" % new_type + if new_type_ptr in mp_to_lv: + mp_to_lv[type_ptr] = mp_to_lv[new_type_ptr] + if new_type in lv_to_mp: + lv_to_mp[type] = lv_to_mp[new_type] + lv_mp_type[type] = lv_mp_type[new_type] + if new_type in lv_to_mp_funcptr: + lv_to_mp_funcptr[type] = lv_to_mp_funcptr[new_type] + if new_type in lv_to_mp_byref: + lv_to_mp_byref[type] = lv_to_mp_byref[new_type] + if new_type_ptr in lv_to_mp: + lv_to_mp[type_ptr] = lv_to_mp[new_type_ptr] + if new_type_ptr in lv_mp_type: + lv_mp_type[type_ptr] = lv_mp_type[new_type_ptr] + # eprint('/* --> %s = (%s) */' % (type, new_type)) + return mp_to_lv[type] return None + # # Helper structs # + def create_helper_struct(struct_str): print(struct_str) struct_str_ast = parser.parse(struct_str).ext[0].type @@ -2309,22 +2730,27 @@ def create_helper_struct(struct_str): try: try_generate_struct(struct_name, struct_str_ast.type) except MissingConversionException as exp: - print ('/* Helper structs NOT generated:\n %s\n*/' % (repr(exp))) + print("/* Helper structs NOT generated:\n %s\n*/" % (repr(exp))) -print(''' + +print( + """ /* * Helper Structs */ - ''') + """ +) -create_helper_struct(''' +create_helper_struct( + """ typedef union { void* ptr_val; const char* str_val; int int_val; unsigned int uint_val; } C_Pointer; -''') +""" +) # # Emit C callback functions @@ -2332,83 +2758,108 @@ def create_helper_struct(struct_str): generated_callbacks = collections.OrderedDict() -def build_callback_func_arg(arg, index, func, func_name = None): - arg_type = get_type(arg.type, remove_quals = True) - cast = '(void*)' if isinstance(arg.type, c_ast.PtrDecl) else '' # needed when field is const. casting to void overrides it + +def build_callback_func_arg(arg, index, func, func_name=None): + arg_type = get_type(arg.type, remove_quals=True) + cast = ( + "(void*)" if isinstance(arg.type, c_ast.PtrDecl) else "" + ) # needed when field is const. casting to void overrides it if arg_type not in lv_to_mp or not lv_to_mp[arg_type]: try_generate_type(arg.type) if arg_type not in lv_to_mp or not lv_to_mp[arg_type]: - raise MissingConversionException("Callback: Missing conversion to %s" % arg_type) - arg_metadata = {'type': lv_mp_type[arg_type]} - if arg.name: arg_metadata['name'] = arg.name - callback_metadata[func_name]['args'].append(arg_metadata) - return 'mp_args[{i}] = {convertor}({cast}arg{i});'.format( - convertor = lv_to_mp[arg_type], - i = index, cast = cast) + raise MissingConversionException( + "Callback: Missing conversion to %s" % arg_type + ) + arg_metadata = {"type": lv_mp_type[arg_type]} + if arg.name: + arg_metadata["name"] = arg.name + callback_metadata[func_name]["args"].append(arg_metadata) + return "mp_args[{i}] = {convertor}({cast}arg{i});".format( + convertor=lv_to_mp[arg_type], i=index, cast=cast + ) -def gen_callback_func(func, func_name = None, user_data_argument = False): +def gen_callback_func(func, func_name=None, user_data_argument=False): global mp_to_lv if func_name in generated_callbacks: return # print('/* --> callback: %s */' % (gen.visit(func))) - callback_metadata[func_name] = {'args':[]} + callback_metadata[func_name] = {"args": []} args = func.args.params enumerated_args = [] for i, arg in enumerate(args): - arg_name = f'arg{i}' + arg_name = f"arg{i}" new_arg = c_ast.Decl( - name=arg_name, - quals=arg.quals, - align=[], - storage=[], - funcspec=[], - type=copy.deepcopy(arg.type), - init=None, - bitsize=None) + name=arg_name, + quals=arg.quals, + align=[], + storage=[], + funcspec=[], + type=copy.deepcopy(arg.type), + init=None, + bitsize=None, + ) t = new_arg - while hasattr(t, 'type'): - if hasattr(t.type, 'declname'): + while hasattr(t, "type"): + if hasattr(t.type, "declname"): t.type.declname = arg_name t = t.type enumerated_args.append(new_arg) # print(f'/* --> {gen.visit(new_arg)} */') - if not func_name: func_name = get_arg_name(func.type) + if not func_name: + func_name = get_arg_name(func.type) # print('/* --> callback: func_name = %s */' % func_name) if is_global_callback(func): - full_user_data = 'MP_STATE_PORT(mp_lv_user_data)' + full_user_data = "MP_STATE_PORT(mp_lv_user_data)" else: user_data, user_data_getter, _ = get_user_data(func, func_name) - if user_data_argument and len(args) > 0 and gen.visit(args[-1].type) == 'void *': - full_user_data = 'arg%d' % (len(args) - 1) + if ( + user_data_argument + and len(args) > 0 + and gen.visit(args[-1].type) == "void *" + ): + full_user_data = "arg%d" % (len(args) - 1) elif user_data: - full_user_data = 'arg0->%s' % user_data - if len(args) < 1 or hasattr(args[0].type.type, 'names') and lv_base_obj_pattern.match(args[0].type.type.names[0]): - raise MissingConversionException("Callback: First argument of callback function must be lv_obj_t") + full_user_data = "arg0->%s" % user_data + if ( + len(args) < 1 + or hasattr(args[0].type.type, "names") + and lv_base_obj_pattern.match(args[0].type.type.names[0]) + ): + raise MissingConversionException( + "Callback: First argument of callback function must be lv_obj_t" + ) elif user_data_getter: - full_user_data = '%s(arg0)' % user_data_getter.name + full_user_data = "%s(arg0)" % user_data_getter.name else: full_user_data = None # if full_user_data: print('/* --> callback: %s user_data found!! %s */' %(gen.visit(func), full_user_data)) # else: print('/* --> callback: full_user_data NOT FOUND !! %s */' % (gen.visit(func))) if not full_user_data: - raise MissingConversionException("Callback: user_data NOT FOUND! %s" % (gen.visit(func))) - return_type = get_type(func.type, remove_quals = False) - if return_type != 'void' and (return_type not in mp_to_lv or not mp_to_lv[return_type]): + raise MissingConversionException( + "Callback: user_data NOT FOUND! %s" % (gen.visit(func)) + ) + return_type = get_type(func.type, remove_quals=False) + if return_type != "void" and ( + return_type not in mp_to_lv or not mp_to_lv[return_type] + ): try_generate_type(func.type) if return_type not in mp_to_lv or not mp_to_lv[return_type]: - raise MissingConversionException("Callback return value: Missing conversion to %s" % return_type) + raise MissingConversionException( + "Callback return value: Missing conversion to %s" % return_type + ) - callback_metadata[func_name]['return_type'] = lv_mp_type[return_type] - print(""" + callback_metadata[func_name]["return_type"] = lv_mp_type[return_type] + print( + """ /* * Callback function {func_name} * {func_prototype} */ -GENMPY_UNUSED STATIC {return_type} {func_name}_callback({func_args}) +GENMPY_UNUSED static {return_type} {func_name}_callback({func_args}) {{ mp_obj_t mp_args[{num_args}]; {build_args} @@ -2419,23 +2870,36 @@ def gen_callback_func(func, func_name = None, user_data_argument = False): return{return_value}; }} """.format( - func_prototype = gen.visit(func), - func_name = sanitize(func_name), - return_type = return_type, - func_args = ', '.join([(gen.visit(arg)) for arg in enumerated_args]), - num_args=len(args), - build_args="\n ".join([build_callback_func_arg(arg, i, func, func_name=func_name) for i,arg in enumerate(args)]), - user_data=full_user_data, - return_value_assignment = '' if return_type == 'void' else 'mp_obj_t callback_result = ', - return_value='' if return_type == 'void' else ' %s(callback_result)' % mp_to_lv[return_type])) + func_prototype=gen.visit(func), + func_name=sanitize(func_name), + return_type=return_type, + func_args=", ".join([(gen.visit(arg)) for arg in enumerated_args]), + num_args=len(args), + build_args="\n ".join( + [ + build_callback_func_arg(arg, i, func, func_name=func_name) + for i, arg in enumerate(args) + ] + ), + user_data=full_user_data, + return_value_assignment="" + if return_type == "void" + else "mp_obj_t callback_result = ", + return_value="" + if return_type == "void" + else " %s(callback_result)" % mp_to_lv[return_type], + ) + ) generated_callbacks[func_name] = True + # # Emit Mpy function definitions # generated_funcs = collections.OrderedDict() + def build_mp_func_arg(arg, index, func, obj_name): if isinstance(arg, c_ast.EllipsisParam): raise MissingConversionException("Cannot convert ellipsis param") @@ -2454,7 +2918,7 @@ def build_mp_func_arg(arg, index, func, obj_name): # 1) last argument is a void* user_data which is passed to callback # 2) first argument is a struct with user_data field, which is passed to callback - callback_name, arg_type = callback + callback_name, arg_type = callback # print('/* --> callback %s ARG TYPE: %s */' % (callback_name, arg_type)) try: @@ -2462,89 +2926,125 @@ def build_mp_func_arg(arg, index, func, obj_name): full_user_data = None user_data_getter = None user_data_setter = None - if len(args) > 0 and gen.visit(args[-1].type) == 'void *' and args[-1].name == 'user_data': - callback_name = '%s_%s' % (func.name, callback_name) - full_user_data = '&user_data' + if ( + len(args) > 0 + and gen.visit(args[-1].type) == "void *" + and args[-1].name == "user_data" + ): + callback_name = "%s_%s" % (func.name, callback_name) + full_user_data = "&user_data" user_data_argument = True else: first_arg = args[0] - struct_name = get_name(first_arg.type.type.type if hasattr(first_arg.type.type,'type') else first_arg.type.type) - callback_name = '%s_%s' % (struct_name, callback_name) - user_data, user_data_getter, user_data_setter = get_user_data(arg_type, callback_name) + struct_name = get_name( + first_arg.type.type.type + if hasattr(first_arg.type.type, "type") + else first_arg.type.type + ) + callback_name = "%s_%s" % (struct_name, callback_name) + user_data, user_data_getter, user_data_setter = get_user_data( + arg_type, callback_name + ) if is_global_callback(arg_type): - full_user_data = '&MP_STATE_PORT(mp_lv_user_data)' + full_user_data = "&MP_STATE_PORT(mp_lv_user_data)" else: if user_data: - full_user_data = '&%s->%s' % (first_arg.name, user_data) + full_user_data = "&%s->%s" % (first_arg.name, user_data) elif user_data_getter and user_data_setter: - full_user_data = 'NULL' # uses getter/setter instead + full_user_data = "NULL" # uses getter/setter instead if index == 0: - raise MissingConversionException("Callback argument '%s' cannot be the first argument! We assume the first argument contains the user_data" % gen.visit(arg)) + raise MissingConversionException( + "Callback argument '%s' cannot be the first argument! We assume the first argument contains the user_data" + % gen.visit(arg) + ) if not full_user_data: - raise MissingConversionException("Callback function '%s' must receive a struct pointer with user_data member as its first argument!" % gen.visit(arg)) + raise MissingConversionException( + "Callback function '%s' must receive a struct pointer with user_data member as its first argument!" + % gen.visit(arg) + ) # eprint("--> callback_metadata= %s_%s" % (struct_name, callback_name)) - gen_callback_func(arg_type, '%s' % callback_name, user_data_argument) - arg_metadata = {'type': 'callback', 'function': callback_metadata[callback_name]} - if arg.name: arg_metadata['name'] = arg.name - func_metadata[func.name]['args'].append(arg_metadata) - return 'void *{arg_name} = mp_lv_callback(mp_args[{i}], &{callback_name}_callback, MP_QSTR_{callback_name}, {full_user_data}, {containing_struct}, (mp_lv_get_user_data){user_data_getter}, (mp_lv_set_user_data){user_data_setter});'.format( - i = index, - arg_name = fixed_arg.name, - callback_name = sanitize(callback_name), - full_user_data = full_user_data, - containing_struct = first_arg.name if user_data_getter and user_data_setter else "NULL", - user_data_getter = user_data_getter.name if user_data_getter else 'NULL', - user_data_setter = user_data_setter.name if user_data_setter else 'NULL') + gen_callback_func(arg_type, "%s" % callback_name, user_data_argument) + arg_metadata = { + "type": "callback", + "function": callback_metadata[callback_name], + } + if arg.name: + arg_metadata["name"] = arg.name + func_metadata[func.name]["args"].append(arg_metadata) + return "void *{arg_name} = mp_lv_callback(mp_args[{i}], &{callback_name}_callback, MP_QSTR_{callback_name}, {full_user_data}, {containing_struct}, (mp_lv_get_user_data){user_data_getter}, (mp_lv_set_user_data){user_data_setter});".format( + i=index, + arg_name=fixed_arg.name, + callback_name=sanitize(callback_name), + full_user_data=full_user_data, + containing_struct=first_arg.name + if user_data_getter and user_data_setter + else "NULL", + user_data_getter=user_data_getter.name if user_data_getter else "NULL", + user_data_setter=user_data_setter.name if user_data_setter else "NULL", + ) except MissingConversionException as exp: gen_func_error(arg, exp) - callback_name = 'NULL' - full_user_data = 'NULL' - if not hasattr(arg, 'type'): - raise MissingConversionException("Cannot convert function argument %s" % repr(arg)) - arg_type = get_type(arg.type, remove_quals = True) + callback_name = "NULL" + full_user_data = "NULL" + if not hasattr(arg, "type"): + raise MissingConversionException( + "Cannot convert function argument %s" % repr(arg) + ) + arg_type = get_type(arg.type, remove_quals=True) # print('/* --> arg = %s, arg_type = %s */' %(gen.visit(arg), arg_type)) if arg_type not in mp_to_lv or not mp_to_lv[arg_type]: try_generate_type(arg.type) if arg_type not in mp_to_lv or not mp_to_lv[arg_type]: - raise MissingConversionException('Missing conversion to %s' % arg_type) - arg_metadata = {'type': lv_mp_type[arg_type]} - if arg.name: arg_metadata['name'] = arg.name - func_metadata[func.name]['args'].append(arg_metadata) - cast = ("(%s)" % gen.visit(fixed_arg.type)) if 'const' in arg.quals else "" # allow conversion from non const to const, sometimes requires cast - return '{var} = {cast}{convertor}(mp_args[{i}]);'.format( - var = gen.visit(fixed_arg), - cast = cast, - convertor = mp_to_lv[arg_type], - i = index) + raise MissingConversionException("Missing conversion to %s" % arg_type) + arg_metadata = {"type": lv_mp_type[arg_type]} + if arg.name: + arg_metadata["name"] = arg.name + func_metadata[func.name]["args"].append(arg_metadata) + cast = ( + ("(%s)" % gen.visit(fixed_arg.type)) if "const" in arg.quals else "" + ) # allow conversion from non const to const, sometimes requires cast + return "{var} = {cast}{convertor}(mp_args[{i}]);".format( + var=gen.visit(fixed_arg), cast=cast, convertor=mp_to_lv[arg_type], i=index + ) + def emit_func_obj(func_obj_name, func_name, param_count, func_ptr, is_static): - print(""" -STATIC {builtin_macro}(mp_{func_obj_name}_mpobj, {param_count}, mp_{func_name}, {func_ptr}); + print( + """ +static {builtin_macro}(mp_{func_obj_name}_mpobj, {param_count}, mp_{func_name}, {func_ptr}); """.format( - func_obj_name = func_obj_name, - func_name = func_name, - func_ptr = func_ptr, - param_count = param_count, - builtin_macro='MP_DEFINE_CONST_LV_FUN_OBJ_STATIC_VAR' if is_static else 'MP_DEFINE_CONST_LV_FUN_OBJ_VAR')) + func_obj_name=func_obj_name, + func_name=func_name, + func_ptr=func_ptr, + param_count=param_count, + builtin_macro="MP_DEFINE_CONST_LV_FUN_OBJ_STATIC_VAR" + if is_static + else "MP_DEFINE_CONST_LV_FUN_OBJ_VAR", + ) + ) + def gen_mp_func(func, obj_name): # print('/* gen_mp_func: %s : %s */' % (obj_name, func)) if func.name in generated_funcs: - print(""" + print( + """ /* * WARNING: %s was declared more than once! */ - """ % func.name) + """ + % func.name + ) return # print("/* gen_mp_func %s */" % func.name) - generated_funcs[func.name] = False # starting to generate the function - func_metadata[func.name] = {'type': 'function', 'args':[]} + generated_funcs[func.name] = False # starting to generate the function + func_metadata[func.name] = {"type": "function", "args": []} args = func.type.args.params if func.type.args else [] enumerated_args = enumerate(args) # Handle the case of a single function argument which is "void" - if len(args)==1 and get_type(args[0].type, remove_quals = True) == "void": + if len(args) == 1 and get_type(args[0].type, remove_quals=True) == "void": param_count = 0 args = [] else: @@ -2556,47 +3056,71 @@ def gen_mp_func(func, obj_name): original_func = func_prototypes[prototype_str] if generated_funcs[original_func.name] == True: print("/* Reusing %s for %s */" % (original_func.name, func.name)) - emit_func_obj(func.name, original_func.name, param_count, func.name, is_static_member(func, base_obj_type)) - func_metadata[func.name]['return_type'] = func_metadata[original_func.name]['return_type'] - func_metadata[func.name]['args'] = func_metadata[original_func.name]['args'] - generated_funcs[func.name] = True # completed generating the function + emit_func_obj( + func.name, + original_func.name, + param_count, + func.name, + is_static_member(func, base_obj_type), + ) + func_metadata[func.name]["return_type"] = func_metadata[original_func.name][ + "return_type" + ] + func_metadata[func.name]["args"] = func_metadata[original_func.name]["args"] + generated_funcs[func.name] = True # completed generating the function return func_prototypes[prototype_str] = func # user_data argument must be handled first, if it exists try: - i = [(arg.name if hasattr(arg, 'name') else None) for arg in args].index('user_data') - if i>0: - enumerated_args = [(i, arg) for i, arg in enumerated_args] # convert enumerate to list - enumerated_args[0], enumerated_args[i] = enumerated_args[i], enumerated_args[0] + i = [(arg.name if hasattr(arg, "name") else None) for arg in args].index( + "user_data" + ) + if i > 0: + enumerated_args = [ + (i, arg) for i, arg in enumerated_args + ] # convert enumerate to list + enumerated_args[0], enumerated_args[i] = ( + enumerated_args[i], + enumerated_args[0], + ) except ValueError: pass - return_type = get_type(func.type.type, remove_quals = False) + return_type = get_type(func.type.type, remove_quals=False) qualified_return_type = gen.visit(func.type.type) - if isinstance(func.type.type, c_ast.PtrDecl) and lv_func_returns_array.match(func.name): + if isinstance(func.type.type, c_ast.PtrDecl) and lv_func_returns_array.match( + func.name + ): try_generate_array_type(func.type.type) # print('/* --> return_type = %s, qualified_return_type = %s\n%s */' % (return_type, qualified_return_type, func.type.type)) if return_type == "void": build_result = "" build_return_value = "mp_const_none" - func_metadata[func.name]['return_type'] = 'NoneType' + func_metadata[func.name]["return_type"] = "NoneType" else: if return_type not in lv_to_mp or not lv_to_mp[return_type]: try_generate_type(func.type.type) if return_type not in lv_to_mp or not lv_to_mp[return_type]: - raise MissingConversionException("Missing conversion from %s" % return_type) + raise MissingConversionException( + "Missing conversion from %s" % return_type + ) build_result = "%s _res = " % qualified_return_type - cast = '(void*)' if isinstance(func.type.type, c_ast.PtrDecl) else '' # needed when field is const. casting to void overrides it - build_return_value = "{type}({cast}_res)".format(type = lv_to_mp[return_type], cast = cast) - func_metadata[func.name]['return_type'] = lv_mp_type[return_type] - print(""" + cast = ( + "(void*)" if isinstance(func.type.type, c_ast.PtrDecl) else "" + ) # needed when field is const. casting to void overrides it + build_return_value = "{type}({cast}_res)".format( + type=lv_to_mp[return_type], cast=cast + ) + func_metadata[func.name]["return_type"] = lv_mp_type[return_type] + print( + """ /* * {module_name} extension definition for: * {print_func} */ -STATIC mp_obj_t mp_{func}(size_t mp_n_args, const mp_obj_t *mp_args, void *lv_func_ptr) +static mp_obj_t mp_{func}(size_t mp_n_args, const mp_obj_t *mp_args, void *lv_func_ptr) {{ {build_args} {build_result}(({func_ptr})lv_func_ptr)({send_args}); @@ -2604,35 +3128,57 @@ def gen_mp_func(func, obj_name): }} """.format( - module_name = module_name, - func=func.name, - func_ptr=prototype_str, - print_func=gen.visit(func), - build_args="\n ".join([build_mp_func_arg(arg, i, func, obj_name) for i,arg in enumerated_args - if isinstance(arg, c_ast.EllipsisParam) or - (not isinstance(arg.type, c_ast.TypeDecl)) or - (not isinstance(arg.type.type, c_ast.IdentifierType)) or - 'void' not in arg.type.type.names]), # Handle the case of 'void' param which should be ignored - send_args=", ".join([(arg.name if (hasattr(arg, 'name') and arg.name) else ("arg%d" % i)) for i,arg in enumerate(args)]), - build_result=build_result, - build_return_value=build_return_value)) - - emit_func_obj(func.name, func.name, param_count, func.name, is_static_member(func, base_obj_type)) - generated_funcs[func.name] = True # completed generating the function + module_name=module_name, + func=func.name, + func_ptr=prototype_str, + print_func=gen.visit(func), + build_args="\n ".join( + [ + build_mp_func_arg(arg, i, func, obj_name) + for i, arg in enumerated_args + if isinstance(arg, c_ast.EllipsisParam) + or (not isinstance(arg.type, c_ast.TypeDecl)) + or (not isinstance(arg.type.type, c_ast.IdentifierType)) + or "void" not in arg.type.type.names + ] + ), # Handle the case of 'void' param which should be ignored + send_args=", ".join( + [ + (arg.name if (hasattr(arg, "name") and arg.name) else ("arg%d" % i)) + for i, arg in enumerate(args) + ] + ), + build_result=build_result, + build_return_value=build_return_value, + ) + ) + + emit_func_obj( + func.name, + func.name, + param_count, + func.name, + is_static_member(func, base_obj_type), + ) + generated_funcs[func.name] = True # completed generating the function # print('/* is_struct_function() = %s, is_static_member() = %s, get_first_arg_type()=%s, obj_name = %s */' % ( # is_struct_function(func), is_static_member(func, base_obj_type), get_first_arg_type(func), base_obj_type)) - def gen_func_error(method, exp): global funcs - print(""" + print( + """ /* * Function NOT generated: * {problem} * {method} */ - """.format(method=gen.visit(method) if isinstance(method, c_ast.Node) else method, problem=exp)) + """.format( + method=gen.visit(method) if isinstance(method, c_ast.Node) else method, + problem=exp, + ) + ) try: funcs.remove(method) except: @@ -2645,36 +3191,77 @@ def gen_func_error(method, exp): enum_referenced = collections.OrderedDict() + def gen_obj_methods(obj_name): global enums - helper_members = ["{ MP_ROM_QSTR(MP_QSTR___cast__), MP_ROM_PTR(&cast_obj_class_method) }"] if len(obj_names) > 0 and obj_name == base_obj_name else [] - members = ["{{ MP_ROM_QSTR(MP_QSTR_{method_name}), MP_ROM_PTR(&mp_{method}_mpobj) }}". - format(method=method.name, method_name=sanitize(method_name_from_func_name(method.name))) for method in get_methods(obj_name)] - obj_metadata[obj_name]['members'].update({method_name_from_func_name(method.name): func_metadata[method.name] for method in get_methods(obj_name)}) + helper_members = ( + ["{ MP_ROM_QSTR(MP_QSTR___cast__), MP_ROM_PTR(&cast_obj_class_method) }"] + if len(obj_names) > 0 and obj_name == base_obj_name + else [] + ) + members = [ + "{{ MP_ROM_QSTR(MP_QSTR_{method_name}), MP_ROM_PTR(&mp_{method}_mpobj) }}".format( + method=method.name, + method_name=sanitize(method_name_from_func_name(method.name)), + ) + for method in get_methods(obj_name) + ] + obj_metadata[obj_name]["members"].update( + { + method_name_from_func_name(method.name): func_metadata[method.name] + for method in get_methods(obj_name) + } + ) # add parent methods parent_members = [] if obj_name in parent_obj_names and parent_obj_names[obj_name] != None: # parent_members += gen_obj_methods(parent_obj_names[obj_name]) - obj_metadata[obj_name]['members'].update(obj_metadata[parent_obj_names[obj_name]]['members']) + obj_metadata[obj_name]["members"].update( + obj_metadata[parent_obj_names[obj_name]]["members"] + ) # add enum members - enum_members = ["{{ MP_ROM_QSTR(MP_QSTR_{enum_member}), MP_ROM_PTR({enum_member_value}) }}". - format(enum_member = sanitize(get_enum_member_name(enum_member_name)), enum_member_value = get_enum_value(obj_name, enum_member_name)) for enum_member_name in get_enum_members(obj_name)] - obj_metadata[obj_name]['members'].update({get_enum_member_name(enum_member_name): {'type':'enum_member'} for enum_member_name in get_enum_members(obj_name)}) + enum_members = [ + "{{ MP_ROM_QSTR(MP_QSTR_{enum_member}), MP_ROM_PTR({enum_member_value}) }}".format( + enum_member=sanitize(get_enum_member_name(enum_member_name)), + enum_member_value=get_enum_value(obj_name, enum_member_name), + ) + for enum_member_name in get_enum_members(obj_name) + ] + obj_metadata[obj_name]["members"].update( + { + get_enum_member_name(enum_member_name): {"type": "enum_member"} + for enum_member_name in get_enum_members(obj_name) + } + ) # add enums that match object name - obj_enums = [enum_name for enum_name in enums.keys() if is_method_of(enum_name, obj_name)] - enum_types = ["{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_lv_{enum}_type_base) }}". - format(name=sanitize(method_name_from_func_name(enum_name)), enum=enum_name) for enum_name in obj_enums] - obj_metadata[obj_name]['members'].update({method_name_from_func_name(enum_name): {'type':'enum_type'} for enum_name in obj_enums}) + obj_enums = [ + enum_name for enum_name in enums.keys() if is_method_of(enum_name, obj_name) + ] + enum_types = [ + "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_lv_{enum}_type_base) }}".format( + name=sanitize(method_name_from_func_name(enum_name)), enum=enum_name + ) + for enum_name in obj_enums + ] + obj_metadata[obj_name]["members"].update( + { + method_name_from_func_name(enum_name): {"type": "enum_type"} + for enum_name in obj_enums + } + ) for enum_name in obj_enums: - obj_metadata[obj_name]['members'][method_name_from_func_name(enum_name)].update(obj_metadata[enum_name]) + obj_metadata[obj_name]["members"][method_name_from_func_name(enum_name)].update( + obj_metadata[enum_name] + ) enum_referenced[enum_name] = True return members + parent_members + enum_members + enum_types + helper_members + def gen_obj(obj_name): # eprint('Generating object %s...' % obj_name) is_obj = has_ctor(obj_name) - should_add_base_methods = is_obj and obj_name != 'obj' - obj_metadata[obj_name] = {'members' : collections.OrderedDict()} + should_add_base_methods = is_obj and obj_name != "obj" + obj_metadata[obj_name] = {"members": collections.OrderedDict()} # Generate object methods for method in get_methods(obj_name): @@ -2693,7 +3280,7 @@ def gen_obj(obj_name): # print([method.name for method in methods]) ctor = """ -STATIC mp_obj_t {obj}_make_new( +static mp_obj_t {obj}_make_new( const mp_obj_type_t *type, size_t n_args, size_t n_kw, @@ -2703,22 +3290,23 @@ def gen_obj(obj_name): }} """ - print(""" + print( + """ /* * {module_name} {obj} object definitions */ - """.format( - module_name = module_name, - obj = obj_name)) + """.format(module_name=module_name, obj=obj_name) + ) - print(""" -STATIC const mp_rom_map_elem_t {obj}_locals_dict_table[] = {{ + print( + """ +static const mp_rom_map_elem_t {obj}_locals_dict_table[] = {{ {locals_dict_entries} }}; -STATIC MP_DEFINE_CONST_DICT({obj}_locals_dict, {obj}_locals_dict_table); +static MP_DEFINE_CONST_DICT({obj}_locals_dict, {obj}_locals_dict_table); -STATIC void {obj}_print(const mp_print_t *print, +static void {obj}_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {{ @@ -2727,7 +3315,7 @@ def gen_obj(obj_name): {ctor} -STATIC MP_DEFINE_CONST_OBJ_TYPE( +static MP_DEFINE_CONST_OBJ_TYPE( mp_lv_{obj}_type_base, MP_QSTR_{obj}, MP_TYPE_FLAG_NONE, @@ -2740,24 +3328,29 @@ def gen_obj(obj_name): locals_dict, &{obj}_locals_dict ); -GENMPY_UNUSED STATIC const mp_lv_obj_type_t mp_lv_{obj}_type = {{ +GENMPY_UNUSED static const mp_lv_obj_type_t mp_lv_{obj}_type = {{ #ifdef LV_OBJ_T .lv_obj_class = {lv_class}, #endif .mp_obj_type = &mp_lv_{obj}_type_base, }}; """.format( - module_name = module_name, - obj = sanitize(obj_name), base_obj = base_obj_name, - base_class = '&mp_%s_type' % base_obj_name if should_add_base_methods else 'NULL', - locals_dict_entries = ",\n ".join(gen_obj_methods(obj_name)), - ctor = ctor.format(obj = obj_name, ctor_name = ctor_func.name) if has_ctor(obj_name) else '', - make_new = 'make_new, %s_make_new,' % obj_name if is_obj else '', - binary_op = 'binary_op, mp_lv_obj_binary_op,' if is_obj else '', - buffer = 'buffer, mp_lv_obj_get_buffer,' if is_obj else '', - parent = 'parent, &mp_lv_%s_type_base,' % parent_obj_names[obj_name] if obj_name in parent_obj_names and parent_obj_names[obj_name] else '', - lv_class = '&lv_%s_class' % obj_name if is_obj else 'NULL', - )) + module_name=module_name, + obj=sanitize(obj_name), + locals_dict_entries=",\n ".join(gen_obj_methods(obj_name)), + ctor=ctor.format(obj=obj_name, ctor_name=ctor_func.name) + if has_ctor(obj_name) + else "", + make_new="make_new, %s_make_new," % obj_name if is_obj else "", + binary_op="binary_op, mp_lv_obj_binary_op," if is_obj else "", + buffer="buffer, mp_lv_obj_get_buffer," if is_obj else "", + parent="parent, &mp_lv_%s_type_base," % parent_obj_names[obj_name] + if obj_name in parent_obj_names and parent_obj_names[obj_name] + else "", + lv_class="&lv_%s_class" % obj_name if is_obj else "NULL", + ) + ) + # # Generate Enum objects @@ -2773,14 +3366,20 @@ def gen_obj(obj_name): generated_obj_names = collections.OrderedDict() for obj_name in obj_names: # eprint("--> %s [%s]" % (obj_name, ", ".join([name for name in generated_obj_names]))) - parent_obj_name = parent_obj_names[obj_name] if obj_name in parent_obj_names else None + parent_obj_name = ( + parent_obj_names[obj_name] if obj_name in parent_obj_names else None + ) - while parent_obj_name != None and not parent_obj_name in generated_obj_names: + while parent_obj_name != None and parent_obj_name not in generated_obj_names: gen_obj(parent_obj_name) generated_obj_names[parent_obj_name] = True - parent_obj_name = parent_obj_names[parent_obj_name] if parent_obj_name in parent_obj_names else None + parent_obj_name = ( + parent_obj_names[parent_obj_name] + if parent_obj_name in parent_obj_names + else None + ) - if not obj_name in generated_obj_names: + if obj_name not in generated_obj_names: # eprint("--> gen obj %s" % obj_name) gen_obj(obj_name) generated_obj_names[obj_name] = True @@ -2792,21 +3391,27 @@ def gen_obj(obj_name): # Otherwise we will not know of all the structs when generating struct-functions # + def try_generate_structs_from_first_argument(): for func in funcs: - if func.name in generated_funcs: continue + if func.name in generated_funcs: + continue args = func.type.args.params if func.type.args else [] - if len(args) < 1: continue - arg_type = get_type(args[0].type, remove_quals = True) + if len(args) < 1: + continue + arg_type = get_type(args[0].type, remove_quals=True) if arg_type not in mp_to_lv or not mp_to_lv[arg_type]: try: try_generate_type(args[0].type) except MissingConversionException as e: - print(''' + print( + """ /* * {struct} not generated: {err} */ - '''.format(struct=arg_type, err=e)) + """.format(struct=arg_type, err=e) + ) + # # Generate globals @@ -2814,6 +3419,7 @@ def try_generate_structs_from_first_argument(): # eprint("/* Generating globals */") + def gen_global(global_name, global_type_ast): global_type = get_type(global_type_ast, remove_quals=True) generated_global = try_generate_type(global_type_ast) @@ -2821,36 +3427,42 @@ def gen_global(global_name, global_type_ast): if global_type not in generated_structs: wrapped_type = lv_mp_type[global_type] if not wrapped_type: - raise MissingConversionException('Missing conversion to %s when generating global %s' % (wrapped_type, global_name)) + raise MissingConversionException( + "Missing conversion to %s when generating global %s" + % (wrapped_type, global_name) + ) global_type = sanitize("_lv_mp_%s_wrapper" % wrapped_type) custom_struct_str = """ typedef struct {{ {type} value; }} {name}; - """.format( - type = wrapped_type, - name = global_type) + """.format(type=wrapped_type, name=global_type) if global_type not in generated_structs: print("/* Global struct wrapper for %s */" % wrapped_type) print(custom_struct_str) # eprint("%s: %s\n" % (wrapped_type, custom_struct_str)) - try_generate_struct(global_type, parser.parse(custom_struct_str).ext[0].type.type) + try_generate_struct( + global_type, parser.parse(custom_struct_str).ext[0].type.type + ) - print(""" + print( + """ /* * {module_name} {global_name} global definitions */ -STATIC const mp_lv_struct_t mp_{global_name} = {{ +static const mp_lv_struct_t mp_{global_name} = {{ {{ &mp_{struct_name}_type }}, ({cast}*)&{global_name} }}; """.format( - module_name = module_name, - global_name = global_name, - struct_name = global_type, - sanitized_struct_name = sanitize(global_type), - cast = gen.visit(global_type_ast))) + module_name=module_name, + global_name=global_name, + struct_name=global_type, + cast=gen.visit(global_type_ast), + ) + ) + generated_globals = [] for global_name in blobs: @@ -2867,14 +3479,18 @@ def gen_global(global_name, global_type_ast): # eprint("/* Generating struct-functions */") try_generate_structs_from_first_argument() + def generate_struct_functions(struct_list): # print('/* List of structs: %s */' % repr(struct_list)) for struct_name in struct_list: - if not generated_structs[struct_name]: continue + if not generated_structs[struct_name]: + continue sanitized_struct_name = sanitize(struct_name) struct_funcs = get_struct_functions(struct_name) # print('/* Struct %s contains: %s */' % (struct_name, [f.name for f in struct_funcs])) - for struct_func in struct_funcs[:]: # clone list because we are changing it in the loop. + for struct_func in struct_funcs[ + : + ]: # clone list because we are changing it in the loop. try: if struct_func.name not in generated_funcs: gen_mp_func(struct_func, struct_name) @@ -2882,67 +3498,89 @@ def generate_struct_functions(struct_list): gen_func_error(struct_func, exp) struct_funcs.remove(struct_func) if struct_name not in structs or structs[struct_name].decls: - struct_size_attr = '{{ MP_ROM_QSTR(MP_QSTR___SIZE__), MP_ROM_PTR(MP_ROM_INT(sizeof({struct_tag}{struct_name}))) }},'.format( - struct_name = struct_name, - struct_tag = 'struct ' if struct_name in structs_without_typedef.keys() else '', + struct_size_attr = "{{ MP_ROM_QSTR(MP_QSTR___SIZE__), MP_ROM_PTR(MP_ROM_INT(sizeof({struct_tag}{struct_name}))) }},".format( + struct_name=struct_name, + struct_tag="struct " + if struct_name in structs_without_typedef.keys() + else "", ) else: - struct_size_attr = '' - print(''' -STATIC const mp_rom_map_elem_t mp_{sanitized_struct_name}_locals_dict_table[] = {{ + struct_size_attr = "" + print( + """ +static const mp_rom_map_elem_t mp_{sanitized_struct_name}_locals_dict_table[] = {{ {struct_size} {functions} }}; -STATIC MP_DEFINE_CONST_DICT(mp_{sanitized_struct_name}_locals_dict, mp_{sanitized_struct_name}_locals_dict_table); - '''.format( - struct_size = struct_size_attr, - sanitized_struct_name = sanitized_struct_name, - functions = ''.join(['{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{func}_mpobj) }},\n '. - format(name = sanitize(noncommon_part(f.name, struct_name)), func = f.name) for f in struct_funcs]), - )) +static MP_DEFINE_CONST_DICT(mp_{sanitized_struct_name}_locals_dict, mp_{sanitized_struct_name}_locals_dict_table); + """.format( + struct_size=struct_size_attr, + sanitized_struct_name=sanitized_struct_name, + functions="".join( + [ + "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{func}_mpobj) }},\n ".format( + name=sanitize(noncommon_part(f.name, struct_name)), + func=f.name, + ) + for f in struct_funcs + ] + ), + ) + ) generated_struct_functions[struct_name] = True + generate_struct_functions(list(generated_structs.keys())) # # Generate all module functions (not including method functions which were already generated) # -print(""" +print( + """ /* * * Global Module Functions * */ -""") +""" +) # eprint("/* Generating global module functions /*") -module_funcs = [func for func in funcs if not func.name in generated_funcs] -for module_func in module_funcs[:]: # clone list because we are changing it in the loop. +module_funcs = [func for func in funcs if func.name not in generated_funcs] +for module_func in module_funcs[ + : +]: # clone list because we are changing it in the loop. if module_func.name in generated_funcs: - continue # generated_funcs could change inside the loop so need to recheck. + continue # generated_funcs could change inside the loop so need to recheck. try: gen_mp_func(module_func, None) # A new function can create new struct with new function structs - new_structs = [s for s in generated_structs if s not in generated_struct_functions] + new_structs = [ + s for s in generated_structs if s not in generated_struct_functions + ] if new_structs: generate_struct_functions(new_structs) except MissingConversionException as exp: gen_func_error(module_func, exp) module_funcs.remove(module_func) -functions_not_generated = [func.name for func in funcs if func.name not in generated_funcs] +functions_not_generated = [ + func.name for func in funcs if func.name not in generated_funcs +] if len(functions_not_generated) > 0: - print(""" + print( + """ /* * Functions not generated: * {funcs} * */ -""".format(funcs = "\n * ".join(functions_not_generated))) +""".format(funcs="\n * ".join(functions_not_generated)) + ) # # Generate callback functions @@ -2959,10 +3597,10 @@ def generate_struct_functions(struct_list): # mp_to_lv[func_name] = mp_to_lv['void *'] # eprint("/* Generating callback functions */") -for (func_name, func, struct_name) in callbacks_used_on_structs: +for func_name, func, struct_name in callbacks_used_on_structs: try: # print('/* --> gen_callback_func %s */' % func_name) - gen_callback_func(func, func_name = '%s_%s' % (struct_name, func_name)) + gen_callback_func(func, func_name="%s_%s" % (struct_name, func_name)) except MissingConversionException as exp: gen_func_error(func, exp) # func_name = get_arg_name(func.type) @@ -2974,13 +3612,14 @@ def generate_struct_functions(struct_list): # # eprint("/* Generating module definition */") -print(""" +print( + """ /* * {module_name} module definitions */ -STATIC const mp_rom_map_elem_t {module_name}_globals_table[] = {{ +static const mp_rom_map_elem_t {module_name}_globals_table[] = {{ {{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_{module_name}) }}, {objects} {functions} @@ -2994,26 +3633,77 @@ def generate_struct_functions(struct_list): #endif // LV_OBJ_T }}; """.format( - module_name = sanitize(module_name), - objects = ''.join(['{{ MP_ROM_QSTR(MP_QSTR_{obj}), MP_ROM_PTR(&mp_lv_{obj}_type_base) }},\n '. - format(obj = sanitize(o)) for o in obj_names]), - functions = ''.join(['{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{func}_mpobj) }},\n '. - format(name = sanitize(simplify_identifier(f.name)), func = f.name) for f in module_funcs]), - enums = ''.join(['{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_lv_{enum}_type_base) }},\n '. - format(name = sanitize(get_enum_name(enum_name)), enum=enum_name) for enum_name in enums.keys() if enum_name not in enum_referenced]), - structs = ''.join(['{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{struct_name}_type) }},\n '. - format(name = sanitize(simplify_identifier(struct_name)), struct_name = sanitize(struct_name)) for struct_name in generated_structs \ - if generated_structs[struct_name]]), - struct_aliases = ''.join(['{{ MP_ROM_QSTR(MP_QSTR_{alias_name}), MP_ROM_PTR(&mp_{struct_name}_type) }},\n '. - format(struct_name = sanitize(struct_name), alias_name = sanitize(simplify_identifier(struct_aliases[struct_name]))) for struct_name in struct_aliases.keys()]), - blobs = ''.join(['{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{global_name}) }},\n '. - format(name = sanitize(simplify_identifier(global_name)), global_name = global_name) for global_name in generated_globals]), - int_constants = ''.join(['{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(MP_ROM_INT({value})) }},\n '. - format(name = sanitize(get_enum_name(int_constant)), value = int_constant) for int_constant in int_constants]))) - - -print(""" -STATIC MP_DEFINE_CONST_DICT ( + module_name=sanitize(module_name), + objects="".join( + [ + "{{ MP_ROM_QSTR(MP_QSTR_{obj}), MP_ROM_PTR(&mp_lv_{obj}_type_base) }},\n ".format( + obj=sanitize(o) + ) + for o in obj_names + ] + ), + functions="".join( + [ + "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{func}_mpobj) }},\n ".format( + name=sanitize(simplify_identifier(f.name)), func=f.name + ) + for f in module_funcs + ] + ), + enums="".join( + [ + "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_lv_{enum}_type_base) }},\n ".format( + name=sanitize(get_enum_name(enum_name)), enum=enum_name + ) + for enum_name in enums.keys() + if enum_name not in enum_referenced + ] + ), + structs="".join( + [ + "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{struct_name}_type) }},\n ".format( + name=sanitize(simplify_identifier(struct_name)), + struct_name=sanitize(struct_name), + ) + for struct_name in generated_structs + if generated_structs[struct_name] + ] + ), + struct_aliases="".join( + [ + "{{ MP_ROM_QSTR(MP_QSTR_{alias_name}), MP_ROM_PTR(&mp_{struct_name}_type) }},\n ".format( + struct_name=sanitize(struct_name), + alias_name=sanitize( + simplify_identifier(struct_aliases[struct_name]) + ), + ) + for struct_name in struct_aliases.keys() + ] + ), + blobs="".join( + [ + "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&mp_{global_name}) }},\n ".format( + name=sanitize(simplify_identifier(global_name)), + global_name=global_name, + ) + for global_name in generated_globals + ] + ), + int_constants="".join( + [ + "{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(MP_ROM_INT({value})) }},\n ".format( + name=sanitize(get_enum_name(int_constant)), value=int_constant + ) + for int_constant in int_constants + ] + ), + ) +) + + +print( + """ +static MP_DEFINE_CONST_DICT ( mp_module_{module_name}_globals, {module_name}_globals_table ); @@ -3026,34 +3716,56 @@ def generate_struct_functions(struct_list): MP_REGISTER_MODULE(MP_QSTR_{module_name}, mp_module_{module_name}); """.format( - module_name = module_name, - )) + module_name=module_name, + ) +) # Add an array of all object types if len(obj_names) > 0: - print(''' -STATIC const mp_lv_obj_type_t *mp_lv_obj_types[] = {{ + print( + """ +static const mp_lv_obj_type_t *mp_lv_obj_types[] = {{ {obj_types}, NULL }}; - '''.format(obj_types = ',\n '.join(['&mp_lv_%s_type' % obj_name for obj_name in obj_names]))) + """.format( + obj_types=",\n ".join( + ["&mp_lv_%s_type" % obj_name for obj_name in obj_names] + ) + ) + ) # Save Metadata File, if specified. if args.metadata: metadata = collections.OrderedDict() - metadata['objects'] = {obj_name: obj_metadata[obj_name] for obj_name in obj_names} - metadata['functions'] = {simplify_identifier(f.name): func_metadata[f.name] for f in module_funcs} - metadata['enums'] = {get_enum_name(enum_name): obj_metadata[enum_name] for enum_name in enums.keys() if enum_name not in enum_referenced} - metadata['structs'] = [simplify_identifier(struct_name) for struct_name in generated_structs if struct_name in generated_structs] - metadata['structs'] += [simplify_identifier(struct_aliases[struct_name]) for struct_name in struct_aliases.keys()] - metadata['blobs'] = [simplify_identifier(global_name) for global_name in generated_globals] - metadata['int_constants'] = [get_enum_name(int_constant) for int_constant in int_constants] + metadata["objects"] = {obj_name: obj_metadata[obj_name] for obj_name in obj_names} + metadata["functions"] = { + simplify_identifier(f.name): func_metadata[f.name] for f in module_funcs + } + metadata["enums"] = { + get_enum_name(enum_name): obj_metadata[enum_name] + for enum_name in enums.keys() + if enum_name not in enum_referenced + } + metadata["structs"] = [ + simplify_identifier(struct_name) + for struct_name in generated_structs + if struct_name in generated_structs + ] + metadata["structs"] += [ + simplify_identifier(struct_aliases[struct_name]) + for struct_name in struct_aliases.keys() + ] + metadata["blobs"] = [ + simplify_identifier(global_name) for global_name in generated_globals + ] + metadata["int_constants"] = [ + get_enum_name(int_constant) for int_constant in int_constants + ] # TODO: struct functions - with open(args.metadata, 'w') as metadata_file: + with open(args.metadata, "w") as metadata_file: json.dump(metadata, metadata_file, indent=4) - - diff --git a/lvgl b/lvgl index 905de3d7c..90a4f00f7 160000 --- a/lvgl +++ b/lvgl @@ -1 +1 @@ -Subproject commit 905de3d7c81367608577e1e38d032831a7871c16 +Subproject commit 90a4f00f727a247fa41cfa954a0112de25af379a From f47c1fa504515f959777b075097f558d212bb82c Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Thu, 25 Apr 2024 22:36:05 +0100 Subject: [PATCH 02/11] lib: Allow using lv_utils in unix-macos port. Fix to allow using lv_utils with asyncio in unix-macos. --- lib/lv_utils.py | 93 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 30 deletions(-) diff --git a/lib/lv_utils.py b/lib/lv_utils.py index e920873ee..a43e3aaf3 100644 --- a/lib/lv_utils.py +++ b/lib/lv_utils.py @@ -12,19 +12,19 @@ # event_loop = lv_utils.event_loop() # # -# uasyncio example with SDL: +# asyncio example with SDL: # # SDL.init(auto_refresh=False) # # Register SDL display driver. # # Register SDL mouse driver # event_loop = lv_utils.event_loop(asynchronous=True) -# uasyncio.Loop.run_forever() +# asyncio.Loop.run_forever() # -# uasyncio example with ili9341: +# asyncio example with ili9341: # # event_loop = lv_utils.event_loop(asynchronous=True) # Optional! # self.disp = ili9341(asynchronous=True) -# uasyncio.Loop.run_forever() +# asyncio.Loop.run_forever() # # MIT license; Copyright (c) 2021 Amir Gonnen # @@ -32,7 +32,7 @@ import lvgl as lv import micropython -import usys +import sys # Try standard machine.Timer, or custom timer from lv_timer, if available @@ -42,34 +42,45 @@ try: from lv_timer import Timer except: - raise RuntimeError("Missing machine.Timer implementation!") + if sys.platform != "darwin": + raise RuntimeError("Missing machine.Timer implementation!") + Timer = False # Try to determine default timer id default_timer_id = 0 -if usys.platform == 'pyboard': +if sys.platform == "pyboard": # stm32 only supports SW timer -1 default_timer_id = -1 - -if usys.platform == 'rp2': + +if sys.platform == "rp2": # rp2 only supports SW timer -1 default_timer_id = -1 -# Try importing uasyncio, if available +# Try importing asyncio, if available try: - import uasyncio - uasyncio_available = True + import asyncio + + asyncio_available = True except: - uasyncio_available = False + asyncio_available = False ############################################################################## -class event_loop(): +class event_loop: _current_instance = None - def __init__(self, freq=25, timer_id=default_timer_id, max_scheduled=2, refresh_cb=None, asynchronous=False, exception_sink=None): + def __init__( + self, + freq=25, + timer_id=default_timer_id, + max_scheduled=2, + refresh_cb=None, + asynchronous=False, + exception_sink=None, + ): if self.is_running(): raise RuntimeError("Event loop is already running!") @@ -80,28 +91,41 @@ def __init__(self, freq=25, timer_id=default_timer_id, max_scheduled=2, refresh_ self.delay = 1000 // freq self.refresh_cb = refresh_cb - self.exception_sink = exception_sink if exception_sink else self.default_exception_sink + self.exception_sink = ( + exception_sink if exception_sink else self.default_exception_sink + ) self.asynchronous = asynchronous if self.asynchronous: - if not uasyncio_available: - raise RuntimeError("Cannot run asynchronous event loop. uasyncio is not available!") - self.refresh_event = uasyncio.Event() - self.refresh_task = uasyncio.create_task(self.async_refresh()) - self.timer_task = uasyncio.create_task(self.async_timer()) + if not asyncio_available: + raise RuntimeError( + "Cannot run asynchronous event loop. asyncio is not available!" + ) + self.refresh_event = asyncio.Event() + self.refresh_task = asyncio.create_task(self.async_refresh()) + self.timer_task = asyncio.create_task(self.async_timer()) else: - self.timer = Timer(timer_id) + if Timer: + self.timer = Timer(timer_id) + self.timer.init( + mode=Timer.PERIODIC, period=self.delay, callback=self.timer_cb + ) self.task_handler_ref = self.task_handler # Allocation occurs here - self.timer.init(mode=Timer.PERIODIC, period=self.delay, callback=self.timer_cb) self.max_scheduled = max_scheduled self.scheduled = 0 + def init_async(self): + self.refresh_event = asyncio.Event() + self.refresh_task = asyncio.create_task(self.async_refresh()) + self.timer_task = asyncio.create_task(self.async_timer()) + def deinit(self): if self.asynchronous: self.refresh_task.cancel() self.timer_task.cancel() else: - self.timer.deinit() + if Timer: + self.timer.deinit() event_loop._current_instance = None def disable(self): @@ -122,12 +146,21 @@ def task_handler(self, _): try: if lv._nesting.value == 0: lv.task_handler() - if self.refresh_cb: self.refresh_cb() + if self.refresh_cb: + self.refresh_cb() self.scheduled -= 1 except Exception as e: if self.exception_sink: self.exception_sink(e) + def tick(self): + self.timer_cb(None) + + def run(self): + if sys.platform == "darwin": + while True: + self.tick() + def timer_cb(self, t): # Can be called in Interrupt context # Use task_handler_ref since passing self.task_handler would cause allocation. @@ -149,15 +182,15 @@ async def async_refresh(self): except Exception as e: if self.exception_sink: self.exception_sink(e) - if self.refresh_cb: self.refresh_cb() + if self.refresh_cb: + self.refresh_cb() async def async_timer(self): while True: - await uasyncio.sleep_ms(self.delay) + await asyncio.sleep_ms(self.delay) lv.tick_inc(self.delay) self.refresh_event.set() - def default_exception_sink(self, e): - usys.print_exception(e) - event_loop.current_instance().deinit() + sys.print_exception(e) + # event_loop.current_instance().deinit() From d7ffcf6f08e938039f006ede75790e6e5595ce2b Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Thu, 25 Apr 2024 22:39:03 +0100 Subject: [PATCH 03/11] unix: Enable lv_binding_micropython as user C mod. --- micropython.mk | 68 ++++++++++++++++++++++++++++++++++++++++++ ports/unix/manifest.py | 6 ++++ 2 files changed, 74 insertions(+) create mode 100644 micropython.mk create mode 100644 ports/unix/manifest.py diff --git a/micropython.mk b/micropython.mk new file mode 100644 index 000000000..6fa0a34ed --- /dev/null +++ b/micropython.mk @@ -0,0 +1,68 @@ + +################################################################################ +# LVGL unix optional libraries +# Update CFLAGS_EXTMOD and LDFLAGS_EXTMOD for LVGL extenral library, +# but do that only on the unix port, for unix specific dependencies + +ifneq ($(UNAME_S),Darwin) +CFLAGS_EXTMOD += -DMICROPY_FB=1 +endif + +SDL_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags sdl2) +SDL_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs sdl2) +ifneq ($(SDL_LDFLAGS_EXTMOD),) +CFLAGS_EXTMOD += $(SDL_CFLAGS_EXTMOD) -DMICROPY_SDL=1 +LDFLAGS_EXTMOD += $(SDL_LDFLAGS_EXTMOD) +endif + +RLOTTIE_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags rlottie) +RLOTTIE_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs rlottie) +ifneq ($(RLOTTIE_LDFLAGS_EXTMOD),) +CFLAGS_EXTMOD += $(RLOTTIE_CFLAGS_EXTMOD) -DMICROPY_RLOTTIE=1 +LDFLAGS_EXTMOD += $(RLOTTIE_LDFLAGS_EXTMOD) +endif + +FREETYPE_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags freetype2) +FREETYPE_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs freetype2) +ifneq ($(FREETYPE_LDFLAGS_EXTMOD),) +CFLAGS_EXTMOD += $(FREETYPE_CFLAGS_EXTMOD) -DMICROPY_FREETYPE=1 +LDFLAGS_EXTMOD += $(FREETYPE_LDFLAGS_EXTMOD) +endif + +FFMPEG_LIBS := libavformat libavcodec libswscale libavutil +FFMPEG_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags $(FFMPEG_LIBS)) +FFMPEG_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs $(FFMPEG_LIBS)) +ifneq ($(FFMPEG_LDFLAGS_EXTMOD),) +CFLAGS_EXTMOD += $(FFMPEG_CFLAGS_EXTMOD) -DMICROPY_FFMPEG=1 +LDFLAGS_EXTMOD += $(FFMPEG_LDFLAGS_EXTMOD) +endif + +################################################################################ + +# LVGL build rules + + +LVGL_BINDING_DIR := $(USERMOD_DIR) + +LVGL_DIR = $(LVGL_BINDING_DIR)/lvgl +LVGL_GENERIC_DRV_DIR = $(LVGL_BINDING_DIR)/driver/generic +INC += -I$(LVGL_BINDING_DIR) +ALL_LVGL_SRC = $(shell find $(LVGL_DIR) -type f -name '*.h') $(LVGL_BINDING_DIR)/lv_conf.h +LVGL_PP = $(BUILD)/lvgl/lvgl.pp.c +LVGL_MPY = $(BUILD)/lvgl/lv_mpy.c +LVGL_MPY_METADATA = $(BUILD)/lvgl/lv_mpy.json +CFLAGS_EXTMOD += $(LV_CFLAGS) + +$(LVGL_MPY): $(ALL_LVGL_SRC) $(LVGL_BINDING_DIR)/gen/gen_mpy.py + $(ECHO) "LVGL-GEN $@" + $(Q)mkdir -p $(dir $@) + $(Q)$(CPP) $(CFLAGS_EXTMOD) -DPYCPARSER -x c -I $(LVGL_BINDING_DIR)/pycparser/utils/fake_libc_include $(INC) $(LVGL_DIR)/lvgl.h > $(LVGL_PP) + $(Q)$(PYTHON) $(LVGL_BINDING_DIR)/gen/gen_mpy.py -M lvgl -MP lv -MD $(LVGL_MPY_METADATA) -E $(LVGL_PP) $(LVGL_DIR)/lvgl.h > $@ + +.PHONY: LVGL_MPY +LVGL_MPY: $(LVGL_MPY) + +CFLAGS_EXTMOD += -Wno-unused-function +CFLAGS_EXTRA += -Wno-unused-function +SRC_THIRDPARTY_C += $(subst $(TOP)/,,$(shell find $(LVGL_DIR)/src $(LVGL_DIR)/examples $(LVGL_GENERIC_DRV_DIR) -type f -name "*.c")) +SRC_EXTMOD_C += $(LVGL_MPY) diff --git a/ports/unix/manifest.py b/ports/unix/manifest.py new file mode 100644 index 000000000..20247131b --- /dev/null +++ b/ports/unix/manifest.py @@ -0,0 +1,6 @@ +module("evdev.py", base_path="../../driver/linux") +module("lv_timer.py", base_path="../../driver/linux") +module("display_driver_utils.py", base_path="../../lib") +module("display_driver.py", base_path="../../lib") +module("fs_driver.py", base_path="../../lib") +module("lv_utils.py", base_path="../../lib") From 6fae7e750fcc5d2bcfd14070facfec2b38bda5dd Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Thu, 25 Apr 2024 22:43:38 +0100 Subject: [PATCH 04/11] esp32: Enable lv_binding_micropython as user C mod. --- micropython.cmake | 47 ++++++++++++ mkrules_usermod.cmake | 157 ++++++++++++++++++++++++++++++++++++++++ ports/esp32/manifest.py | 1 + 3 files changed, 205 insertions(+) create mode 100644 micropython.cmake create mode 100644 mkrules_usermod.cmake create mode 100644 ports/esp32/manifest.py diff --git a/micropython.cmake b/micropython.cmake new file mode 100644 index 000000000..6ba1db4c8 --- /dev/null +++ b/micropython.cmake @@ -0,0 +1,47 @@ + +# This file is to be given as "make USER_C_MODULES=..." when building Micropython port + +# Include LVGL component, ignore KCONFIG + +idf_build_set_property(LV_MICROPYTHON 1) +idf_build_component(${CMAKE_CURRENT_LIST_DIR}/lvgl) +idf_build_set_property(COMPILE_DEFINITIONS "-DLV_KCONFIG_IGNORE" APPEND) +separate_arguments(LV_CFLAGS_ENV UNIX_COMMAND $ENV{LV_CFLAGS}) +idf_build_set_property(COMPILE_DEFINITIONS "${LV_CFLAGS}" APPEND) +idf_build_set_property(COMPILE_OPTIONS "-Wno-unused-function" APPEND) +idf_build_set_property(SRCS "${LV_SRC}" APPEND) +idf_build_set_property(INCLUDE_DIRS "${LV_INCLUDE}" APPEND) + +include(${CMAKE_CURRENT_LIST_DIR}/mkrules_usermod.cmake) + +# Add lv_bindings rules + +all_lv_bindings() + + +# # # make usermod (target declared by Micropython for all user compiled modules) link to bindings +# # # this way the bindings (and transitively lvgl_interface) get proper compilation flags +# target_link_libraries(usermod INTERFACE usermod_lvgl) + +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_LIST_DIR}/lvgl/src/*.c) + +add_library(lvgl_interface INTERFACE) + +target_sources(lvgl_interface INTERFACE ${SOURCES}) +target_compile_options(lvgl_interface INTERFACE ${LV_CFLAGS}) + +# # lvgl bindings target (the mpy module) + +add_library(usermod_lvgl INTERFACE) +target_sources(usermod_lvgl INTERFACE ${LV_SRC}) +target_include_directories(usermod_lvgl INTERFACE ${LV_INCLUDE}) + + +file(WRITE ${LV_MP} "") + +target_link_libraries(usermod_lvgl INTERFACE lvgl_interface) + +# # # make usermod (target declared by Micropython for all user compiled modules) link to bindings +# # # this way the bindings (and transitively lvgl_interface) get proper compilation flags +target_link_libraries(usermod INTERFACE usermod_lvgl) + diff --git a/mkrules_usermod.cmake b/mkrules_usermod.cmake new file mode 100644 index 000000000..9846dbe90 --- /dev/null +++ b/mkrules_usermod.cmake @@ -0,0 +1,157 @@ + + +find_package(Python3 REQUIRED COMPONENTS Interpreter) +find_program(AWK awk mawk gawk) + +set(LV_BINDINGS_DIR ${CMAKE_CURRENT_LIST_DIR}) + +# Common function for creating LV bindings + +function(lv_bindings) + set(_options) + set(_one_value_args OUTPUT) + set(_multi_value_args INPUT DEPENDS COMPILE_OPTIONS PP_OPTIONS GEN_OPTIONS FILTER) + cmake_parse_arguments( + PARSE_ARGV 0 LV + "${_options}" + "${_one_value_args}" + "${_multi_value_args}" + ) + + set(LV_PP ${LV_OUTPUT}.pp) + set(LV_MPY_METADATA ${LV_OUTPUT}.json) + + add_custom_command( + OUTPUT + ${LV_PP} + COMMAND + ${CMAKE_C_COMPILER} -E -DPYCPARSER ${LV_COMPILE_OPTIONS} ${LV_PP_OPTIONS} "${LV_CFLAGS}" -I ${LV_BINDINGS_DIR}/pycparser/utils/fake_libc_include ${MICROPY_CPP_FLAGS} ${LV_INPUT} > ${LV_PP} + DEPENDS + ${LV_INPUT} + ${LV_DEPENDS} + ${LV_BINDINGS_DIR}/pycparser/utils/fake_libc_include + IMPLICIT_DEPENDS + C ${LV_INPUT} + VERBATIM + COMMAND_EXPAND_LISTS + ) + + # if(ESP_PLATFORM) + # target_compile_options(${COMPONENT_LIB} PRIVATE ${LV_COMPILE_OPTIONS}) + # else() + # target_compile_options(usermod_lv_bindings INTERFACE ${LV_COMPILE_OPTIONS}) + # endif() + + if (DEFINED LV_FILTER) + + set(LV_PP_FILTERED ${LV_PP}.filtered) + set(LV_AWK_CONDITION) + foreach(_f ${LV_FILTER}) + string(APPEND LV_AWK_CONDITION "\$3!~\"${_f}\" && ") + endforeach() + string(APPEND LV_AWK_COMMAND "\$1==\"#\"{p=(${LV_AWK_CONDITION} 1)} p{print}") + + # message("AWK COMMAND: ${LV_AWK_COMMAND}") + + add_custom_command( + OUTPUT + ${LV_PP_FILTERED} + COMMAND + ${AWK} ${LV_AWK_COMMAND} ${LV_PP} > ${LV_PP_FILTERED} + DEPENDS + ${LV_PP} + VERBATIM + COMMAND_EXPAND_LISTS + ) + else() + set(LV_PP_FILTERED ${LV_PP}) + endif() + + add_custom_command( + OUTPUT + ${LV_OUTPUT} + COMMAND + ${Python3_EXECUTABLE} ${LV_BINDINGS_DIR}/gen/gen_mpy.py ${LV_GEN_OPTIONS} -MD ${LV_MPY_METADATA} -E ${LV_PP_FILTERED} ${LV_INPUT} > ${LV_OUTPUT} || (rm -f ${LV_OUTPUT} && /bin/false) + DEPENDS + ${LV_BINDINGS_DIR}/gen/gen_mpy.py + ${LV_PP_FILTERED} + COMMAND_EXPAND_LISTS + ) + +endfunction() + +# Definitions for specific bindings + +set(LVGL_DIR ${LV_BINDINGS_DIR}/lvgl) + +set(LV_MP ${CMAKE_BINARY_DIR}/lv_mp.c) +# if(ESP_PLATFORM) +# set(LV_ESPIDF ${CMAKE_BINARY_DIR}/lv_espidf.c) +# endif() + +# Function for creating all specific bindings + +function(all_lv_bindings) + + # LVGL bindings + + file(GLOB_RECURSE LVGL_HEADERS ${LVGL_DIR}/src/*.h ${LV_BINDINGS_DIR}/lv_conf.h) + lv_bindings( + OUTPUT + ${LV_MP} + INPUT + ${LVGL_DIR}/lvgl.h + DEPENDS + ${LVGL_HEADERS} + GEN_OPTIONS + -M lvgl -MP lv + ) + + + # ESPIDF bindings + + # if(ESP_PLATFORM) + # file(GLOB_RECURSE LV_ESPIDF_HEADERS ${IDF_PATH}/components/*.h ${LV_BINDINGS_DIR}/driver/esp32/*.h) + # lv_bindings( + # OUTPUT + # ${LV_ESPIDF} + # INPUT + # ${LV_BINDINGS_DIR}/driver/esp32/espidf.h + # DEPENDS + # ${LV_ESPIDF_HEADERS} + # GEN_OPTIONS + # -M espidf + # FILTER + # i2s_ll.h + # i2s_hal.h + # esp_intr_alloc.h + # soc/spi_periph.h + # rom/ets_sys.h + # soc/sens_struct.h + # soc/rtc.h + # driver/periph_ctrl.h + # ) + # endif(ESP_PLATFORM) + +endfunction() + +# Add includes to CMake component + +set(LV_INCLUDE + ${LV_BINDINGS_DIR} +) + +# Add sources to CMake component + +set(LV_SRC + ${LV_MP} +) + +# if(ESP_PLATFORM) +# LIST(APPEND LV_SRC +# ${LV_BINDINGS_DIR}/driver/esp32/espidf.c +# ${LV_BINDINGS_DIR}/driver/esp32/modrtch.c +# ${LV_BINDINGS_DIR}/driver/esp32/sh2lib.c +# ${LV_ESPIDF} +# ) +# endif(ESP_PLATFORM) diff --git a/ports/esp32/manifest.py b/ports/esp32/manifest.py new file mode 100644 index 000000000..58dc529a6 --- /dev/null +++ b/ports/esp32/manifest.py @@ -0,0 +1 @@ +module("lv_utils.py", base_path="../../lib") From 53f858c3d13ba0201340f812fc9cded7dd672628 Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Sun, 12 May 2024 16:15:04 +0100 Subject: [PATCH 05/11] feat(tests): Add tests for MicroPython test suite. --- micropython.mk | 3 +- ports/stm32/manifest.py | 1 + tests/README.md | 46 ++++++ tests/api/basic.py | 60 ++++++++ tests/api/basic.py.exp | 34 ++++ tests/api/basic_indev.py | 77 ++++++++++ tests/api/basic_indev.py.exp | 47 ++++++ tests/api/basic_slider.py | 77 ++++++++++ tests/api/basic_slider.py.exp | 37 +++++ tests/display/basic.py | 60 ++++++++ tests/display/basic.py.exp | 5 + tests/display/basic_indev.py | 77 ++++++++++ tests/display/basic_indev.py.exp | 18 +++ tests/display/basic_slider.py | 77 ++++++++++ tests/display/basic_slider.py.exp | 8 + tests/display/display_mode.py | 1 + tests/imageconvert.py | 22 +++ tests/indev/basic.py | 69 +++++++++ tests/indev/basic.py.exp | 5 + tests/indev/basic_slider.py | 76 +++++++++ tests/indev/basic_slider.py.exp | 5 + tests/indev/display_mode.py | 2 + tests/testdisplay.py | 247 ++++++++++++++++++++++++++++++ tests/testrunner.py | 65 ++++++++ 24 files changed, 1118 insertions(+), 1 deletion(-) create mode 100644 ports/stm32/manifest.py create mode 100644 tests/README.md create mode 100644 tests/api/basic.py create mode 100644 tests/api/basic.py.exp create mode 100644 tests/api/basic_indev.py create mode 100644 tests/api/basic_indev.py.exp create mode 100644 tests/api/basic_slider.py create mode 100644 tests/api/basic_slider.py.exp create mode 100644 tests/display/basic.py create mode 100644 tests/display/basic.py.exp create mode 100644 tests/display/basic_indev.py create mode 100644 tests/display/basic_indev.py.exp create mode 100644 tests/display/basic_slider.py create mode 100644 tests/display/basic_slider.py.exp create mode 100644 tests/display/display_mode.py create mode 100755 tests/imageconvert.py create mode 100644 tests/indev/basic.py create mode 100644 tests/indev/basic.py.exp create mode 100644 tests/indev/basic_slider.py create mode 100644 tests/indev/basic_slider.py.exp create mode 100644 tests/indev/display_mode.py create mode 100644 tests/testdisplay.py create mode 100644 tests/testrunner.py diff --git a/micropython.mk b/micropython.mk index 6fa0a34ed..5288c80c9 100644 --- a/micropython.mk +++ b/micropython.mk @@ -3,7 +3,7 @@ # LVGL unix optional libraries # Update CFLAGS_EXTMOD and LDFLAGS_EXTMOD for LVGL extenral library, # but do that only on the unix port, for unix specific dependencies - +ifeq ($(notdir $(CURDIR)),unix) ifneq ($(UNAME_S),Darwin) CFLAGS_EXTMOD += -DMICROPY_FB=1 endif @@ -36,6 +36,7 @@ ifneq ($(FFMPEG_LDFLAGS_EXTMOD),) CFLAGS_EXTMOD += $(FFMPEG_CFLAGS_EXTMOD) -DMICROPY_FFMPEG=1 LDFLAGS_EXTMOD += $(FFMPEG_LDFLAGS_EXTMOD) endif +endif ################################################################################ diff --git a/ports/stm32/manifest.py b/ports/stm32/manifest.py new file mode 100644 index 000000000..58dc529a6 --- /dev/null +++ b/ports/stm32/manifest.py @@ -0,0 +1 @@ +module("lv_utils.py", base_path="../../lib") diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..46383acb5 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,46 @@ + +### Tests: + +- `api/`: These tests are to test MicroPython-LVGL bindings. They can be +automated/included in CI. +To run from `micropython/tests`: +``` + ./run-tests.py ../../user_modules/lv_binding_micropython/tests/api/basic*.py -r . +``` + +- `display/`: These are to test the `api` + display driver. Intended for HIL +(Hardware in the loop) testing. Display only, no touch interface, touch is +automated and simulated in software. +To run from `micropython/tests`: +``` +./run-tests.py ../../user_modules/lv_binding_micropython/tests/display/basic*.py -r . +``` +e.g. in unix port a display will appear to provide visual feedback. + + +- `indev/`: These are to test the `display` + indev (touch) driver. Intended for +interactive HIL testing, e.g. they expect user input to complete the test. + +To run from `micropython/tests`: +``` +./run-tests.py ../../user_modules/lv_binding_micropython/tests/indev/basic*.py -r . +``` +e.g. in unix port a display will appear to allow user input. + +All tests are intended/expected to be run both in desktop (unix port) and in devices with the same result. + +For devices `testrunner.py`, `testdisplay.py` and `display_mode.py` need to be +uploaded. Also for display/indev testing a `hwdisplay.py` with a display driver +called `display` is expected. This `display` driver is expected to have at least a +```py + + def blit(self, x1, y1, w, h, buff): +``` +method or handle the lv display setup by itself (e.g setting buffers, `flush_cb`, etc) + +For interactive indev tests, it is required to have a +```py + + def read_cb(self, indev, data): +``` +method too, or handle indev creation by itself. diff --git a/tests/api/basic.py b/tests/api/basic.py new file mode 100644 index 000000000..703249234 --- /dev/null +++ b/tests/api/basic.py @@ -0,0 +1,60 @@ +import lvgl as lv +import sys +import asyncio +import os + +sys.path.append("..") +sys.path.append(os.getcwd()) +import testrunner + +# This is a basic test to test buttons, labels, +# RGB colors, layout aligment and events. + + +async def demo(scr, display=None): + def get_button(scr, text, align, color): + _btn = lv.button(scr) + _btn.set_size(lv.pct(25), lv.pct(10)) + _lab = lv.label(_btn) + _lab.set_text(text) + _lab.center() + _btn.set_style_align(align, 0) + _btn.set_style_bg_color(lv.color_make(*color), 0) + return _btn, text + + buttons = [ + ("RED", lv.ALIGN.TOP_MID, (255, 0, 0)), + ("GREEN", lv.ALIGN.BOTTOM_MID, (0, 255, 0)), + ("BLUE", lv.ALIGN.CENTER, (0, 0, 255)), + ] + + def button_cb(event, name): + print(f"{name} PRESSED") + + _all_btns = [get_button(scr, *btn) for btn in buttons] + + for btn, name in _all_btns: + btn.add_event_cb( + lambda event, button_name=name: button_cb(event, button_name), + lv.EVENT.CLICKED, + None, + ) + + await asyncio.sleep_ms(500) # await so the frame can be rendered + print("EVENT TEST:") + for _btn, name in _all_btns: + _btn.send_event(lv.EVENT.CLICKED, None) + await asyncio.sleep_ms(200) + + return _all_btns + + +__file__ = globals().get("__file__", "test") + +try: + from display_mode import MODE as _mode +except Exception: + _mode = None + +testrunner.run(demo, __file__, mode=_mode) +testrunner.devicereset() diff --git a/tests/api/basic.py.exp b/tests/api/basic.py.exp new file mode 100644 index 000000000..c0de908b1 --- /dev/null +++ b/tests/api/basic.py.exp @@ -0,0 +1,34 @@ + +FRAME: 0 (0, 0, 240, 32, 23040) +d5c5d09cff879bb12cb926dc44bf10161cded58d2057806e7cbde536540b1421 + +FRAME: 1 (0, 32, 240, 32, 23040) +f281e1fce42dc013342ad8a4573d74874238d995e6dff46dc29a1d68b780f920 + +FRAME: 2 (0, 64, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 3 (0, 96, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 4 (0, 128, 240, 32, 23040) +424125778438a53da017c2dca09964ec2cec5ad4e2689038dd0e830125112fd8 + +FRAME: 5 (0, 160, 240, 32, 23040) +9abb7f9219bb7ccc8784119c784b1bf41c451f9957989fd2a9fc12a15606b1d0 + +FRAME: 6 (0, 192, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 7 (0, 224, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 8 (0, 256, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 9 (0, 288, 240, 32, 23040) +f546d8ae7340f5fb71e30358ef0d6f33a4f2d72946d9b312444b07fa9d659396 +EVENT TEST: +RED PRESSED +GREEN PRESSED +BLUE PRESSED diff --git a/tests/api/basic_indev.py b/tests/api/basic_indev.py new file mode 100644 index 000000000..38ac0ed88 --- /dev/null +++ b/tests/api/basic_indev.py @@ -0,0 +1,77 @@ +import lvgl as lv +import sys +import asyncio +import os + +sys.path.append("..") +sys.path.append(os.getcwd()) +import testrunner + +# This is a basic test to test buttons, labels, +# RGB colors, layout aligment and events. + + +async def demo(scr, display=None): + def get_button(scr, text, align, color): + _btn = lv.button(scr) + _btn.set_size(lv.pct(25), lv.pct(10)) + _lab = lv.label(_btn) + _lab.set_text(text) + _lab.center() + _btn.set_style_align(align, 0) + _btn.set_style_bg_color(lv.color_make(*color), 0) + return _btn, text + + buttons = [ + ("RED", lv.ALIGN.TOP_MID, (255, 0, 0)), + ("GREEN", lv.ALIGN.BOTTOM_MID, (0, 255, 0)), + ("BLUE", lv.ALIGN.CENTER, (0, 0, 255)), + ] + + def button_cb(event, name): + print(f"{name} PRESSED") + + _all_btns = [get_button(scr, *btn) for btn in buttons] + + for btn, name in _all_btns: + btn.add_event_cb( + lambda event, button_name=name: button_cb(event, button_name), + lv.EVENT.CLICKED, + None, + ) + + await asyncio.sleep_ms(500) # await so the frame can be rendered + print("EVENT TEST:") + for _btn, name in _all_btns: + _btn.send_event(lv.EVENT.CLICKED, None) + await asyncio.sleep_ms(200) + + # simulate touch events + if display: + print("INDEV TEST:") + await display.touch(100, 100) + + await asyncio.sleep_ms(500) + + print("INDEV + BUTTONS TEST:") + # display.debug_indev(press=False, release=False) + display.debug_display(False) + for _btn, name in _all_btns: + pos = _btn.get_x(), _btn.get_y() + await display.touch(*pos) + await asyncio.sleep_ms(1000) + + await asyncio.sleep_ms(500) + + return _all_btns + + +__file__ = globals().get("__file__", "test") + +try: + from display_mode import MODE as _mode +except Exception: + _mode = None + +testrunner.run(demo, __file__, mode=_mode) +testrunner.devicereset() diff --git a/tests/api/basic_indev.py.exp b/tests/api/basic_indev.py.exp new file mode 100644 index 000000000..d8cbf7b85 --- /dev/null +++ b/tests/api/basic_indev.py.exp @@ -0,0 +1,47 @@ + +FRAME: 0 (0, 0, 240, 32, 23040) +d5c5d09cff879bb12cb926dc44bf10161cded58d2057806e7cbde536540b1421 + +FRAME: 1 (0, 32, 240, 32, 23040) +f281e1fce42dc013342ad8a4573d74874238d995e6dff46dc29a1d68b780f920 + +FRAME: 2 (0, 64, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 3 (0, 96, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 4 (0, 128, 240, 32, 23040) +424125778438a53da017c2dca09964ec2cec5ad4e2689038dd0e830125112fd8 + +FRAME: 5 (0, 160, 240, 32, 23040) +9abb7f9219bb7ccc8784119c784b1bf41c451f9957989fd2a9fc12a15606b1d0 + +FRAME: 6 (0, 192, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 7 (0, 224, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 8 (0, 256, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 9 (0, 288, 240, 32, 23040) +f546d8ae7340f5fb71e30358ef0d6f33a4f2d72946d9b312444b07fa9d659396 +EVENT TEST: +RED PRESSED +GREEN PRESSED +BLUE PRESSED +INDEV TEST: +[PRESSED]: (100,100) +[RELEASED]: (100,100) +INDEV + BUTTONS TEST: +[PRESSED]: (90,0) +RED PRESSED +[RELEASED]: (90,0) +[PRESSED]: (90,288) +GREEN PRESSED +[RELEASED]: (90,288) +[PRESSED]: (90,144) +BLUE PRESSED +[RELEASED]: (90,144) diff --git a/tests/api/basic_slider.py b/tests/api/basic_slider.py new file mode 100644 index 000000000..f226c5368 --- /dev/null +++ b/tests/api/basic_slider.py @@ -0,0 +1,77 @@ +import lvgl as lv +import sys +import asyncio +import os + +sys.path.append("..") +sys.path.append(os.getcwd()) +import testrunner # noqa + +# This is a basic test to test buttons, labels, +# RGB colors, layout aligment and events. + + +async def demo(scr, display=None): + def get_button(scr, text, align, color): + scr.set_style_pad_all(10, 0) + _btn = lv.slider(scr) + _btn.set_width(lv.pct(75)) + _btn.set_height(lv.pct(10)) + _lab = lv.label(_btn) + _lab.set_text(text) + _lab.set_style_text_color(lv.color_white(), 0) + _lab.center() + _btn.set_style_align(align, 0) + _btn.set_style_bg_color(lv.color_make(*color), lv.PART.INDICATOR) + _btn.set_style_bg_color(lv.color_make(*color), lv.PART.MAIN) + _btn.set_style_bg_color(lv.color_make(*color), lv.PART.KNOB) + return _btn, text + + buttons = [ + ("RED", lv.ALIGN.TOP_MID, (255, 0, 0)), + ("GREEN", lv.ALIGN.BOTTOM_MID, (0, 255, 0)), + ("BLUE", lv.ALIGN.CENTER, (0, 0, 255)), + ] + + def button_cb(event, name, slider): + if slider.get_value() == 100: + print(f"{name} VALUE: {slider.get_value()}") + + _all_btns = [get_button(scr, *btn) for btn in buttons] + + for btn, name in _all_btns: + btn.add_event_cb( + lambda event, button_name=name, slider=btn: button_cb( + event, button_name, slider + ), + lv.EVENT.VALUE_CHANGED, + None, + ) + + await asyncio.sleep_ms(500) # await so the frame can be rendered + # simulate touch events + if display: + print("INDEV + SLIDER TEST:") + display.debug_indev(press=False) + display.debug_display(False) + for _btn, name in _all_btns: + pos = _btn.get_x(), _btn.get_y() + pos2 = _btn.get_x2(), _btn.get_y2() + x1, y1 = pos + x2, y2 = pos2 + y_mid = y2 - ((y2 - y1) // 2) + await display.swipe(x1 + 5, y_mid, x2 + (y2 - y1), y_mid, ms=500) + await asyncio.sleep_ms(100) + + return _all_btns + + +__file__ = globals().get("__file__", "test") + +try: + from display_mode import MODE as _mode +except Exception: + _mode = None + +testrunner.run(demo, __file__, mode=_mode) +testrunner.devicereset() diff --git a/tests/api/basic_slider.py.exp b/tests/api/basic_slider.py.exp new file mode 100644 index 000000000..c6359c382 --- /dev/null +++ b/tests/api/basic_slider.py.exp @@ -0,0 +1,37 @@ + +FRAME: 0 (0, 0, 240, 32, 23040) +6e5737038637abc5ea724930a5113dd9193a3e708b13c3be75d2e5164ccfc57a + +FRAME: 1 (0, 32, 240, 32, 23040) +5c139099d39acc6aa2081459fb397f698035937288fd088b60325f11d8d839a9 + +FRAME: 2 (0, 64, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 3 (0, 96, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 4 (0, 128, 240, 32, 23040) +85b39d4c5e001bd4aa82e2c0efd390d2f1c20517426ebc07a63a64c8315dcdc4 + +FRAME: 5 (0, 160, 240, 32, 23040) +798e52f4dd160d6e592d0d4d075ef4779eeffed0a4f2339aa9b524e2fe008eae + +FRAME: 6 (0, 192, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 7 (0, 224, 240, 32, 23040) +46e2096b907947368d310929303a04005b39c4a278e3a7de2225c355b4522694 + +FRAME: 8 (0, 256, 240, 32, 23040) +a6b9cdacc2013dbb3ce95198217d24ce42333d0178da7fddd15ff353ab012891 + +FRAME: 9 (0, 288, 240, 32, 23040) +08943f10a0eeb2c8a3b8ec437fd2d0725b5a764d29375282553eaafd51ff704a +INDEV + SLIDER TEST: +RED VALUE: 100 +[RELEASED]: (218,15) +GREEN VALUE: 100 +[RELEASED]: (218,285) +BLUE VALUE: 100 +[RELEASED]: (218,150) diff --git a/tests/display/basic.py b/tests/display/basic.py new file mode 100644 index 000000000..703249234 --- /dev/null +++ b/tests/display/basic.py @@ -0,0 +1,60 @@ +import lvgl as lv +import sys +import asyncio +import os + +sys.path.append("..") +sys.path.append(os.getcwd()) +import testrunner + +# This is a basic test to test buttons, labels, +# RGB colors, layout aligment and events. + + +async def demo(scr, display=None): + def get_button(scr, text, align, color): + _btn = lv.button(scr) + _btn.set_size(lv.pct(25), lv.pct(10)) + _lab = lv.label(_btn) + _lab.set_text(text) + _lab.center() + _btn.set_style_align(align, 0) + _btn.set_style_bg_color(lv.color_make(*color), 0) + return _btn, text + + buttons = [ + ("RED", lv.ALIGN.TOP_MID, (255, 0, 0)), + ("GREEN", lv.ALIGN.BOTTOM_MID, (0, 255, 0)), + ("BLUE", lv.ALIGN.CENTER, (0, 0, 255)), + ] + + def button_cb(event, name): + print(f"{name} PRESSED") + + _all_btns = [get_button(scr, *btn) for btn in buttons] + + for btn, name in _all_btns: + btn.add_event_cb( + lambda event, button_name=name: button_cb(event, button_name), + lv.EVENT.CLICKED, + None, + ) + + await asyncio.sleep_ms(500) # await so the frame can be rendered + print("EVENT TEST:") + for _btn, name in _all_btns: + _btn.send_event(lv.EVENT.CLICKED, None) + await asyncio.sleep_ms(200) + + return _all_btns + + +__file__ = globals().get("__file__", "test") + +try: + from display_mode import MODE as _mode +except Exception: + _mode = None + +testrunner.run(demo, __file__, mode=_mode) +testrunner.devicereset() diff --git a/tests/display/basic.py.exp b/tests/display/basic.py.exp new file mode 100644 index 000000000..bdb7f65e8 --- /dev/null +++ b/tests/display/basic.py.exp @@ -0,0 +1,5 @@ +DISPLAY_MODE: INTERACTIVE +EVENT TEST: +RED PRESSED +GREEN PRESSED +BLUE PRESSED diff --git a/tests/display/basic_indev.py b/tests/display/basic_indev.py new file mode 100644 index 000000000..6dde2bc63 --- /dev/null +++ b/tests/display/basic_indev.py @@ -0,0 +1,77 @@ +import lvgl as lv +import sys +import asyncio +import os + +sys.path.append("..") +sys.path.append(os.getcwd()) +import testrunner + +# This is a basic test to test buttons, labels, +# RGB colors, layout aligment and events. + + +async def demo(scr, display=None): + def get_button(scr, text, align, color): + _btn = lv.button(scr) + _btn.set_size(lv.pct(25), lv.pct(10)) + _lab = lv.label(_btn) + _lab.set_text(text) + _lab.center() + _btn.set_style_align(align, 0) + _btn.set_style_bg_color(lv.color_make(*color), 0) + return _btn, text + + buttons = [ + ("RED", lv.ALIGN.TOP_MID, (255, 0, 0)), + ("GREEN", lv.ALIGN.BOTTOM_MID, (0, 255, 0)), + ("BLUE", lv.ALIGN.CENTER, (0, 0, 255)), + ] + + def button_cb(event, name): + print(f"{name} PRESSED") + + _all_btns = [get_button(scr, *btn) for btn in buttons] + + for btn, name in _all_btns: + btn.add_event_cb( + lambda event, button_name=name: button_cb(event, button_name), + lv.EVENT.CLICKED, + None, + ) + + await asyncio.sleep_ms(500) # await so the frame can be rendered + print("EVENT TEST:") + for _btn, name in _all_btns: + _btn.send_event(lv.EVENT.CLICKED, None) + await asyncio.sleep_ms(200) + + # simulate touch events + if display: + print("INDEV TEST:") + await display.touch(100, 100) + + await asyncio.sleep_ms(500) + + print("INDEV + BUTTONS TEST:") + # display.debug_indev(press=True, release=True) + display.debug_display(False) + for _btn, name in _all_btns: + pos = _btn.get_x(), _btn.get_y() + await display.touch(*pos) + await asyncio.sleep_ms(1000) + + await asyncio.sleep_ms(500) + + return _all_btns + + +__file__ = globals().get("__file__", "test") + +try: + from display_mode import MODE as _mode +except Exception: + _mode = None + +testrunner.run(demo, __file__, mode=_mode) +testrunner.devicereset() diff --git a/tests/display/basic_indev.py.exp b/tests/display/basic_indev.py.exp new file mode 100644 index 000000000..25f341f5e --- /dev/null +++ b/tests/display/basic_indev.py.exp @@ -0,0 +1,18 @@ +DISPLAY_MODE: INTERACTIVE +EVENT TEST: +RED PRESSED +GREEN PRESSED +BLUE PRESSED +INDEV TEST: +[PRESSED]: (100,100) +[RELEASED]: (100,100) +INDEV + BUTTONS TEST: +[PRESSED]: (90,0) +RED PRESSED +[RELEASED]: (90,0) +[PRESSED]: (90,288) +GREEN PRESSED +[RELEASED]: (90,288) +[PRESSED]: (90,144) +BLUE PRESSED +[RELEASED]: (90,144) diff --git a/tests/display/basic_slider.py b/tests/display/basic_slider.py new file mode 100644 index 000000000..f226c5368 --- /dev/null +++ b/tests/display/basic_slider.py @@ -0,0 +1,77 @@ +import lvgl as lv +import sys +import asyncio +import os + +sys.path.append("..") +sys.path.append(os.getcwd()) +import testrunner # noqa + +# This is a basic test to test buttons, labels, +# RGB colors, layout aligment and events. + + +async def demo(scr, display=None): + def get_button(scr, text, align, color): + scr.set_style_pad_all(10, 0) + _btn = lv.slider(scr) + _btn.set_width(lv.pct(75)) + _btn.set_height(lv.pct(10)) + _lab = lv.label(_btn) + _lab.set_text(text) + _lab.set_style_text_color(lv.color_white(), 0) + _lab.center() + _btn.set_style_align(align, 0) + _btn.set_style_bg_color(lv.color_make(*color), lv.PART.INDICATOR) + _btn.set_style_bg_color(lv.color_make(*color), lv.PART.MAIN) + _btn.set_style_bg_color(lv.color_make(*color), lv.PART.KNOB) + return _btn, text + + buttons = [ + ("RED", lv.ALIGN.TOP_MID, (255, 0, 0)), + ("GREEN", lv.ALIGN.BOTTOM_MID, (0, 255, 0)), + ("BLUE", lv.ALIGN.CENTER, (0, 0, 255)), + ] + + def button_cb(event, name, slider): + if slider.get_value() == 100: + print(f"{name} VALUE: {slider.get_value()}") + + _all_btns = [get_button(scr, *btn) for btn in buttons] + + for btn, name in _all_btns: + btn.add_event_cb( + lambda event, button_name=name, slider=btn: button_cb( + event, button_name, slider + ), + lv.EVENT.VALUE_CHANGED, + None, + ) + + await asyncio.sleep_ms(500) # await so the frame can be rendered + # simulate touch events + if display: + print("INDEV + SLIDER TEST:") + display.debug_indev(press=False) + display.debug_display(False) + for _btn, name in _all_btns: + pos = _btn.get_x(), _btn.get_y() + pos2 = _btn.get_x2(), _btn.get_y2() + x1, y1 = pos + x2, y2 = pos2 + y_mid = y2 - ((y2 - y1) // 2) + await display.swipe(x1 + 5, y_mid, x2 + (y2 - y1), y_mid, ms=500) + await asyncio.sleep_ms(100) + + return _all_btns + + +__file__ = globals().get("__file__", "test") + +try: + from display_mode import MODE as _mode +except Exception: + _mode = None + +testrunner.run(demo, __file__, mode=_mode) +testrunner.devicereset() diff --git a/tests/display/basic_slider.py.exp b/tests/display/basic_slider.py.exp new file mode 100644 index 000000000..6dc1b3cc5 --- /dev/null +++ b/tests/display/basic_slider.py.exp @@ -0,0 +1,8 @@ +DISPLAY_MODE: INTERACTIVE +INDEV + SLIDER TEST: +RED VALUE: 100 +[RELEASED]: (218,15) +GREEN VALUE: 100 +[RELEASED]: (218,285) +BLUE VALUE: 100 +[RELEASED]: (218,150) diff --git a/tests/display/display_mode.py b/tests/display/display_mode.py new file mode 100644 index 000000000..c6af2743b --- /dev/null +++ b/tests/display/display_mode.py @@ -0,0 +1 @@ +MODE = "interactive" diff --git a/tests/imageconvert.py b/tests/imageconvert.py new file mode 100755 index 000000000..4157cfe3d --- /dev/null +++ b/tests/imageconvert.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +from PIL import Image +import sys +import argparse + +parser = argparse.ArgumentParser(description="RGB888 to PNG converter") +parser.add_argument("file", help="file/s name", nargs="*") + +args = parser.parse_args() + +for file in args.file: + with open(file, "rb") as ff: + w, h, cs = ff.readline().split(b":") + width, height = int(w.decode()), int(h.decode()) + frame = ff.read() + assert len(frame) == width * height * int(cs) + + image = Image.new("RGB", (width, height)) + image.frombytes(frame) + image.save(file.replace(".bin", ".png"), "PNG") + image.close() diff --git a/tests/indev/basic.py b/tests/indev/basic.py new file mode 100644 index 000000000..1a96ad735 --- /dev/null +++ b/tests/indev/basic.py @@ -0,0 +1,69 @@ +import lvgl as lv +import sys +import asyncio +import os + +sys.path.append("..") +sys.path.append(os.getcwd()) +import testrunner + +# This is a basic test to test buttons, labels, +# RGB colors, layout aligment and events. + + +class TestButton(lv.button): + def __init__(self, parent): + super().__init__(parent) + self.event_press = asyncio.Event() + + +async def demo(scr, display=None): + def get_button(scr, text, align, color): + _btn = TestButton(scr) + _btn.set_size(lv.pct(25), lv.pct(10)) + _lab = lv.label(_btn) + _lab.set_text(text) + _lab.center() + _btn.set_style_align(align, 0) + _btn.set_style_bg_color(lv.color_make(*color), 0) + return _btn, text + + buttons = [ + ("RED", lv.ALIGN.TOP_MID, (255, 0, 0)), + ("GREEN", lv.ALIGN.BOTTOM_MID, (0, 255, 0)), + ("BLUE", lv.ALIGN.CENTER, (0, 0, 255)), + ] + + def button_cb(event, name, button): + print(f"{name} PRESSED") + button.event_press.set() + + _all_btns = [get_button(scr, *btn) for btn in buttons] + + for btn, name in _all_btns: + btn.add_event_cb( + lambda event, button_name=name, button=btn: button_cb( + event, button_name, button + ), + lv.EVENT.CLICKED, + None, + ) + + await asyncio.sleep_ms(500) # await so the frame can be rendered + print("PRESS EVENT TEST:") + for _btn, name in _all_btns: + await _btn.event_press.wait() + return _all_btns + + +__file__ = globals().get("__file__", "test") + +try: + from display_mode import MODE as _mode + from display_mode import POINTER as _pointer +except Exception: + _mode = "test" + _pointer = "sim" + +testrunner.run(demo, __file__, mode=_mode, pointer=_pointer) +testrunner.devicereset() diff --git a/tests/indev/basic.py.exp b/tests/indev/basic.py.exp new file mode 100644 index 000000000..83badc00a --- /dev/null +++ b/tests/indev/basic.py.exp @@ -0,0 +1,5 @@ +DISPLAY_MODE: INTERACTIVE +PRESS EVENT TEST: +RED PRESSED +BLUE PRESSED +GREEN PRESSED diff --git a/tests/indev/basic_slider.py b/tests/indev/basic_slider.py new file mode 100644 index 000000000..481ddb453 --- /dev/null +++ b/tests/indev/basic_slider.py @@ -0,0 +1,76 @@ +import lvgl as lv +import sys +import asyncio +import os + +sys.path.append("..") +sys.path.append(os.getcwd()) +import testrunner # noqa + +# This is a basic test to test buttons, labels, +# RGB colors, layout aligment and events. + + +class TestSlider(lv.slider): + def __init__(self, parent): + super().__init__(parent) + self.event_completed = asyncio.Event() + + +async def demo(scr, display=None): + def get_button(scr, text, align, color): + scr.set_style_pad_all(10, 0) + _btn = TestSlider(scr) + _btn.set_width(lv.pct(75)) + _btn.set_height(lv.pct(10)) + _lab = lv.label(_btn) + _lab.set_text(text) + _lab.set_style_text_color(lv.color_white(), 0) + _lab.center() + _btn.set_style_align(align, 0) + _btn.set_style_bg_color(lv.color_make(*color), lv.PART.INDICATOR) + _btn.set_style_bg_color(lv.color_make(*color), lv.PART.MAIN) + _btn.set_style_bg_color(lv.color_make(*color), lv.PART.KNOB) + return _btn, text + + buttons = [ + ("RED", lv.ALIGN.TOP_MID, (255, 0, 0)), + ("GREEN", lv.ALIGN.BOTTOM_MID, (0, 255, 0)), + ("BLUE", lv.ALIGN.CENTER, (0, 0, 255)), + ] + + def button_cb(event, name, slider): + if slider.get_value() == 100: + if not slider.event_completed.is_set(): + print(f"{name} VALUE: {slider.get_value()}") + slider.event_completed.set() + + _all_btns = [get_button(scr, *btn) for btn in buttons] + + for btn, name in _all_btns: + btn.add_event_cb( + lambda event, button_name=name, slider=btn: button_cb( + event, button_name, slider + ), + lv.EVENT.VALUE_CHANGED, + None, + ) + + print("INDEV + SLIDER TEST:") + display.debug_indev(press=False) + display.debug_display(False) + for _btn, name in _all_btns: + await _btn.event_completed.wait() + return _all_btns + + +__file__ = globals().get("__file__", "test") + +try: + from display_mode import MODE as _mode + from display_mode import POINTER as _pointer +except Exception: + _mode = None + +testrunner.run(demo, __file__, mode=_mode, pointer=_pointer) +testrunner.devicereset() diff --git a/tests/indev/basic_slider.py.exp b/tests/indev/basic_slider.py.exp new file mode 100644 index 000000000..fe869d3ed --- /dev/null +++ b/tests/indev/basic_slider.py.exp @@ -0,0 +1,5 @@ +DISPLAY_MODE: INTERACTIVE +INDEV + SLIDER TEST: +RED VALUE: 100 +BLUE VALUE: 100 +GREEN VALUE: 100 diff --git a/tests/indev/display_mode.py b/tests/indev/display_mode.py new file mode 100644 index 000000000..54dfb8c14 --- /dev/null +++ b/tests/indev/display_mode.py @@ -0,0 +1,2 @@ +MODE = "interactive" +POINTER = False diff --git a/tests/testdisplay.py b/tests/testdisplay.py new file mode 100644 index 000000000..1f7e71433 --- /dev/null +++ b/tests/testdisplay.py @@ -0,0 +1,247 @@ +# adapted from https://github.com/bdbarnett/mpdisplay +# utils/lv_mpdisplay.py + +import lvgl as lv +import hashlib +from binascii import hexlify +import lv_utils +import sys +import asyncio + + +class TestDisplayDriver: + def __init__( + self, + display_drv, + frame_buffer1, + frame_buffer2, + color_format=lv.COLOR_FORMAT.RGB565, + mode="test", + pointer="sim", + fps=25, + ): + self.display_drv = display_drv + self._color_size = lv.color_format_get_size(color_format) + self._frame_buffer1 = frame_buffer1 + self._frame_buffer2 = frame_buffer2 + self._x = 0 + self._y = 0 + self._dstate = None + self._press_event = False + self._release_event = False + self._debug_press = True + self._debug_release = True + self.mode = mode + + render_mode = lv.DISPLAY_RENDER_MODE.PARTIAL + + if not lv_utils.event_loop.is_running(): + self.event_loop = lv_utils.event_loop(freq=fps, asynchronous=True) + + else: + self.event_loop = lv_utils.event_loop.current_instance() + self.event_loop.scheduled = 0 + + if not lv.is_initialized(): + lv.init() + + if mode == "test" or ( + mode == "interactive" and not isinstance(display_drv, DummyDisplay) + ): + if hasattr(display_drv, "blit"): + self.lv_display = lv.display_create( + self.display_drv.width, self.display_drv.height + ) + self.lv_display.set_color_format(color_format) + self.lv_display.set_flush_cb(self._flush_cb) + self.lv_display.set_buffers( + self._frame_buffer1, + self._frame_buffer2, + len(self._frame_buffer1), + render_mode, + ) + self.indev_test = lv.indev_create() + self.indev_test.set_display(lv.display_get_default()) + self.indev_test.set_group(lv.group_get_default()) + # TODO: test other types of indev + self.indev_test.set_type(lv.INDEV_TYPE.POINTER) + if hasattr(display_drv, "read_cb") and pointer != "sim": + self.indev_test.set_read_cb(display_drv.read_cb) + + else: + self.indev_test.set_read_cb(self._read_cb) + + else: # interactive + DummyDisplay -> SDL + self.group = lv.group_create() + self.group.set_default() + self.lv_display_int = lv.sdl_window_create( + display_drv.width, display_drv.height + ) + lv.sdl_window_set_title(self.lv_display_int, "MicroPython-LVGL") + self.mouse = lv.sdl_mouse_create() + self.keyboard = lv.sdl_keyboard_create() + self.keyboard.set_group(self.group) + if pointer == "sim": + self.indev = lv.indev_create() + self.indev.set_display(self.lv_display_int) + self.indev.set_group(self.group) + self.indev.set_type(lv.INDEV_TYPE.POINTER) + # NOTE: only one indev pointer allowed, use the keyboard + # for interactive control + self.indev.set_read_cb(self._read_cb) + + def set_test_name(self, name): + self.display_drv.test_name = name + + def debug_indev(self, press=None, release=None): + self._debug_press = press if press is not None else self._debug_press + self._debug_release = release if release is not None else self._debug_release + + def debug_display(self, debug=True): + if hasattr(self.display_drv, "debug"): + self.display_drv.debug = debug + + async def touch(self, x, y, ms=100): + self._x = x + self._y = y + self._dstate = lv.INDEV_STATE.PRESSED + self._press_event = True + await asyncio.sleep_ms(ms) + + self._dstate = lv.INDEV_STATE.RELEASED + self._release_event = True + await asyncio.sleep_ms(25) + + async def swipe(self, x1, y1, x2, y2, steps=5, ms=100): + self._dstate = lv.INDEV_STATE.PRESSED + if y1 == y2: # HORIZONTAL + self._y = y1 + self._x = x1 + if x2 < x1: + steps = -steps # RIGHT-LEFT + for xi in range(x1, x2, steps): + self._x = xi + self._press_event = True + await asyncio.sleep_ms(ms // steps) + elif x1 == x2: + self._y = y1 + self._x = x1 + if y2 < y1: + steps = -steps # BOTTOM-UP + for yi in range(y1, y2, steps): + self._y = yi + self._press_event = True + await asyncio.sleep_ms(ms // steps) + + self._dstate = lv.INDEV_STATE.RELEASED + self._release_event = True + await asyncio.sleep_ms(25) + + def _flush_cb(self, disp_drv, area, color_p): + width = area.x2 - area.x1 + 1 + height = area.y2 - area.y1 + 1 + + self.display_drv.blit( + area.x1, + area.y1, + width, + height, + color_p.__dereference__(width * height * self._color_size), + ) + self.lv_display.flush_ready() + + def _read_cb(self, indev, data): + if self._press_event: + self._press_event = False + if self._debug_press: + print(f"[PRESSED]: ({self._x},{self._y})") + data.point = lv.point_t({"x": self._x, "y": self._y}) + data.state = self._dstate + elif self._release_event: + self._release_event = False + if self._debug_release: + print(f"[RELEASED]: ({self._x},{self._y})") + data.state = self._dstate + + +class DummyDisplay: + def __init__(self, width=240, height=320, color_format=lv.COLOR_FORMAT.RGB565): + self.width = width + self.height = height + self.color_depth = lv.color_format_get_bpp(color_format) + self.color_size = lv.color_format_get_size(color_format) + self.n = 0 + self.test_name = "testframe" + self._header_set = False + self._save_frame = sys.platform in ["darwin", "linux"] + self._debug = True + + @property + def debug(self): + return self._debug + + @debug.setter + def debug(self, x): + self._debug = x + self._save_frame = x + + def save_frame(self, data): + if not self._header_set: + self._header_set = True + with open(f"{self.test_name}.bin", "wb") as fr: + fr.write(f"{self.width}:{self.height}:{self.color_size}\n".encode()) + + with open(f"{self.test_name}.bin", "ab") as fr: + fr.write(data) + + def _shasum_frame(self, data): + _hash = hashlib.sha256() + _hash.update(data) + _result = _hash.digest() + result = hexlify(_result).decode() + return result + + def blit(self, x1, y1, w, h, buff): + if self.debug: + print(f"\nFRAME: {self.n} {(x1, y1, w, h, len(buff[:]))}") + print(self._shasum_frame(bytes(buff[:]))) + if self._save_frame: + self.save_frame(buff[:]) + self.n += 1 + + +tdisp = DummyDisplay(color_format=lv.COLOR_FORMAT.RGB888) + + +alloc_buffer = lambda buffersize: memoryview(bytearray(buffer_size)) + +factor = 10 ### Must be 1 if using an RGBBus +double_buf = True ### Must be False if using an RGBBus + +buffer_size = tdisp.width * tdisp.height * (tdisp.color_depth // 8) // factor + +fbuf1 = alloc_buffer(buffer_size) +fbuf2 = alloc_buffer(buffer_size) if double_buf else None + + +def get_display( + width=240, + height=320, + disp=tdisp, + color_format=lv.COLOR_FORMAT.RGB888, + mode="test", + pointer="sim", +): + if mode == "test": + disp = tdisp + elif mode == "interactive": + print("DISPLAY_MODE: INTERACTIVE") + try: + from hwdisplay import display as disp + except Exception as e: + if sys.platform not in ["darwin", "linux"]: + sys.print_exception(e) + if hasattr(disp, "width") and hasattr(disp, "height"): + disp.width = width + disp.height = height + return TestDisplayDriver(disp, fbuf1, fbuf2, color_format, mode, pointer) diff --git a/tests/testrunner.py b/tests/testrunner.py new file mode 100644 index 000000000..71176c09f --- /dev/null +++ b/tests/testrunner.py @@ -0,0 +1,65 @@ +import sys +import os + +sys.path.append("..") +sys.path.append(os.getcwd()) + +from testdisplay import get_display # noqa + +_int = sys.argv.pop() if sys.platform in ["darwin", "linux"] else "" +_mode = "test" +if _int in ("-id", "-d"): + _mode = "interactive" + + +async def run_test(func, display=None): + import lvgl as lv # noqa + + lv.init() + + scr = lv.obj() + scr.set_style_bg_color(lv.color_black(), 0) + lv.screen_load(scr) + + resp = await func(scr, display) + return scr, resp + + +def run(func, filename, w=240, h=320, mode=None, **kwargs): + import asyncio + + # import micropython # noqa + + # micropython.mem_info() + + async def _run(func, w, h, mode=None, **kwargs): + display = get_display( + w, + h, + mode=mode if mode is not None else _mode, + pointer=kwargs.get("pointer", "sim"), + ) + + if display.mode == "test": + display.set_test_name(f"{filename.replace('.py', '')}.{func.__name__}") + await run_test(func, display) + await asyncio.sleep_ms(100) + elif display.mode == "interactive": + await run_test(func, display) + if _int == "-id": + while True: + try: + await asyncio.sleep_ms(1000) + except KeyboardInterrupt: + sys.exit() + except Exception as e: + sys.print_exception(e) + + asyncio.run(_run(func, w, h, mode, **kwargs)) + + +def devicereset(): + import lvgl as lv + + if lv.is_initialized(): + lv.deinit() From f76c25fbc5a19071ebc693c3d03b8aa04a5296bd Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Mon, 24 Jun 2024 02:21:18 +0100 Subject: [PATCH 06/11] fix(init/deinit): Properly init/deinit lvgl module. Properly handle root pointers on lvgl init/deinit which fixes init error after a soft reset (see #343). --- gen/gen_mpy.py | 44 +++++++++++++++++++++++++++++++++++++++++--- lv_conf.h | 2 ++ lvgl | 2 +- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/gen/gen_mpy.py b/gen/gen_mpy.py index 22756548e..44e1961cb 100644 --- a/gen/gen_mpy.py +++ b/gen/gen_mpy.py @@ -1337,18 +1337,54 @@ def register_int_ptr_type(convertor, *types): // Register LVGL root pointers MP_REGISTER_ROOT_POINTER(void *mp_lv_roots); MP_REGISTER_ROOT_POINTER(void *mp_lv_user_data); +MP_REGISTER_ROOT_POINTER(int mp_lv_roots_initialized); +MP_REGISTER_ROOT_POINTER(int lvgl_mod_initialized); void *mp_lv_roots; +void *mp_lv_user_data; +int mp_lv_roots_initialized = 0; +int lvgl_mod_initialized = 0; void mp_lv_init_gc() { - static bool mp_lv_roots_initialized = false; - if (!mp_lv_roots_initialized) { + if (!MP_STATE_VM(mp_lv_roots_initialized)) { + // mp_printf(&mp_plat_print, "[ INIT GC ]"); mp_lv_roots = MP_STATE_VM(mp_lv_roots) = m_new0(lv_global_t, 1); - mp_lv_roots_initialized = true; + mp_lv_roots_initialized = MP_STATE_VM(mp_lv_roots_initialized) = 1; } } +void mp_lv_deinit_gc() +{ + + // mp_printf(&mp_plat_print, "[ DEINIT GC ]"); + mp_lv_roots = MP_STATE_VM(mp_lv_roots) = NULL; + mp_lv_user_data = MP_STATE_VM(mp_lv_user_data) = NULL; + mp_lv_roots_initialized = MP_STATE_VM(mp_lv_roots_initialized) = 0; + lvgl_mod_initialized = MP_STATE_VM(lvgl_mod_initialized) = 0; + +} + +static mp_obj_t lvgl_mod___init__(void) { + if (!MP_STATE_VM(lvgl_mod_initialized)) { + // __init__ for builtins is called each time the module is imported, + // so ensure that initialisation only happens once. + MP_STATE_VM(lvgl_mod_initialized) = true; + lv_init(); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(lvgl_mod___init___obj, lvgl_mod___init__); + + +static mp_obj_t lvgl_mod___del__(void) { + if (MP_STATE_VM(lvgl_mod_initialized)) { + lv_deinit(); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(lvgl_mod___del___obj, lvgl_mod___del__); + #else // LV_OBJ_T typedef struct mp_lv_obj_type_t { @@ -3621,6 +3657,8 @@ def generate_struct_functions(struct_list): static const mp_rom_map_elem_t {module_name}_globals_table[] = {{ {{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_{module_name}) }}, + {{ MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&lvgl_mod___init___obj) }}, + {{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&lvgl_mod___del___obj) }}, {objects} {functions} {enums} diff --git a/lv_conf.h b/lv_conf.h index c2d4e6c59..e624057bc 100644 --- a/lv_conf.h +++ b/lv_conf.h @@ -299,7 +299,9 @@ /*Garbage Collector settings *Used if LVGL is bound to higher level language and the memory is managed by that language*/ extern void mp_lv_init_gc(); +extern void mp_lv_deinit_gc(); #define LV_GC_INIT() mp_lv_init_gc() +#define LV_GC_DEINIT() mp_lv_deinit_gc() #define LV_ENABLE_GLOBAL_CUSTOM 1 #if LV_ENABLE_GLOBAL_CUSTOM diff --git a/lvgl b/lvgl index 90a4f00f7..00d07390e 160000 --- a/lvgl +++ b/lvgl @@ -1 +1 @@ -Subproject commit 90a4f00f727a247fa41cfa954a0112de25af379a +Subproject commit 00d07390edbab7c13bdad83ed0b389616cb4d96b From c0eecf6e9918b2a23b973564a158ad98b159bd29 Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Tue, 12 Nov 2024 14:45:21 +0000 Subject: [PATCH 07/11] fix(gen_mpy.py): update lv_to_mp float conversion. Upate for lvgl 9.2.x see diff in lvgl @ 84b28ff --- gen/gen_mpy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen/gen_mpy.py b/gen/gen_mpy.py index 44e1961cb..57b560bd3 100644 --- a/gen/gen_mpy.py +++ b/gen/gen_mpy.py @@ -845,7 +845,7 @@ class MissingConversionException(ValueError): "long int": "mp_obj_new_int", "long long": "mp_obj_new_int_from_ll", "long long int": "mp_obj_new_int_from_ll", - "float": "mp_obj_new_float", + "float": "mp_obj_new_float_from_f", } lv_mp_type = { From 4ff1ea980b12f05a159b857c1ecfddb6f0ea3b3c Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Tue, 12 Nov 2024 14:46:44 +0000 Subject: [PATCH 08/11] fix(esp32): lvgl component error in idf v5.2.x. --- micropython.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/micropython.cmake b/micropython.cmake index 6ba1db4c8..1afd0dfef 100644 --- a/micropython.cmake +++ b/micropython.cmake @@ -12,6 +12,11 @@ idf_build_set_property(COMPILE_OPTIONS "-Wno-unused-function" APPEND) idf_build_set_property(SRCS "${LV_SRC}" APPEND) idf_build_set_property(INCLUDE_DIRS "${LV_INCLUDE}" APPEND) +# Fix for idf 5.2.x +idf_build_get_property(component_targets __COMPONENT_TARGETS) +string(REPLACE "___idf_lvgl" "" component_targets "${component_targets}") +idf_build_set_property(__COMPONENT_TARGETS "${component_targets}") + include(${CMAKE_CURRENT_LIST_DIR}/mkrules_usermod.cmake) # Add lv_bindings rules From 6a5eb8810d76730097b760f1036e8218ee0ae3a5 Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Thu, 5 Dec 2024 21:58:55 +0000 Subject: [PATCH 09/11] fix(lv_conf): enable LV_USE_PRIVATE_API for v9.2.0 --- lv_conf.h | 3 +++ lvgl | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lv_conf.h b/lv_conf.h index e624057bc..2e0d08d7c 100644 --- a/lv_conf.h +++ b/lv_conf.h @@ -295,6 +295,9 @@ /*------------- * Others *-----------*/ +/* PRIVATE API */ + +#define LV_USE_PRIVATE_API 1 /*Garbage Collector settings *Used if LVGL is bound to higher level language and the memory is managed by that language*/ diff --git a/lvgl b/lvgl index 00d07390e..c5052862e 160000 --- a/lvgl +++ b/lvgl @@ -1 +1 @@ -Subproject commit 00d07390edbab7c13bdad83ed0b389616cb4d96b +Subproject commit c5052862e066dd012372b2935ce52138b9e705d0 From 12955be6b0a58bdc95d32ed0d8ce202e7bdf5459 Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Wed, 25 Dec 2024 22:40:56 +0000 Subject: [PATCH 10/11] fix(build): enable LV_CONF_PATH option This allows to set custom `lv_conf.h` file per board in `mpconfigboard.(h,cmake)` --- micropython.cmake | 13 ++++++++++++- micropython.mk | 35 +++++++++++++++++++++++++++-------- mkrules_usermod.cmake | 28 +++++++++++++++++++++++++--- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/micropython.cmake b/micropython.cmake index 1afd0dfef..3b04356e3 100644 --- a/micropython.cmake +++ b/micropython.cmake @@ -12,6 +12,11 @@ idf_build_set_property(COMPILE_OPTIONS "-Wno-unused-function" APPEND) idf_build_set_property(SRCS "${LV_SRC}" APPEND) idf_build_set_property(INCLUDE_DIRS "${LV_INCLUDE}" APPEND) +# idf_build_set_property(INCLUDE_DIRS "${LV_INCLUDE}" APPEND) + +# DEBUG LV_CONF_PATH +message(STATUS "LV_CONF_PATH=${LV_CONF_PATH}") + # Fix for idf 5.2.x idf_build_get_property(component_targets __COMPONENT_TARGETS) string(REPLACE "___idf_lvgl" "" component_targets "${component_targets}") @@ -40,7 +45,9 @@ target_compile_options(lvgl_interface INTERFACE ${LV_CFLAGS}) add_library(usermod_lvgl INTERFACE) target_sources(usermod_lvgl INTERFACE ${LV_SRC}) target_include_directories(usermod_lvgl INTERFACE ${LV_INCLUDE}) - +if (DEFINED LV_CONF_DIR) + target_include_directories(usermod_lvgl INTERFACE ${LV_CONF_DIR}) +endif() file(WRITE ${LV_MP} "") @@ -48,5 +55,9 @@ target_link_libraries(usermod_lvgl INTERFACE lvgl_interface) # # # make usermod (target declared by Micropython for all user compiled modules) link to bindings # # # this way the bindings (and transitively lvgl_interface) get proper compilation flags +if (DEFINED LV_CONF_DIR) + target_include_directories(usermod INTERFACE ${LV_CONF_DIR}) +endif() +target_compile_options(usermod INTERFACE -DLV_CONF_PATH=${LV_CONF_PATH}) target_link_libraries(usermod INTERFACE usermod_lvgl) diff --git a/micropython.mk b/micropython.mk index 5288c80c9..d15cee24d 100644 --- a/micropython.mk +++ b/micropython.mk @@ -15,6 +15,11 @@ CFLAGS_EXTMOD += $(SDL_CFLAGS_EXTMOD) -DMICROPY_SDL=1 LDFLAGS_EXTMOD += $(SDL_LDFLAGS_EXTMOD) endif +# Avoid including unwanted local headers other than sdl2 +ifeq ($(UNAME_S),Darwin) +CFLAGS_EXTMOD:=$(filter-out -I/usr/local/include,$(CFLAGS_EXTMOD)) +endif + RLOTTIE_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags rlottie) RLOTTIE_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs rlottie) ifneq ($(RLOTTIE_LDFLAGS_EXTMOD),) @@ -29,13 +34,15 @@ CFLAGS_EXTMOD += $(FREETYPE_CFLAGS_EXTMOD) -DMICROPY_FREETYPE=1 LDFLAGS_EXTMOD += $(FREETYPE_LDFLAGS_EXTMOD) endif -FFMPEG_LIBS := libavformat libavcodec libswscale libavutil -FFMPEG_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags $(FFMPEG_LIBS)) -FFMPEG_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs $(FFMPEG_LIBS)) -ifneq ($(FFMPEG_LDFLAGS_EXTMOD),) -CFLAGS_EXTMOD += $(FFMPEG_CFLAGS_EXTMOD) -DMICROPY_FFMPEG=1 -LDFLAGS_EXTMOD += $(FFMPEG_LDFLAGS_EXTMOD) -endif +# Enable FFMPEG +# FFMPEG_LIBS := libavformat libavcodec libswscale libavutil +# FFMPEG_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags $(FFMPEG_LIBS)) +# FFMPEG_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs $(FFMPEG_LIBS)) +# ifneq ($(FFMPEG_LDFLAGS_EXTMOD),) +# CFLAGS_EXTMOD += $(FFMPEG_CFLAGS_EXTMOD) -DMICROPY_FFMPEG=1 +# LDFLAGS_EXTMOD += $(FFMPEG_LDFLAGS_EXTMOD) +# endif + endif ################################################################################ @@ -44,15 +51,27 @@ endif LVGL_BINDING_DIR := $(USERMOD_DIR) +ifeq ($(LV_CONF_PATH),) +LV_CONF_PATH = $(LVGL_BINDING_DIR)/lv_conf.h +endif + +# LV_CONF_PATH DEBUG +$(info LV_CONF_PATH is $(LV_CONF_PATH)) + LVGL_DIR = $(LVGL_BINDING_DIR)/lvgl LVGL_GENERIC_DRV_DIR = $(LVGL_BINDING_DIR)/driver/generic INC += -I$(LVGL_BINDING_DIR) -ALL_LVGL_SRC = $(shell find $(LVGL_DIR) -type f -name '*.h') $(LVGL_BINDING_DIR)/lv_conf.h +ALL_LVGL_SRC = $(shell find $(LVGL_DIR) -type f -name '*.h') $(LV_CONF_PATH) LVGL_PP = $(BUILD)/lvgl/lvgl.pp.c LVGL_MPY = $(BUILD)/lvgl/lv_mpy.c LVGL_MPY_METADATA = $(BUILD)/lvgl/lv_mpy.json CFLAGS_EXTMOD += $(LV_CFLAGS) +CFLAGS_EXTMOD += -DLV_CONF_PATH=$(LV_CONF_PATH) + + +# CFLAGS DEBUG +$(info CFLAGS_EXTMOD is $(CFLAGS_EXTMOD)) $(LVGL_MPY): $(ALL_LVGL_SRC) $(LVGL_BINDING_DIR)/gen/gen_mpy.py $(ECHO) "LVGL-GEN $@" diff --git a/mkrules_usermod.cmake b/mkrules_usermod.cmake index 9846dbe90..d10f711ec 100644 --- a/mkrules_usermod.cmake +++ b/mkrules_usermod.cmake @@ -5,6 +5,13 @@ find_program(AWK awk mawk gawk) set(LV_BINDINGS_DIR ${CMAKE_CURRENT_LIST_DIR}) +# first check +if(NOT DEFINED LV_CONF_PATH) + set(LV_CONF_PATH ${LV_BINDINGS_DIR}/lv_conf.h) + + message(STATUS "LV_CONF_PATH=${LV_CONF_PATH}") +endif() + # Common function for creating LV bindings function(lv_bindings) @@ -21,17 +28,25 @@ function(lv_bindings) set(LV_PP ${LV_OUTPUT}.pp) set(LV_MPY_METADATA ${LV_OUTPUT}.json) + # message(STATUS "LV_CONF_PATH=${LV_CONF_PATH}") + # message(STATUS "LV_COMPILE_OPTIONS=${LV_COMPILE_OPTIONS}") + + # message(STATUS "LV_PP_OPTIONS=${LV_PP_OPTIONS}") + # message(STATUS "LV_CFLAGS=${LV_CFLAGS}") + add_custom_command( OUTPUT ${LV_PP} COMMAND - ${CMAKE_C_COMPILER} -E -DPYCPARSER ${LV_COMPILE_OPTIONS} ${LV_PP_OPTIONS} "${LV_CFLAGS}" -I ${LV_BINDINGS_DIR}/pycparser/utils/fake_libc_include ${MICROPY_CPP_FLAGS} ${LV_INPUT} > ${LV_PP} + ${CMAKE_C_COMPILER} -E -DPYCPARSER -DLV_CONF_PATH=${LV_CONF_PATH} ${LV_COMPILE_OPTIONS} ${LV_PP_OPTIONS} "${LV_CFLAGS}" -I ${LV_BINDINGS_DIR}/pycparser/utils/fake_libc_include ${MICROPY_CPP_FLAGS} ${LV_INPUT} > ${LV_PP} DEPENDS ${LV_INPUT} ${LV_DEPENDS} ${LV_BINDINGS_DIR}/pycparser/utils/fake_libc_include IMPLICIT_DEPENDS C ${LV_INPUT} + COMMENT + "LV_BINDINGS CPP" VERBATIM COMMAND_EXPAND_LISTS ) @@ -60,6 +75,9 @@ function(lv_bindings) ${AWK} ${LV_AWK_COMMAND} ${LV_PP} > ${LV_PP_FILTERED} DEPENDS ${LV_PP} + + COMMENT + "LV_BINDINGS: FILTER" VERBATIM COMMAND_EXPAND_LISTS ) @@ -71,10 +89,13 @@ function(lv_bindings) OUTPUT ${LV_OUTPUT} COMMAND - ${Python3_EXECUTABLE} ${LV_BINDINGS_DIR}/gen/gen_mpy.py ${LV_GEN_OPTIONS} -MD ${LV_MPY_METADATA} -E ${LV_PP_FILTERED} ${LV_INPUT} > ${LV_OUTPUT} || (rm -f ${LV_OUTPUT} && /bin/false) + ${Python3_EXECUTABLE} ${LV_BINDINGS_DIR}/gen/gen_mpy.py ${LV_GEN_OPTIONS} -DLV_CONF_PATH=${LV_CONF_PATH} -MD ${LV_MPY_METADATA} -E ${LV_PP_FILTERED} ${LV_INPUT} > ${LV_OUTPUT} || (rm -f ${LV_OUTPUT} && /bin/false) DEPENDS ${LV_BINDINGS_DIR}/gen/gen_mpy.py ${LV_PP_FILTERED} + + COMMENT + "LV_BINDINGS GEN MPY" COMMAND_EXPAND_LISTS ) @@ -95,7 +116,7 @@ function(all_lv_bindings) # LVGL bindings - file(GLOB_RECURSE LVGL_HEADERS ${LVGL_DIR}/src/*.h ${LV_BINDINGS_DIR}/lv_conf.h) + file(GLOB_RECURSE LVGL_HEADERS ${LVGL_DIR}/src/*.h ${LV_CONF_PATH}) lv_bindings( OUTPUT ${LV_MP} @@ -155,3 +176,4 @@ set(LV_SRC # ${LV_ESPIDF} # ) # endif(ESP_PLATFORM) + From a6fffeaaf17899375574617d312ec049d872e9f3 Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Wed, 25 Dec 2024 22:49:07 +0000 Subject: [PATCH 11/11] fix(tests): fix testdisplay, add hwdisplay example --- tests/hwdisplay.py | 26 ++++++++++++++++++++++++++ tests/testdisplay.py | 33 ++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 tests/hwdisplay.py diff --git a/tests/hwdisplay.py b/tests/hwdisplay.py new file mode 100644 index 000000000..ea7954e6c --- /dev/null +++ b/tests/hwdisplay.py @@ -0,0 +1,26 @@ +import lvgl as lv +import pyb + + +class HwDisplayDriver: + def __init__(self, width=240, height=320, color_format=lv.COLOR_FORMAT.RGB565): + self.width = width + self.height = height + self.color_depth = lv.color_format_get_bpp(color_format) + self.color_size = lv.color_format_get_size(color_format) + self._debug = False + + @property + def debug(self): + return self._debug + + @debug.setter + def debug(self, x): + self._debug = x + + def blit(self, x1, y1, w, h, buff): + pyb.LED((y1 % 4) + 1).toggle() + ... + + +display = HwDisplayDriver() diff --git a/tests/testdisplay.py b/tests/testdisplay.py index 1f7e71433..a82ad4217 100644 --- a/tests/testdisplay.py +++ b/tests/testdisplay.py @@ -213,15 +213,15 @@ def blit(self, x1, y1, w, h, buff): tdisp = DummyDisplay(color_format=lv.COLOR_FORMAT.RGB888) -alloc_buffer = lambda buffersize: memoryview(bytearray(buffer_size)) +# alloc_buffer = lambda buffersize: memoryview(bytearray(buffer_size)) -factor = 10 ### Must be 1 if using an RGBBus -double_buf = True ### Must be False if using an RGBBus +# factor = 10 ### Must be 1 if using an RGBBus +# double_buf = True ### Must be False if using an RGBBus -buffer_size = tdisp.width * tdisp.height * (tdisp.color_depth // 8) // factor +# buffer_size = tdisp.width * tdisp.height * (tdisp.color_depth // 8) // factor -fbuf1 = alloc_buffer(buffer_size) -fbuf2 = alloc_buffer(buffer_size) if double_buf else None +# fbuf1 = alloc_buffer(buffer_size) +# fbuf2 = alloc_buffer(buffer_size) if double_buf else None def get_display( @@ -241,7 +241,22 @@ def get_display( except Exception as e: if sys.platform not in ["darwin", "linux"]: sys.print_exception(e) - if hasattr(disp, "width") and hasattr(disp, "height"): - disp.width = width - disp.height = height + assert hasattr(disp, "width") is True, "expected width attribute in display driver" + assert ( + hasattr(disp, "height") is True + ), "expected height attribute in display driver" + + assert ( + hasattr(disp, "color_depth") is True + ), "expected color_depth attribute in display driver" + + alloc_buffer = lambda buffersize: memoryview(bytearray(buffer_size)) + + factor = 10 ### Must be 1 if using an RGBBus + double_buf = True ### Must be False if using an RGBBus + + buffer_size = disp.width * disp.height * (disp.color_depth // 8) // factor + + fbuf1 = alloc_buffer(buffer_size) + fbuf2 = alloc_buffer(buffer_size) if double_buf else None return TestDisplayDriver(disp, fbuf1, fbuf2, color_format, mode, pointer)