Files
MontaukOS/programs/src/desktop/apps/apps_common.hpp
T

242 lines
8.5 KiB
C++

/*
* apps_common.hpp
* Shared inline utilities and forward declarations for desktop apps
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <montauk/syscall.h>
#include <montauk/string.h>
#include <montauk/heap.h>
#include <gui/gui.hpp>
#include <gui/framebuffer.hpp>
#include <gui/font.hpp>
#include <gui/draw.hpp>
#include <gui/svg.hpp>
#include <gui/widgets.hpp>
#include <gui/window.hpp>
#include <gui/canvas.hpp>
#include <gui/terminal.hpp>
#include <gui/desktop.hpp>
// Placement new for freestanding environment
inline void* operator new(unsigned long, void* p) { return p; }
using namespace gui;
// ============================================================================
// Minimal snprintf
// ============================================================================
using va_list = __builtin_va_list;
#define va_start __builtin_va_start
#define va_end __builtin_va_end
#define va_arg __builtin_va_arg
struct PfState { char* buf; int pos; int max; };
inline void pf_putc(PfState* st, char c) {
if (st->pos < st->max) st->buf[st->pos] = c;
st->pos++;
}
inline void pf_putnum(PfState* st, unsigned long val, int base, int width, char pad, int neg) {
char tmp[24]; int i = 0;
const char* digits = "0123456789abcdef";
if (val == 0) { tmp[i++] = '0'; }
else { while (val > 0) { tmp[i++] = digits[val % base]; val /= base; } }
int total = (neg ? 1 : 0) + i;
if (neg && pad == '0') pf_putc(st, '-');
for (int w = total; w < width; w++) pf_putc(st, pad);
if (neg && pad != '0') pf_putc(st, '-');
while (i > 0) pf_putc(st, tmp[--i]);
}
inline int snprintf(char* buf, int size, const char* fmt, ...) {
va_list ap; va_start(ap, fmt);
PfState st; st.buf = buf; st.pos = 0; st.max = size > 0 ? size - 1 : 0;
while (*fmt) {
if (*fmt != '%') { pf_putc(&st, *fmt++); continue; }
fmt++;
char pad = ' ';
if (*fmt == '0') { pad = '0'; fmt++; }
int width = 0;
while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; }
if (*fmt == 'l') fmt++;
switch (*fmt) {
case 'd': case 'i': {
long val = va_arg(ap, int);
int neg = 0; unsigned long uval;
if (val < 0) { neg = 1; uval = (unsigned long)(-val); } else uval = (unsigned long)val;
pf_putnum(&st, uval, 10, width, pad, neg); break;
}
case 'u': { unsigned val = va_arg(ap, unsigned); pf_putnum(&st, val, 10, width, pad, 0); break; }
case 'x': { unsigned val = va_arg(ap, unsigned); pf_putnum(&st, val, 16, width, pad, 0); break; }
case 's': {
const char* s = va_arg(ap, const char*); if (!s) s = "(null)";
int slen = 0; while (s[slen]) slen++;
for (int w = slen; w < width; w++) pf_putc(&st, ' ');
for (int j = 0; j < slen; j++) pf_putc(&st, s[j]);
break;
}
case 'c': { char c = (char)va_arg(ap, int); pf_putc(&st, c); break; }
case '%': pf_putc(&st, '%'); break;
default: pf_putc(&st, '%'); pf_putc(&st, *fmt); break;
}
if (*fmt) fmt++;
}
if (size > 0) { if (st.pos < size) st.buf[st.pos] = '\0'; else st.buf[size - 1] = '\0'; }
va_end(ap); return st.pos;
}
// ============================================================================
// String helpers
// ============================================================================
inline void str_append(char* dst, const char* src, int max) {
int len = montauk::slen(dst);
int i = 0;
while (src[i] && len < max - 1) {
dst[len++] = src[i++];
}
dst[len] = '\0';
}
inline int str_compare_ci(const char* a, const char* b) {
while (*a && *b) {
char ca = (*a >= 'A' && *a <= 'Z') ? (*a + 32) : *a;
char cb = (*b >= 'A' && *b <= 'Z') ? (*b + 32) : *b;
if (ca != cb) return ca - cb;
a++; b++;
}
return (unsigned char)*a - (unsigned char)*b;
}
// ============================================================================
// Network formatting helpers
// ============================================================================
inline void format_ip(char* buf, uint32_t ip) {
snprintf(buf, 20, "%d.%d.%d.%d",
(int)(ip & 0xFF), (int)((ip >> 8) & 0xFF),
(int)((ip >> 16) & 0xFF), (int)((ip >> 24) & 0xFF));
}
inline void format_mac(char* buf, const uint8_t* mac) {
snprintf(buf, 20, "%02x:%02x:%02x:%02x:%02x:%02x",
(unsigned)mac[0], (unsigned)mac[1], (unsigned)mac[2],
(unsigned)mac[3], (unsigned)mac[4], (unsigned)mac[5]);
}
// ============================================================================
// File size formatting
// ============================================================================
inline void format_size(char* buf, uint64_t size) {
// Pick the largest unit that keeps the integer part below 1024, then show
// one decimal place for small values. 64-bit throughout: no 2 GiB overflow.
static const char* const units[] = { "B", "KB", "MB", "GB", "TB", "PB" };
if (size < 1024) {
snprintf(buf, 16, "%d B", (int)size);
return;
}
int unit = 0;
uint64_t scaled = size;
uint64_t rem = 0;
while (scaled >= 1024 && unit < 5) {
rem = scaled % 1024;
scaled /= 1024;
unit++;
}
int whole = (int)scaled;
int frac = (int)((rem * 10) / 1024);
if (whole < 10) {
snprintf(buf, 16, "%d.%d %s", whole, frac, units[unit]);
} else {
snprintf(buf, 16, "%d %s", whole, units[unit]);
}
}
// ============================================================================
// Desktop builtins
// ============================================================================
enum DesktopBuiltinId : int {
DESKTOP_BUILTIN_NONE = -1,
DESKTOP_BUILTIN_FILES = 1,
DESKTOP_BUILTIN_DESKTOP_SETTINGS = 11,
DESKTOP_BUILTIN_REBOOT = 12,
DESKTOP_BUILTIN_SHUTDOWN = 14,
DESKTOP_BUILTIN_LOG_OUT = 16,
DESKTOP_BUILTIN_LOCK_SCREEN = 17,
DESKTOP_BUILTIN_SLEEP = 18,
DESKTOP_BUILTIN_USER_MANAGEMENT = 19,
DESKTOP_BUILTIN_SYSTEM_CONFIGURATION = 20,
};
struct DesktopBuiltinEntry {
DesktopBuiltinId id;
const char* label;
int launcher_category;
bool launcher_footer;
const char* system_configuration_id;
const char* icon_path;
};
const DesktopBuiltinEntry* desktop_builtin_registry(int* count);
const DesktopBuiltinEntry* desktop_builtin_entry(DesktopBuiltinId id);
SvgIcon* desktop_builtin_menu_icon(DesktopState* ds, DesktopBuiltinId id);
void desktop_launch_builtin(DesktopState* ds, DesktopBuiltinId id);
void desktop_lock_screen(DesktopState* ds);
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,
DesktopAppletEntry* out,
int max_entries);
bool desktop_launch_system_configuration_applet(DesktopState* ds,
const DesktopAppletEntry* applet);
// ============================================================================
// Forward declarations for desktop launch entry points
// ============================================================================
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);
void open_klog(DesktopState* ds);
void open_settings(DesktopState* ds);
void open_user_manager(DesktopState* ds);
void open_reboot_dialog(DesktopState* ds);
void open_wordprocessor(DesktopState* ds);
void open_shutdown_dialog(DesktopState* ds);
void open_sleep_dialog(DesktopState* ds);
// Hand a graceful power-off request to login.elf and exit the desktop so the
// user returns to the login screen, where the shutdown stages run (Bluetooth
// teardown, filesystem flush) before the final ACPI power-off / reset. Pass
// montauk::abi::POWER_REQ_SHUTDOWN or montauk::abi::POWER_REQ_REBOOT.
[[noreturn]] inline void desktop_request_power(int action) {
montauk::power_request(action);
montauk::exit(0);
}
bool desktop_poll_external_windows(DesktopState* ds);