From a39bc77ea526d66fd0b6392c39eced852043128f Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Wed, 8 Jul 2026 10:00:45 +0200 Subject: [PATCH] fix: fix desktop app loading cap --- programs/include/gui/desktop.hpp | 12 +- programs/src/desktop/desktop_internal.hpp | 33 ++++-- programs/src/desktop/launcher.cpp | 13 +- programs/src/desktop/main.cpp | 137 +++++++++++++--------- 4 files changed, 124 insertions(+), 71 deletions(-) diff --git a/programs/include/gui/desktop.hpp b/programs/include/gui/desktop.hpp index 444d7cb..2085d25 100644 --- a/programs/include/gui/desktop.hpp +++ b/programs/include/gui/desktop.hpp @@ -17,8 +17,6 @@ namespace gui { static constexpr int MAX_WINDOWS = 8; static constexpr int PANEL_HEIGHT = 32; -static constexpr int MAX_EXTERNAL_APPS = 32; -static constexpr int MAX_LAUNCHER_ITEMS = 64; // External app discovered from 0:/apps/ manifest struct ExternalApp { @@ -95,9 +93,10 @@ struct DesktopState { bool launcher_open; char launcher_query[64]; int launcher_query_len; - LauncherItem launcher_items[MAX_LAUNCHER_ITEMS]; + LauncherItem* launcher_items; // dynamically grown int launcher_item_count; - int launcher_results[MAX_LAUNCHER_ITEMS]; + int launcher_item_capacity; + int* launcher_results; // indices into launcher_items, same capacity int launcher_result_count; int launcher_selected; int launcher_scroll; @@ -151,9 +150,10 @@ struct DesktopState { SvgIcon icon_volume; - // External apps discovered from 0:/apps/ manifests - ExternalApp external_apps[MAX_EXTERNAL_APPS]; + // External apps discovered from 0:/apps/ manifests (dynamically grown) + ExternalApp* external_apps; int external_app_count; + int external_app_capacity; bool ctx_menu_open; int ctx_menu_x, ctx_menu_y; diff --git a/programs/src/desktop/desktop_internal.hpp b/programs/src/desktop/desktop_internal.hpp index f7ac528..f231915 100644 --- a/programs/src/desktop/desktop_internal.hpp +++ b/programs/src/desktop/desktop_internal.hpp @@ -29,9 +29,23 @@ struct MenuRow { SvgIcon* icon; // loaded menu icon (null for categories/dividers) }; -static constexpr int MAX_MENU_ROWS = 48; -inline MenuRow menu_rows[MAX_MENU_ROWS]; +inline MenuRow* menu_rows = nullptr; inline int menu_row_count = 0; +inline int menu_row_capacity = 0; + +// Return the next menu row, doubling the array as needed. +// Returns nullptr if allocation failed. +inline MenuRow* menu_alloc_row() { + if (menu_row_count >= menu_row_capacity) { + int new_cap = menu_row_capacity ? menu_row_capacity * 2 : 64; + auto* grown = (MenuRow*)montauk::realloc( + menu_rows, (uint64_t)new_cap * sizeof(MenuRow)); + if (!grown) return nullptr; + menu_rows = grown; + menu_row_capacity = new_cap; + } + return &menu_rows[menu_row_count++]; +} // Collapsible category state (categories 0-3 are toggleable; divider category always expanded) static constexpr int MENU_NUM_CATS = 5; @@ -81,8 +95,9 @@ inline int menu_total_height() { // ============================================================================ inline int menu_add_category(const char* label) { - if (menu_row_count >= MAX_MENU_ROWS) return -1; - MenuRow& r = menu_rows[menu_row_count++]; + MenuRow* rp = menu_alloc_row(); + if (!rp) return -1; + MenuRow& r = *rp; r.is_category = true; montauk::strncpy(r.label, label, sizeof(r.label)); r.app_id = DESKTOP_BUILTIN_NONE; @@ -94,8 +109,9 @@ inline int menu_add_category(const char* label) { } inline int menu_add_embedded(const char* label, DesktopBuiltinId app_id, SvgIcon* icon) { - if (menu_row_count >= MAX_MENU_ROWS) return -1; - MenuRow& r = menu_rows[menu_row_count++]; + MenuRow* rp = menu_alloc_row(); + if (!rp) return -1; + MenuRow& r = *rp; r.is_category = false; montauk::strncpy(r.label, label, sizeof(r.label)); r.app_id = app_id; @@ -107,8 +123,9 @@ inline int menu_add_embedded(const char* label, DesktopBuiltinId app_id, SvgIcon } inline int menu_add_external(const char* label, const char* binary, SvgIcon* icon, bool launch_with_home) { - if (menu_row_count >= MAX_MENU_ROWS) return -1; - MenuRow& r = menu_rows[menu_row_count++]; + MenuRow* rp = menu_alloc_row(); + if (!rp) return -1; + MenuRow& r = *rp; r.is_category = false; montauk::strncpy(r.label, label, sizeof(r.label)); r.app_id = DESKTOP_BUILTIN_NONE; diff --git a/programs/src/desktop/launcher.cpp b/programs/src/desktop/launcher.cpp index f4f5c7e..1822e74 100644 --- a/programs/src/desktop/launcher.cpp +++ b/programs/src/desktop/launcher.cpp @@ -174,7 +174,18 @@ static void launcher_add_item(DesktopState* ds, LauncherItemKind kind, SvgIcon* icon, int ref_index, const char* path = nullptr, const char* keywords = nullptr) { - if (ds->launcher_item_count >= MAX_LAUNCHER_ITEMS) return; + if (ds->launcher_item_count >= ds->launcher_item_capacity) { + int new_cap = ds->launcher_item_capacity ? ds->launcher_item_capacity * 2 : 64; + auto* items = (LauncherItem*)montauk::realloc( + ds->launcher_items, (uint64_t)new_cap * sizeof(LauncherItem)); + if (!items) return; + ds->launcher_items = items; + auto* results = (int*)montauk::realloc( + ds->launcher_results, (uint64_t)new_cap * sizeof(int)); + if (!results) return; + ds->launcher_results = results; + ds->launcher_item_capacity = new_cap; + } LauncherItem* item = &ds->launcher_items[ds->launcher_item_count++]; montauk::memset(item, 0, sizeof(LauncherItem)); diff --git a/programs/src/desktop/main.cpp b/programs/src/desktop/main.cpp index f0e34ac..92aa1c3 100644 --- a/programs/src/desktop/main.cpp +++ b/programs/src/desktop/main.cpp @@ -35,79 +35,104 @@ static void extract_basename(char* out, int outSz, const char* entry) { out[base_len] = '\0'; } +// Return a slot for the next external app, doubling the array as needed. +// Returns nullptr if allocation failed. +static ExternalApp* next_external_app_slot(DesktopState* ds) { + if (ds->external_app_count >= ds->external_app_capacity) { + int new_cap = ds->external_app_capacity ? ds->external_app_capacity * 2 : 32; + auto* grown = (ExternalApp*)montauk::realloc( + ds->external_apps, (uint64_t)new_cap * sizeof(ExternalApp)); + if (!grown) return nullptr; + ds->external_apps = grown; + ds->external_app_capacity = new_cap; + } + return &ds->external_apps[ds->external_app_count]; +} + void desktop_scan_apps(DesktopState* ds) { ds->external_app_count = 0; // Ensure the apps directory exists montauk::fmkdir("0:/apps"); - const char* entries[32]; - int count = montauk::readdir("0:/apps", entries, 32); + // Page through the directory with readdir_at; a single readdir call + // returns at most one batch and would silently drop apps past it. + constexpr int BATCH = 128; + const char* entries[BATCH]; + int scanned = 0; + int got; + while ((got = montauk::readdir_at("0:/apps", entries, BATCH, scanned)) > 0) { + scanned += got; - for (int i = 0; i < count && ds->external_app_count < MAX_EXTERNAL_APPS; i++) { - // readdir returns paths like "apps/doom/" — extract just "doom" - char dirname[64]; - extract_basename(dirname, sizeof(dirname), entries[i]); - if (dirname[0] == '\0') continue; + for (int i = 0; i < got; i++) { + // readdir returns paths like "apps/doom/" — extract just "doom" + char dirname[64]; + extract_basename(dirname, sizeof(dirname), entries[i]); + if (dirname[0] == '\0') continue; - // Try to open the manifest in this app directory - char manifest_path[128]; - snprintf(manifest_path, sizeof(manifest_path), "0:/apps/%s/manifest.toml", dirname); + // Try to open the manifest in this app directory + char manifest_path[128]; + snprintf(manifest_path, sizeof(manifest_path), "0:/apps/%s/manifest.toml", dirname); - int fh = montauk::open(manifest_path); - if (fh < 0) continue; + int fh = montauk::open(manifest_path); + if (fh < 0) continue; - uint64_t sz = montauk::getsize(fh); - if (sz == 0 || sz > 4096) { montauk::close(fh); continue; } + uint64_t sz = montauk::getsize(fh); + if (sz == 0 || sz > 4096) { montauk::close(fh); continue; } - char* text = (char*)montauk::malloc(sz + 1); - if (!text) { montauk::close(fh); continue; } - int bytes_read = montauk::read(fh, (uint8_t*)text, 0, sz); - montauk::close(fh); - if (bytes_read != (int)sz) { + char* text = (char*)montauk::malloc(sz + 1); + if (!text) { montauk::close(fh); continue; } + int bytes_read = montauk::read(fh, (uint8_t*)text, 0, sz); + montauk::close(fh); + if (bytes_read != (int)sz) { + montauk::mfree(text); + continue; + } + text[sz] = '\0'; + + auto doc = montauk::toml::parse(text); montauk::mfree(text); - continue; - } - text[sz] = '\0'; - auto doc = montauk::toml::parse(text); - montauk::mfree(text); + // Read manifest fields + const char* name = doc.get_string("app.name", "Unknown"); + const char* binary = doc.get_string("app.binary", ""); + const char* icon_file = doc.get_string("app.icon", ""); + const char* category = doc.get_string("menu.category", "Applications"); + bool visible = doc.get_bool("menu.visible", true); + bool launch_with_home = doc.get_bool("launch.pass_home_dir", true); + if (binary[0] == '\0') { + doc.destroy(); + continue; + } - ExternalApp* app = &ds->external_apps[ds->external_app_count]; + ExternalApp* app = next_external_app_slot(ds); + if (!app) { + doc.destroy(); + return; + } + + montauk::strncpy(app->name, name, sizeof(app->name)); + montauk::strncpy(app->category, category, sizeof(app->category)); + app->menu_visible = visible; + app->launch_with_home = launch_with_home; + + // Build full binary path: 0:/apps// + snprintf(app->binary_path, sizeof(app->binary_path), + "0:/apps/%s/%s", dirname, binary); + + // Load icon from app directory + app->icon = {}; + app->icon_path[0] = '\0'; + if (icon_file[0]) { + snprintf(app->icon_path, sizeof(app->icon_path), + "0:/apps/%s/%s", dirname, icon_file); + Color defColor = colors::ICON_COLOR; + app->icon = svg_load(app->icon_path, 20, 20, defColor); + } - // Read manifest fields - const char* name = doc.get_string("app.name", "Unknown"); - const char* binary = doc.get_string("app.binary", ""); - const char* icon_file = doc.get_string("app.icon", ""); - const char* category = doc.get_string("menu.category", "Applications"); - bool visible = doc.get_bool("menu.visible", true); - bool launch_with_home = doc.get_bool("launch.pass_home_dir", true); - if (binary[0] == '\0') { doc.destroy(); - continue; + ds->external_app_count++; } - - montauk::strncpy(app->name, name, sizeof(app->name)); - montauk::strncpy(app->category, category, sizeof(app->category)); - app->menu_visible = visible; - app->launch_with_home = launch_with_home; - - // Build full binary path: 0:/apps// - snprintf(app->binary_path, sizeof(app->binary_path), - "0:/apps/%s/%s", dirname, binary); - - // Load icon from app directory - app->icon = {}; - app->icon_path[0] = '\0'; - if (icon_file[0]) { - snprintf(app->icon_path, sizeof(app->icon_path), - "0:/apps/%s/%s", dirname, icon_file); - Color defColor = colors::ICON_COLOR; - app->icon = svg_load(app->icon_path, 20, 20, defColor); - } - - doc.destroy(); - ds->external_app_count++; } }