From eda82ddafaa3e3bd7239c7dda7ff0885a1019927 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Sat, 5 Jul 2025 20:31:40 +0200 Subject: [PATCH 1/2] Add directory support to fs_driver Support directory operations (dir_open_cb, dir_read_cb, dir_close_cb) in fs_driver.py so that the file_explorer widget, when using fs_driver, is able to browse the filesystem. --- api_drivers/py_api_drivers/fs_driver.py | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/api_drivers/py_api_drivers/fs_driver.py b/api_drivers/py_api_drivers/fs_driver.py index fa187be3..988d82c5 100644 --- a/api_drivers/py_api_drivers/fs_driver.py +++ b/api_drivers/py_api_drivers/fs_driver.py @@ -74,6 +74,41 @@ def _fs_write_cb(drv, fs_file, buf, btw, bw): return lv.FS_RES.OK +def _fs_dir_open_cb(drv, path): + #print(f"_fs_dir_open_cb for path '{path}'") + try: + import os # for ilistdir() + return {'iterator' : os.ilistdir(path)} + except Exception as e: + print(f"_fs_dir_open_cb exception: {e}") + return None + +def _fs_dir_read_cb(drv, lv_fs_dir_t, buf, btr): + try: + iterator = lv_fs_dir_t.__cast__()['iterator'] + nextfile = iterator.__next__() + #print(f"nextfile: {nextfile}") + filename = nextfile[0] + entry_type = nextfile[1] # Type field + if entry_type == 0x4000: + #print(f"{filename} is a directory") + filename = f"/{filename}" + # Convert filename to bytes with null terminator + tmp_data_bytes = filename.encode() + b'\x00' + buf.__dereference__(btr)[0:len(tmp_data_bytes)] = tmp_data_bytes + return lv.FS_RES.OK + except StopIteration: + # Clear buffer and return FS_ERR when iteration ends + buf.__dereference__(btr)[0:1] = b'\x00' # Empty string (null byte) + return lv.FS_RES.NOT_EX # Next entry "does not exist" + except Exception as e: + print(f"_fs_dir_read_cb exception: {e}") + return lv.FS_RES.UNKNOWN + +def _fs_dir_close_cb(drv, lv_fs_dir_t): + #print(f"_fs_dir_close_cb called") + # No need to cleanup the iterator so nothing to do + return lv.FS_RES.OK def fs_register(fs_drv, letter, cache_size=500): @@ -85,6 +120,9 @@ def fs_register(fs_drv, letter, cache_size=500): fs_drv.seek_cb = _fs_seek_cb fs_drv.tell_cb = _fs_tell_cb fs_drv.close_cb = _fs_close_cb + fs_drv.dir_open_cb = _fs_dir_open_cb + fs_drv.dir_read_cb = _fs_dir_read_cb + #fs_drv.dir_close_cb = _fs_dir_close_cb if cache_size >= 0: fs_drv.cache_size = cache_size From ec1985807ca8e54483101c2b1a516d6091f2ac19 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Sun, 6 Jul 2025 21:51:57 +0200 Subject: [PATCH 2/2] Adjust binding code generator to fix UnicodeError This change addresses a UnicodeError in the MicroPython-LVGL binding generator by explicitly handling `char *` arguments in `build_callback_func_arg`. Previously, the function relied on the `lv_to_mp` dictionary for all type conversions, which could lead to incorrect or missing converters for `char *`, causing encoding issues. Now, `char *` arguments are assigned the `ptr_to_mp` converter directly, ensuring proper conversion to MicroPython string objects. Additionally, the initial type extraction preserves qualifiers (`remove_quals=False`) to improve type handling accuracy, while maintaining compatibility for non-`char *` types by stripping qualifiers in the fallback path. --- gen/python_api_gen_mpy.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/gen/python_api_gen_mpy.py b/gen/python_api_gen_mpy.py index f5b17127..f1c6289f 100644 --- a/gen/python_api_gen_mpy.py +++ b/gen/python_api_gen_mpy.py @@ -2865,12 +2865,18 @@ def create_helper_struct(struct_str): def build_callback_func_arg(arg, index, func, func_name = None): - arg_type = get_type(arg.type, remove_quals = True) + arg_type = get_type(arg.type, remove_quals = False) 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 == 'char *': + converter = 'ptr_to_mp' + else: + arg_type = get_type(arg.type, remove_quals = True) if arg_type not in lv_to_mp or not lv_to_mp[arg_type]: - raise MissingConversionException("Callback: Missing conversion to %s" % 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) + converter = lv_to_mp[arg_type] arg_metadata = {'c_type': arg_type, 'py_type': get_py_type(arg_type)} @@ -2880,8 +2886,9 @@ def build_callback_func_arg(arg, index, func, func_name = None): arg_metadata['name'] = None callback_metadata[func_name]['args'].append(arg_metadata) + return 'mp_args[{i}] = {convertor}({cast}arg{i});'.format( - convertor = lv_to_mp[arg_type], + convertor = converter, i = index, cast = cast)