diff --git a/.gitignore b/.gitignore index f39bfd4..75719ac 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ toolchain/src/ programs/bin/ programs/obj/ programs/src/*/obj/ +programs/libs/*/obj/ programs/lib/libc/liblibc.a programs/gui/icons/ CLAUDE.md diff --git a/programs/GNUmakefile b/programs/GNUmakefile index 955b3d9..7f27e03 100644 --- a/programs/GNUmakefile +++ b/programs/GNUmakefile @@ -382,6 +382,7 @@ clean: $(MAKE) -C src/libloader clean $(MAKE) -C libs/libmath clean $(MAKE) -C libs/libhello clean + $(MAKE) -C libs/libprintersapplet clean $(MAKE) -C libs/libtimezonesapplet clean $(MAKE) -C src/test_dialogs clean $(MAKE) -C src/test_dl clean diff --git a/programs/include/gui/desktop.hpp b/programs/include/gui/desktop.hpp index 72248be..f4a3f5d 100644 --- a/programs/include/gui/desktop.hpp +++ b/programs/include/gui/desktop.hpp @@ -24,6 +24,7 @@ static constexpr int MAX_LAUNCHER_ITEMS = 64; struct ExternalApp { char name[48]; char binary_path[128]; + char icon_path[128]; char category[24]; SvgIcon icon; bool menu_visible; @@ -131,9 +132,9 @@ struct DesktopState { SvgIcon icon_home_folder_lg; SvgIcon icon_apps; SvgIcon icon_apps_lg; - SvgIcon icon_system_tools_menu; - SvgIcon icon_system_tools; - SvgIcon icon_system_tools_lg; + SvgIcon icon_system_configuration_menu; + SvgIcon icon_system_configuration; + SvgIcon icon_system_configuration_lg; // Special user folder icons (16x16 and 48x48) static constexpr int SPECIAL_FOLDER_COUNT = 6; diff --git a/programs/src/desktop/Makefile b/programs/src/desktop/Makefile index cc60486..c39ec57 100644 --- a/programs/src/desktop/Makefile +++ b/programs/src/desktop/Makefile @@ -68,7 +68,7 @@ LDFLAGS := \ # ---- C++ source files ---- -CORE_SRCS := main.cpp window.cpp panel.cpp compose.cpp input.cpp dialogs.cpp launcher.cpp desktop_builtin.cpp shared_applets.cpp font_data.cpp stb_truetype_impl.cpp +CORE_SRCS := main.cpp window.cpp panel.cpp compose.cpp input.cpp dialogs.cpp launcher.cpp desktop_builtin.cpp desktop_applets.cpp font_data.cpp stb_truetype_impl.cpp APP_SRCS := $(sort $(shell find apps -name '*.cpp' -print)) SRCS := $(CORE_SRCS) $(APP_SRCS) OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o)) diff --git a/programs/src/desktop/apps/apps_common.hpp b/programs/src/desktop/apps/apps_common.hpp index 092faad..63bf256 100644 --- a/programs/src/desktop/apps/apps_common.hpp +++ b/programs/src/desktop/apps/apps_common.hpp @@ -172,15 +172,12 @@ enum DesktopBuiltinId : int { DESKTOP_BUILTIN_SYSTEM_CONFIGURATION = 20, }; -inline constexpr const char* FILEMANAGER_PATH_SYSTEM_CONFIGURATION = - "virtual://system-configuration"; - struct DesktopBuiltinEntry { DesktopBuiltinId id; const char* label; int launcher_category; bool launcher_footer; - bool system_tools_applet; + const char* system_configuration_id; const char* icon_path; }; @@ -190,17 +187,26 @@ SvgIcon* desktop_builtin_menu_icon(DesktopState* ds, DesktopBuiltinId id); void desktop_launch_builtin(DesktopState* ds, DesktopBuiltinId id); void desktop_lock_screen(DesktopState* ds); -struct DesktopSharedAppletEntry { +enum DesktopAppletKind : int { + DESKTOP_APPLET_NONE = 0, + DESKTOP_APPLET_BUILTIN, + DESKTOP_APPLET_SHARED_LIBRARY, +}; + +struct DesktopAppletEntry { + DesktopAppletKind kind; char id[48]; char label[64]; char icon_path[128]; + DesktopBuiltinId builtin_id; char library_path[128]; }; int desktop_list_system_configuration_applets(DesktopState* ds, - DesktopSharedAppletEntry* out, + DesktopAppletEntry* out, int max_entries); -bool desktop_launch_shared_applet(DesktopState* ds, const char* lib_path); +bool desktop_launch_system_configuration_applet(DesktopState* ds, + const DesktopAppletEntry* applet); // ============================================================================ // Forward declarations for desktop launch entry points @@ -209,6 +215,7 @@ bool desktop_launch_shared_applet(DesktopState* ds, const char* lib_path); void open_terminal(DesktopState* ds); void open_filemanager(DesktopState* ds); void open_filemanager_path(DesktopState* ds, const char* path); +void open_system_configuration(DesktopState* ds); void ensure_filemanager_icons_loaded(DesktopState* ds); void open_calculator(DesktopState* ds); void open_texteditor(DesktopState* ds); diff --git a/programs/src/desktop/apps/filemanager/actions.cpp b/programs/src/desktop/apps/filemanager/actions.cpp index 4ec0b28..59ccd8f 100644 --- a/programs/src/desktop/apps/filemanager/actions.cpp +++ b/programs/src/desktop/apps/filemanager/actions.cpp @@ -158,7 +158,7 @@ void filemanager_open_ctx_menu(FileManagerState* fm, int local_x, int local_y, i int tt = fm->entry_types[target_idx]; if ((filemanager_is_computer_view(fm) && (tt == FM_ENTRY_DRIVE || tt == FM_ENTRY_HOME || tt == FM_ENTRY_APPS_ROOT - || tt == FM_ENTRY_SPECIAL_DIR || tt == FM_ENTRY_SYSTEM_TOOLS_ROOT)) + || tt == FM_ENTRY_SPECIAL_DIR || tt == FM_ENTRY_SYSTEM_CONFIGURATION_ROOT)) || (filemanager_is_virtual_view(fm) && tt == FM_ENTRY_VIRTUAL_APP)) { // Drive, Home, Apps shortcut, or app entry: only Open fm->ctx_items[fm->ctx_item_count++] = CTX_OPEN; diff --git a/programs/src/desktop/apps/filemanager/app.cpp b/programs/src/desktop/apps/filemanager/app.cpp index addfac1..8baeebb 100644 --- a/programs/src/desktop/apps/filemanager/app.cpp +++ b/programs/src/desktop/apps/filemanager/app.cpp @@ -8,11 +8,13 @@ namespace filemanager { +static constexpr const char* FM_PATH_SYSTEM_CONFIGURATION = "virtual://system-configuration"; + static bool filemanager_open_virtual_path(FileManagerState* fm, const char* path) { if (!fm || !path) return false; - if (montauk::streq(path, FILEMANAGER_PATH_SYSTEM_CONFIGURATION)) { - filemanager_read_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_TOOLS); + if (montauk::streq(path, FM_PATH_SYSTEM_CONFIGURATION)) { + filemanager_read_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION); return true; } @@ -74,9 +76,12 @@ void ensure_filemanager_icons_loaded(DesktopState* ds) { ds->icon_home_folder_lg = svg_load("0:/icons/folder-blue-home.svg", 48, 48, defColor); ds->icon_apps = svg_load("0:/icons/folder-blue-development.svg", 16, 16, defColor); ds->icon_apps_lg = svg_load("0:/icons/folder-blue-development.svg", 48, 48, defColor); - ds->icon_system_tools_menu = svg_load("0:/icons/preferences-system.svg", 20, 20, defColor); - ds->icon_system_tools = svg_load("0:/icons/preferences-system.svg", 16, 16, defColor); - ds->icon_system_tools_lg = svg_load("0:/icons/preferences-system.svg", 48, 48, defColor); + ds->icon_system_configuration_menu = + svg_load("0:/icons/preferences-system.svg", 20, 20, defColor); + ds->icon_system_configuration = + svg_load("0:/icons/preferences-system.svg", 16, 16, defColor); + ds->icon_system_configuration_lg = + svg_load("0:/icons/preferences-system.svg", 48, 48, defColor); for (int sf = 0; sf < filemanager::SF_COUNT; sf++) { char icon_path[128]; @@ -99,3 +104,7 @@ void open_filemanager(DesktopState* ds) { void open_filemanager_path(DesktopState* ds, const char* path) { filemanager::open_filemanager_internal(ds, path); } + +void open_system_configuration(DesktopState* ds) { + filemanager::open_filemanager_internal(ds, filemanager::FM_PATH_SYSTEM_CONFIGURATION); +} diff --git a/programs/src/desktop/apps/filemanager/filemanager_internal.hpp b/programs/src/desktop/apps/filemanager/filemanager_internal.hpp index 8ac9afe..d6e6e1b 100644 --- a/programs/src/desktop/apps/filemanager/filemanager_internal.hpp +++ b/programs/src/desktop/apps/filemanager/filemanager_internal.hpp @@ -21,20 +21,19 @@ enum FileManagerEntryType : int { FM_ENTRY_APPS_ROOT = 5, FM_ENTRY_VIRTUAL_APP = 6, FM_ENTRY_SPECIAL_DIR = 7, - FM_ENTRY_SYSTEM_TOOLS_ROOT = 8, + FM_ENTRY_SYSTEM_CONFIGURATION_ROOT = 8, }; enum FileManagerVirtualViewKind : uint8_t { FM_VIRTUAL_VIEW_NONE = 0, FM_VIRTUAL_VIEW_APPS, - FM_VIRTUAL_VIEW_SYSTEM_TOOLS, + FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION, }; enum FileManagerVirtualEntryKind : uint8_t { FM_VIRTUAL_ENTRY_NONE = 0, FM_VIRTUAL_ENTRY_EXTERNAL_APP, - FM_VIRTUAL_ENTRY_BUILTIN, - FM_VIRTUAL_ENTRY_SHARED_APPLET, + FM_VIRTUAL_ENTRY_SYSTEM_CONFIGURATION_APPLET, }; struct FileManagerLocation { @@ -45,8 +44,7 @@ struct FileManagerLocation { struct FileManagerVirtualEntry { FileManagerVirtualEntryKind kind; int external_app_index; - DesktopBuiltinId builtin_id; - char shared_applet_path[128]; + DesktopAppletEntry applet; SvgIcon icon_lg; SvgIcon icon_sm; }; @@ -147,7 +145,7 @@ inline bool filemanager_is_computer_view(const FileManagerState* fm) { inline const char* filemanager_virtual_view_label(FileManagerVirtualViewKind view) { switch (view) { case FM_VIRTUAL_VIEW_APPS: return "Apps"; - case FM_VIRTUAL_VIEW_SYSTEM_TOOLS: return "System Configuration"; + case FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION: return "System Configuration"; default: return "Computer"; } } @@ -173,7 +171,7 @@ void filemanager_read_dir(FileManagerState* fm); void filemanager_free_app_icons(FileManagerState* fm); void filemanager_read_virtual_view(FileManagerState* fm, FileManagerVirtualViewKind view); void filemanager_read_apps(FileManagerState* fm); -void filemanager_read_system_tools(FileManagerState* fm); +void filemanager_read_system_configuration(FileManagerState* fm); bool filemanager_delete_recursive(const char* path); bool filemanager_copy_file(const char* src, const char* dst); diff --git a/programs/src/desktop/apps/filemanager/filesystem.cpp b/programs/src/desktop/apps/filemanager/filesystem.cpp index 93498b3..b166f57 100644 --- a/programs/src/desktop/apps/filemanager/filesystem.cpp +++ b/programs/src/desktop/apps/filemanager/filesystem.cpp @@ -5,18 +5,9 @@ */ #include "filemanager_internal.hpp" -#include namespace filemanager { -static int filemanager_find_external_app(DesktopState* ds, const char* bin_path) { - if (!ds || !bin_path) return -1; - for (int x = 0; x < ds->external_app_count; x++) { - if (montauk::streq(ds->external_apps[x].binary_path, bin_path)) return x; - } - return -1; -} - static void filemanager_reset_list_state(FileManagerState* fm) { fm->selected = -1; fm->scroll_offset = 0; @@ -29,8 +20,9 @@ static void filemanager_reset_virtual_entry(FileManagerVirtualEntry* entry) { if (!entry) return; entry->kind = FM_VIRTUAL_ENTRY_NONE; entry->external_app_index = -1; - entry->builtin_id = DESKTOP_BUILTIN_NONE; - entry->shared_applet_path[0] = '\0'; + montauk::memset(&entry->applet, 0, sizeof(entry->applet)); + entry->applet.kind = DESKTOP_APPLET_NONE; + entry->applet.builtin_id = DESKTOP_BUILTIN_NONE; entry->icon_lg = {}; entry->icon_sm = {}; } @@ -49,14 +41,11 @@ static void filemanager_add_root_entry(FileManagerState* fm, fm->drive_indices[idx] = drive_index; } -static void filemanager_add_virtual_entry(FileManagerState* fm, - const char* name, - const char* icon_path, - FileManagerVirtualEntryKind kind, - int external_index, - DesktopBuiltinId builtin_id, - const char* shared_applet_path) { - if (!fm || fm->entry_count >= 64) return; +static int filemanager_add_virtual_entry(FileManagerState* fm, + const char* name, + const char* icon_path, + FileManagerVirtualEntryKind kind) { + if (!fm || fm->entry_count >= 64) return -1; int idx = fm->entry_count; montauk::strncpy(fm->entry_names[idx], name, 63); @@ -66,13 +55,6 @@ static void filemanager_add_virtual_entry(FileManagerState* fm, fm->drive_indices[idx] = -1; filemanager_reset_virtual_entry(&fm->virtual_entries[idx]); fm->virtual_entries[idx].kind = kind; - fm->virtual_entries[idx].external_app_index = external_index; - fm->virtual_entries[idx].builtin_id = builtin_id; - if (shared_applet_path && shared_applet_path[0]) { - montauk::strncpy(fm->virtual_entries[idx].shared_applet_path, - shared_applet_path, - sizeof(fm->virtual_entries[idx].shared_applet_path)); - } if (icon_path && icon_path[0]) { Color defColor = colors::ICON_COLOR; @@ -82,6 +64,27 @@ static void filemanager_add_virtual_entry(FileManagerState* fm, fm->entry_count++; fm->virtual_entry_count = fm->entry_count; + return idx; +} + +static void filemanager_add_external_app_entry(FileManagerState* fm, + const ExternalApp& app, + int external_index) { + int idx = filemanager_add_virtual_entry(fm, app.name, app.icon_path, + FM_VIRTUAL_ENTRY_EXTERNAL_APP); + if (idx >= 0) { + fm->virtual_entries[idx].external_app_index = external_index; + } +} + +static void filemanager_add_system_configuration_applet_entry( + FileManagerState* fm, + const DesktopAppletEntry& applet) { + int idx = filemanager_add_virtual_entry(fm, applet.label, applet.icon_path, + FM_VIRTUAL_ENTRY_SYSTEM_CONFIGURATION_APPLET); + if (idx >= 0) { + fm->virtual_entries[idx].applet = applet; + } } static void filemanager_begin_virtual_view(FileManagerState* fm, FileManagerVirtualViewKind view) { @@ -126,7 +129,8 @@ void filemanager_read_drives(FileManagerState* fm) { } // System configuration entry - filemanager_add_root_entry(fm, "System Configuration", FM_ENTRY_SYSTEM_TOOLS_ROOT, -1); + filemanager_add_root_entry(fm, "System Configuration", + FM_ENTRY_SYSTEM_CONFIGURATION_ROOT, -1); // Drive entries int drives[FM_MAX_DRIVES]; @@ -292,7 +296,9 @@ void filemanager_free_app_icons(FileManagerState* fm) { void filemanager_read_virtual_view(FileManagerState* fm, FileManagerVirtualViewKind view) { switch (view) { case FM_VIRTUAL_VIEW_APPS: filemanager_read_apps(fm); break; - case FM_VIRTUAL_VIEW_SYSTEM_TOOLS: filemanager_read_system_tools(fm); break; + case FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION: + filemanager_read_system_configuration(fm); + break; default: filemanager_read_drives(fm); break; } } @@ -303,86 +309,20 @@ void filemanager_read_apps(FileManagerState* fm) { DesktopState* ds = fm->desktop; if (!ds) return; - // Scan manifests directly (like desktop_scan_apps but load icons at FM sizes) - montauk::fmkdir("0:/apps"); - const char* entries[32]; - int count = montauk::readdir("0:/apps", entries, 32); - - // Extract basenames from readdir results (strip "apps/" prefix) - - /* - TODO: Fix crash if 16-app ceiling is removed and remove ceiling - */ - for (int i = 0; i < count && fm->entry_count < 64; i++) { - const char* raw = entries[i]; - // Strip "apps/" prefix - if (raw[0] == 'a' && raw[1] == 'p' && raw[2] == 'p' && raw[3] == 's' && raw[4] == '/') - raw += 5; - char dirname[64]; - montauk::strncpy(dirname, raw, 63); - int dlen = montauk::slen(dirname); - if (dlen > 0 && dirname[dlen - 1] == '/') dirname[dlen - 1] = '\0'; - if (dirname[0] == '\0') continue; - - // Open manifest - char manifest_path[128]; - snprintf(manifest_path, 128, "0:/apps/%s/manifest.toml", dirname); - 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; } - - char* text = (char*)montauk::malloc(sz + 1); - montauk::read(fh, (uint8_t*)text, 0, sz); - montauk::close(fh); - text[sz] = '\0'; - - auto doc = montauk::toml::parse(text); - montauk::mfree(text); - - 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", ""); - - char bin_path[128]; - snprintf(bin_path, 128, "0:/apps/%s/%s", dirname, binary); - char icon_path[128]; - icon_path[0] = '\0'; - if (icon_file[0]) { - snprintf(icon_path, 128, "0:/apps/%s/%s", dirname, icon_file); - } - - filemanager_add_virtual_entry(fm, name, icon_path, - FM_VIRTUAL_ENTRY_EXTERNAL_APP, - filemanager_find_external_app(ds, bin_path), - DESKTOP_BUILTIN_NONE, nullptr); - - doc.destroy(); + for (int i = 0; i < ds->external_app_count && fm->entry_count < 64; i++) { + filemanager_add_external_app_entry(fm, ds->external_apps[i], i); } filemanager_reset_list_state(fm); } -void filemanager_read_system_tools(FileManagerState* fm) { - filemanager_begin_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_TOOLS); +void filemanager_read_system_configuration(FileManagerState* fm) { + filemanager_begin_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION); - int builtin_count = 0; - const DesktopBuiltinEntry* builtins = desktop_builtin_registry(&builtin_count); - for (int i = 0; i < builtin_count; i++) { - if (!builtins[i].system_tools_applet) continue; - filemanager_add_virtual_entry(fm, builtins[i].label, builtins[i].icon_path, - FM_VIRTUAL_ENTRY_BUILTIN, - -1, builtins[i].id, nullptr); - } - - DesktopSharedAppletEntry applets[32]; + DesktopAppletEntry applets[32]; int applet_count = desktop_list_system_configuration_applets(fm->desktop, applets, 32); for (int i = 0; i < applet_count && fm->entry_count < 64; i++) { - filemanager_add_virtual_entry(fm, applets[i].label, applets[i].icon_path, - FM_VIRTUAL_ENTRY_SHARED_APPLET, - -1, DESKTOP_BUILTIN_NONE, - applets[i].library_path); + filemanager_add_system_configuration_applet_entry(fm, applets[i]); } filemanager_reset_list_state(fm); diff --git a/programs/src/desktop/apps/filemanager/navigation.cpp b/programs/src/desktop/apps/filemanager/navigation.cpp index 642d5bd..a9f5dd4 100644 --- a/programs/src/desktop/apps/filemanager/navigation.cpp +++ b/programs/src/desktop/apps/filemanager/navigation.cpp @@ -188,8 +188,9 @@ void filemanager_open_entry(FileManagerState* fm, int idx) { return; } - if (filemanager_is_computer_view(fm) && fm->entry_types[idx] == FM_ENTRY_SYSTEM_TOOLS_ROOT) { - filemanager_read_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_TOOLS); + if (filemanager_is_computer_view(fm) + && fm->entry_types[idx] == FM_ENTRY_SYSTEM_CONFIGURATION_ROOT) { + filemanager_read_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION); filemanager_push_history(fm); return; } @@ -197,11 +198,6 @@ void filemanager_open_entry(FileManagerState* fm, int idx) { if (filemanager_is_virtual_view(fm) && fm->entry_types[idx] == FM_ENTRY_VIRTUAL_APP) { DesktopState* ds = fm->desktop; const FileManagerVirtualEntry& entry = fm->virtual_entries[idx]; - if (ds && entry.kind == FM_VIRTUAL_ENTRY_BUILTIN - && entry.builtin_id != DESKTOP_BUILTIN_NONE) { - desktop_launch_builtin(ds, entry.builtin_id); - return; - } if (ds && entry.kind == FM_VIRTUAL_ENTRY_EXTERNAL_APP && entry.external_app_index >= 0 && entry.external_app_index < ds->external_app_count) { @@ -211,9 +207,8 @@ void filemanager_open_entry(FileManagerState* fm, int idx) { } else { montauk::spawn(app.binary_path); } - } else if (ds && entry.kind == FM_VIRTUAL_ENTRY_SHARED_APPLET - && entry.shared_applet_path[0] != '\0') { - desktop_launch_shared_applet(ds, entry.shared_applet_path); + } else if (ds && entry.kind == FM_VIRTUAL_ENTRY_SYSTEM_CONFIGURATION_APPLET) { + desktop_launch_system_configuration_applet(ds, &entry.applet); } return; } diff --git a/programs/src/desktop/apps/filemanager/render.cpp b/programs/src/desktop/apps/filemanager/render.cpp index a44609d..37d400b 100644 --- a/programs/src/desktop/apps/filemanager/render.cpp +++ b/programs/src/desktop/apps/filemanager/render.cpp @@ -324,9 +324,9 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) { && i < fm->virtual_entry_count && fm->virtual_entries[i].icon_lg.pixels) { c.icon(icon_x, icon_y, fm->virtual_entries[i].icon_lg); - } else if (ds && fm->entry_types[i] == FM_ENTRY_SYSTEM_TOOLS_ROOT - && ds->icon_system_tools_lg.pixels) { - c.icon(icon_x, icon_y, ds->icon_system_tools_lg); + } else if (ds && fm->entry_types[i] == FM_ENTRY_SYSTEM_CONFIGURATION_ROOT + && ds->icon_system_configuration_lg.pixels) { + c.icon(icon_x, icon_y, ds->icon_system_configuration_lg); } else if (ds && fm->entry_types[i] == FM_ENTRY_APPS_ROOT && ds->icon_apps_lg.pixels) { c.icon(icon_x, icon_y, ds->icon_apps_lg); } else if (ds && fm->entry_types[i] == FM_ENTRY_HOME && ds->icon_home_folder_lg.pixels) { @@ -455,9 +455,9 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) { && i < fm->virtual_entry_count && fm->virtual_entries[i].icon_sm.pixels) { c.icon(ico_x, ico_y, fm->virtual_entries[i].icon_sm); - } else if (ds && fm->entry_types[i] == FM_ENTRY_SYSTEM_TOOLS_ROOT - && ds->icon_system_tools.pixels) { - c.icon(ico_x, ico_y, ds->icon_system_tools); + } else if (ds && fm->entry_types[i] == FM_ENTRY_SYSTEM_CONFIGURATION_ROOT + && ds->icon_system_configuration.pixels) { + c.icon(ico_x, ico_y, ds->icon_system_configuration); } else if (ds && fm->entry_types[i] == FM_ENTRY_APPS_ROOT && ds->icon_apps.pixels) { c.icon(ico_x, ico_y, ds->icon_apps); } else if (ds && fm->entry_types[i] == FM_ENTRY_HOME && ds->icon_home_folder.pixels) { @@ -537,9 +537,13 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) { // Type if (type_col_x > 160 && ty >= list_y && ty + fm_sfh <= c.h) { const char* type_str = "File"; - if (fm->entry_types[i] == FM_ENTRY_VIRTUAL_APP) type_str = "App"; + if (fm->entry_types[i] == FM_ENTRY_VIRTUAL_APP) { + type_str = fm->virtual_view == FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION + ? "Applet" + : "App"; + } else if (fm->entry_types[i] == FM_ENTRY_APPS_ROOT) type_str = "Apps"; - else if (fm->entry_types[i] == FM_ENTRY_SYSTEM_TOOLS_ROOT) type_str = "System"; + else if (fm->entry_types[i] == FM_ENTRY_SYSTEM_CONFIGURATION_ROOT) type_str = "System"; else if (fm->entry_types[i] == FM_ENTRY_HOME) type_str = "Home"; else if (fm->entry_types[i] == FM_ENTRY_DRIVE) type_str = "Drive"; else if (fm->entry_types[i] == FM_ENTRY_DIR) type_str = "Dir"; diff --git a/programs/src/desktop/compose.cpp b/programs/src/desktop/compose.cpp index 36fcc5a..be5e08c 100644 --- a/programs/src/desktop/compose.cpp +++ b/programs/src/desktop/compose.cpp @@ -243,7 +243,7 @@ void gui::desktop_compose(DesktopState* ds) { CtxItem ctx_items[CTX_ITEM_COUNT] = { { "Terminal", &ds->icon_terminal }, { "Files", &ds->icon_filemanager }, - { "System Configuration", &ds->icon_system_tools_menu }, + { "System Configuration", &ds->icon_system_configuration_menu }, { "Sleep", &ds->icon_sleep }, { "Reboot", &ds->icon_reboot }, { "Shutdown", &ds->icon_shutdown }, diff --git a/programs/src/desktop/shared_applets.cpp b/programs/src/desktop/desktop_applets.cpp similarity index 52% rename from programs/src/desktop/shared_applets.cpp rename to programs/src/desktop/desktop_applets.cpp index 7d3da42..354ca52 100644 --- a/programs/src/desktop/shared_applets.cpp +++ b/programs/src/desktop/desktop_applets.cpp @@ -1,6 +1,6 @@ /* - * shared_applets.cpp - * Shared-library desktop applet discovery and launch helpers + * desktop_applets.cpp + * Desktop applet registry and launch helpers * Copyright (c) 2026 Daniel Hammer */ @@ -18,6 +18,15 @@ using gui::desktop_applet::QueryFn; static constexpr const char* DEFAULT_APPLET_ICON = "0:/icons/preferences-system.svg"; static constexpr const char* APPLET_CONFIG_NAME = "desktop-applets"; static constexpr const char* APPLET_TABLE_PREFIX = "applets."; +static constexpr uint32_t SYSTEM_CONFIGURATION_CAP = + gui::desktop_applet::CAP_SYSTEM_CONFIGURATION; + +static void clear_applet_entry(DesktopAppletEntry* entry) { + if (!entry) return; + montauk::memset(entry, 0, sizeof(*entry)); + entry->kind = DESKTOP_APPLET_NONE; + entry->builtin_id = DESKTOP_BUILTIN_NONE; +} static bool str_ends_with(const char* s, const char* suffix) { if (!s || !suffix) return false; @@ -55,6 +64,7 @@ static const char* applet_id_from_table_key(const char* table_key) { } static bool resolve_shared_applet(LibHandle* lib, + uint32_t required_capability, const Descriptor** out_desc, OpenFn* out_open) { if (out_desc) *out_desc = nullptr; @@ -67,6 +77,7 @@ static bool resolve_shared_applet(LibHandle* lib, const Descriptor* desc = query(); if (!desc || desc->abi_version != gui::desktop_applet::ABI_VERSION) return false; + if ((desc->capabilities & required_capability) == 0) return false; if (!desc->applet_id || !desc->applet_id[0]) return false; if (!desc->display_name || !desc->display_name[0]) return false; @@ -75,11 +86,44 @@ static bool resolve_shared_applet(LibHandle* lib, return true; } -static void sort_shared_applets(DesktopSharedAppletEntry* entries, int count) { +static bool load_shared_applet_metadata(const montauk::toml::Doc& doc, + const char* table_key, + const char* library, + DesktopAppletEntry* out) { + if (!out || !library || !library[0]) return false; + + LibHandle* lib = libloader::dlopen(library); + if (!lib) return false; + + const Descriptor* desc = nullptr; + bool ok = resolve_shared_applet(lib, SYSTEM_CONFIGURATION_CAP, &desc, nullptr); + if (ok) { + clear_applet_entry(out); + out->kind = DESKTOP_APPLET_SHARED_LIBRARY; + montauk::strncpy(out->id, desc->applet_id, sizeof(out->id)); + montauk::strncpy(out->library_path, library, sizeof(out->library_path)); + + const char* label_override = applet_field_string(doc, table_key, "label", ""); + const char* icon_override = applet_field_string(doc, table_key, "icon", ""); + const char* icon = icon_override[0] + ? icon_override + : (desc->icon_path && desc->icon_path[0] ? desc->icon_path : DEFAULT_APPLET_ICON); + + montauk::strncpy(out->label, + label_override[0] ? label_override : desc->display_name, + sizeof(out->label)); + montauk::strncpy(out->icon_path, icon, sizeof(out->icon_path)); + } + + libloader::dlclose(lib); + return ok; +} + +static void sort_applets(DesktopAppletEntry* entries, int count) { if (!entries || count <= 1) return; for (int i = 1; i < count; i++) { - DesktopSharedAppletEntry tmp = entries[i]; + DesktopAppletEntry tmp = entries[i]; int j = i - 1; while (j >= 0 && str_compare_ci(tmp.label, entries[j].label) < 0) { entries[j + 1] = entries[j]; @@ -89,54 +133,16 @@ static void sort_shared_applets(DesktopSharedAppletEntry* entries, int count) { } } -} // namespace - -int desktop_list_system_configuration_applets(DesktopState* ds, - DesktopSharedAppletEntry* out, - int max_entries) { - (void)ds; - if (!out || max_entries <= 0) return 0; - - auto doc = montauk::config::load(APPLET_CONFIG_NAME); - int written = 0; - for (int i = 0; i < doc.entries.count && written < max_entries; i++) { - montauk::toml::Value* item = doc.entries.items[i]; - if (!item || item->type != montauk::toml::Type::Table || !item->key) continue; - - const char* applet_id = applet_id_from_table_key(item->key); - if (!applet_id) continue; - if (!applet_field_bool(doc, item->key, "enabled", true)) continue; - - const char* library = applet_field_string(doc, item->key, "library"); - if (!library[0] || !str_ends_with(library, ".lib")) continue; - - const char* label = applet_field_string(doc, item->key, "label", applet_id); - const char* icon = applet_field_string(doc, item->key, "icon", DEFAULT_APPLET_ICON); - - montauk::memset(&out[written], 0, sizeof(out[written])); - montauk::strncpy(out[written].id, applet_id, sizeof(out[written].id)); - montauk::strncpy(out[written].label, label[0] ? label : library, sizeof(out[written].label)); - montauk::strncpy(out[written].icon_path, - icon[0] ? icon : DEFAULT_APPLET_ICON, - sizeof(out[written].icon_path)); - montauk::strncpy(out[written].library_path, library, sizeof(out[written].library_path)); - written++; - } - - doc.destroy(); - sort_shared_applets(out, written); - return written; -} - -bool desktop_launch_shared_applet(DesktopState* ds, const char* lib_path) { +static bool launch_shared_applet(DesktopState* ds, + const char* lib_path, + uint32_t required_capability) { if (!ds || !lib_path || !lib_path[0]) return false; LibHandle* lib = libloader::dlopen(lib_path); if (!lib) return false; - const Descriptor* desc = nullptr; OpenFn open = nullptr; - bool ok = resolve_shared_applet(lib, &desc, &open); + bool ok = resolve_shared_applet(lib, required_capability, nullptr, &open); if (ok) { gui::desktop_applet::Host host = {}; montauk::strncpy(host.current_user, ds->current_user, sizeof(host.current_user)); @@ -149,3 +155,68 @@ bool desktop_launch_shared_applet(DesktopState* ds, const char* lib_path) { libloader::dlclose(lib); return ok; } + +} // namespace + +int desktop_list_system_configuration_applets(DesktopState* ds, + DesktopAppletEntry* out, + int max_entries) { + (void)ds; + if (!out || max_entries <= 0) return 0; + + int written = 0; + int builtin_count = 0; + const DesktopBuiltinEntry* builtins = desktop_builtin_registry(&builtin_count); + for (int i = 0; i < builtin_count && written < max_entries; i++) { + if (!builtins[i].system_configuration_id) continue; + + clear_applet_entry(&out[written]); + out[written].kind = DESKTOP_APPLET_BUILTIN; + out[written].builtin_id = builtins[i].id; + montauk::strncpy(out[written].id, + builtins[i].system_configuration_id, + sizeof(out[written].id)); + montauk::strncpy(out[written].label, builtins[i].label, sizeof(out[written].label)); + if (builtins[i].icon_path) { + montauk::strncpy(out[written].icon_path, + builtins[i].icon_path, + sizeof(out[written].icon_path)); + } + written++; + } + + auto doc = montauk::config::load(APPLET_CONFIG_NAME); + for (int i = 0; i < doc.entries.count && written < max_entries; i++) { + montauk::toml::Value* item = doc.entries.items[i]; + if (!item || item->type != montauk::toml::Type::Table || !item->key) continue; + if (!applet_id_from_table_key(item->key)) continue; + if (!applet_field_bool(doc, item->key, "enabled", true)) continue; + + const char* library = applet_field_string(doc, item->key, "library"); + if (!library[0] || !str_ends_with(library, ".lib")) continue; + + if (load_shared_applet_metadata(doc, item->key, library, &out[written])) { + written++; + } + } + doc.destroy(); + + sort_applets(out, written); + return written; +} + +bool desktop_launch_system_configuration_applet(DesktopState* ds, + const DesktopAppletEntry* applet) { + if (!ds || !applet) return false; + + switch (applet->kind) { + case DESKTOP_APPLET_BUILTIN: + if (applet->builtin_id == DESKTOP_BUILTIN_NONE) return false; + desktop_launch_builtin(ds, applet->builtin_id); + return true; + case DESKTOP_APPLET_SHARED_LIBRARY: + return launch_shared_applet(ds, applet->library_path, SYSTEM_CONFIGURATION_CAP); + default: + return false; + } +} diff --git a/programs/src/desktop/desktop_builtin.cpp b/programs/src/desktop/desktop_builtin.cpp index 9d70846..051b027 100644 --- a/programs/src/desktop/desktop_builtin.cpp +++ b/programs/src/desktop/desktop_builtin.cpp @@ -8,18 +8,18 @@ #include static const DesktopBuiltinEntry builtin_entries[] = { - { DESKTOP_BUILTIN_FILES, "Files", 0, false, false, "0:/icons/system-file-manager.svg" }, - { DESKTOP_BUILTIN_DESKTOP_SETTINGS, "Desktop Settings", -1, false, true, + { DESKTOP_BUILTIN_FILES, "Files", 0, false, nullptr, "0:/icons/system-file-manager.svg" }, + { DESKTOP_BUILTIN_DESKTOP_SETTINGS, "Desktop Settings", -1, false, "desktop-settings", "0:/icons/preferences-desktop-apps.svg" }, - { DESKTOP_BUILTIN_SYSTEM_CONFIGURATION, "System Configuration", -1, true, false, + { DESKTOP_BUILTIN_SYSTEM_CONFIGURATION, "System Configuration", -1, true, nullptr, "0:/icons/preferences-system.svg" }, - { DESKTOP_BUILTIN_USER_MANAGEMENT, "User Management", -1, false, true, + { DESKTOP_BUILTIN_USER_MANAGEMENT, "User Management", -1, false, "user-management", "0:/icons/user-home.svg" }, - { DESKTOP_BUILTIN_LOCK_SCREEN, "Lock Screen", -1, true, false, nullptr }, - { DESKTOP_BUILTIN_LOG_OUT, "Log Out", -1, true, false, nullptr }, - { DESKTOP_BUILTIN_SLEEP, "Sleep", -1, true, false, nullptr }, - { DESKTOP_BUILTIN_REBOOT, "Reboot", -1, true, false, nullptr }, - { DESKTOP_BUILTIN_SHUTDOWN, "Shutdown", -1, true, false, nullptr }, + { DESKTOP_BUILTIN_LOCK_SCREEN, "Lock Screen", -1, true, nullptr, nullptr }, + { DESKTOP_BUILTIN_LOG_OUT, "Log Out", -1, true, nullptr, nullptr }, + { DESKTOP_BUILTIN_SLEEP, "Sleep", -1, true, nullptr, nullptr }, + { DESKTOP_BUILTIN_REBOOT, "Reboot", -1, true, nullptr, nullptr }, + { DESKTOP_BUILTIN_SHUTDOWN, "Shutdown", -1, true, nullptr, nullptr }, }; static constexpr int builtin_entry_count = sizeof(builtin_entries) / sizeof(builtin_entries[0]); @@ -44,7 +44,7 @@ SvgIcon* desktop_builtin_menu_icon(DesktopState* ds, DesktopBuiltinId id) { case DESKTOP_BUILTIN_DESKTOP_SETTINGS: return &ds->icon_settings; case DESKTOP_BUILTIN_SYSTEM_CONFIGURATION: ensure_filemanager_icons_loaded(ds); - return &ds->icon_system_tools_menu; + return &ds->icon_system_configuration_menu; case DESKTOP_BUILTIN_LOCK_SCREEN: return &ds->icon_lock; case DESKTOP_BUILTIN_LOG_OUT: return &ds->icon_logout; case DESKTOP_BUILTIN_SLEEP: return &ds->icon_sleep; @@ -89,9 +89,7 @@ void desktop_launch_builtin(DesktopState* ds, DesktopBuiltinId id) { switch (id) { case DESKTOP_BUILTIN_FILES: open_filemanager(ds); break; case DESKTOP_BUILTIN_DESKTOP_SETTINGS: open_settings(ds); break; - case DESKTOP_BUILTIN_SYSTEM_CONFIGURATION: - open_filemanager_path(ds, FILEMANAGER_PATH_SYSTEM_CONFIGURATION); - break; + case DESKTOP_BUILTIN_SYSTEM_CONFIGURATION: open_system_configuration(ds); break; case DESKTOP_BUILTIN_USER_MANAGEMENT: open_user_manager(ds); break; case DESKTOP_BUILTIN_REBOOT: open_reboot_dialog(ds); break; case DESKTOP_BUILTIN_SHUTDOWN: open_shutdown_dialog(ds); break; diff --git a/programs/src/desktop/main.cpp b/programs/src/desktop/main.cpp index 9b0ef4c..679b196 100644 --- a/programs/src/desktop/main.cpp +++ b/programs/src/desktop/main.cpp @@ -89,12 +89,12 @@ void desktop_scan_apps(DesktopState* ds) { // Load icon from app directory app->icon = {}; + app->icon_path[0] = '\0'; if (icon_file[0]) { - char icon_path[128]; - snprintf(icon_path, sizeof(icon_path), + snprintf(app->icon_path, sizeof(app->icon_path), "0:/apps/%s/%s", dirname, icon_file); Color defColor = colors::ICON_COLOR; - app->icon = svg_load(icon_path, 20, 20, defColor); + app->icon = svg_load(app->icon_path, 20, 20, defColor); } doc.destroy();