diff --git a/apps/com.example.launcher/assets/main.py b/apps/com.example.launcher/assets/main.py index 38d03c21..6f8c4bc2 100644 --- a/apps/com.example.launcher/assets/main.py +++ b/apps/com.example.launcher/assets/main.py @@ -1,28 +1,4 @@ import uos -import uio -import machine - -def parse_manifest(manifest_path): - name = "Unknown" - main_script = None # Default to None if Main-Script is not found - try: - with uio.open(manifest_path, 'r') as f: - for line in f: - line = line.strip() - if line.startswith("Name:"): - name = line.split(":", 1)[1].strip() - elif line.startswith("Main-Script:"): - main_script = line.split(":", 1)[1].strip() - except OSError: - print(f"Error reading {manifest_path}") - return name, main_script - - -def on_app_click(event, app_name, main_script, app_dir): - if event.get_code() == lv.EVENT.CLICKED: - print(f"Launching app: {app_name} by starting {main_script} in {app_dir})") - run_app(main_script, True) - # Create a container for the grid cont = lv.obj(subwindow) @@ -38,26 +14,20 @@ iconcont_width = icon_size + 24 iconcont_height = icon_size + label_height # Get list of app directories +# Should we skip 'Launcher' apps from the list here? apps_dir = "/apps" -for app_dir in [d for d in uos.listdir(apps_dir) if uos.stat(f"{apps_dir}/{d}")[0] & 0x4000]: # TODO: skip 'Launcher' apps from the list here +for app_dir in [d for d in uos.listdir(apps_dir) if uos.stat(f"{apps_dir}/{d}")[0] & 0x4000]: # Paths - base_path = f"{apps_dir}/{app_dir}" - #icon_path = f"{base_path}/res/mipmap-mdpi/launcher_icon.png" - icon_path = "/resources/icon_64x64.bin" - manifest_path = f"{base_path}/META-INF/MANIFEST.MF" - # Get app name from MANIFEST.MF - app_name, main_script = parse_manifest(manifest_path) - if main_script: - main_script = f"{base_path}/{main_script}" - else: - main_script = f"{base_path}/assets/main.py" # default + app_dir_fullpath = f"{apps_dir}/{app_dir}" + app_name, main_script = parse_manifest(f"{app_dir_fullpath}/META-INF/MANIFEST.MF") # Create a container for each app (icon + label) app_cont = lv.obj(cont) app_cont.set_size(iconcont_width, iconcont_height) app_cont.set_style_border_width(0, 0) app_cont.set_style_pad_all(0, 0) - #app_cont.set_style_bg_color(lv.color_hex(0x00FF00), 0) # Load and display icon + #icon_path = f"{base_path}/res/mipmap-mdpi/launcher_icon.png" + icon_path = "/resources/icon_64x64.bin" image = lv.image(app_cont) try: with open(icon_path, 'rb') as f: @@ -77,7 +47,6 @@ for app_dir in [d for d in uos.listdir(apps_dir) if uos.stat(f"{apps_dir}/{d}")[ image.set_src(image_dsc) except Exception as e: print(f"Error loading icon {icon_path}: {e}") - # Fallback: create a placeholder image image.set_src(lv.SYMBOL.DUMMY) # Or use a default image image.align(lv.ALIGN.TOP_MID, 0, 0) image.set_size(icon_size, icon_size) @@ -88,6 +57,6 @@ for app_dir in [d for d in uos.listdir(apps_dir) if uos.stat(f"{apps_dir}/{d}")[ label.set_width(iconcont_width) label.align(lv.ALIGN.BOTTOM_MID, 0, 0) label.set_style_text_align(lv.TEXT_ALIGN.CENTER, 0) - app_cont.add_event_cb(lambda e, name=app_name, main_script=main_script, dir=app_dir: on_app_click(e, name, main_script, dir), lv.EVENT.CLICKED, None) + app_cont.add_event_cb(lambda e, app_dir=app_dir: start_app(app_dir_fullpath), lv.EVENT.CLICKED, None) diff --git a/main.py b/main.py index 84ca5fae..b1e02d45 100644 --- a/main.py +++ b/main.py @@ -235,8 +235,24 @@ restart_btn.add_event_cb(lambda event: machine.reset(),lv.EVENT.CLICKED,None) import _thread import traceback +import uio -def get_filename(path): +def parse_manifest(manifest_path): + name = "Unknown" + main_script = "assets/main.py" + try: + with uio.open(manifest_path, 'r') as f: + for line in f: + line = line.strip() + if line.startswith("Name:"): + name = line.split(":", 1)[1].strip() + elif line.startswith("Main-Script:"): + main_script = line.split(":", 1)[1].strip() + except OSError: + print(f"Error reading {manifest_path}") + return name, main_script + +def long_path_to_filename(path): try: if not path or not isinstance(path, str): return None @@ -248,6 +264,7 @@ def get_filename(path): print(f"Error extracting filename: {str(e)}") return None +# Run the script in the current thread: def execute_script(script_source, is_file, lvgl_obj, return_to_launcher): thread_id = _thread.get_ident() print(f"Thread {thread_id}: executing script") @@ -255,7 +272,8 @@ def execute_script(script_source, is_file, lvgl_obj, return_to_launcher): script_globals = { 'lv': lv, 'subwindow': lvgl_obj, - 'run_app': run_app, + 'start_app': start_app, # for launcher apps + 'parse_manifest': parse_manifest, # for launcher apps '__name__': "__main__" } if is_file: @@ -264,7 +282,7 @@ def execute_script(script_source, is_file, lvgl_obj, return_to_launcher): script_source = f.read() print(f"Thread {thread_id}: starting script") try: - compile_name = 'script' if not is_file else get_filename(script_source) # Only filename, to avoid 'name too long' error + compile_name = 'script' if not is_file else long_path_to_filename(script_source) # Only filename, to avoid 'name too long' error compiled_script = compile(script_source, compile_name, 'exec') exec(compiled_script, script_globals) except Exception as e: @@ -281,25 +299,29 @@ def execute_script(script_source, is_file, lvgl_obj, return_to_launcher): tb = getattr(e, '__traceback__', None) traceback.print_exception(type(e), e, tb) -def run_app(scriptname, is_file, return_to_launcher=True): - gc.collect() - print("/main.py: free memory before starting thread:", gc.mem_free()) +# Run the script in a new thread: +def execute_script_new_thread(scriptname, is_file, return_to_launcher): + print(f"/main.py: execute_script_new_thread({scriptname},{is_file},{return_to_launcher})") try: - print("/main.py: cleaning subwindow...") + print("/main.py: execute_script_new_thread(): cleaning subwindow...") subwindow.clean() # 168KB maximum at startup but 136KB after loading display, drivers, LVGL gui etc so let's go for 128KB for now, still a lot... # But then no additional threads can be created. A stacksize of 32KB allows for 4 threads, so 3 in the app itself, which might be tight. _thread.stack_size(16384) # A stack size of 16KB allows for around 10 threads in the app, which should be plenty. _thread.start_new_thread(execute_script, (scriptname, is_file, subwindow, return_to_launcher)) except Exception as e: - print("/main.py: error starting new thread thread: ", e) + print("/main.py: execute_script_new_thread(): error starting new thread thread: ", e) +def start_app(app_dir, return_to_launcher=True): + print(f"/main.py start_app({app_dir},{return_to_launcher}") + manifest_path = f"{app_dir}/META-INF/MANIFEST.MF" + app_name, main_script = parse_manifest(manifest_path) + main_script_fullpath = f"{app_dir}/{main_script}" + execute_script_new_thread(main_script_fullpath, True, return_to_launcher) def run_launcher(): - run_app("/apps/com.example.launcher/assets/main.py", True, False) + start_app("/apps/com.example.launcher", False) -#run_app("/autostart.py", True, subwindow, False) +execute_script_new_thread("/autostart.py", True, False) +run_launcher() -execute_script("/autostart.py", True, subwindow, False) - -run_launcher() # not needed because autostart.py will return_to_launcher