feat(theme): themes as files (theme crate: toml subset + ints, contrast check, wire); whimsy dark + light; display reads themes/, serves THEME/THEME_NAME, init SET_THEME -> THEME_CHANGED to all windows, bar themed; k-ui follows; settings appearance; init keeps choice in sealed users/<id>/settings/, reapplies at sign-in + display restart

This commit is contained in:
2026-10-28 13:36:57 +01:00
parent 29becf297c
commit 6827e1455d
43 changed files with 1140 additions and 179 deletions

View File

@@ -9,6 +9,12 @@ use types::bounded::CapSlot;
/// Where init put them (see init's manifest: own endpoint, then `uses`).
const EP: CapSlot = CapSlot::const_new(0);
const INPUT: CapSlot = CapSlot::const_new(1);
/// `themes/`, read-only.
const THEMES: CapSlot = CapSlot::const_new(2);
/// Where storage's buffer is mapped while the themes are read.
const STORAGE_BUF_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x0180_0000);
/// The longest theme file read.
const THEME_BYTES: usize = 4096;
libkos::entry!(main);
@@ -38,7 +44,47 @@ fn subscribe_to_input() -> Option<CapSlot> {
}
fn server<F: port_framebuffer::Framebuffer>(fb: F, input: Option<CapSlot>) -> display_server::server::DisplayServer<F, display_server::host::KernelHost> {
display_server::server::DisplayServer::new(fb, display_server::host::KernelHost::new(EP, input))
display_server::server::DisplayServer::new(fb, display_server::host::KernelHost::new(EP, input), themes())
}
/// Every theme in `themes/` that reads and passes contrast; the built-in
/// one if none does.
fn themes() -> display_server::themes::Themes {
let mut themes = display_server::themes::Themes::new();
let Ok(mut fs) = libkos::fs::Storage::attach(THEMES, STORAGE_BUF_VA) else {
libkos::println!("display: no themes folder; the built-in theme only");
return themes;
};
let mut names = [([0u8; types::storage::DIR_NAME_SIZE], 0usize); display_server::themes::MAX_THEMES];
let mut found = 0;
let _ = fs.list_dir(b"", |e| {
if !e.is_dir && e.name().ends_with(b".toml") && found < names.len() {
names[found] = (e.name, e.name_len);
found += 1;
}
});
let mut text = [0u8; THEME_BYTES];
for (name, len) in &names[..found] {
let name = &name[..*len];
let read = fs.open(name).and_then(|h| {
let n = fs.read(h, &mut text);
let _ = fs.close(h);
n
});
let shown = core::str::from_utf8(name).unwrap_or("?");
match read.ok().and_then(|n| core::str::from_utf8(&text[..n]).ok()) {
Some(t) => {
if let Err(e) = themes.add(t) {
libkos::println!("display: theme {} refused: {:?}", shown, e);
}
}
None => libkos::println!("display: cannot read theme {}", shown),
}
}
fs.detach();
// Storage has few connections to give; this one is done with.
let _ = libkos::fs::release(THEMES);
themes
}
#[inline(never)]