/* * desktop_internal.hpp * Shared declarations for desktop source files * Copyright (c) 2026 Daniel Hammer */ #pragma once #include "apps/apps_common.hpp" #include "wallpaper.hpp" #include #include // ============================================================================ // Desktop Background Context Menu // ============================================================================ enum DesktopCtxItem : int { DESKTOP_CTX_TERMINAL = 0, DESKTOP_CTX_FILES, DESKTOP_CTX_SETTINGS, DESKTOP_CTX_SLEEP, DESKTOP_CTX_REBOOT, DESKTOP_CTX_SHUTDOWN, DESKTOP_CTX_COUNT, }; static constexpr int DESKTOP_CTX_MENU_W = 160; static constexpr int DESKTOP_CTX_ITEM_H = 28; inline void desktop_ctx_menu_items(gui::mtk::ContextMenuItem items[DESKTOP_CTX_COUNT]) { items[DESKTOP_CTX_TERMINAL] = {"Terminal", DESKTOP_CTX_TERMINAL, true}; items[DESKTOP_CTX_FILES] = {"Files", DESKTOP_CTX_FILES, true}; items[DESKTOP_CTX_SETTINGS] = {"Settings", DESKTOP_CTX_SETTINGS, true}; items[DESKTOP_CTX_SLEEP] = {"Sleep", DESKTOP_CTX_SLEEP, true}; items[DESKTOP_CTX_REBOOT] = {"Reboot", DESKTOP_CTX_REBOOT, true}; items[DESKTOP_CTX_SHUTDOWN] = {"Shutdown", DESKTOP_CTX_SHUTDOWN, true}; } // ============================================================================ // App Menu Data (dynamic — built at startup from embedded + external apps) // ============================================================================ static constexpr int MENU_W = 220; static constexpr int MENU_ITEM_H = 36; static constexpr int MENU_CAT_H = 32; static constexpr int MENU_DIV_H = 10; struct MenuRow { bool is_category; char label[48]; DesktopBuiltinId app_id; // desktop builtin ID, or DESKTOP_BUILTIN_NONE for categories/dividers bool external; // true = spawn binary_path char binary_path[128]; // full VFS path for external apps bool launch_with_home; // true = pass home_dir as first argument SvgIcon* icon; // loaded menu icon (null for categories/dividers) }; 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; inline bool menu_cat_expanded[MENU_NUM_CATS] = { false, false, false, false, true }; inline int menu_get_cat(int row_idx) { int cat = -1; for (int i = 0; i <= row_idx; i++) if (menu_rows[i].is_category) cat++; return cat; } inline bool menu_row_visible(int row_idx) { const MenuRow& row = menu_rows[row_idx]; if (row.is_category) return true; int cat = menu_get_cat(row_idx); if (cat < 0 || cat >= MENU_NUM_CATS) return true; return menu_cat_expanded[cat]; } inline int menu_row_height(const MenuRow& row) { if (!row.is_category) return MENU_ITEM_H; return row.label[0] ? MENU_CAT_H : MENU_DIV_H; } inline int menu_total_height() { // Single-pass walk to avoid O(N^2) from menu_row_visible repeatedly // counting categories from the start. int h = 10; // top + bottom padding int cur_cat = -1; for (int i = 0; i < menu_row_count; i++) { const MenuRow& row = menu_rows[i]; if (row.is_category) cur_cat++; bool visible = true; if (!row.is_category) { if (cur_cat >= 0 && cur_cat < MENU_NUM_CATS) { visible = menu_cat_expanded[cur_cat]; } } if (visible) h += menu_row_height(row); } return h; } // ============================================================================ // Menu Builder Helpers // ============================================================================ inline int menu_add_category(const char* label) { 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; r.external = false; r.binary_path[0] = '\0'; r.launch_with_home = false; r.icon = nullptr; return menu_row_count - 1; } inline int menu_add_embedded(const char* label, DesktopBuiltinId app_id, SvgIcon* icon) { 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; r.external = false; r.binary_path[0] = '\0'; r.launch_with_home = false; r.icon = icon; return menu_row_count - 1; } inline int menu_add_external(const char* label, const char* binary, SvgIcon* icon, bool launch_with_home) { 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; r.external = true; montauk::strncpy(r.binary_path, binary, sizeof(r.binary_path)); r.launch_with_home = launch_with_home; r.icon = icon; return menu_row_count - 1; } // ============================================================================ // Forward Declarations (internal functions) // ============================================================================ inline bool desktop_window_fullscreen(const gui::Window* win) { return win && win->external && (win->ext_flags & montauk::abi::WIN_FLAG_FULLSCREEN); } inline gui::Rect desktop_content_rect(const gui::Window* win) { return desktop_window_fullscreen(win) ? win->frame : win->content_rect(); } // window.cpp gui::ResizeEdge hit_test_resize_edge(const gui::Rect& f, int mx, int my); gui::CursorStyle cursor_for_edge(gui::ResizeEdge edge); // panel.cpp void desktop_draw_app_menu(gui::DesktopState* ds); void desktop_draw_net_popup(gui::DesktopState* ds); void desktop_draw_vol_popup(gui::DesktopState* ds); const montauk::abi::NetIfInfo* desktop_eth_iface(const gui::DesktopState* ds); bool desktop_eth_online(const gui::DesktopState* ds); // wifi.cpp void desktop_wifi_init(gui::DesktopState* ds); bool desktop_wifi_poll(gui::DesktopState* ds, uint64_t now); void desktop_draw_wifi_popup(gui::DesktopState* ds); bool desktop_wifi_handle_mouse(gui::DesktopState* ds, int mx, int my, bool left_pressed, int scroll); // compose.cpp void desktop_draw_lock_screen(gui::DesktopState* ds); void desktop_mark_background_dirty(gui::DesktopState* ds); // main.cpp void desktop_refresh_netifs(gui::DesktopState* ds); void desktop_scan_apps(gui::DesktopState* ds); void desktop_build_menu(gui::DesktopState* ds); void desktop_open_launcher(gui::DesktopState* ds); void desktop_close_launcher(gui::DesktopState* ds); // Month names (shared by panel clock) inline const char* month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; // launcher.cpp void desktop_init_launcher(gui::DesktopState* ds); bool desktop_handle_launcher_keyboard(gui::DesktopState* ds, const montauk::abi::KeyEvent& key); void desktop_handle_launcher_mouse(gui::DesktopState* ds); void desktop_draw_launcher(gui::DesktopState* ds); uint64_t desktop_launcher_blink_token(const gui::DesktopState* ds, uint64_t now);