feat: architectural improvements for Apps and System Configuration virtual folders in Files
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -5,18 +5,9 @@
|
||||
*/
|
||||
|
||||
#include "filemanager_internal.hpp"
|
||||
#include <montauk/toml.h>
|
||||
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 },
|
||||
|
||||
+117
-46
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -8,18 +8,18 @@
|
||||
#include <montauk/user.h>
|
||||
|
||||
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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user