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

@@ -50,10 +50,67 @@ pub fn subscribe(window: CapSlot, notify: CapSlot, badge: types::badge::Badge) -
sent.is_ok_and(|r| r.words()[0] == w::OK)
}
/// What a window hears.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Event {
Input(crate::input::InputEvent),
/// Ask for the theme again (`theme`).
ThemeChanged,
}
/// `window`'s next event, if one is waiting; ones not known here are
/// skipped.
pub fn poll(window: CapSlot) -> Option<Event> {
loop {
let words = syscall::request(window, [w::POLL, 0, 0, 0]).ok()?;
match words[0] {
0 => return None,
w::THEME_CHANGED => return Some(Event::ThemeChanged),
_ => {
if let Some(e) = crate::input::decode(words) {
return Some(Event::Input(e));
}
}
}
}
}
/// `window`'s next input event, if one is waiting.
pub fn poll_input(window: CapSlot) -> Option<crate::input::InputEvent> {
let words = syscall::request(window, [w::POLL, 0, 0, 0]).ok()?;
crate::input::decode(words)
loop {
if let Event::Input(e) = poll(window)? {
return Some(e);
}
}
}
/// The theme in use, as `theme::wire` words it, and its place in the
/// display's list (`theme_name`).
pub fn theme(window: CapSlot) -> Option<(usize, [usize; w::THEME_WORDS])> {
// A switch mid-read is seen and read again; a few in a row, given up.
for _ in 0..3 {
let mut words = [0; w::THEME_WORDS];
let mut first = None;
let mut torn = false;
for part in 0..w::THEME_WORDS / 2 {
let r = syscall::request(window, [w::THEME, part, 0, 0]).ok()?;
if r[0] != w::OK {
return None;
}
torn |= *first.get_or_insert(r[1]) != r[1];
words[part * 2..part * 2 + 2].copy_from_slice(&r[2..]);
}
if !torn {
return first.map(|head| (head >> 32, words));
}
}
None
}
/// The name of the display's `index`th theme, if it has that many.
pub fn theme_name(window: CapSlot, index: usize) -> Option<([u8; w::NAME_BYTES], usize)> {
let r = syscall::request(window, [w::THEME_NAME, index, 0, 0]).ok()?;
(r[0] == w::OK).then(|| w::unpack_name([r[1], r[2], r[3]]))
}
/// A changed area, as corners.

View File

@@ -31,3 +31,10 @@ pub fn who(session: CapSlot, name: &mut [u8; types::name::BYTES]) -> Option<(usi
pub fn sign_out(session: CapSlot) -> bool {
syscall::send_msg(session, [SIGN_OUT, 0, 0, 0], None).is_ok()
}
/// Makes the theme named `name` the signed-in user's (kept, and shown at
/// once); whether init took it.
pub fn set_theme(session: CapSlot, name: &[u8]) -> bool {
let [a, b, c] = types::name::pack(name);
syscall::request(session, [types::spawn::SET_THEME, a, b, c]).is_ok_and(|r| r[0] == types::spawn::reply::OK)
}