refactor: unify desktop apps and Settings under item catalog

This commit is contained in:
2026-07-29 13:59:46 +01:00
parent 84ea28a978
commit 75ae7ede56
38 changed files with 322 additions and 797 deletions
+88 -29
View File
@@ -35,22 +35,46 @@ 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.
// Return a slot for the next desktop item, 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));
static DesktopItem* next_desktop_item_slot(DesktopState* ds) {
if (ds->desktop_item_count >= ds->desktop_item_capacity) {
int new_cap = ds->desktop_item_capacity ? ds->desktop_item_capacity * 2 : 32;
auto* grown = (DesktopItem*)montauk::realloc(
ds->desktop_items, (uint64_t)new_cap * sizeof(DesktopItem));
if (!grown) return nullptr;
ds->external_apps = grown;
ds->external_app_capacity = new_cap;
ds->desktop_items = grown;
ds->desktop_item_capacity = new_cap;
}
return &ds->desktop_items[ds->desktop_item_count];
}
static void desktop_add_builtin_catalog_items(DesktopState* ds) {
int builtin_count = 0;
const DesktopBuiltinEntry* builtins = desktop_builtin_registry(&builtin_count);
for (int i = 0; i < builtin_count; i++) {
if (!builtins[i].system_configuration_id) continue;
DesktopItem* item = next_desktop_item_slot(ds);
if (!item) return;
montauk::memset(item, 0, sizeof(*item));
montauk::strncpy(item->id, builtins[i].system_configuration_id, sizeof(item->id));
montauk::strncpy(item->name, builtins[i].label, sizeof(item->name));
if (builtins[i].icon_path) {
montauk::strncpy(item->icon_path, builtins[i].icon_path, sizeof(item->icon_path));
}
SvgIcon* icon = desktop_builtin_menu_icon(ds, builtins[i].id);
if (icon) item->icon = *icon;
item->section = DESKTOP_ITEM_SECTION_SETTINGS;
item->launch_kind = DESKTOP_ITEM_LAUNCH_BUILTIN;
item->builtin_id = (int)builtins[i].id;
ds->desktop_item_count++;
}
return &ds->external_apps[ds->external_app_count];
}
void desktop_scan_apps(DesktopState* ds) {
ds->external_app_count = 0;
ds->desktop_item_count = 0;
desktop_add_builtin_catalog_items(ds);
// Ensure the apps directory exists
montauk::fmkdir("0:/apps");
@@ -94,44 +118,78 @@ void desktop_scan_apps(DesktopState* ds) {
montauk::mfree(text);
// Read manifest fields
const char* id = doc.get_string("app.id", dirname);
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') {
const char* section_name = doc.get_string("desktop.section", "apps");
bool admin_only = doc.get_bool("desktop.admin_only", false);
if (id[0] == '\0' || binary[0] == '\0') {
montauk::print("desktop: skipping app manifest with missing id or binary\n");
doc.destroy();
continue;
}
ExternalApp* app = next_external_app_slot(ds);
if (!app) {
bool duplicate = false;
for (int existing = 0; existing < ds->desktop_item_count; existing++) {
if (montauk::streq(ds->desktop_items[existing].id, id)) {
duplicate = true;
break;
}
}
if (duplicate) {
montauk::print("desktop: skipping app manifest with duplicate id\n");
doc.destroy();
continue;
}
DesktopItemSection section = DESKTOP_ITEM_SECTION_APPS;
if (montauk::streq(section_name, "settings")) {
section = DESKTOP_ITEM_SECTION_SETTINGS;
} else if (montauk::streq(section_name, "hidden")) {
section = DESKTOP_ITEM_SECTION_HIDDEN;
} else if (!montauk::streq(section_name, "apps")) {
montauk::print("desktop: skipping app manifest with invalid desktop.section\n");
doc.destroy();
continue;
}
DesktopItem* item = next_desktop_item_slot(ds);
if (!item) {
doc.destroy();
return;
}
montauk::memset(item, 0, sizeof(*item));
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;
montauk::strncpy(item->id, id, sizeof(item->id));
montauk::strncpy(item->name, name, sizeof(item->name));
montauk::strncpy(item->category, category, sizeof(item->category));
item->menu_visible = visible;
item->launch_with_home = launch_with_home;
item->admin_only = admin_only;
item->launch_kind = DESKTOP_ITEM_LAUNCH_EXECUTABLE;
item->builtin_id = -1;
item->section = section;
// Build full binary path: 0:/apps/<dir>/<binary>
snprintf(app->binary_path, sizeof(app->binary_path),
snprintf(item->binary_path, sizeof(item->binary_path),
"0:/apps/%s/%s", dirname, binary);
// Load icon from app directory
app->icon = {};
app->icon_path[0] = '\0';
item->icon = {};
item->icon_path[0] = '\0';
if (icon_file[0]) {
snprintf(app->icon_path, sizeof(app->icon_path),
snprintf(item->icon_path, sizeof(item->icon_path),
"0:/apps/%s/%s", dirname, icon_file);
Color defColor = colors::ICON_COLOR;
app->icon = svg_load(app->icon_path, 20, 20, defColor);
item->icon = svg_load(item->icon_path, 20, 20, defColor);
}
doc.destroy();
ds->external_app_count++;
ds->desktop_item_count++;
}
}
}
@@ -161,12 +219,13 @@ void desktop_build_menu(DesktopState* ds) {
}
}
// Add external apps in this category
for (int x = 0; x < ds->external_app_count; x++) {
ExternalApp* app = &ds->external_apps[x];
if (!app->menu_visible) continue;
if (montauk::streq(app->category, CATEGORY_NAMES[cat])) {
menu_add_external(app->name, app->binary_path, &app->icon, app->launch_with_home);
// Add manifest-backed items in this category.
for (int x = 0; x < ds->desktop_item_count; x++) {
DesktopItem* item = &ds->desktop_items[x];
if (!item->menu_visible || (item->admin_only && !ds->is_admin)) continue;
if (montauk::streq(item->category, CATEGORY_NAMES[cat])) {
menu_add_external(item->name, item->binary_path,
&item->icon, item->launch_with_home);
}
}
}