diff --git a/firmware/acid-firmware/src/main.rs b/firmware/acid-firmware/src/main.rs index a82f0bc..abfe14b 100644 --- a/firmware/acid-firmware/src/main.rs +++ b/firmware/acid-firmware/src/main.rs @@ -95,6 +95,14 @@ mod console; // For more information see: esp_bootloader_esp_idf::esp_app_desc!(); +// Memory allocation regions. +// These can be debugged using `xtensa-esp32s3-elf-size -A `. + +/// Total heap size +const HEAP_SIZE: usize = 128 * 1024; +/// Size of the app core's stack +const STACK_SIZE_CORE_APP: usize = 64 * 1024; + // const FRAME_DURATION_MIN: Duration = Duration::from_millis(40); // 25 FPS const FRAME_DURATION_MIN: Duration = Duration::from_millis(100); // 10 FPS @@ -149,12 +157,10 @@ async fn main(_spawner: Spawner) { let alt_uart_rx_task = async {}; // Use the internal DRAM as the heap. - // TODO: Can we combine the regular link section with dram2? - // esp_alloc::heap_allocator!(size: 80 * 1024); - // esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 72 * 1024); - //esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 64 * 1024); - esp_alloc::heap_allocator!(#[ram(reclaimed)] size: 64 * 1024); - esp_alloc::heap_allocator!(size: 32 * 1024); + // Memory reclaimed from the esp-idf bootloader. + const HEAP_SIZE_RECLAIMED: usize = 64 * 1024; + esp_alloc::heap_allocator!(#[ram(reclaimed)] size: HEAP_SIZE_RECLAIMED); + esp_alloc::heap_allocator!(size: HEAP_SIZE - HEAP_SIZE_RECLAIMED); info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats()); // Initialize the PSRAM allocator. @@ -235,15 +241,15 @@ async fn main(_spawner: Spawner) { // Initialize USB #[cfg(not(feature = "no-usb"))] let usb_driver = { - use core::ptr::addr_of_mut; use esp_hal::otg_fs::Usb; use esp_hal::otg_fs::asynch::{Config, Driver}; - static mut EP_MEMORY: [u8; 1024] = [0; 1024]; + static EP_MEMORY: StaticCell<[u8; 1024]> = StaticCell::new(); + let ep_memory = EP_MEMORY.init_with(|| [0_u8; _]); let usb = Usb::new(peripherals.USB0, peripherals.GPIO20, peripherals.GPIO19); // Create the driver, from the HAL. let config = Config::default(); - let driver = Driver::new(usb, unsafe { &mut *addr_of_mut!(EP_MEMORY) }, config); + let driver = Driver::new(usb, ep_memory, config); info!("USB driver for RMK built!"); @@ -381,7 +387,7 @@ async fn main(_spawner: Spawner) { let window_size = [framebuffer.height, framebuffer.width]; let framebuffer_ptr = FramebufferPtr(framebuffer.as_target_pixels() as _); - static SECOND_CORE_STACK: StaticCell> = StaticCell::new(); + static SECOND_CORE_STACK: StaticCell> = StaticCell::new(); let second_core_stack = SECOND_CORE_STACK.init(Stack::new()); esp_rtos::start_second_core( peripherals.CPU_CTRL, diff --git a/firmware/acid-firmware/src/ui/mod.rs b/firmware/acid-firmware/src/ui/mod.rs index 940d91b..bed30d0 100644 --- a/firmware/acid-firmware/src/ui/mod.rs +++ b/firmware/acid-firmware/src/ui/mod.rs @@ -9,6 +9,7 @@ use alloc::{ borrow::Cow, boxed::Box, ffi::CString, + rc::Rc, string::{String, ToString}, vec, vec::Vec, @@ -25,7 +26,7 @@ use itertools::Itertools; use log::{info, warn}; use rmk::futures::TryFutureExt; use serde::{Deserialize, Serialize}; -use slint::SharedString; +use slint::{ModelRc, SharedString, StandardListViewItem, VecModel}; use spectre_api_sys::{ SpectreAlgorithm, SpectreCounter, SpectreKeyPurpose, SpectreResultType, SpectreUserKey, }; @@ -243,7 +244,25 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition let main = AppWindow::new().unwrap(); - main.on_accepted(|string| { + main.on_login_pw_accepted({ + let main = main.clone_strong(); + move |username, password| { + info!("username = {username:?}, password = {password:?}"); + main.set_app_state(AppState::UserSites); + } + }); + + // let sites = Rc::new(VecModel::default()); + // sites.push("First".into()); + // sites.push("Second".into()); + // main.set_sites(ModelRc::new(ModelRc::new(sites.clone()).map( + // |mut site: StandardListViewItem| { + // site.text += "10"; + // site + // }, + // ))); + + main.on_site_pw_accepted(|string| { warn!("Accepted: {string}"); let Ok(c_string) = CString::new(&*string) else { warn!("String cannot be converted to a C string: {string:?}"); diff --git a/firmware/acid-firmware/ui/globals.slint b/firmware/acid-firmware/ui/globals.slint new file mode 100644 index 0000000..1c5f0d5 --- /dev/null +++ b/firmware/acid-firmware/ui/globals.slint @@ -0,0 +1,3 @@ +export global Style { + in property spacing: 8px; +} diff --git a/firmware/acid-firmware/ui/images/help-circle.svg b/firmware/acid-firmware/ui/images/help-circle.svg new file mode 100644 index 0000000..51fddd8 --- /dev/null +++ b/firmware/acid-firmware/ui/images/help-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/firmware/acid-firmware/ui/images/key.svg b/firmware/acid-firmware/ui/images/key.svg new file mode 100644 index 0000000..e778e74 --- /dev/null +++ b/firmware/acid-firmware/ui/images/key.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/firmware/acid-firmware/ui/images/log-in.svg b/firmware/acid-firmware/ui/images/log-in.svg new file mode 100644 index 0000000..ba0da59 --- /dev/null +++ b/firmware/acid-firmware/ui/images/log-in.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/firmware/acid-firmware/ui/images/log-out.svg b/firmware/acid-firmware/ui/images/log-out.svg new file mode 100644 index 0000000..c9002c9 --- /dev/null +++ b/firmware/acid-firmware/ui/images/log-out.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/firmware/acid-firmware/ui/images/sliders.svg b/firmware/acid-firmware/ui/images/sliders.svg new file mode 100644 index 0000000..19c9385 --- /dev/null +++ b/firmware/acid-firmware/ui/images/sliders.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/firmware/acid-firmware/ui/images/trash-2.svg b/firmware/acid-firmware/ui/images/trash-2.svg new file mode 100644 index 0000000..f24d55b --- /dev/null +++ b/firmware/acid-firmware/ui/images/trash-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/firmware/acid-firmware/ui/images/users.svg b/firmware/acid-firmware/ui/images/users.svg new file mode 100644 index 0000000..aacf6b0 --- /dev/null +++ b/firmware/acid-firmware/ui/images/users.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/firmware/acid-firmware/ui/login-view.slint b/firmware/acid-firmware/ui/login-view.slint new file mode 100644 index 0000000..37a9845 --- /dev/null +++ b/firmware/acid-firmware/ui/login-view.slint @@ -0,0 +1,38 @@ +import { ComboBox, LineEdit } from "std-widgets.slint"; +import { Style } from "globals.slint"; +import { IconButton } from "widgets/icon-button.slint"; + +export component LoginView inherits VerticalLayout { + padding: Style.spacing; + spacing: Style.spacing; + callback pw_accepted(string, string); + Rectangle { } + + HorizontalLayout { + spacing: Style.spacing; + IconButton { + icon: @image-url("images/users.svg"); + } + + combo_box_username := ComboBox { + model: ["first", "second"]; + } + + line_edit_user_pw := LineEdit { + input-type: InputType.password; + placeholder-text: "Password"; + accepted(text) => { + root.pw_accepted(combo_box_username.current-value, text); + } + } + + IconButton { + icon: @image-url("images/log-in.svg"); + clicked => { + root.pw_accepted(combo_box_username.current-value, line_edit_user_pw.text); + } + } + } + + Rectangle { } +} diff --git a/firmware/acid-firmware/ui/main.slint b/firmware/acid-firmware/ui/main.slint index e7528f4..9ba981b 100644 --- a/firmware/acid-firmware/ui/main.slint +++ b/firmware/acid-firmware/ui/main.slint @@ -8,27 +8,46 @@ import { Button, } from "std-widgets.slint"; */ -import { Button, VerticalBox, LineEdit, GridBox } from "std-widgets.slint"; +import { + Button, + VerticalBox, + LineEdit, + GridBox, + StandardListView, + ListView, + ComboBox, +} from "std-widgets.slint"; // See https://github.com/slint-ui/slint/issues/4956 for issues with fonts. import "../fonts/IBM_Plex_Mono/IBMPlexMono-Regular.ttf"; +import { UserSitesView } from "user-sites-view.slint"; +import { Style } from "globals.slint"; +import { UsersView } from "users-view.slint"; +import { LoginView } from "login-view.slint"; -/* -TODO: A bigger stack for the 2nd core might be needed to prevent crashing. export enum AppState { - PasswordForm, + login, + user-sites, + users, } -*/ export component AppWindow inherits Window { - in property dummy: "ÄÖÜÁÉÍÓÚÝŔŚĹŹĆŃĚĽŽŠČŘĎŤŇŮÅäöüáéíóúýŕśĺźćńěľžščřďťňůåß„“”‘’—–@&$%+=¡¿¢£$¥€²³¼½¬¤¦§©®™°"; + in property dummy: "ÄÖÜÁÉÍÓÚÝŔŚĹŹĆŃĚĽŽŠČŘĎŤŇŮÅäöüáéíóúýŕśĺźćńěľžščřďťňůåß„“”‘’—–@&$%+=¡¿¢£$¥€²³¼½¬¤¦§©®™°´ˇ¨"; default-font-family: "IBM Plex Mono"; default-font-size: 16pt; height: 368px; width: 960px; - in-out property counter: 42; - callback request-increase-value(); - callback accepted(string); + in property app-state: AppState.login; + // Login View + callback login_pw_accepted <=> login_view.pw_accepted; + // Sites View + in property <[StandardListViewItem]> sites <=> user_sites_view.model; + callback site_pw_edited <=> user_sites_view.pw_edited; + callback site_pw_accepted <=> user_sites_view.pw_accepted; + // Sites View + in property <[StandardListViewItem]> usernames <=> users_view.model; + callback user_username_edited <=> users_view.pw_edited; + callback user_username_accepted <=> users_view.pw_accepted; VerticalBox { width: 960px; height: 368px; @@ -36,53 +55,21 @@ export component AppWindow inherits Window { padding-top: 120px; padding-bottom: 8px; Rectangle { + height: 240px; + background: #00141d; // For debugging bounds. - // background: #2c82ff; // border-color: #ffcf00; // border-width: 1px; - GridBox { - VerticalBox { - Text { - text: "Counter: \{root.counter}"; - } + login_view := LoginView { + visible: app-state == AppState.login; + } - Button { - text: "Increase value"; - clicked => { - root.counter += 1; - root.request-increase-value(); - } - } + user_sites_view := UserSitesView { + visible: app-state == AppState.user-sites; + } - LineEdit { - input-type: InputType.text; - text: "LineEdit"; - accepted(text) => { - root.accepted(text); - } - /*changed text => { - root.changed(self.text); - }*/ - } - } - -/* - TabWidget { - Tab { - title: "first"; - Button { - text: "First"; - } - } - - Tab { - title: "second"; - Button { - text: "Second"; - } - } - } - */ + users_view := UsersView { + visible: app-state == AppState.users; } } } diff --git a/firmware/acid-firmware/ui/user-sites-view.slint b/firmware/acid-firmware/ui/user-sites-view.slint new file mode 100644 index 0000000..aaf9183 --- /dev/null +++ b/firmware/acid-firmware/ui/user-sites-view.slint @@ -0,0 +1,53 @@ +import { LineEdit, StandardListView, Button } from "std-widgets.slint"; +import { Style } from "globals.slint"; +import { IconButton } from "widgets/icon-button.slint"; + +export component UserSitesView inherits HorizontalLayout { + padding: Style.spacing; + spacing: Style.spacing; + in property <[StandardListViewItem]> model <=> list_view_sites.model; + in-out property current-item <=> list_view_sites.current-item; + callback pw_edited <=> line_edit_site_pw.edited; + callback pw_accepted <=> line_edit_site_pw.accepted; + VerticalLayout { + spacing: Style.spacing; + Text { + text: "Send password for:"; + } + + line_edit_site_pw := LineEdit { + input-type: InputType.text; + placeholder-text: "example.org"; + } + + list_view_sites := StandardListView { + model: [ + { text: "Test" }, + { text: "Test" }, + ]; + } + } + + VerticalLayout { + spacing: Style.spacing; + // IconButton { + // icon: @image-url("images/log-out.svg"); + // } + + IconButton { + icon: @image-url("images/sliders.svg"); + } + + IconButton { + icon: @image-url("images/help-circle.svg"); + } + + IconButton { + icon: @image-url("images/key.svg"); + } + + IconButton { + icon: @image-url("images/trash-2.svg"); + } + } +} diff --git a/firmware/acid-firmware/ui/users-view.slint b/firmware/acid-firmware/ui/users-view.slint new file mode 100644 index 0000000..c426152 --- /dev/null +++ b/firmware/acid-firmware/ui/users-view.slint @@ -0,0 +1,41 @@ +import { LineEdit, StandardListView, Button } from "std-widgets.slint"; +import { Style } from "globals.slint"; +import { IconButton } from "widgets/icon-button.slint"; + +export component UsersView inherits HorizontalLayout { + padding: Style.spacing; + spacing: Style.spacing; + in property <[StandardListViewItem]> model <=> list_view_sites.model; + in-out property current-item <=> list_view_sites.current-item; + callback pw_edited <=> line_edit_site_pw.edited; + callback pw_accepted <=> line_edit_site_pw.accepted; + VerticalLayout { + spacing: Style.spacing; + Text { + text: "Username:"; + } + + line_edit_site_pw := LineEdit { + input-type: InputType.text; + placeholder-text: "Full Name"; + } + + list_view_sites := StandardListView { + model: [ + { text: "Test" }, + { text: "Test" }, + ]; + } + } + + VerticalLayout { + spacing: Style.spacing; + IconButton { + icon: @image-url("images/key.svg"); + } + + IconButton { + icon: @image-url("images/trash-2.svg"); + } + } +} diff --git a/firmware/acid-firmware/ui/widgets/icon-button.slint b/firmware/acid-firmware/ui/widgets/icon-button.slint new file mode 100644 index 0000000..cced914 --- /dev/null +++ b/firmware/acid-firmware/ui/widgets/icon-button.slint @@ -0,0 +1,8 @@ +import { Button } from "std-widgets.slint"; + +export component IconButton inherits Button { + colorize-icon: true; + icon-size: 24px; + width: 48px; + height: 48px; +}