|
| 1 | +# Original author: mhepp(https://forum.lvgl.io/u/mhepp/summary) |
| 2 | +# Copyright (c) 2024 - 2025 Kevin G. Schlosser |
| 3 | +# Added directory support, upstreamed at https://github.com/lvgl-micropython/lvgl_micropython/issues/398 |
| 4 | + |
| 5 | + |
| 6 | +import lvgl as lv |
| 7 | +import struct |
| 8 | + |
| 9 | + |
| 10 | +def _fs_open_cb(drv, path, mode): |
| 11 | + |
| 12 | + if mode == lv.FS_MODE.WR: |
| 13 | + p_mode = 'wb' |
| 14 | + elif mode == lv.FS_MODE.RD: |
| 15 | + p_mode = 'rb' |
| 16 | + elif mode == lv.FS_MODE.WR | lv.FS_MODE.RD: |
| 17 | + p_mode = 'rb+' |
| 18 | + else: |
| 19 | + raise RuntimeError("fs_open_callback() - open mode error, %s is invalid mode" % mode) |
| 20 | + |
| 21 | + try: |
| 22 | + f = open(path, p_mode) |
| 23 | + |
| 24 | + except OSError as e: |
| 25 | + raise RuntimeError("fs_open_callback(%s) exception: %s" % (path, e)) |
| 26 | + |
| 27 | + return {'file' : f, 'path': path} |
| 28 | + |
| 29 | + |
| 30 | +def _fs_close_cb(drv, fs_file): |
| 31 | + try: |
| 32 | + fs_file.__cast__()['file'].close() |
| 33 | + except OSError as e: |
| 34 | + raise RuntimeError("fs_close_callback(%s) exception: %s" % (fs_file.__cast__()['path'], e)) |
| 35 | + |
| 36 | + return lv.FS_RES.OK |
| 37 | + |
| 38 | + |
| 39 | +def _fs_read_cb(drv, fs_file, buf, btr, br): |
| 40 | + try: |
| 41 | + tmp_data = fs_file.__cast__()['file'].read(btr) |
| 42 | + buf.__dereference__(btr)[0:len(tmp_data)] = tmp_data |
| 43 | + br.__dereference__(4)[0:4] = struct.pack("<L", len(tmp_data)) |
| 44 | + except OSError as e: |
| 45 | + raise RuntimeError("fs_read_callback(%s) exception %s" % (fs_file.__cast__()['path'], e)) |
| 46 | + |
| 47 | + return lv.FS_RES.OK |
| 48 | + |
| 49 | + |
| 50 | +def _fs_seek_cb(drv, fs_file, pos, whence): |
| 51 | + try: |
| 52 | + fs_file.__cast__()['file'].seek(pos, whence) |
| 53 | + except OSError as e: |
| 54 | + raise RuntimeError("fs_seek_callback(%s) exception %s" % (fs_file.__cast__()['path'], e)) |
| 55 | + |
| 56 | + return lv.FS_RES.OK |
| 57 | + |
| 58 | + |
| 59 | +def _fs_tell_cb(drv, fs_file, pos): |
| 60 | + try: |
| 61 | + tpos = fs_file.__cast__()['file'].tell() |
| 62 | + pos.__dereference__(4)[0:4] = struct.pack("<L", tpos) |
| 63 | + except OSError as e: |
| 64 | + raise RuntimeError("fs_tell_callback(%s) exception %s" % (fs_file.__cast__()['path'], e)) |
| 65 | + |
| 66 | + return lv.FS_RES.OK |
| 67 | + |
| 68 | + |
| 69 | +def _fs_write_cb(drv, fs_file, buf, btw, bw): |
| 70 | + try: |
| 71 | + wr = fs_file.__cast__()['file'].write(buf.__dereference__(btw)[0:btw]) |
| 72 | + bw.__dereference__(4)[0:4] = struct.pack("<L", wr) |
| 73 | + except OSError as e: |
| 74 | + raise RuntimeError("fs_write_callback(%s) exception %s" % (fs_file.__cast__()['path'], e)) |
| 75 | + |
| 76 | + return lv.FS_RES.OK |
| 77 | + |
| 78 | +def _fs_dir_open_cb(drv, path): |
| 79 | + #print(f"_fs_dir_open_cb for path '{path}'") |
| 80 | + try: |
| 81 | + import os # for ilistdir() |
| 82 | + return {'iterator' : os.ilistdir(path)} |
| 83 | + except Exception as e: |
| 84 | + print(f"_fs_dir_open_cb exception: {e}") |
| 85 | + return None |
| 86 | + |
| 87 | +def _fs_dir_read_cb(drv, lv_fs_dir_t, buf, btr): |
| 88 | + try: |
| 89 | + iterator = lv_fs_dir_t.__cast__()['iterator'] |
| 90 | + nextfile = iterator.__next__() |
| 91 | + #print(f"nextfile: {nextfile}") |
| 92 | + filename = nextfile[0] |
| 93 | + entry_type = nextfile[1] # Type field |
| 94 | + if entry_type == 0x4000: |
| 95 | + #print(f"{filename} is a directory") |
| 96 | + filename = f"/{filename}" |
| 97 | + # Convert filename to bytes with null terminator |
| 98 | + tmp_data_bytes = filename.encode() + b'\x00' |
| 99 | + buf.__dereference__(btr)[0:len(tmp_data_bytes)] = tmp_data_bytes |
| 100 | + return lv.FS_RES.OK |
| 101 | + except StopIteration: |
| 102 | + # Clear buffer and return FS_ERR when iteration ends |
| 103 | + buf.__dereference__(btr)[0:1] = b'\x00' # Empty string (null byte) |
| 104 | + return lv.FS_RES.NOT_EX # Next entry "does not exist" |
| 105 | + except Exception as e: |
| 106 | + print(f"_fs_dir_read_cb exception: {e}") |
| 107 | + return lv.FS_RES.UNKNOWN |
| 108 | + |
| 109 | +def _fs_dir_close_cb(drv, lv_fs_dir_t): |
| 110 | + #print(f"_fs_dir_close_cb called") |
| 111 | + # No need to cleanup the iterator so nothing to do |
| 112 | + return lv.FS_RES.OK |
| 113 | + |
| 114 | +def fs_register(fs_drv, letter, cache_size=500): |
| 115 | + |
| 116 | + fs_drv.init() |
| 117 | + fs_drv.letter = ord(letter) |
| 118 | + fs_drv.open_cb = _fs_open_cb |
| 119 | + fs_drv.read_cb = _fs_read_cb |
| 120 | + fs_drv.write_cb = _fs_write_cb |
| 121 | + fs_drv.seek_cb = _fs_seek_cb |
| 122 | + fs_drv.tell_cb = _fs_tell_cb |
| 123 | + fs_drv.close_cb = _fs_close_cb |
| 124 | + fs_drv.dir_open_cb = _fs_dir_open_cb |
| 125 | + fs_drv.dir_read_cb = _fs_dir_read_cb |
| 126 | + #fs_drv.dir_close_cb = _fs_dir_close_cb |
| 127 | + |
| 128 | + if cache_size >= 0: |
| 129 | + fs_drv.cache_size = cache_size |
| 130 | + |
| 131 | + fs_drv.register() |
| 132 | + |
0 commit comments