diff --git a/Cargo.lock b/Cargo.lock index f61ae00..67d4adb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1934,6 +1934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ "deranged", + "js-sys", "serde", "time-core", "time-macros", @@ -1956,9 +1957,9 @@ dependencies = [ [[package]] name = "time-tz" -version = "1.0.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a422f65dfdf08a81317d54fa00b45dc58cbccab69be78c1447391cc39ae8c9d4" +checksum = "733bc522e97980eb421cbf381160ff225bd14262a48a739110f6653c6258d625" dependencies = [ "cfg-if", "parse-zoneinfo", @@ -1967,6 +1968,7 @@ dependencies = [ "serde", "serde-xml-rs", "time", + "wasm-bindgen", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 11484c9..fabb02d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ embedded-hal = "0.2.5" libc = "0.2.98" rusqlite = "0.27.0" time = { version = "0.3.9", features = ["parsing"] } -time-tz = "1.0.1" +time-tz = "2" image = { version = "0.24.1", optional = true } serde_json = "1.0.79" serde_derive = "1.0.136" diff --git a/default.nix b/default.nix index 2757dde..db6df6f 100644 --- a/default.nix +++ b/default.nix @@ -16,7 +16,7 @@ rustPlatform.buildRustPackage { nativeBuildInputs = [ pkg-config ]; - cargoBuildFlags = [ "--no-default-features" ]; + cargoBuildFlags = [ "--no-default-features" "--bin" "main_loop" ]; buildInputs = [ sqlite ]; diff --git a/src/bin/main_loop.rs b/src/bin/main_loop.rs index 3c12659..0ed564a 100644 --- a/src/bin/main_loop.rs +++ b/src/bin/main_loop.rs @@ -1,3 +1,5 @@ +#![feature(array_windows)] + use std::{ cell::RefCell, thread, @@ -67,7 +69,21 @@ impl> ContextDefault { return false; } let a = active.last().unwrap(); - a.draw(disp, rng).unwrap_or(true) + if !a.expired() { + return a.draw(disp, rng).unwrap_or(true); + } + drop(active); + self.active.borrow_mut().pop(); + self.loop_iter(disp, rng) + } + + fn pop_action_and_clear(&mut self, disp: &mut D) -> Result<(), D::Error> { + let active = self.active.get_mut(); + if active.len() > 1 { + active.pop(); + disp.clear(BLACK)?; + } + Ok(()) } } @@ -78,8 +94,10 @@ impl> Context for ContextDefault { for s in &self.screensavers { if s.id() == id { self.active.borrow_mut().push(s.convert_draw()); + return; } } + println!("warning: screensaver not found"); }, } } @@ -90,7 +108,7 @@ fn pc_main() {} #[cfg(feature = "pc")] fn pc_main() { - use std::num::NonZeroU32; + use std::{env, num::NonZeroU32}; use winit::{ dpi::LogicalSize, @@ -102,6 +120,16 @@ fn pc_main() { use crate::screensaver::{Screensaver, DUOLINGO}; use raspi_oled::FrameOutput; + let args: Vec<_> = env::args().map(|x| x.to_string()).collect(); + for [key, val] in args.array_windows() { + match key.as_str() { + "--speed" => { + screensaver::SPEED.store(val.parse().unwrap(), std::sync::atomic::Ordering::Relaxed); + }, + _ => {}, + } + } + let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_inner_size(LogicalSize::new(128, 128)) @@ -116,6 +144,7 @@ fn pc_main() { let mut buffer_dirty = true; let mut ctx = ContextDefault::new(); + ctx.do_action(Action::Screensaver("plate")); let mut rng = Xoroshiro128StarStar::seed_from_u64(17381); event_loop.run(move |event, _, control_flow| { @@ -156,81 +185,9 @@ fn pc_main() { .unwrap(); // redraw - if Instant::now().duration_since(start) > Duration::from_millis(iters * 1000) { + if Instant::now().duration_since(start) > Duration::from_millis(iters * 66) { iters += 1; buffer_dirty = ctx.loop_iter(&mut disp, &mut rng); - //loop_iter(&mut disp).unwrap(); - /* - let mut time = OffsetDateTime::now_utc().to_timezone(BERLIN); - //time += Duration::new(iters * 60, 0); - disp.clear(Rgb565::new(0, 0, 0)).unwrap(); - display_clock(&mut disp, &time).unwrap(); - */ - //DUOLINGO.draw(&mut disp, &mut rng).unwrap(); - /* - let iters = iters % 300; - let (s, c) = (iters as f32 * 0.1).sin_cos(); - let (mut x, mut y) = (s * iters as f32 * 0.005, c * iters as f32 * 0.005); - x *= 64.; - y *= 64.; - x += 64.; - y += 64.; - let variation = iters as u32 / 16 + 1; - for _ in 0..16 { - let dx = (rng.next_u32() % variation) as i32 - variation as i32 / 2; - let dy = (rng.next_u32() % variation) as i32 - variation as i32 / 2; - let color = rng.next_u32(); - let p = Rectangle::new(Point::new(x as i32 + dx, y as i32 + dy), Size::new(1, 1)); - let s = PrimitiveStyleBuilder::new() - .fill_color(Rgb565::new( - color as u8 & 0b11111, - ((color >> 8) & 0b111111) as u8, - ((color >> 16) & 0b111111) as u8, - )) - .build(); - p.draw_styled(&s, &mut disp).unwrap(); - } - if iters % 300 == 0 { - disp.clear(Rgb565::new(0, 0, 0)).unwrap(); - } - */ - /* - for _ in 0..16 { - let x = (rng.next_u32() % 128) as usize; - let y = (rng.next_u32() % 128) as usize; - let dx = (rng.next_u32() % 8) as i32 - 4; - let dy = (rng.next_u32() % 8) as i32 - 4; - let red = STAR[y * 128 * 3 + x * 3]; - let green = STAR[y * 128 * 3 + x * 3 + 1]; - if red == 0xff { - let color = rng.next_u32(); - let r; - let g; - let b; - // star - r = (color as u8 & 0b11111).saturating_mul(2); - g = (((color >> 8) & 0b111111) as u8).saturating_mul(2); - b = ((color >> 16) & 0b111111) as u8 / 3; - // rpi - /* - if red > green { - r = (color as u8 & 0b11111).saturating_mul(2); - g = ((color >> 8) & 0b111111) as u8 / 3; - b = ((color >> 16) & 0b111111) as u8 / 3; - } else { - r = (color as u8 & 0b11111) / 2; - g = (((color >> 8) & 0b111111) as u8).saturating_mul(2); - b = ((color >> 16) & 0b111111) as u8 / 3; - } - */ - let p = Rectangle::new(Point::new(x as i32 + dx, y as i32 + dy), Size::new(1, 1)); - let s = PrimitiveStyleBuilder::new() - .fill_color(Rgb565::new(r as u8, g as u8, b as u8)) - .build(); - p.draw_styled(&s, &mut disp).unwrap(); - } - } - */ } let mut buffer = surface.buffer_mut().unwrap(); @@ -256,6 +213,9 @@ fn pc_main() { pub trait Draw> { fn draw(&self, disp: &mut D, rng: &mut Rng) -> Result; + fn expired(&self) -> bool { + false + } } fn rpi_main() { @@ -318,7 +278,6 @@ fn main_loop(mut disp: Oled) { }, 6 => { menu.push(2); - ctx.do_action(Action::Screensaver("rpi")); }, 5 => { menu.push(3); @@ -327,6 +286,15 @@ fn main_loop(mut disp: Oled) { println!("unknown offset: {}", e.offset); }, } + match &*menu { + [2] => { + let _ = ctx.pop_action_and_clear(&mut disp); + }, + [3] => { + ctx.do_action(Action::Screensaver("rpi")); + }, + _ => {}, + } //println!("menu: {menu:?}"); } // clean up stale menu selection @@ -338,6 +306,6 @@ fn main_loop(mut disp: Oled) { if dirty { let _ = disp.flush(); // ignore bus write errors, they are harmless } - thread::sleep(Duration::from_millis(1000)); + thread::sleep(Duration::from_millis(66)); } } diff --git a/src/bin/schedule/mod.rs b/src/bin/schedule/mod.rs index b9aa527..918e772 100644 --- a/src/bin/schedule/mod.rs +++ b/src/bin/schedule/mod.rs @@ -40,6 +40,8 @@ impl Reminder { static DUOLINGO: Reminder = Reminder::new(11, 30, Action::Screensaver("duolingo")); static DUOLINGO_NIGHT: Reminder = Reminder::new(23, 30, Action::Screensaver("duolingo")); +static FOOD: Reminder = Reminder::new(13, 15, Action::Screensaver("plate")); + pub fn reminders() -> Vec> { - vec![Box::new(DUOLINGO), Box::new(DUOLINGO_NIGHT)] + vec![Box::new(DUOLINGO), Box::new(DUOLINGO_NIGHT), Box::new(FOOD)] } diff --git a/src/bin/screensaver/mod.rs b/src/bin/screensaver/mod.rs index f85f1de..e6fec54 100644 --- a/src/bin/screensaver/mod.rs +++ b/src/bin/screensaver/mod.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::sync::atomic::{AtomicU32, AtomicU64}; use embedded_graphics::mono_font::ascii::FONT_10X20; use embedded_graphics::{ @@ -15,15 +16,28 @@ use time_tz::{timezones::db::europe::BERLIN, OffsetDateTimeExt}; use crate::{Draw, Rng}; +pub static SPEED: AtomicU64 = AtomicU64::new(32); + pub trait Screensaver>: Draw { fn id(&self) -> &'static str; fn convert_draw(&self) -> Box>; } -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] pub struct SimpleScreensaver { id: &'static str, data: &'static [u8], + iters: AtomicU32, +} + +impl Clone for SimpleScreensaver { + fn clone(&self) -> Self { + Self { + id: self.id, + data: self.data, + iters: AtomicU32::new(self.iters.load(std::sync::atomic::Ordering::Relaxed)), + } + } } impl> Screensaver for SimpleScreensaver { @@ -32,13 +46,13 @@ impl> Screensaver for SimpleScreensaver { } fn convert_draw(&self) -> Box> { - Box::new(*self) + Box::new(self.clone()) } } impl> Draw for SimpleScreensaver { fn draw(&self, disp: &mut D, rng: &mut Rng) -> Result { - for _ in 0..512 { + for _ in 0..SPEED.load(std::sync::atomic::Ordering::Relaxed) { let x = (rng.next_u32() % 128) as usize; let y = (rng.next_u32() % 128) as usize; let dx = (rng.next_u32() % 8) as i32 - 4; @@ -59,8 +73,13 @@ impl> Draw for SimpleScreensaver { p.draw_styled(&s, disp)?; } } + self.iters.fetch_add(1, std::sync::atomic::Ordering::Relaxed); Ok(true) } + + fn expired(&self) -> bool { + self.iters.load(std::sync::atomic::Ordering::Relaxed) > 1000 + } } impl SimpleScreensaver { @@ -68,7 +87,11 @@ impl SimpleScreensaver { if data.len() != 128 * 128 * 3 { panic!("invalid screensaver size"); } - SimpleScreensaver { id, data } + SimpleScreensaver { + id, + data, + iters: AtomicU32::new(0), + } } } @@ -140,7 +163,15 @@ impl> Draw for TimeDisplay { pub static STAR: SimpleScreensaver = SimpleScreensaver::new("star", include_bytes!("./star.raw")); pub static RPI: SimpleScreensaver = SimpleScreensaver::new("rpi", include_bytes!("./rpi.raw")); pub static DUOLINGO: SimpleScreensaver = SimpleScreensaver::new("duolingo", include_bytes!("./duolingo.raw")); +pub static SPAGHETTI: SimpleScreensaver = SimpleScreensaver::new("spaghetti", include_bytes!("./spaghetti.raw")); +pub static PLATE: SimpleScreensaver = SimpleScreensaver::new("plate", include_bytes!("./plate.raw")); pub fn screensavers>() -> Vec>> { - vec![Box::new(STAR), Box::new(RPI), Box::new(DUOLINGO)] + vec![ + Box::new(STAR.clone()), + Box::new(RPI.clone()), + Box::new(DUOLINGO.clone()), + Box::new(SPAGHETTI.clone()), + Box::new(PLATE.clone()), + ] } diff --git a/src/bin/screensaver/plate.raw b/src/bin/screensaver/plate.raw new file mode 100644 index 0000000..0ef519b Binary files /dev/null and b/src/bin/screensaver/plate.raw differ diff --git a/src/bin/screensaver/spaghetti.raw b/src/bin/screensaver/spaghetti.raw new file mode 100644 index 0000000..18f7819 Binary files /dev/null and b/src/bin/screensaver/spaghetti.raw differ