// #![cfg_attr(not(feature = "simulator"), no_main)] use core::{ iter::Chain, ops::{Deref, DerefMut, Range}, }; use alloc::{borrow::Cow, boxed::Box, ffi::CString, vec::Vec}; use ekv::{Database, MountError, flash::PageID}; use embassy_embedded_hal::{adapter::BlockingAsync, flash::partition::Partition}; use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; use embassy_time::{Duration, Instant}; use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; use esp_hal::rng::Trng; use esp_storage::FlashStorage; use itertools::Itertools; use log::{info, warn}; use rmk::futures::TryFutureExt; use slint::SharedString; use spectre_api_sys::{SpectreAlgorithm, SpectreCounter, SpectreKeyPurpose, SpectreUserKey}; #[cfg(feature = "limit-fps")] use crate::FRAME_DURATION_MIN; use crate::{ SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER, db::{AcidDatabase, PartitionAcid}, ui::backend::SlintBackend, util::DurationExt, }; pub mod backend; pub mod window_adapter; slint::include_modules!(); #[embassy_executor::task] pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: PartitionAcid) { let db = AcidDatabase::mount(flash_part_acid).await; let write = db.write_transaction().await; // TODO: // * Store a config as a versioned postcard-serialized struct // * Store accounts and sites as ranges in the DB slint::platform::set_platform(Box::new(backend)).expect("backend already initialized"); let main = AppWindow::new().unwrap(); main.on_accepted(|string| { warn!("Accepted: {string}"); let Ok(c_string) = CString::new(&*string) else { warn!("String cannot be converted to a C string: {string:?}"); return; }; unsafe { let user_key_start = Instant::now(); let user_key = &*spectre_api_sys::spectre_user_key( c"test".as_ptr(), c_string.as_ptr(), SpectreAlgorithm::Current, ); let user_key_duration = Instant::now().duration_since(user_key_start); warn!( "User key derived in {} seconds:\n{user_key:02x?}", user_key_duration.display_as_secs() ); let site_key_start = Instant::now(); let site_key = &*spectre_api_sys::spectre_site_key( user_key as *const SpectreUserKey, c"example.org".as_ptr(), SpectreCounter::Initial, SpectreKeyPurpose::Authentication, c"".as_ptr(), ); let site_key_duration = Instant::now().duration_since(site_key_start); warn!( "Site key derived in {} seconds:\n{site_key:02x?}", site_key_duration.display_as_secs() ); // TODO: Free memory } }); run_event_loop(main).await; } /// Instead of having a `loop` in the non-async `SlintBackend::run_event_loop`, we achieve /// async by having only one iteration of the loop run, and `await`ing here. /// The following block is analogous to `main.run()`. async fn run_event_loop(main: AppWindow) { main.show().unwrap(); loop { slint::run_event_loop().unwrap(); SIGNAL_LCD_SUBMIT.signal(()); #[cfg(feature = "limit-fps")] embassy_time::Timer::after(FRAME_DURATION_MIN).await; SIGNAL_UI_RENDER.wait().await; } #[expect(unreachable_code)] main.hide().unwrap(); }