feat: move fonts/icons/man to 0:/os, add capabilities GUI app
This commit is contained in:
1 parent
80b5beb17d
commit
75e85f24ef
35 files changed
+1035
-177
No files matched your search
@@ -0,0 +1,66 @@
|
||||
# Makefile for capabilities (capability grant viewer) on MontaukOS
|
||||
# Copyright (c) 2026 Daniel Hammer
|
||||
|
||||
MAKEFLAGS += -rR
|
||||
.SUFFIXES:
|
||||
|
||||
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-montauk-
|
||||
ifeq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),)
|
||||
CXX = $(error x86_64-montauk toolchain not found. Run ./toolchain/build-montauk-toolchain.sh first)
|
||||
else
|
||||
CXX := $(TOOLCHAIN_PREFIX)g++
|
||||
endif
|
||||
|
||||
PROG_INC := ../../include
|
||||
LINK_LD := ../../link.ld
|
||||
BINDIR := ../../bin
|
||||
OBJDIR := obj
|
||||
LIBDIR := ../../lib
|
||||
|
||||
CXXFLAGS := \
|
||||
-std=gnu++20 \
|
||||
-g -O2 -pipe \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wno-unused-parameter \
|
||||
-Wno-unused-function \
|
||||
-ffreestanding \
|
||||
-fno-stack-protector \
|
||||
-fno-stack-check \
|
||||
-fno-rtti \
|
||||
-fno-exceptions \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
-msse \
|
||||
-msse2 \
|
||||
-MMD -MP \
|
||||
-I $(PROG_INC) \
|
||||
-isystem $(PROG_INC)/libc
|
||||
|
||||
LDFLAGS := \
|
||||
-nostdlib \
|
||||
-Wl,--gc-sections \
|
||||
-T $(LINK_LD)
|
||||
|
||||
SRCS := main.cpp stb_truetype_impl.cpp
|
||||
OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o))
|
||||
|
||||
TARGET := $(BINDIR)/apps/capabilities/capabilities.elf
|
||||
LIBS := $(LIBDIR)/libc/liblibc.a
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBS)
|
||||
mkdir -p $(BINDIR)/apps/capabilities
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
|
||||
|
||||
$(OBJDIR)/%.o: %.cpp Makefile
|
||||
mkdir -p $(dir $@)
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
-include $(OBJS:.o=.d)
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJDIR) $(TARGET)
|
||||
@@ -0,0 +1,744 @@
|
||||
/*
|
||||
* main.cpp
|
||||
* MontaukOS capability grant viewer
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include <montauk/syscall.h>
|
||||
#include <montauk/string.h>
|
||||
#include <montauk/config.h>
|
||||
#include <montauk/capabilities.h>
|
||||
#include <gui/mtk.hpp>
|
||||
#include <gui/mtk/settings.hpp>
|
||||
#include <gui/standalone.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <stdio.h>
|
||||
}
|
||||
|
||||
using namespace gui;
|
||||
|
||||
static constexpr int WIN_W = 760;
|
||||
static constexpr int WIN_H = 560;
|
||||
static constexpr int TAB_H = 36;
|
||||
static constexpr int FOOTER_H = 44;
|
||||
static constexpr int PAD = 18;
|
||||
static constexpr int BUTTON_W = 88;
|
||||
static constexpr int LIST_W = 200;
|
||||
static constexpr int LIST_ROW_H = 30;
|
||||
static constexpr int GRID_ROW_H = 22;
|
||||
static constexpr int GRID_COL_W = 86;
|
||||
static constexpr int MAX_GRANTS = 64;
|
||||
static constexpr int MAX_ISSUES = 4;
|
||||
|
||||
static constexpr int CAP_COUNT =
|
||||
(int)(sizeof(montauk::caps::NAMES) / sizeof(montauk::caps::NAMES[0]));
|
||||
|
||||
enum Tab {
|
||||
TAB_PROGRAMS = 0,
|
||||
TAB_CAPS = 1,
|
||||
TAB_COUNT = 2,
|
||||
};
|
||||
|
||||
static const char* const kTabLabels[TAB_COUNT] = {
|
||||
"Programs",
|
||||
"Capabilities",
|
||||
};
|
||||
|
||||
// What each capability gates, in the order of montauk::caps::NAMES.
|
||||
static const char* const kCapDescriptions[CAP_COUNT] = {
|
||||
"Inspect and terminate other processes",
|
||||
"Ask the session to shut down or restart",
|
||||
"Power the machine off or reset it directly",
|
||||
"Put the machine to sleep (S3)",
|
||||
"Format, mount and manage volumes",
|
||||
"Read and write raw block devices",
|
||||
"Configure network interfaces, DHCP and Wi-Fi",
|
||||
"Set the system clock",
|
||||
"Manage user accounts and session identities",
|
||||
"Change display modes and panel brightness",
|
||||
"Manage hardware devices (Bluetooth, radio)",
|
||||
"Read the kernel log",
|
||||
"Write to 0:/os and 0:/apps (never part of \"all\")",
|
||||
};
|
||||
|
||||
// ==== Grant table model ====
|
||||
|
||||
enum Field {
|
||||
FIELD_EFFECTIVE = 0,
|
||||
FIELD_DELEGABLE = 1,
|
||||
FIELD_PERMITTED = 2,
|
||||
FIELD_COUNT = 3,
|
||||
};
|
||||
|
||||
static const char* const kFieldKeys[FIELD_COUNT] = {
|
||||
"effective", "delegable", "permitted",
|
||||
};
|
||||
|
||||
static const char* const kFieldLabels[FIELD_COUNT] = {
|
||||
"Effective", "Delegable", "Permitted",
|
||||
};
|
||||
|
||||
struct Grant {
|
||||
char id[32];
|
||||
char path[96];
|
||||
uint64_t mask[FIELD_COUNT];
|
||||
bool declared[FIELD_COUNT];
|
||||
bool uses_all[FIELD_COUNT];
|
||||
bool binary_present;
|
||||
char issues[MAX_ISSUES][96];
|
||||
int issue_count;
|
||||
};
|
||||
|
||||
static Grant g_grants[MAX_GRANTS];
|
||||
static int g_grant_count = 0;
|
||||
static bool g_file_present = false;
|
||||
static bool g_truncated = false;
|
||||
|
||||
static WsWindow g_win;
|
||||
static Tab g_tab = TAB_PROGRAMS;
|
||||
static int g_mouse_x = -1;
|
||||
static int g_mouse_y = -1;
|
||||
static Color g_accent = colors::ACCENT;
|
||||
static bool g_is_admin = false;
|
||||
static int g_selected_grant = 0;
|
||||
static int g_selected_cap = 0;
|
||||
static int g_list_scroll = 0;
|
||||
static bool g_list_sb_dragging = false;
|
||||
static int g_list_sb_drag_offset = 0;
|
||||
static char g_status[128] = {};
|
||||
static uint64_t g_status_time = 0;
|
||||
|
||||
// Same test as montauk::user::is_admin(), without pulling in its BearSSL
|
||||
// password-hashing dependency.
|
||||
static bool session_is_admin() {
|
||||
char user[64] = {};
|
||||
if (montauk::getuser(user, sizeof(user)) <= 0 || !user[0]) return false;
|
||||
|
||||
char key[96];
|
||||
snprintf(key, sizeof(key), "users.%s.role", user);
|
||||
montauk::toml::Doc doc = montauk::config::load("users");
|
||||
bool admin = montauk::streq(doc.get_string(key, ""), "admin");
|
||||
doc.destroy();
|
||||
return admin;
|
||||
}
|
||||
|
||||
static void add_issue(Grant& g, const char* fmt, const char* a = "", const char* b = "") {
|
||||
if (g.issue_count >= MAX_ISSUES) return;
|
||||
snprintf(g.issues[g.issue_count++], sizeof(g.issues[0]), fmt, a, b);
|
||||
}
|
||||
|
||||
static Grant* find_or_add_grant(const char* id, int idLen) {
|
||||
for (int i = 0; i < g_grant_count; i++) {
|
||||
const char* have = g_grants[i].id;
|
||||
int k = 0;
|
||||
while (k < idLen && have[k] == id[k]) k++;
|
||||
if (k == idLen && have[k] == '\0') return &g_grants[i];
|
||||
}
|
||||
if (g_grant_count >= MAX_GRANTS) {
|
||||
g_truncated = true;
|
||||
return nullptr;
|
||||
}
|
||||
Grant* g = &g_grants[g_grant_count++];
|
||||
montauk::memset(g, 0, sizeof(*g));
|
||||
int n = idLen < (int)sizeof(g->id) - 1 ? idLen : (int)sizeof(g->id) - 1;
|
||||
montauk::memcpy(g->id, id, n);
|
||||
g->id[n] = '\0';
|
||||
return g;
|
||||
}
|
||||
|
||||
static void read_field(Grant& g, int field, montauk::toml::Value* v) {
|
||||
g.declared[field] = true;
|
||||
if (v->type != montauk::toml::Type::Array) {
|
||||
add_issue(g, "'%s' is not a list of capability names", kFieldKeys[field]);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < v->array.count; i++) {
|
||||
montauk::toml::Value* item = v->array.items[i];
|
||||
if (item == nullptr || item->type != montauk::toml::Type::String) continue;
|
||||
uint64_t bit = montauk::caps::bit_for_name(item->str);
|
||||
if (bit == 0) {
|
||||
add_issue(g, "Unknown capability \"%s\" in %s (ignored)",
|
||||
item->str, kFieldKeys[field]);
|
||||
continue;
|
||||
}
|
||||
if (montauk::streq(item->str, "all")) g.uses_all[field] = true;
|
||||
g.mask[field] |= bit;
|
||||
}
|
||||
}
|
||||
|
||||
// Mirrors montauk::caps::lookup(), but keeps everything the launcher would
|
||||
// silently discard so it can be shown.
|
||||
static void load_grants() {
|
||||
g_grant_count = 0;
|
||||
g_truncated = false;
|
||||
|
||||
int fh = montauk::open("0:/config/capabilities.toml");
|
||||
g_file_present = fh >= 0;
|
||||
if (fh >= 0) montauk::close(fh);
|
||||
|
||||
montauk::toml::Doc doc = montauk::config::load(montauk::caps::GRANT_CONFIG);
|
||||
int prefixLen = montauk::slen(montauk::caps::GRANT_PREFIX);
|
||||
|
||||
for (int i = 0; i < doc.entries.count; i++) {
|
||||
montauk::toml::Value* entry = doc.entries.items[i];
|
||||
if (entry == nullptr || entry->key == nullptr) continue;
|
||||
if (!montauk::starts_with(entry->key, montauk::caps::GRANT_PREFIX)) continue;
|
||||
|
||||
const char* rest = entry->key + prefixLen;
|
||||
int lastDot = -1;
|
||||
for (int k = 0; rest[k]; k++)
|
||||
if (rest[k] == '.') lastDot = k;
|
||||
if (lastDot <= 0) continue;
|
||||
|
||||
Grant* g = find_or_add_grant(rest, lastDot);
|
||||
if (g == nullptr) continue;
|
||||
const char* field = rest + lastDot + 1;
|
||||
|
||||
if (montauk::streq(field, "path")) {
|
||||
if (entry->type == montauk::toml::Type::String)
|
||||
snprintf(g->path, sizeof(g->path), "%s", entry->str);
|
||||
continue;
|
||||
}
|
||||
bool known = false;
|
||||
for (int f = 0; f < FIELD_COUNT; f++) {
|
||||
if (montauk::streq(field, kFieldKeys[f])) {
|
||||
read_field(*g, f, entry);
|
||||
known = true;
|
||||
}
|
||||
}
|
||||
if (!known) add_issue(*g, "Unknown key '%s' (ignored)", field);
|
||||
}
|
||||
doc.destroy();
|
||||
|
||||
for (int i = 0; i < g_grant_count; i++) {
|
||||
Grant& g = g_grants[i];
|
||||
uint64_t* m = g.mask;
|
||||
|
||||
// Same rule as caps::lookup(): an undeclared permitted set is exactly
|
||||
// what the grant can use or pass on.
|
||||
if (!g.declared[FIELD_PERMITTED] || m[FIELD_PERMITTED] == 0)
|
||||
m[FIELD_PERMITTED] = m[FIELD_EFFECTIVE] | m[FIELD_DELEGABLE];
|
||||
else if ((m[FIELD_EFFECTIVE] | m[FIELD_DELEGABLE]) & ~m[FIELD_PERMITTED])
|
||||
add_issue(g, "Effective/delegable exceed permitted; the excess is dropped at launch");
|
||||
|
||||
if (g.path[0] == '\0') {
|
||||
add_issue(g, "No path; this grant never applies");
|
||||
continue;
|
||||
}
|
||||
int bh = montauk::open(g.path);
|
||||
g.binary_present = bh >= 0;
|
||||
if (bh >= 0) montauk::close(bh);
|
||||
if (!g.binary_present) add_issue(g, "Binary not found on this system");
|
||||
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (montauk::streq(g_grants[j].path, g.path)) {
|
||||
add_issue(g, "Shadowed by [grant.%s]; the first entry for a path wins",
|
||||
g_grants[j].id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_selected_grant >= g_grant_count) g_selected_grant = 0;
|
||||
}
|
||||
|
||||
// ==== Layout ====
|
||||
|
||||
static mtk::Theme app_theme() {
|
||||
return mtk::make_theme(g_accent);
|
||||
}
|
||||
|
||||
static Rect tab_bar_rect() {
|
||||
return {0, 0, g_win.width, TAB_H};
|
||||
}
|
||||
|
||||
static Rect footer_rect() {
|
||||
return {0, g_win.height - FOOTER_H, g_win.width, FOOTER_H};
|
||||
}
|
||||
|
||||
static constexpr int LIST_TOP_INSET = 10;
|
||||
|
||||
static int sidebar_w() {
|
||||
return PAD + LIST_W + PAD;
|
||||
}
|
||||
|
||||
// The whole left column, from the tab bar to the footer. The scrollbar runs
|
||||
// its full height against the divider, like a pane scrollbar.
|
||||
static Rect list_rect() {
|
||||
return {0, TAB_H, sidebar_w(), footer_rect().y - TAB_H};
|
||||
}
|
||||
|
||||
static int list_count() {
|
||||
return g_tab == TAB_PROGRAMS ? g_grant_count : CAP_COUNT;
|
||||
}
|
||||
|
||||
static int list_visible_rows() {
|
||||
return (list_rect().h - LIST_TOP_INSET) / LIST_ROW_H;
|
||||
}
|
||||
|
||||
static int list_view_extent() {
|
||||
return list_visible_rows() * LIST_ROW_H;
|
||||
}
|
||||
|
||||
static int list_content_extent() {
|
||||
return list_count() * LIST_ROW_H;
|
||||
}
|
||||
|
||||
static bool list_scrollbar_visible() {
|
||||
return mtk::scrollbar_needed(list_content_extent(), list_view_extent());
|
||||
}
|
||||
|
||||
static Rect list_scrollbar_track() {
|
||||
if (!list_scrollbar_visible()) return {0, 0, 0, 0};
|
||||
return mtk::scrollbar_track_rect(list_rect());
|
||||
}
|
||||
|
||||
static Rect list_scrollbar_thumb() {
|
||||
Rect track = list_scrollbar_track();
|
||||
if (track.empty()) return {0, 0, 0, 0};
|
||||
return mtk::scrollbar_thumb_rect(track, list_content_extent(),
|
||||
list_view_extent(), g_list_scroll * LIST_ROW_H);
|
||||
}
|
||||
|
||||
static Rect list_row_rect(int visibleIndex) {
|
||||
Rect list = list_rect();
|
||||
int right = list.w - (list_scrollbar_visible() ? mtk::SCROLLBAR_W + 6 : PAD);
|
||||
return {PAD, list.y + LIST_TOP_INSET + visibleIndex * LIST_ROW_H,
|
||||
right - PAD, LIST_ROW_H};
|
||||
}
|
||||
|
||||
static int pane_x() {
|
||||
return sidebar_w() + 1 + PAD;
|
||||
}
|
||||
|
||||
static int pane_w() {
|
||||
return g_win.width - pane_x() - PAD;
|
||||
}
|
||||
|
||||
static Rect reload_button() {
|
||||
Rect foot = footer_rect();
|
||||
return {foot.x + foot.w - PAD - BUTTON_W, foot.y + 7, BUTTON_W, 30};
|
||||
}
|
||||
|
||||
static bool mouse_in(const Rect& rect) {
|
||||
return rect.contains(g_mouse_x, g_mouse_y);
|
||||
}
|
||||
|
||||
static void clamp_list_scroll() {
|
||||
int max_scroll = list_count() - list_visible_rows();
|
||||
if (max_scroll < 0) max_scroll = 0;
|
||||
if (g_list_scroll > max_scroll) g_list_scroll = max_scroll;
|
||||
if (g_list_scroll < 0) g_list_scroll = 0;
|
||||
}
|
||||
|
||||
static void set_status(const char* msg) {
|
||||
snprintf(g_status, sizeof(g_status), "%s", msg ? msg : "");
|
||||
g_status_time = montauk::get_milliseconds();
|
||||
}
|
||||
|
||||
static bool status_visible() {
|
||||
return g_status[0] && montauk::get_milliseconds() - g_status_time < 5000;
|
||||
}
|
||||
|
||||
// ==== Drawing ====
|
||||
|
||||
static void fit_text(char* out, int outCap, const char* text, int maxWidth) {
|
||||
if (!out || outCap <= 0) return;
|
||||
out[0] = '\0';
|
||||
if (!text || !text[0] || maxWidth <= 0) return;
|
||||
if (text_width(text) <= maxWidth) {
|
||||
snprintf(out, outCap, "%s", text);
|
||||
return;
|
||||
}
|
||||
|
||||
static const char* ellipsis = "...";
|
||||
int len = 0;
|
||||
while (text[len] && len < outCap - 4) {
|
||||
out[len] = text[len];
|
||||
out[len + 1] = '\0';
|
||||
if (text_width(out) + text_width(ellipsis) > maxWidth) break;
|
||||
len++;
|
||||
}
|
||||
out[len++] = '.';
|
||||
out[len++] = '.';
|
||||
out[len++] = '.';
|
||||
out[len] = '\0';
|
||||
}
|
||||
|
||||
static void draw_text_fit(Canvas& c, int x, int y, const char* text,
|
||||
int maxWidth, Color color) {
|
||||
char fitted[160];
|
||||
fit_text(fitted, sizeof(fitted), text, maxWidth);
|
||||
c.text(x, y, fitted, color);
|
||||
}
|
||||
|
||||
static void draw_list(Canvas& c, const mtk::Theme& theme) {
|
||||
int fh = system_font_height();
|
||||
int count = list_count();
|
||||
int visible = list_visible_rows();
|
||||
for (int row = 0; row < visible; row++) {
|
||||
int index = g_list_scroll + row;
|
||||
if (index >= count) break;
|
||||
Rect r = list_row_rect(row);
|
||||
bool selected = index == (g_tab == TAB_PROGRAMS ? g_selected_grant : g_selected_cap);
|
||||
mtk::draw_list_row(c, r, selected, false, theme);
|
||||
if (!selected && mouse_in(r))
|
||||
c.fill_rounded_rect(r.x, r.y, r.w, r.h, theme.radius_md, theme.surface_hover);
|
||||
|
||||
const char* label = g_tab == TAB_PROGRAMS
|
||||
? g_grants[index].id : montauk::caps::NAMES[index].name;
|
||||
Color fg = selected ? theme.accent_fg : theme.text;
|
||||
int ty = r.y + (r.h - fh) / 2;
|
||||
draw_text_fit(c, r.x + 10, ty, label, r.w - 34, fg);
|
||||
|
||||
if (g_tab == TAB_PROGRAMS && g_grants[index].issue_count > 0) {
|
||||
Color dot = selected ? theme.accent_fg : theme.danger;
|
||||
fill_circle(c, r.x + r.w - 14, r.y + r.h / 2, 4, dot);
|
||||
}
|
||||
}
|
||||
|
||||
if (list_scrollbar_visible()) {
|
||||
Rect track = list_scrollbar_track();
|
||||
bool hovered = track.contains(g_mouse_x, g_mouse_y) || g_list_sb_dragging;
|
||||
mtk::draw_scrollbar(c, track, list_scrollbar_thumb(), hovered,
|
||||
g_list_sb_dragging, theme);
|
||||
}
|
||||
|
||||
int sx = sidebar_w();
|
||||
c.fill_rect(sx, TAB_H, 1, footer_rect().y - TAB_H, theme.border);
|
||||
}
|
||||
|
||||
static void draw_mark(Canvas& c, int cx, int cy, bool on, const mtk::Theme& theme) {
|
||||
Rect cell = {cx - 8, cy - 8, 16, 16};
|
||||
mtk::draw_checkbox(c, cell, "", mtk::check_state(on), theme, true, false);
|
||||
}
|
||||
|
||||
static void draw_programs_pane(Canvas& c, const mtk::Theme& theme) {
|
||||
int x = pane_x();
|
||||
int w = pane_w();
|
||||
int fh = system_font_height();
|
||||
int y = TAB_H + PAD;
|
||||
|
||||
if (g_grant_count == 0) {
|
||||
c.text(x, y, g_file_present ? "No grants are declared."
|
||||
: "0:/config/capabilities.toml was not found.",
|
||||
theme.text_subtle);
|
||||
c.text(x, y + fh + 8, "Programs without a grant run with no capabilities.",
|
||||
theme.text_muted);
|
||||
return;
|
||||
}
|
||||
|
||||
const Grant& g = g_grants[g_selected_grant];
|
||||
char title[64];
|
||||
snprintf(title, sizeof(title), "[grant.%s]", g.id);
|
||||
c.text(x, y, title, theme.text);
|
||||
|
||||
if (g.issue_count > 0) {
|
||||
const char* state = "Needs attention";
|
||||
c.text(x + w - text_width(state), y, state, theme.danger);
|
||||
}
|
||||
|
||||
y += fh + 6;
|
||||
draw_text_fit(c, x, y, g.path[0] ? g.path : "(no path)", w, theme.text_subtle);
|
||||
|
||||
y += fh + 14;
|
||||
mtk::draw_separator(c, x, y, w, theme);
|
||||
y += 12;
|
||||
|
||||
// Grid: one row per capability, one column per mask.
|
||||
int colX = x + w - GRID_COL_W * FIELD_COUNT;
|
||||
c.text(x, y, "CAPABILITY", theme.text_muted);
|
||||
for (int f = 0; f < FIELD_COUNT; f++) {
|
||||
const char* label = kFieldLabels[f];
|
||||
int cx = colX + f * GRID_COL_W + GRID_COL_W / 2;
|
||||
c.text(cx - text_width(label) / 2, y, label, theme.text_muted);
|
||||
}
|
||||
y += fh + 4;
|
||||
for (int f = 0; f < FIELD_COUNT; f++) {
|
||||
const char* note = nullptr;
|
||||
if (g.uses_all[f]) note = "(all)";
|
||||
else if (f == FIELD_PERMITTED && !g.declared[f]) note = "(implied)";
|
||||
if (!note) continue;
|
||||
int cx = colX + f * GRID_COL_W + GRID_COL_W / 2;
|
||||
c.text(cx - text_width(note) / 2, y, note, theme.text_muted);
|
||||
}
|
||||
y += fh + 4;
|
||||
|
||||
for (int i = 0; i < CAP_COUNT; i++) {
|
||||
uint64_t bit = montauk::caps::NAMES[i].bit;
|
||||
bool any = (g.mask[FIELD_PERMITTED] & bit) != 0;
|
||||
if (i % 2 == 1)
|
||||
c.fill_rect(x, y, w, GRID_ROW_H, theme.surface_alt);
|
||||
c.text(x + 6, y + (GRID_ROW_H - fh) / 2, montauk::caps::NAMES[i].name,
|
||||
any ? theme.text : theme.text_muted);
|
||||
for (int f = 0; f < FIELD_COUNT; f++) {
|
||||
int cx = colX + f * GRID_COL_W + GRID_COL_W / 2;
|
||||
draw_mark(c, cx, y + GRID_ROW_H / 2, (g.mask[f] & bit) != 0, theme);
|
||||
}
|
||||
y += GRID_ROW_H;
|
||||
}
|
||||
|
||||
y += 10;
|
||||
for (int i = 0; i < g.issue_count; i++) {
|
||||
draw_text_fit(c, x, y, g.issues[i], w, theme.danger);
|
||||
y += fh + 4;
|
||||
}
|
||||
}
|
||||
|
||||
static int draw_holder_list(Canvas& c, int x, int y, int w, int bottom,
|
||||
const char* heading, int field, uint64_t bit,
|
||||
const mtk::Theme& theme) {
|
||||
int fh = system_font_height();
|
||||
c.text(x, y, heading, theme.text_muted);
|
||||
y += fh + 8;
|
||||
|
||||
int total = 0;
|
||||
for (int i = 0; i < g_grant_count; i++)
|
||||
if (g_grants[i].mask[field] & bit) total++;
|
||||
if (total == 0) {
|
||||
c.text(x + 10, y, "No program", theme.text_subtle);
|
||||
return y + fh + 16;
|
||||
}
|
||||
|
||||
int shown = 0;
|
||||
for (int i = 0; i < g_grant_count; i++) {
|
||||
const Grant& g = g_grants[i];
|
||||
if (!(g.mask[field] & bit)) continue;
|
||||
if (y + (fh + 6) * 2 > bottom && shown < total - 1) {
|
||||
char more[48];
|
||||
snprintf(more, sizeof(more), "+%d more", total - shown);
|
||||
c.text(x + 10, y, more, theme.text_muted);
|
||||
return y + fh + 16;
|
||||
}
|
||||
c.text(x + 10, y, g.id, theme.text);
|
||||
draw_text_fit(c, x + 130, y, g.path, w - 130, theme.text_subtle);
|
||||
y += fh + 6;
|
||||
shown++;
|
||||
}
|
||||
return y + 12;
|
||||
}
|
||||
|
||||
static void draw_caps_pane(Canvas& c, const mtk::Theme& theme) {
|
||||
int x = pane_x();
|
||||
int w = pane_w();
|
||||
int fh = system_font_height();
|
||||
int y = TAB_H + PAD;
|
||||
int bottom = footer_rect().y - PAD;
|
||||
|
||||
const auto& cap = montauk::caps::NAMES[g_selected_cap];
|
||||
c.text(x, y, cap.name, theme.text);
|
||||
y += fh + 6;
|
||||
draw_text_fit(c, x, y, kCapDescriptions[g_selected_cap], w, theme.text_subtle);
|
||||
y += fh + 14;
|
||||
mtk::draw_separator(c, x, y, w, theme);
|
||||
y += 14;
|
||||
|
||||
y = draw_holder_list(c, x, y, w, bottom - (fh + 6) * 4,
|
||||
"USES IT (EFFECTIVE)", FIELD_EFFECTIVE, cap.bit, theme);
|
||||
draw_holder_list(c, x, y, w, bottom,
|
||||
"MAY PASS IT ON (DELEGABLE)", FIELD_DELEGABLE, cap.bit, theme);
|
||||
}
|
||||
|
||||
static void draw_locked(Canvas& c, const mtk::Theme& theme) {
|
||||
int fh = system_font_height();
|
||||
int y = TAB_H + 60;
|
||||
const char* title = "Administrator access required";
|
||||
c.text((g_win.width - text_width(title)) / 2, y, title, theme.text);
|
||||
const char* body = "Only administrators can review the capability grant table.";
|
||||
c.text((g_win.width - text_width(body)) / 2, y + fh + 10, body, theme.text_subtle);
|
||||
}
|
||||
|
||||
static void draw_footer(Canvas& c, const mtk::Theme& theme) {
|
||||
Rect foot = footer_rect();
|
||||
c.fill_rect(foot.x, foot.y, foot.w, foot.h, theme.surface);
|
||||
mtk::draw_separator(c, 0, foot.y, g_win.width, theme);
|
||||
|
||||
char summary[128];
|
||||
if (status_visible()) {
|
||||
snprintf(summary, sizeof(summary), "%s", g_status);
|
||||
} else if (!g_is_admin) {
|
||||
snprintf(summary, sizeof(summary), "Read-only");
|
||||
} else {
|
||||
int flagged = 0;
|
||||
for (int i = 0; i < g_grant_count; i++)
|
||||
if (g_grants[i].issue_count > 0) flagged++;
|
||||
snprintf(summary, sizeof(summary), "%d grant%s%s, %d flagged - read-only view",
|
||||
g_grant_count, g_grant_count == 1 ? "" : "s",
|
||||
g_truncated ? " (list truncated)" : "", flagged);
|
||||
}
|
||||
c.text(PAD, foot.y + (foot.h - system_font_height()) / 2, summary, theme.text_subtle);
|
||||
|
||||
if (g_is_admin) {
|
||||
Rect reload = reload_button();
|
||||
mtk::draw_button(c, reload, "Reload", mtk::BUTTON_SECONDARY,
|
||||
mtk::widget_state(false, mouse_in(reload), true), theme);
|
||||
}
|
||||
}
|
||||
|
||||
static void render() {
|
||||
mtk::StandaloneHost host(&g_win);
|
||||
Canvas c = host.canvas();
|
||||
mtk::Theme theme = app_theme();
|
||||
c.fill(theme.window_bg);
|
||||
if (g_is_admin) {
|
||||
mtk::draw_tab_bar(c, tab_bar_rect(), kTabLabels, TAB_COUNT, (int)g_tab, theme);
|
||||
draw_list(c, theme);
|
||||
if (g_tab == TAB_PROGRAMS) draw_programs_pane(c, theme);
|
||||
else draw_caps_pane(c, theme);
|
||||
} else {
|
||||
draw_locked(c, theme);
|
||||
}
|
||||
draw_footer(c, theme);
|
||||
host.present();
|
||||
}
|
||||
|
||||
// ==== Input ====
|
||||
|
||||
static void reload() {
|
||||
load_grants();
|
||||
clamp_list_scroll();
|
||||
set_status("Reloaded 0:/config/capabilities.toml");
|
||||
}
|
||||
|
||||
static void select_index(int index) {
|
||||
int count = list_count();
|
||||
if (count <= 0) return;
|
||||
if (index < 0) index = 0;
|
||||
if (index >= count) index = count - 1;
|
||||
if (g_tab == TAB_PROGRAMS) g_selected_grant = index;
|
||||
else g_selected_cap = index;
|
||||
|
||||
int visible = list_visible_rows();
|
||||
if (index < g_list_scroll) g_list_scroll = index;
|
||||
else if (index >= g_list_scroll + visible) g_list_scroll = index - visible + 1;
|
||||
clamp_list_scroll();
|
||||
}
|
||||
|
||||
static void switch_tab(Tab tab) {
|
||||
g_tab = tab;
|
||||
g_list_scroll = 0;
|
||||
select_index(tab == TAB_PROGRAMS ? g_selected_grant : g_selected_cap);
|
||||
}
|
||||
|
||||
static void handle_mouse(const montauk::abi::WinEvent& ev) {
|
||||
if (!g_is_admin) return;
|
||||
int mx = ev.mouse.x;
|
||||
int my = ev.mouse.y;
|
||||
bool pressed = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
|
||||
|
||||
Rect track = list_scrollbar_track();
|
||||
Rect thumb = list_scrollbar_thumb();
|
||||
if (g_list_sb_dragging) {
|
||||
if ((ev.mouse.buttons & 1) && !track.empty()) {
|
||||
int offset = mtk::scrollbar_offset_from_thumb_top(
|
||||
track, list_content_extent(), list_view_extent(),
|
||||
thumb.h, my - g_list_sb_drag_offset);
|
||||
g_list_scroll = offset / LIST_ROW_H;
|
||||
clamp_list_scroll();
|
||||
return;
|
||||
}
|
||||
g_list_sb_dragging = false;
|
||||
}
|
||||
|
||||
if (ev.mouse.scroll != 0 && list_rect().contains(mx, my)) {
|
||||
g_list_scroll += ev.mouse.scroll > 0 ? -1 : 1;
|
||||
clamp_list_scroll();
|
||||
return;
|
||||
}
|
||||
if (!pressed) return;
|
||||
|
||||
int tab = mtk::hit_tab_bar(tab_bar_rect(), TAB_COUNT, mx, my);
|
||||
if (tab >= 0) {
|
||||
switch_tab((Tab)tab);
|
||||
return;
|
||||
}
|
||||
if (!track.empty() && track.contains(mx, my)) {
|
||||
if (thumb.contains(mx, my)) {
|
||||
g_list_sb_dragging = true;
|
||||
g_list_sb_drag_offset = my - thumb.y;
|
||||
} else {
|
||||
int visible = list_visible_rows();
|
||||
int page = visible > 1 ? visible - 1 : 1;
|
||||
g_list_scroll += my < thumb.y ? -page : page;
|
||||
clamp_list_scroll();
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (int row = 0; row < list_visible_rows(); row++) {
|
||||
int index = g_list_scroll + row;
|
||||
if (index >= list_count()) break;
|
||||
if (list_row_rect(row).contains(mx, my)) {
|
||||
select_index(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (reload_button().contains(mx, my)) reload();
|
||||
}
|
||||
|
||||
static bool handle_key(const montauk::abi::KeyEvent& key) {
|
||||
if (!key.pressed) return false;
|
||||
if (key.scancode == 0x01) {
|
||||
g_win.closed = true;
|
||||
return true;
|
||||
}
|
||||
if (!g_is_admin) return false;
|
||||
if (key.ascii == '\t') {
|
||||
switch_tab(g_tab == TAB_PROGRAMS ? TAB_CAPS : TAB_PROGRAMS);
|
||||
return true;
|
||||
}
|
||||
int current = g_tab == TAB_PROGRAMS ? g_selected_grant : g_selected_cap;
|
||||
if (key.scancode == 0x48) { select_index(current - 1); return true; } // Up
|
||||
if (key.scancode == 0x50) { select_index(current + 1); return true; } // Down
|
||||
if (key.ascii == 'r' || key.ascii == 'R') {
|
||||
reload();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" void _start() {
|
||||
if (!fonts::init()) montauk::exit(1);
|
||||
g_accent = mtk::load_system_accent();
|
||||
|
||||
g_is_admin = session_is_admin();
|
||||
if (g_is_admin) load_grants();
|
||||
|
||||
if (!g_win.create("Capabilities", WIN_W, WIN_H)) montauk::exit(1);
|
||||
render();
|
||||
|
||||
while (g_win.id >= 0 && !g_win.closed) {
|
||||
montauk::abi::WinEvent ev;
|
||||
int result = g_win.poll(&ev);
|
||||
if (result < 0) break;
|
||||
if (result == 0) {
|
||||
// Keep drawing while a status message is up, plus once more to
|
||||
// clear it when it expires.
|
||||
static bool status_drawn = false;
|
||||
if (status_visible() || status_drawn) {
|
||||
status_drawn = status_visible();
|
||||
render();
|
||||
}
|
||||
montauk::sleep_ms(16);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool redraw = false;
|
||||
if (ev.type == 3) break;
|
||||
if (ev.type == 2 || ev.type == 4) {
|
||||
clamp_list_scroll();
|
||||
redraw = true;
|
||||
} else if (ev.type == 1) {
|
||||
g_mouse_x = ev.mouse.x;
|
||||
g_mouse_y = ev.mouse.y;
|
||||
handle_mouse(ev);
|
||||
redraw = true;
|
||||
} else if (ev.type == 0) {
|
||||
redraw = handle_key(ev.key);
|
||||
}
|
||||
if (redraw) render();
|
||||
}
|
||||
|
||||
g_win.destroy();
|
||||
montauk::exit(0);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
[app]
|
||||
id = "capabilities"
|
||||
name = "Capabilities"
|
||||
binary = "capabilities.elf"
|
||||
icon = "stock_lock.svg"
|
||||
|
||||
[menu]
|
||||
category = "System"
|
||||
visible = false
|
||||
|
||||
[desktop]
|
||||
section = "settings"
|
||||
admin_only = true
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* stb_truetype_impl.cpp
|
||||
* Single compilation unit for stb_truetype in MontaukOS freestanding environment
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <montauk/heap.h>
|
||||
#include <montauk/string.h>
|
||||
#include <gui/stb_math.h>
|
||||
|
||||
#define STBTT_ifloor(x) ((int) stb_floor(x))
|
||||
#define STBTT_iceil(x) ((int) stb_ceil(x))
|
||||
#define STBTT_sqrt(x) stb_sqrt(x)
|
||||
#define STBTT_pow(x,y) stb_pow(x,y)
|
||||
#define STBTT_fmod(x,y) stb_fmod(x,y)
|
||||
#define STBTT_cos(x) stb_cos(x)
|
||||
#define STBTT_acos(x) stb_acos(x)
|
||||
#define STBTT_fabs(x) stb_fabs(x)
|
||||
|
||||
#define STBTT_malloc(x,u) ((void)(u), montauk::malloc(x))
|
||||
#define STBTT_free(x,u) ((void)(u), montauk::mfree(x))
|
||||
#define STBTT_memcpy(d,s,n) montauk::memcpy(d,s,n)
|
||||
#define STBTT_memset(d,v,n) montauk::memset(d,v,n)
|
||||
#define STBTT_strlen(x) montauk::slen(x)
|
||||
#define STBTT_assert(x) ((void)(x))
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include <gui/stb_truetype.h>
|
||||
@@ -78,33 +78,33 @@ void ensure_filemanager_icons_loaded(DesktopState* ds) {
|
||||
if (!ds || ds->icon_drive.pixels) return;
|
||||
|
||||
Color defColor = colors::ICON_COLOR;
|
||||
ds->icon_drive = svg_load("0:/icons/drive-harddisk.svg", 16, 16, defColor);
|
||||
ds->icon_drive_lg = svg_load("0:/icons/drive-harddisk.svg", 48, 48, defColor);
|
||||
ds->icon_drive_usb = svg_load("0:/icons/drive-harddisk-usb.svg", 16, 16, defColor);
|
||||
ds->icon_drive_usb_lg = svg_load("0:/icons/drive-harddisk-usb.svg", 48, 48, defColor);
|
||||
ds->icon_home_folder = svg_load("0:/icons/folder-blue-home.svg", 16, 16, defColor);
|
||||
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_drive = svg_load("0:/os/icons/drive-harddisk.svg", 16, 16, defColor);
|
||||
ds->icon_drive_lg = svg_load("0:/os/icons/drive-harddisk.svg", 48, 48, defColor);
|
||||
ds->icon_drive_usb = svg_load("0:/os/icons/drive-harddisk-usb.svg", 16, 16, defColor);
|
||||
ds->icon_drive_usb_lg = svg_load("0:/os/icons/drive-harddisk-usb.svg", 48, 48, defColor);
|
||||
ds->icon_home_folder = svg_load("0:/os/icons/folder-blue-home.svg", 16, 16, defColor);
|
||||
ds->icon_home_folder_lg = svg_load("0:/os/icons/folder-blue-home.svg", 48, 48, defColor);
|
||||
ds->icon_apps = svg_load("0:/os/icons/folder-blue-development.svg", 16, 16, defColor);
|
||||
ds->icon_apps_lg = svg_load("0:/os/icons/folder-blue-development.svg", 48, 48, defColor);
|
||||
ds->icon_system_configuration_menu =
|
||||
svg_load("0:/icons/preferences-system.svg", 20, 20, defColor);
|
||||
svg_load("0:/os/icons/preferences-system.svg", 20, 20, defColor);
|
||||
ds->icon_system_configuration =
|
||||
svg_load("0:/icons/preferences-system.svg", 16, 16, defColor);
|
||||
svg_load("0:/os/icons/preferences-system.svg", 16, 16, defColor);
|
||||
ds->icon_system_configuration_lg =
|
||||
svg_load("0:/icons/preferences-system.svg", 48, 48, defColor);
|
||||
svg_load("0:/os/icons/preferences-system.svg", 48, 48, defColor);
|
||||
|
||||
for (int sf = 0; sf < filemanager::SF_COUNT; sf++) {
|
||||
char icon_path[128];
|
||||
snprintf(icon_path, 128, "0:/icons/%s", filemanager::sf_icons[sf]);
|
||||
snprintf(icon_path, 128, "0:/os/icons/%s", filemanager::sf_icons[sf]);
|
||||
ds->icon_special_folder[sf] = svg_load(icon_path, 16, 16, defColor);
|
||||
ds->icon_special_folder_lg[sf] = svg_load(icon_path, 48, 48, defColor);
|
||||
}
|
||||
ds->icon_delete = svg_load("0:/icons/trash-empty.svg", 16, 16, defColor);
|
||||
ds->icon_copy = svg_load("0:/icons/edit-copy.svg", 16, 16, defColor);
|
||||
ds->icon_cut = svg_load("0:/icons/edit-cut.svg", 16, 16, defColor);
|
||||
ds->icon_paste = svg_load("0:/icons/edit-paste.svg", 16, 16, defColor);
|
||||
ds->icon_rename = svg_load("0:/icons/edit-rename.svg", 16, 16, defColor);
|
||||
ds->icon_folder_new = svg_load("0:/icons/folder-new.svg", 16, 16, defColor);
|
||||
ds->icon_delete = svg_load("0:/os/icons/trash-empty.svg", 16, 16, defColor);
|
||||
ds->icon_copy = svg_load("0:/os/icons/edit-copy.svg", 16, 16, defColor);
|
||||
ds->icon_cut = svg_load("0:/os/icons/edit-cut.svg", 16, 16, defColor);
|
||||
ds->icon_paste = svg_load("0:/os/icons/edit-paste.svg", 16, 16, defColor);
|
||||
ds->icon_rename = svg_load("0:/os/icons/edit-rename.svg", 16, 16, defColor);
|
||||
ds->icon_folder_new = svg_load("0:/os/icons/folder-new.svg", 16, 16, defColor);
|
||||
}
|
||||
|
||||
void open_filemanager(DesktopState* ds) {
|
||||
|
||||
@@ -63,7 +63,7 @@ static void battery_icon_path(const DesktopState* ds, char* out, int cap) {
|
||||
const montauk::abi::BatteryInfo& b = ds->battery_info;
|
||||
|
||||
if (!b.present) {
|
||||
montauk::strncpy(out, "0:/icons/battery-missing-symbolic.svg", cap);
|
||||
montauk::strncpy(out, "0:/os/icons/battery-missing-symbolic.svg", cap);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -72,11 +72,11 @@ static void battery_icon_path(const DesktopState* ds, char* out, int cap) {
|
||||
// A full battery on the charger gets the "charged" glyph; the pack has no
|
||||
// 100% charging variant, and "charged" is what that state means anyway.
|
||||
if (b.charging && bucket >= 100) {
|
||||
montauk::strncpy(out, "0:/icons/battery-level-100-charged-symbolic.svg", cap);
|
||||
montauk::strncpy(out, "0:/os/icons/battery-level-100-charged-symbolic.svg", cap);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(out, cap, "0:/icons/battery-level-%d%s-symbolic.svg",
|
||||
snprintf(out, cap, "0:/os/icons/battery-level-%d%s-symbolic.svg",
|
||||
bucket, b.charging ? "-charging" : "");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
#include <montauk/user.h>
|
||||
|
||||
static const DesktopBuiltinEntry builtin_entries[] = {
|
||||
{ DESKTOP_BUILTIN_FILES, "Files", 0, false, nullptr, "0:/icons/system-file-manager.svg" },
|
||||
{ DESKTOP_BUILTIN_FILES, "Files", 0, false, nullptr, "0:/os/icons/system-file-manager.svg" },
|
||||
{ DESKTOP_BUILTIN_DESKTOP_SETTINGS, "Desktop Settings", -1, false, "desktop-settings",
|
||||
"0:/icons/preferences-desktop-apps.svg" },
|
||||
"0:/os/icons/preferences-desktop-apps.svg" },
|
||||
{ DESKTOP_BUILTIN_SYSTEM_CONFIGURATION, "Settings", -1, true, nullptr,
|
||||
"0:/icons/preferences-system.svg" },
|
||||
"0:/os/icons/preferences-system.svg" },
|
||||
{ DESKTOP_BUILTIN_USER_MANAGEMENT, "User Management", -1, false, "user-management",
|
||||
"0:/icons/user-home.svg" },
|
||||
"0:/os/icons/user-home.svg" },
|
||||
{ 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 },
|
||||
|
||||
@@ -269,33 +269,33 @@ void gui::desktop_init(DesktopState* ds) {
|
||||
|
||||
// Load SVG icons — scalable (colorful) for app menu, symbolic for toolbar/panel
|
||||
Color defColor = colors::ICON_COLOR;
|
||||
ds->icon_terminal = svg_load("0:/icons/utilities-terminal.svg", 20, 20, defColor);
|
||||
ds->icon_filemanager = svg_load("0:/icons/system-file-manager.svg", 20, 20, defColor);
|
||||
ds->icon_appmenu = svg_load("0:/icons/view-app-grid-symbolic.svg", 20, 20, colors::PANEL_TEXT);
|
||||
ds->icon_folder = svg_load("0:/icons/folder.svg", 16, 16, defColor);
|
||||
ds->icon_file = svg_load("0:/icons/text-x-generic.svg", 16, 16, defColor);
|
||||
ds->icon_network = svg_load("0:/icons/network-wired-symbolic.svg", 16, 16, colors::PANEL_TEXT);
|
||||
ds->icon_wifi = svg_load("0:/icons/network-wireless-symbolic.svg", 16, 16, colors::PANEL_TEXT);
|
||||
ds->icon_go_up = svg_load("0:/icons/go-up-symbolic.svg", 16, 16, defColor);
|
||||
ds->icon_go_back = svg_load("0:/icons/go-previous-symbolic.svg", 16, 16, defColor);
|
||||
ds->icon_go_forward = svg_load("0:/icons/go-next-symbolic.svg", 16, 16, defColor);
|
||||
ds->icon_home = svg_load("0:/icons/user-home.svg", 16, 16, defColor);
|
||||
ds->icon_refresh = svg_load("0:/icons/view-refresh-symbolic.svg", 16, 16, defColor);
|
||||
ds->icon_exec = svg_load("0:/icons/utilities-terminal.svg", 16, 16, defColor);
|
||||
ds->icon_terminal = svg_load("0:/os/icons/utilities-terminal.svg", 20, 20, defColor);
|
||||
ds->icon_filemanager = svg_load("0:/os/icons/system-file-manager.svg", 20, 20, defColor);
|
||||
ds->icon_appmenu = svg_load("0:/os/icons/view-app-grid-symbolic.svg", 20, 20, colors::PANEL_TEXT);
|
||||
ds->icon_folder = svg_load("0:/os/icons/folder.svg", 16, 16, defColor);
|
||||
ds->icon_file = svg_load("0:/os/icons/text-x-generic.svg", 16, 16, defColor);
|
||||
ds->icon_network = svg_load("0:/os/icons/network-wired-symbolic.svg", 16, 16, colors::PANEL_TEXT);
|
||||
ds->icon_wifi = svg_load("0:/os/icons/network-wireless-symbolic.svg", 16, 16, colors::PANEL_TEXT);
|
||||
ds->icon_go_up = svg_load("0:/os/icons/go-up-symbolic.svg", 16, 16, defColor);
|
||||
ds->icon_go_back = svg_load("0:/os/icons/go-previous-symbolic.svg", 16, 16, defColor);
|
||||
ds->icon_go_forward = svg_load("0:/os/icons/go-next-symbolic.svg", 16, 16, defColor);
|
||||
ds->icon_home = svg_load("0:/os/icons/user-home.svg", 16, 16, defColor);
|
||||
ds->icon_refresh = svg_load("0:/os/icons/view-refresh-symbolic.svg", 16, 16, defColor);
|
||||
ds->icon_exec = svg_load("0:/os/icons/utilities-terminal.svg", 16, 16, defColor);
|
||||
|
||||
ds->icon_folder_lg = svg_load("0:/icons/folder.svg", 48, 48, defColor);
|
||||
ds->icon_file_lg = svg_load("0:/icons/text-x-generic.svg", 48, 48, defColor);
|
||||
ds->icon_exec_lg = svg_load("0:/icons/utilities-terminal.svg", 48, 48, defColor);
|
||||
ds->icon_folder_lg = svg_load("0:/os/icons/folder.svg", 48, 48, defColor);
|
||||
ds->icon_file_lg = svg_load("0:/os/icons/text-x-generic.svg", 48, 48, defColor);
|
||||
ds->icon_exec_lg = svg_load("0:/os/icons/utilities-terminal.svg", 48, 48, defColor);
|
||||
// drive icons loaded lazily by file manager to reduce startup heap pressure
|
||||
|
||||
ds->icon_settings = svg_load("0:/icons/preferences-desktop-apps.svg", 20, 20, defColor);
|
||||
ds->icon_reboot = svg_load("0:/icons/system-reboot.svg", 20, 20, defColor);
|
||||
ds->icon_shutdown = svg_load("0:/icons/system-shutdown.svg", 20, 20, defColor);
|
||||
ds->icon_sleep = svg_load("0:/icons/sleep.svg", 20, 20, defColor);
|
||||
ds->icon_logout = svg_load("0:/icons/gnome-logout.svg", 20, 20, defColor);
|
||||
ds->icon_settings = svg_load("0:/os/icons/preferences-desktop-apps.svg", 20, 20, defColor);
|
||||
ds->icon_reboot = svg_load("0:/os/icons/system-reboot.svg", 20, 20, defColor);
|
||||
ds->icon_shutdown = svg_load("0:/os/icons/system-shutdown.svg", 20, 20, defColor);
|
||||
ds->icon_sleep = svg_load("0:/os/icons/sleep.svg", 20, 20, defColor);
|
||||
ds->icon_logout = svg_load("0:/os/icons/gnome-logout.svg", 20, 20, defColor);
|
||||
|
||||
ds->icon_volume = svg_load("0:/icons/audio-volume-high-symbolic.svg", 16, 16, colors::PANEL_TEXT);
|
||||
ds->icon_lock = svg_load("0:/icons/lock.svg", 20, 20, defColor);
|
||||
ds->icon_volume = svg_load("0:/os/icons/audio-volume-high-symbolic.svg", 16, 16, colors::PANEL_TEXT);
|
||||
ds->icon_lock = svg_load("0:/os/icons/lock.svg", 20, 20, defColor);
|
||||
|
||||
// Scan 0:/apps/ for external app manifests and build the menu
|
||||
desktop_scan_apps(ds);
|
||||
|
||||
@@ -381,21 +381,21 @@ int special_folder_index(const char* name) {
|
||||
|
||||
void load_file_icons(FileDialogState* st) {
|
||||
Color def = colors::ICON_COLOR;
|
||||
st->icons.icon_go_back = svg_load("0:/icons/go-previous-symbolic.svg", 16, 16, def);
|
||||
st->icons.icon_go_forward = svg_load("0:/icons/go-next-symbolic.svg", 16, 16, def);
|
||||
st->icons.icon_go_up = svg_load("0:/icons/go-up-symbolic.svg", 16, 16, def);
|
||||
st->icons.icon_home = svg_load("0:/icons/user-home.svg", 16, 16, def);
|
||||
st->icons.icon_folder = svg_load("0:/icons/folder.svg", 16, 16, def);
|
||||
st->icons.icon_folder_lg = svg_load("0:/icons/folder.svg", 48, 48, def);
|
||||
st->icons.icon_file = svg_load("0:/icons/text-x-generic.svg", 16, 16, def);
|
||||
st->icons.icon_file_lg = svg_load("0:/icons/text-x-generic.svg", 48, 48, def);
|
||||
st->icons.icon_drive = svg_load("0:/icons/drive-harddisk.svg", 16, 16, def);
|
||||
st->icons.icon_drive_lg = svg_load("0:/icons/drive-harddisk.svg", 48, 48, def);
|
||||
st->icons.icon_home_folder = svg_load("0:/icons/folder-blue-home.svg", 16, 16, def);
|
||||
st->icons.icon_home_folder_lg = svg_load("0:/icons/folder-blue-home.svg", 48, 48, def);
|
||||
st->icons.icon_go_back = svg_load("0:/os/icons/go-previous-symbolic.svg", 16, 16, def);
|
||||
st->icons.icon_go_forward = svg_load("0:/os/icons/go-next-symbolic.svg", 16, 16, def);
|
||||
st->icons.icon_go_up = svg_load("0:/os/icons/go-up-symbolic.svg", 16, 16, def);
|
||||
st->icons.icon_home = svg_load("0:/os/icons/user-home.svg", 16, 16, def);
|
||||
st->icons.icon_folder = svg_load("0:/os/icons/folder.svg", 16, 16, def);
|
||||
st->icons.icon_folder_lg = svg_load("0:/os/icons/folder.svg", 48, 48, def);
|
||||
st->icons.icon_file = svg_load("0:/os/icons/text-x-generic.svg", 16, 16, def);
|
||||
st->icons.icon_file_lg = svg_load("0:/os/icons/text-x-generic.svg", 48, 48, def);
|
||||
st->icons.icon_drive = svg_load("0:/os/icons/drive-harddisk.svg", 16, 16, def);
|
||||
st->icons.icon_drive_lg = svg_load("0:/os/icons/drive-harddisk.svg", 48, 48, def);
|
||||
st->icons.icon_home_folder = svg_load("0:/os/icons/folder-blue-home.svg", 16, 16, def);
|
||||
st->icons.icon_home_folder_lg = svg_load("0:/os/icons/folder-blue-home.svg", 48, 48, def);
|
||||
for (int i = 0; i < SPECIAL_FOLDER_COUNT; i++) {
|
||||
char path[128];
|
||||
snprintf(path, sizeof(path), "0:/icons/%s", SPECIAL_FOLDER_ICONS[i]);
|
||||
snprintf(path, sizeof(path), "0:/os/icons/%s", SPECIAL_FOLDER_ICONS[i]);
|
||||
st->icons.icon_special[i] = svg_load(path, 16, 16, def);
|
||||
st->icons.icon_special_lg[i] = svg_load(path, 48, 48, def);
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ void init_print_dialog(const gui::dialogs::Request& req) {
|
||||
PrintDialogState* st = &g_print;
|
||||
montauk::memset(st, 0, sizeof(*st));
|
||||
Color def = colors::ICON_COLOR;
|
||||
st->printer_icon = svg_load("0:/icons/printer-symbolic.svg", 24, 24, def);
|
||||
st->printer_icon = svg_load("0:/os/icons/printer-symbolic.svg", 24, 24, def);
|
||||
safe_copy(st->source_path, sizeof(st->source_path), req.source_path);
|
||||
safe_copy(st->job_name, sizeof(st->job_name), req.job_name);
|
||||
st->submit_mode = req.mode == gui::dialogs::PRINT_DIALOG_SUBMIT;
|
||||
|
||||
@@ -327,7 +327,7 @@ extern "C" void _start() {
|
||||
if (!f->init(path)) { montauk::mfree(f); return nullptr; }
|
||||
return f;
|
||||
};
|
||||
g_ui_font = load_font("0:/fonts/Roboto-Medium.ttf");
|
||||
g_ui_font = load_font("0:/os/fonts/Roboto-Medium.ttf");
|
||||
|
||||
// Window title from filename
|
||||
char title[64] = "Font Preview";
|
||||
|
||||
@@ -539,7 +539,7 @@ extern "C" void _start() {
|
||||
TrueTypeFont* f = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont));
|
||||
if (f) {
|
||||
montauk::memset(f, 0, sizeof(TrueTypeFont));
|
||||
if (!f->init("0:/fonts/Roboto-Medium.ttf")) { montauk::mfree(f); f = nullptr; }
|
||||
if (!f->init("0:/os/fonts/Roboto-Medium.ttf")) { montauk::mfree(f); f = nullptr; }
|
||||
}
|
||||
g_font = f;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ extern "C" void _start() {
|
||||
char path[128];
|
||||
|
||||
if (section > 0) {
|
||||
const char* prefix = "0:/man/";
|
||||
const char* prefix = "0:/os/man/";
|
||||
int p = 0;
|
||||
while (prefix[p]) { path[p] = prefix[p]; p++; }
|
||||
int t = 0;
|
||||
@@ -159,7 +159,7 @@ extern "C" void _start() {
|
||||
if (handle >= 0) foundSection = section;
|
||||
} else {
|
||||
for (int s = 1; s <= 9; s++) {
|
||||
const char* prefix = "0:/man/";
|
||||
const char* prefix = "0:/os/man/";
|
||||
int p = 0;
|
||||
while (prefix[p]) { path[p] = prefix[p]; p++; }
|
||||
int t = 0;
|
||||
|
||||
@@ -286,7 +286,7 @@ extern "C" void _start() {
|
||||
g_font = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont));
|
||||
if (g_font) {
|
||||
montauk::memset(g_font, 0, sizeof(TrueTypeFont));
|
||||
if (!g_font->init("0:/fonts/Roboto-Medium.ttf")) {
|
||||
if (!g_font->init("0:/os/fonts/Roboto-Medium.ttf")) {
|
||||
montauk::mfree(g_font);
|
||||
g_font = nullptr;
|
||||
}
|
||||
|
||||
+17
-17
@@ -1562,7 +1562,7 @@ extern "C" void _start() {
|
||||
TrueTypeFont* f = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont));
|
||||
if (f) {
|
||||
montauk::memset(f, 0, sizeof(TrueTypeFont));
|
||||
if (!f->init("0:/fonts/Roboto-Medium.ttf")) { montauk::mfree(f); f = nullptr; }
|
||||
if (!f->init("0:/os/fonts/Roboto-Medium.ttf")) { montauk::mfree(f); f = nullptr; }
|
||||
}
|
||||
g.font = f;
|
||||
}
|
||||
@@ -1570,22 +1570,22 @@ extern "C" void _start() {
|
||||
// Load transport icons (dark + white variants for colored backgrounds)
|
||||
{
|
||||
Color ic_color = TEXT_COLOR;
|
||||
g.ico_rewind = svg_load("0:/icons/media-rewind.svg", 16, 16, ic_color);
|
||||
g.ico_play = svg_load("0:/icons/media-play.svg", 16, 16, ic_color);
|
||||
g.ico_pause = svg_load("0:/icons/media-pause.svg", 16, 16, ic_color);
|
||||
g.ico_stop = svg_load("0:/icons/media-stop.svg", 16, 16, ic_color);
|
||||
g.ico_forward = svg_load("0:/icons/media-forward.svg", 16, 16, ic_color);
|
||||
g.ico_play_w = svg_load("0:/icons/media-play.svg", 16, 16, WHITE);
|
||||
g.ico_pause_w = svg_load("0:/icons/media-pause.svg", 16, 16, WHITE);
|
||||
g.ico_stop_w = svg_load("0:/icons/media-stop.svg", 16, 16, WHITE);
|
||||
g.ico_visualizer = svg_load("0:/icons/view-media-equalizer.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, ic_color);
|
||||
g.ico_visualizer_w = svg_load("0:/icons/view-media-equalizer.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, WHITE);
|
||||
g.ico_shuffle = svg_load("0:/icons/media-playlist-shuffle.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, ic_color);
|
||||
g.ico_shuffle_w = svg_load("0:/icons/media-playlist-shuffle.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, WHITE);
|
||||
g.ico_repeat_all = svg_load("0:/icons/media-playlist-repeat.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, ic_color);
|
||||
g.ico_repeat_all_w = svg_load("0:/icons/media-playlist-repeat.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, WHITE);
|
||||
g.ico_repeat_one = svg_load("0:/icons/media-playlist-repeat-song.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, ic_color);
|
||||
g.ico_repeat_one_w = svg_load("0:/icons/media-playlist-repeat-song.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, WHITE);
|
||||
g.ico_rewind = svg_load("0:/os/icons/media-rewind.svg", 16, 16, ic_color);
|
||||
g.ico_play = svg_load("0:/os/icons/media-play.svg", 16, 16, ic_color);
|
||||
g.ico_pause = svg_load("0:/os/icons/media-pause.svg", 16, 16, ic_color);
|
||||
g.ico_stop = svg_load("0:/os/icons/media-stop.svg", 16, 16, ic_color);
|
||||
g.ico_forward = svg_load("0:/os/icons/media-forward.svg", 16, 16, ic_color);
|
||||
g.ico_play_w = svg_load("0:/os/icons/media-play.svg", 16, 16, WHITE);
|
||||
g.ico_pause_w = svg_load("0:/os/icons/media-pause.svg", 16, 16, WHITE);
|
||||
g.ico_stop_w = svg_load("0:/os/icons/media-stop.svg", 16, 16, WHITE);
|
||||
g.ico_visualizer = svg_load("0:/os/icons/view-media-equalizer.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, ic_color);
|
||||
g.ico_visualizer_w = svg_load("0:/os/icons/view-media-equalizer.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, WHITE);
|
||||
g.ico_shuffle = svg_load("0:/os/icons/media-playlist-shuffle.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, ic_color);
|
||||
g.ico_shuffle_w = svg_load("0:/os/icons/media-playlist-shuffle.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, WHITE);
|
||||
g.ico_repeat_all = svg_load("0:/os/icons/media-playlist-repeat.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, ic_color);
|
||||
g.ico_repeat_all_w = svg_load("0:/os/icons/media-playlist-repeat.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, WHITE);
|
||||
g.ico_repeat_one = svg_load("0:/os/icons/media-playlist-repeat-song.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, ic_color);
|
||||
g.ico_repeat_one_w = svg_load("0:/os/icons/media-playlist-repeat-song.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, WHITE);
|
||||
}
|
||||
|
||||
// Scan for audio files
|
||||
|
||||
@@ -256,7 +256,7 @@ extern "C" void _start() {
|
||||
g_font = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont));
|
||||
if (g_font) {
|
||||
montauk::memset(g_font, 0, sizeof(TrueTypeFont));
|
||||
if (!g_font->init("0:/fonts/Roboto-Medium.ttf")) {
|
||||
if (!g_font->init("0:/os/fonts/Roboto-Medium.ttf")) {
|
||||
montauk::mfree(g_font);
|
||||
g_font = nullptr;
|
||||
}
|
||||
|
||||
@@ -162,14 +162,14 @@ extern "C" void _start() {
|
||||
if (!f->init(path)) { montauk::mfree(f); return nullptr; }
|
||||
return f;
|
||||
};
|
||||
g_font = load_font("0:/fonts/Roboto-Medium.ttf");
|
||||
g_font_bold = load_font("0:/fonts/Roboto-Bold.ttf");
|
||||
g_font_mono = load_font("0:/fonts/JetBrainsMono-Regular.ttf");
|
||||
g_font = load_font("0:/os/fonts/Roboto-Medium.ttf");
|
||||
g_font_bold = load_font("0:/os/fonts/Roboto-Bold.ttf");
|
||||
g_font_mono = load_font("0:/os/fonts/JetBrainsMono-Regular.ttf");
|
||||
|
||||
// Load toolbar icons
|
||||
Color icon_color = HEADER_TEXT;
|
||||
g_icon_folder = svg_load("0:/icons/folder.svg", 16, 16, icon_color);
|
||||
g_icon_print = svg_load("0:/icons/printer-symbolic.svg", 16, 16, icon_color);
|
||||
g_icon_folder = svg_load("0:/os/icons/folder.svg", 16, 16, icon_color);
|
||||
g_icon_print = svg_load("0:/os/icons/printer-symbolic.svg", 16, 16, icon_color);
|
||||
|
||||
// Check for file argument
|
||||
char args[512] = {};
|
||||
|
||||
@@ -26,8 +26,8 @@ namespace {
|
||||
|
||||
static constexpr int PAGE_W = 1240;
|
||||
static constexpr int PAGE_H = 1754;
|
||||
static constexpr const char* ROBOTO_MEDIUM_PATH = "0:/fonts/Roboto-Medium.ttf";
|
||||
static constexpr const char* ROBOTO_BOLD_PATH = "0:/fonts/Roboto-Bold.ttf";
|
||||
static constexpr const char* ROBOTO_MEDIUM_PATH = "0:/os/fonts/Roboto-Medium.ttf";
|
||||
static constexpr const char* ROBOTO_BOLD_PATH = "0:/os/fonts/Roboto-Bold.ttf";
|
||||
|
||||
struct JpegBuffer {
|
||||
uint8_t* data;
|
||||
|
||||
@@ -173,7 +173,7 @@ static void load_accent() {
|
||||
|
||||
static void load_icons() {
|
||||
if (g_printer_icon.pixels) svg_free(g_printer_icon);
|
||||
g_printer_icon = svg_load("0:/icons/printer-symbolic.svg",
|
||||
g_printer_icon = svg_load("0:/os/icons/printer-symbolic.svg",
|
||||
PRINTER_ICON_SIZE, PRINTER_ICON_SIZE,
|
||||
Color::from_rgb(0x6A, 0x72, 0x7D));
|
||||
}
|
||||
|
||||
@@ -18,15 +18,15 @@ SvgIcon g_icon_paste = {};
|
||||
void ss_load_icons() {
|
||||
Color def_color = HEADER_TEXT;
|
||||
if (!g_icon_folder.pixels)
|
||||
g_icon_folder = svg_load("0:/icons/folder.svg", 16, 16, def_color);
|
||||
g_icon_folder = svg_load("0:/os/icons/folder.svg", 16, 16, def_color);
|
||||
if (!g_icon_save.pixels)
|
||||
g_icon_save = svg_load("0:/icons/document-save-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_save = svg_load("0:/os/icons/document-save-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_cut.pixels)
|
||||
g_icon_cut = svg_load("0:/icons/edit-cut.svg", 16, 16, def_color);
|
||||
g_icon_cut = svg_load("0:/os/icons/edit-cut.svg", 16, 16, def_color);
|
||||
if (!g_icon_copy.pixels)
|
||||
g_icon_copy = svg_load("0:/icons/edit-copy.svg", 16, 16, def_color);
|
||||
g_icon_copy = svg_load("0:/os/icons/edit-copy.svg", 16, 16, def_color);
|
||||
if (!g_icon_paste.pixels)
|
||||
g_icon_paste = svg_load("0:/icons/edit-paste.svg", 16, 16, def_color);
|
||||
g_icon_paste = svg_load("0:/os/icons/edit-paste.svg", 16, 16, def_color);
|
||||
}
|
||||
|
||||
void ss_unload_icons() {
|
||||
@@ -339,8 +339,8 @@ extern "C" void _start() {
|
||||
if (!f->init(path)) { montauk::mfree(f); return nullptr; }
|
||||
return f;
|
||||
};
|
||||
g_font = load_font("0:/fonts/Roboto-Medium.ttf");
|
||||
g_font_bold = load_font("0:/fonts/Roboto-Bold.ttf");
|
||||
g_font = load_font("0:/os/fonts/Roboto-Medium.ttf");
|
||||
g_font_bold = load_font("0:/os/fonts/Roboto-Bold.ttf");
|
||||
|
||||
ss_load_icons();
|
||||
|
||||
|
||||
@@ -1438,13 +1438,13 @@ extern "C" void _start() {
|
||||
if (!f->init(path)) { montauk::mfree(f); return nullptr; }
|
||||
return f;
|
||||
};
|
||||
g_font = load_font("0:/fonts/Roboto-Medium.ttf");
|
||||
g_font_mono = load_font("0:/fonts/JetBrainsMono-Regular.ttf");
|
||||
g_font = load_font("0:/os/fonts/Roboto-Medium.ttf");
|
||||
g_font_mono = load_font("0:/os/fonts/JetBrainsMono-Regular.ttf");
|
||||
|
||||
// Load toolbar icons
|
||||
Color icon_color = Color::from_rgb(0x44, 0x44, 0x44);
|
||||
g_icon_folder = svg_load("0:/icons/folder.svg", 16, 16, icon_color);
|
||||
g_icon_save = svg_load("0:/icons/document-save-symbolic.svg", 16, 16, icon_color);
|
||||
g_icon_folder = svg_load("0:/os/icons/folder.svg", 16, 16, icon_color);
|
||||
g_icon_save = svg_load("0:/os/icons/document-save-symbolic.svg", 16, 16, icon_color);
|
||||
|
||||
// Check for file argument
|
||||
char args[512] = {};
|
||||
|
||||
+11
-11
@@ -1311,7 +1311,7 @@ extern "C" void _start() {
|
||||
TrueTypeFont* font = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont));
|
||||
if (font) {
|
||||
montauk::memset(font, 0, sizeof(TrueTypeFont));
|
||||
if (!font->init("0:/fonts/Roboto-Medium.ttf")) {
|
||||
if (!font->init("0:/os/fonts/Roboto-Medium.ttf")) {
|
||||
montauk::mfree(font);
|
||||
font = nullptr;
|
||||
}
|
||||
@@ -1319,16 +1319,16 @@ extern "C" void _start() {
|
||||
g.font = font;
|
||||
|
||||
Color icon_fg = TEXT;
|
||||
g.ico_rewind = svg_load("0:/icons/media-rewind.svg", 16, 16, icon_fg);
|
||||
g.ico_play = svg_load("0:/icons/media-play.svg", 16, 16, icon_fg);
|
||||
g.ico_pause = svg_load("0:/icons/media-pause.svg", 16, 16, icon_fg);
|
||||
g.ico_stop = svg_load("0:/icons/media-stop.svg", 16, 16, icon_fg);
|
||||
g.ico_forward = svg_load("0:/icons/media-forward.svg", 16, 16, icon_fg);
|
||||
g.ico_play_w = svg_load("0:/icons/media-play.svg", 16, 16, WHITE);
|
||||
g.ico_pause_w = svg_load("0:/icons/media-pause.svg", 16, 16, WHITE);
|
||||
g.ico_stop_w = svg_load("0:/icons/media-stop.svg", 16, 16, WHITE);
|
||||
g.ico_repeat = svg_load("0:/icons/media-playlist-repeat.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, icon_fg);
|
||||
g.ico_repeat_w = svg_load("0:/icons/media-playlist-repeat.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, WHITE);
|
||||
g.ico_rewind = svg_load("0:/os/icons/media-rewind.svg", 16, 16, icon_fg);
|
||||
g.ico_play = svg_load("0:/os/icons/media-play.svg", 16, 16, icon_fg);
|
||||
g.ico_pause = svg_load("0:/os/icons/media-pause.svg", 16, 16, icon_fg);
|
||||
g.ico_stop = svg_load("0:/os/icons/media-stop.svg", 16, 16, icon_fg);
|
||||
g.ico_forward = svg_load("0:/os/icons/media-forward.svg", 16, 16, icon_fg);
|
||||
g.ico_play_w = svg_load("0:/os/icons/media-play.svg", 16, 16, WHITE);
|
||||
g.ico_pause_w = svg_load("0:/os/icons/media-pause.svg", 16, 16, WHITE);
|
||||
g.ico_stop_w = svg_load("0:/os/icons/media-stop.svg", 16, 16, WHITE);
|
||||
g.ico_repeat = svg_load("0:/os/icons/media-playlist-repeat.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, icon_fg);
|
||||
g.ico_repeat_w = svg_load("0:/os/icons/media-playlist-repeat.svg", TOOL_ICON_SIZE, TOOL_ICON_SIZE, WHITE);
|
||||
|
||||
scan_directory();
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ static void load_weather_icon(const char* icon_name) {
|
||||
if (g_icon.pixels) svg_free(g_icon);
|
||||
|
||||
static char path[80];
|
||||
snprintf(path, sizeof(path), "0:/icons/%s", icon_name);
|
||||
snprintf(path, sizeof(path), "0:/os/icons/%s", icon_name);
|
||||
// ColorScheme-Text paths (main shape) use this fill_color; accent classes
|
||||
// (ColorScheme-Highlight=blue, ColorScheme-NeutralText=yellow, etc.) use their
|
||||
// CSS-defined colors as parsed from the SVG's <style> block by svg_load.
|
||||
@@ -379,8 +379,8 @@ extern "C" void _start() {
|
||||
if (!f->init(path)) { montauk::mfree(f); return nullptr; }
|
||||
return f;
|
||||
};
|
||||
g_font = load_font("0:/fonts/Roboto-Medium.ttf");
|
||||
g_font_bold = load_font("0:/fonts/Roboto-Bold.ttf");
|
||||
g_font = load_font("0:/os/fonts/Roboto-Medium.ttf");
|
||||
g_font_bold = load_font("0:/os/fonts/Roboto-Bold.ttf");
|
||||
if (!g_font) montauk::exit(1);
|
||||
|
||||
apply_scale(montauk::win_getscale());
|
||||
|
||||
@@ -20,16 +20,16 @@ static bool init_buffers_and_fonts() {
|
||||
g_extract_buf = (char*)malloc(RESP_MAX + 1);
|
||||
if (!g_lines || !g_resp_buf || !g_extract_buf) return false;
|
||||
|
||||
g_body_fonts[(int)ReaderFontFace::SANS] = load_font("0:/fonts/Roboto-Regular.ttf");
|
||||
g_body_fonts[(int)ReaderFontFace::SANS] = load_font("0:/os/fonts/Roboto-Regular.ttf");
|
||||
if (!g_body_fonts[(int)ReaderFontFace::SANS])
|
||||
g_body_fonts[(int)ReaderFontFace::SANS] = load_font("0:/fonts/Roboto-Medium.ttf");
|
||||
g_heading_fonts[(int)ReaderFontFace::SANS] = load_font("0:/fonts/Roboto-Bold.ttf");
|
||||
g_body_fonts[(int)ReaderFontFace::SANS] = load_font("0:/os/fonts/Roboto-Medium.ttf");
|
||||
g_heading_fonts[(int)ReaderFontFace::SANS] = load_font("0:/os/fonts/Roboto-Bold.ttf");
|
||||
|
||||
g_body_fonts[(int)ReaderFontFace::SERIF] = load_font("0:/fonts/NotoSerif-Regular.ttf");
|
||||
g_heading_fonts[(int)ReaderFontFace::SERIF] = load_font("0:/fonts/NotoSerif-SemiBold.ttf");
|
||||
g_body_fonts[(int)ReaderFontFace::SERIF] = load_font("0:/os/fonts/NotoSerif-Regular.ttf");
|
||||
g_heading_fonts[(int)ReaderFontFace::SERIF] = load_font("0:/os/fonts/NotoSerif-SemiBold.ttf");
|
||||
|
||||
g_body_fonts[(int)ReaderFontFace::BOOK] = load_font("0:/fonts/C059-Roman.ttf");
|
||||
g_heading_fonts[(int)ReaderFontFace::BOOK] = load_font("0:/fonts/C059-Bold.ttf");
|
||||
g_body_fonts[(int)ReaderFontFace::BOOK] = load_font("0:/os/fonts/C059-Roman.ttf");
|
||||
g_heading_fonts[(int)ReaderFontFace::BOOK] = load_font("0:/os/fonts/C059-Bold.ttf");
|
||||
|
||||
if (!g_body_fonts[(int)ReaderFontFace::SANS]) return false;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ static constexpr int RESP_MAX = 131072;
|
||||
static constexpr int MAX_LINES = 2000;
|
||||
|
||||
inline const char WIKI_HOST[] = "en.wikipedia.org";
|
||||
inline const char DISPLAY_ICON_PATH[] = "0:/icons/cog.svg";
|
||||
inline const char DISPLAY_ICON_PATH[] = "0:/os/icons/cog.svg";
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
|
||||
@@ -446,7 +446,7 @@ static int wp_split_at(WordProcessorState* wp, int abs_pos) {
|
||||
// ============================================================================
|
||||
// Font discovery
|
||||
//
|
||||
// Fonts live in 0:/fonts as "<Family>-<Style>.ttf" (e.g. Roboto-BoldItalic.ttf).
|
||||
// Fonts live in 0:/os/fonts as "<Family>-<Style>.ttf" (e.g. Roboto-BoldItalic.ttf).
|
||||
// We scan that directory at startup, group files by family, and slot each file
|
||||
// into one of four style variants. Families are sorted deterministically so a
|
||||
// given set of installed fonts yields stable font ids across runs. Documents
|
||||
@@ -560,7 +560,7 @@ static void wp_register_font_file(const char* filename) {
|
||||
WpFontFamily* fam = &g_wp_fonts.families[fi];
|
||||
if (fam->paths[variant][0]) return; // first file wins for this slot
|
||||
|
||||
montauk::strncpy(fam->paths[variant], "0:/fonts/", WP_FONT_PATH_MAX);
|
||||
montauk::strncpy(fam->paths[variant], "0:/os/fonts/", WP_FONT_PATH_MAX);
|
||||
int plen = montauk::slen(fam->paths[variant]);
|
||||
montauk::strncpy(fam->paths[variant] + plen, filename, WP_FONT_PATH_MAX - plen);
|
||||
montauk::strncpy(fam->basenames[variant], stem, WP_FONT_BASENAME_MAX);
|
||||
@@ -601,7 +601,7 @@ void wp_load_fonts() {
|
||||
g_wp_fonts.count = 0;
|
||||
|
||||
const char* names[256];
|
||||
int count = montauk::readdir("0:/fonts", names, 256);
|
||||
int count = montauk::readdir("0:/os/fonts", names, 256);
|
||||
|
||||
// Sort entries so per-slot conflicts resolve deterministically. With
|
||||
// first-wins below, "Roboto-Medium" sorts before "Roboto-Regular" and
|
||||
@@ -653,7 +653,7 @@ void wp_load_fonts() {
|
||||
}
|
||||
}
|
||||
|
||||
// Defensive fallback: if 0:/fonts yielded nothing usable, expose the
|
||||
// Defensive fallback: if 0:/os/fonts yielded nothing usable, expose the
|
||||
// shared system font so the editor still renders.
|
||||
if (g_wp_fonts.count == 0 && fonts::system_font) {
|
||||
WpFontFamily* fam = &g_wp_fonts.families[0];
|
||||
|
||||
@@ -31,36 +31,36 @@ TrueTypeFont* g_ui_bold = nullptr;
|
||||
void wp_load_icons() {
|
||||
Color def_color = colors::ICON_COLOR;
|
||||
if (!g_icon_folder.pixels)
|
||||
g_icon_folder = svg_load("0:/icons/folder.svg", 16, 16, def_color);
|
||||
g_icon_folder = svg_load("0:/os/icons/folder.svg", 16, 16, def_color);
|
||||
if (!g_icon_save.pixels)
|
||||
g_icon_save = svg_load("0:/icons/document-save-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_save = svg_load("0:/os/icons/document-save-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_print.pixels)
|
||||
g_icon_print = svg_load("0:/icons/printer-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_print = svg_load("0:/os/icons/printer-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_export_pdf.pixels) {
|
||||
g_icon_export_pdf = svg_load("0:/icons/document-export-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_export_pdf = svg_load("0:/os/icons/document-export-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_export_pdf.pixels)
|
||||
g_icon_export_pdf = svg_load("0:/icons/text-x-generic-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_export_pdf = svg_load("0:/os/icons/text-x-generic-symbolic.svg", 16, 16, def_color);
|
||||
}
|
||||
if (!g_icon_undo.pixels)
|
||||
g_icon_undo = svg_load("0:/icons/edit-undo-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_undo = svg_load("0:/os/icons/edit-undo-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_redo.pixels)
|
||||
g_icon_redo = svg_load("0:/icons/edit-redo-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_redo = svg_load("0:/os/icons/edit-redo-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_align_left.pixels)
|
||||
g_icon_align_left = svg_load("0:/icons/format-justify-left-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_align_left = svg_load("0:/os/icons/format-justify-left-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_align_center.pixels)
|
||||
g_icon_align_center = svg_load("0:/icons/format-justify-center-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_align_center = svg_load("0:/os/icons/format-justify-center-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_align_right.pixels)
|
||||
g_icon_align_right = svg_load("0:/icons/format-justify-right-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_align_right = svg_load("0:/os/icons/format-justify-right-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_list_bullet.pixels)
|
||||
g_icon_list_bullet = svg_load("0:/icons/view-list-bullet-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_list_bullet = svg_load("0:/os/icons/view-list-bullet-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_list_number.pixels)
|
||||
g_icon_list_number = svg_load("0:/icons/view-list-ordered-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_list_number = svg_load("0:/os/icons/view-list-ordered-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_indent_less.pixels)
|
||||
g_icon_indent_less = svg_load("0:/icons/format-indent-less-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_indent_less = svg_load("0:/os/icons/format-indent-less-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_indent_more.pixels)
|
||||
g_icon_indent_more = svg_load("0:/icons/format-indent-more-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_indent_more = svg_load("0:/os/icons/format-indent-more-symbolic.svg", 16, 16, def_color);
|
||||
if (!g_icon_page_view.pixels)
|
||||
g_icon_page_view = svg_load("0:/icons/view-paged-symbolic.svg", 16, 16, def_color);
|
||||
g_icon_page_view = svg_load("0:/os/icons/view-paged-symbolic.svg", 16, 16, def_color);
|
||||
}
|
||||
|
||||
void wp_cleanup_state() {
|
||||
|
||||
@@ -82,12 +82,12 @@ static constexpr int WP_DIVIDER_FLYOUT_W = 216;
|
||||
static constexpr int WP_SPECIAL_CHAR_ROW_H = 24;
|
||||
static constexpr int WP_SPECIAL_CHAR_FLYOUT_W = 144;
|
||||
|
||||
// Fonts are discovered at runtime by scanning 0:/fonts (see wp_load_fonts).
|
||||
// Fonts are discovered at runtime by scanning 0:/os/fonts (see wp_load_fonts).
|
||||
// A font id is an index into the discovered family table (g_wp_fonts). The
|
||||
// table is bounded by WP_MAX_FONTS; ids beyond the table fall back to default.
|
||||
static constexpr int WP_MAX_FONTS = 24; // max discovered families
|
||||
static constexpr int WP_FONT_NAME_MAX = 32; // family/display name
|
||||
static constexpr int WP_FONT_PATH_MAX = 96; // "0:/fonts/<file>.ttf"
|
||||
static constexpr int WP_FONT_PATH_MAX = 96; // "0:/os/fonts/<file>.ttf"
|
||||
static constexpr int WP_FONT_BASENAME_MAX = 48; // filename stem (PDF base font)
|
||||
|
||||
static constexpr uint8_t STYLE_BOLD = 0x01;
|
||||
|
||||
Reference in new issue
Block a user