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:
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user