From af043c3a18c71403ffa570ee57221cf392af68aa Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:09:30 +0800 Subject: [PATCH 01/35] make usb-cramium suitable as a dependency for hosted emulation --- services/usb-cramium/Cargo.toml | 14 +++++++++++--- services/usb-cramium/src/lib.rs | 3 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/services/usb-cramium/Cargo.toml b/services/usb-cramium/Cargo.toml index 3af1d7e28..e1c54ae39 100644 --- a/services/usb-cramium/Cargo.toml +++ b/services/usb-cramium/Cargo.toml @@ -18,8 +18,9 @@ cramium-hal = { path = "../../libs/cramium-hal", features = [ "debug-print-usb", # "verbose-debug", "axp2101", -] } -cram-hal-service = { path = "../cram-hal-service" } +], optional = true } +cram-hal-service = { path = "../cram-hal-service", optional = true } +cramium-emu = { path = "../cramium-emu", optional = true } xous-usb-hid = { git = "https://github.com/betrusted-io/xous-usb-hid.git", branch = "main" } rkyv = { version = "0.8.8", default-features = false, features = [ "std", @@ -37,7 +38,14 @@ version = "0.2.8" features = ["log"] [features] -cramium-soc = ["utralib/cramium-soc", "utralib/std"] +cramium-soc = [] +board-baosec = [ + "utralib/cramium-soc", + "utralib/std", + "cramium-hal", + "cram-hal-service", +] +hosted-baosec = ["utralib/hosted", "utralib/std", "cramium-emu"] # none of these other modes are actually supported, but are listed here # to allow direct re-use of API code precursor = ["utralib/precursor", "utralib/std"] diff --git a/services/usb-cramium/src/lib.rs b/services/usb-cramium/src/lib.rs index 87a1bafe9..8d1e4716f 100644 --- a/services/usb-cramium/src/lib.rs +++ b/services/usb-cramium/src/lib.rs @@ -1,6 +1,9 @@ #![cfg_attr(target_os = "none", no_std)] +#[cfg(feature = "cramium-soc")] use cram_hal_service::trng; +#[cfg(feature = "hosted-baosec")] +use cramium_emu::trng; pub mod api; pub use api::*; From 2b36849683f5c9b1c89c53ee6db1d8f74018b11c Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:09:54 +0800 Subject: [PATCH 02/35] add console to the emulation --- xtask/src/main.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index d0a37d2ef..47bd652ac 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -365,7 +365,15 @@ fn main() -> Result<(), Box> { .add_apps(&get_cratespecs()); } Some("baosec-emu") => { - let bao_pkgs = ["xous-ticktimer", "xous-log", "xous-names", "modals", "bao-video", "cramium-emu"]; + let bao_pkgs = [ + "xous-ticktimer", + "xous-log", + "xous-names", + "modals", + "bao-video", + "cramium-emu", + "bao-console", + ]; builder // hosted-baosec feature added below .target_hosted_baosec() From 2957cce789f418051405f66906d0c069f86906d2 Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:10:24 +0800 Subject: [PATCH 03/35] add some v2 graphics api adapters and opcodes --- libs/ux-api/src/service/api.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/ux-api/src/service/api.rs b/libs/ux-api/src/service/api.rs index 9cd9568f5..3193e4205 100644 --- a/libs/ux-api/src/service/api.rs +++ b/libs/ux-api/src/service/api.rs @@ -12,6 +12,8 @@ impl Gid { pub fn new(id: [u32; 4]) -> Self { Gid { gid: id } } pub fn gid(&self) -> [u32; 4] { self.gid } + + pub fn dummy() -> Self { Gid { gid: [0xdead, 0xbeef, 0xdead, 0xbeef] } } } impl Hash for Gid { fn hash(&self, state: &mut H) @@ -89,6 +91,10 @@ pub enum GfxOpcode { /// Handle Camera IRQs CamIrq, + /// V2 API for claiming ownership of screen for modal operation + AcquireModal, + ReleaseModal, + /// Gutter for invalid calls InvalidCall, From b1ce74faa0509ddc08bb0b2b469a83874466623f Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:10:50 +0800 Subject: [PATCH 04/35] add handler for unspecified clipping areas in v2 targets --- libs/ux-api/src/minigfx/handlers.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libs/ux-api/src/minigfx/handlers.rs b/libs/ux-api/src/minigfx/handlers.rs index e73a39293..2e5123e9c 100644 --- a/libs/ux-api/src/minigfx/handlers.rs +++ b/libs/ux-api/src/minigfx/handlers.rs @@ -166,7 +166,12 @@ pub fn draw_text_view(display: &mut T, msg: &mut xous::envelope: let mut tv = buffer.to_original::().unwrap(); if tv.clip_rect.is_none() { - return; + if cfg!(feature = "hosted-baosec") || cfg!(feature = "cramium-soc") || cfg!(feature = "board-baosec") + { + tv.clip_rect = Some(Rectangle::new(Point::new(0, 0), display.dimensions())); + } else { + return; + } } // if no clipping rectangle is specified, nothing to draw // this is the clipping rectangle of the canvas in screen coordinates From 433d632926119025e3648c3dcded70ba7ad0dd3f Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:11:23 +0800 Subject: [PATCH 05/35] add a clear, modals & text bounds api calls the text bounds api call is needed in v2 because there is no gam to mediate this call clear allows for a fast clear operation that may have hardware dependent consequences acquire/release of modals mode is added to the graphics server as well to implement a feature previously handled by the gam --- libs/ux-api/src/service/gfx.rs | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/libs/ux-api/src/service/gfx.rs b/libs/ux-api/src/service/gfx.rs index f70a095c3..b75a448a8 100644 --- a/libs/ux-api/src/service/gfx.rs +++ b/libs/ux-api/src/service/gfx.rs @@ -310,6 +310,31 @@ impl Gfx { Ok(()) } + /// Bounds computation does no checks on security since it's a non-drawing operation. While normal drawing + /// always takes the bounds from the canvas, the caller can specify a clip_rect in this tv, instead of + /// drawing the clip_rect from the Canvas associated with the tv. + pub fn bounds_compute_textview(&self, tv: &mut TextView) -> Result<(), xous::Error> { + let mut tv_query = tv.clone(); + tv_query.set_dry_run(true); + let mut buf = Buffer::into_buf(tv_query).or(Err(xous::Error::InternalError))?; + buf.lend_mut(self.conn, GfxOpcode::DrawTextView.to_u32().unwrap()) + .or(Err(xous::Error::InternalError))?; + let tvr = buf.to_original::().unwrap(); + + tv.cursor = tvr.cursor; + tv.bounds_computed = tvr.bounds_computed; + tv.overflow = tvr.overflow; + // don't update the animation state when just computing the textview bounds + // tv.busy_animation_state = tvr.busy_animation_state; + Ok(()) + } + + /// Clear the screen in a device-optimized fashion. The exact background color depends on the device. + pub fn clear(&self) -> Result<(), xous::Error> { + send_message(self.conn, Message::new_scalar(GfxOpcode::Clear.to_usize().unwrap(), 0, 0, 0, 0)) + .map(|_| ()) + } + /// Draws a line with clipping. /// /// This function sends a message to the graphics server to draw the specified `Line` within the specified @@ -665,6 +690,21 @@ impl Gfx { .expect("couldn't pop"); } } + + /// This will cause the caller to block until it is granted a lock on the modal state. + /// + /// This is a v2-only API. Calling it on a v1 system will cause a panic. + pub fn acquire_modal(&self) -> Result { + send_message( + self.conn, + Message::new_blocking_scalar(GfxOpcode::AcquireModal.to_usize().unwrap(), 0, 0, 0, 0), + ) + } + + /// This is a v2-only API. Calling it on a v1 system will cause a panic. + pub fn release_modal(&self) -> Result { + send_message(self.conn, Message::new_scalar(GfxOpcode::ReleaseModal.to_usize().unwrap(), 0, 0, 0, 0)) + } } use core::sync::atomic::{AtomicU32, Ordering}; From da5c215477c6c8d36e95dc412153657e2413f1f4 Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:12:57 +0800 Subject: [PATCH 06/35] fix type defaults for trait part of the i16->isize transition --- libs/ux-api/src/widgets/action.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/ux-api/src/widgets/action.rs b/libs/ux-api/src/widgets/action.rs index 854ed07af..15694e583 100644 --- a/libs/ux-api/src/widgets/action.rs +++ b/libs/ux-api/src/widgets/action.rs @@ -24,8 +24,10 @@ pub enum ActionType { #[enum_dispatch] pub trait ActionApi { - fn height(&self, glyph_height: i16, margin: i16, _modal: &Modal) -> i16 { glyph_height + margin * 2 } - fn redraw(&self, _at_height: i16, _modal: &Modal) { unimplemented!() } + fn height(&self, glyph_height: isize, margin: isize, _modal: &Modal) -> isize { + glyph_height + margin * 2 + } + fn redraw(&self, _at_height: isize, _modal: &Modal) { unimplemented!() } fn close(&mut self) {} fn is_password(&self) -> bool { false } /// navigation is one of '∴' | '←' | '→' | '↑' | '↓' From 558b96cfd5c10f76cdb550bb07d7433e62f2fd40 Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:13:52 +0800 Subject: [PATCH 07/35] add missing api for emulation --- services/cramium-emu/src/trng.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/services/cramium-emu/src/trng.rs b/services/cramium-emu/src/trng.rs index 245310145..fcde8bfe9 100644 --- a/services/cramium-emu/src/trng.rs +++ b/services/cramium-emu/src/trng.rs @@ -50,3 +50,13 @@ impl RngCore for Trng { } impl CryptoRng for Trng {} + +pub mod api { + #[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive, PartialEq, Eq, Copy, Clone)] + pub enum TrngTestMode { + // No test mode configured. Whitened data is sampled. + None, + // Raw TRNG output. + Raw, + } +} From 85a216ff0f7fe41f613b4b62fe1da5380d01fde6 Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:14:05 +0800 Subject: [PATCH 08/35] add a warning if v2 api is sent into graphics-server which is v1 only --- services/graphics-server/src/main.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/graphics-server/src/main.rs b/services/graphics-server/src/main.rs index 85792b790..9ddd35545 100644 --- a/services/graphics-server/src/main.rs +++ b/services/graphics-server/src/main.rs @@ -471,6 +471,11 @@ fn wrapped_main(main_thread_token: backend::MainThreadToken) -> ! { } // no failure if it's not } + GfxOpcode::AcquireModal | GfxOpcode::ReleaseModal => { + unimplemented!( + "Acquire/Release modal is not supported for this target. Use the GAM instead." + ); + } GfxOpcode::Quit => break, GfxOpcode::InvalidCall => { log::error!("Received invalid GfxOpcode. Ignoring."); From 3dec223d9db945527f3c89df54e7b43e697accec Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:14:43 +0800 Subject: [PATCH 09/35] allow modals testing in more modes --- services/modals/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/modals/src/lib.rs b/services/modals/src/lib.rs index 999614426..001c17008 100644 --- a/services/modals/src/lib.rs +++ b/services/modals/src/lib.rs @@ -2,7 +2,7 @@ pub mod api; use api::*; -#[cfg(feature = "ditherpunk")] +#[cfg(any(feature = "ditherpunk", feature = "modal-testing"))] pub mod tests; use core::cell::Cell; From 531f5ee4f9e36aa86d8064b1d2b9fed5577dc890 Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:14:58 +0800 Subject: [PATCH 10/35] add modals testing mode --- services/modals/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/services/modals/Cargo.toml b/services/modals/Cargo.toml index 31c3fe5f9..f30343126 100644 --- a/services/modals/Cargo.toml +++ b/services/modals/Cargo.toml @@ -43,6 +43,7 @@ cramium-soc = ["utralib/cramium-soc", "cram-hal-service"] hosted-baosec = ["ux-api/hosted-baosec", "cramium-emu"] board-baosec = ["ux-api/board-baosec"] doc-deps = ["gam", "ux-api/doc-deps"] +modal-testing = [] tts = [] ditherpunk = [] From d1ec6e021ab01d016f889e2124801e2b36fc5d4b Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:15:16 +0800 Subject: [PATCH 11/35] add feature adapters for bao-console so it can work in hosted mode --- services/bao-console/Cargo.toml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/services/bao-console/Cargo.toml b/services/bao-console/Cargo.toml index 0a6af484b..b5aeb2413 100644 --- a/services/bao-console/Cargo.toml +++ b/services/bao-console/Cargo.toml @@ -12,11 +12,27 @@ log-server = { package = "xous-api-log", version = "0.1.63" } log = "0.4.14" num-derive = { version = "0.4.2", default-features = false } num-traits = { version = "0.2.14", default-features = false } -cramium-hal = { path = "../../libs/cramium-hal", features = ["std"] } +cramium-hal = { path = "../../libs/cramium-hal", features = [ + "std", +], optional = true } cramium-api = { path = "../../libs/cramium-api" } -cram-hal-service = { path = "../cram-hal-service" } +cram-hal-service = { path = "../cram-hal-service", optional = true } +cramium-emu = { path = "../cramium-emu", optional = true } usb-cramium = { path = "../usb-cramium" } +modals = { path = "../modals" } [features] cramium-soc = ["utralib/cramium-soc"] +board-baosec = [ + "modals/board-baosec", + "cram-hal-service", + "cramium-hal", + "usb-cramium/board-baosec", +] +hosted-baosec = [ + "modals/hosted-baosec", + "usb-cramium/hosted-baosec", + "cramium-emu", +] + default = [] From 9297879f7673ed6f39e9beb037ed6af0995da3ba Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 3 Mar 2025 02:15:46 +0800 Subject: [PATCH 12/35] select trng based on emulation mode --- services/bao-console/src/cmds.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/bao-console/src/cmds.rs b/services/bao-console/src/cmds.rs index 771074c7d..ebf2ebd1c 100644 --- a/services/bao-console/src/cmds.rs +++ b/services/bao-console/src/cmds.rs @@ -2,7 +2,10 @@ use core::fmt::Write; use std::collections::HashMap; use String; +#[cfg(feature = "cramium-soc")] use cram_hal_service::trng; +#[cfg(feature = "hosted-baosec")] +use cramium_emu::trng; use xous::MessageEnvelope; /////////////////////////// Common items to all commands From 06d87602098f68a6314fe1c782a883d8a20aa470 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 01:24:41 +0800 Subject: [PATCH 13/35] add routine to set pwm mode on axp2101 --- libs/cramium-hal/src/axp2101.rs | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/libs/cramium-hal/src/axp2101.rs b/libs/cramium-hal/src/axp2101.rs index fd041bde8..06ac8cb2f 100644 --- a/libs/cramium-hal/src/axp2101.rs +++ b/libs/cramium-hal/src/axp2101.rs @@ -3,6 +3,7 @@ use cramium_api::*; pub const AXP2101_DEV: u8 = 0x34; const REG_DCDC_ENA: usize = 0x80; +const REG_DCDC_PWM: usize = 0x81; const REG_DCDC1_V: usize = 0x82; const REG_DCDC2_V: usize = 0x83; const REG_DCDC3_V: usize = 0x84; @@ -281,6 +282,39 @@ impl Axp2101 { let data = VBUS_INSERT_MASK | VBUS_REMOVE_MASK; i2c.i2c_write(AXP2101_DEV, REG_IRQ_STATUS1, &[data]).map(|_| ()) } + + pub fn set_pwm_mode( + &mut self, + i2c: &mut dyn I2cApi, + which: WhichDcDc, + always: bool, + ) -> Result<(), xous::Error> { + match which { + WhichDcDc::Dcdc5 => Err(xous::Error::BadAddress), + _ => { + let mut buf = [0u8]; + i2c.i2c_read(AXP2101_DEV, REG_DCDC_PWM as u8, &mut buf, false).map(|_| ())?; + if always { + buf[0] |= 4u8 << (which as usize as u8); + } else { + buf[0] &= !(4u8 << (which as usize as u8)); + } + i2c.i2c_write(AXP2101_DEV, REG_DCDC_PWM as u8, &buf).map(|_| ()) + } + } + } + + pub fn debug(&mut self, i2c: &mut dyn I2cApi) { + let mut buf = [0u8, 0u8]; + i2c.i2c_read(AXP2101_DEV, REG_DCDC_ENA as u8, &mut buf, false).unwrap(); + crate::println!("ena|pwm bef: {:x?}", buf); + // force CCM mode + i2c.i2c_write(AXP2101_DEV, REG_DCDC_ENA as u8, &[buf[0] | 0b0100_0000]).unwrap(); + // disable spreading, force PWM on DCDC2 + i2c.i2c_write(AXP2101_DEV, REG_DCDC_PWM as u8, &[(buf[1] & 0b0011_1111) | 0b0000_1000]).unwrap(); + i2c.i2c_read(AXP2101_DEV, REG_DCDC_ENA as u8, &mut buf, false).unwrap(); + crate::println!("ena|pwm aft: {:x?}", buf); + } } pub fn parse_dcdc_ena(d: u8) -> ([bool; 4], bool, bool) { From 845e40884bfd2aa77bb919831d382dc7e74aefe1 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 01:25:02 +0800 Subject: [PATCH 14/35] add pwm mode setting to loader start --- loader/src/platform/cramium/cramium.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/loader/src/platform/cramium/cramium.rs b/loader/src/platform/cramium/cramium.rs index 301fdcf7d..d311709da 100644 --- a/loader/src/platform/cramium/cramium.rs +++ b/loader/src/platform/cramium/cramium.rs @@ -329,6 +329,9 @@ pub fn early_init() -> u32 { }; } if let Some(mut pmic) = pmic { + // disable CCM, force PWM on for DCDC2 - required for rail stability + pmic.set_pwm_mode(&mut i2c, cramium_hal::axp2101::WhichDcDc::Dcdc2, true).unwrap(); + pmic.set_ldo(&mut i2c, Some(2.5), cramium_hal::axp2101::WhichLdo::Aldo2).unwrap(); pmic.set_dcdc(&mut i2c, Some((1.2, false)), cramium_hal::axp2101::WhichDcDc::Dcdc4).unwrap(); crate::println!("AXP2101 configure: {:?}", pmic); From c67cb7666c102675a75cb13596bdf13a670b8876 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 01:26:03 +0800 Subject: [PATCH 15/35] fixup feature dependencies --- libs/ux-api/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/ux-api/src/lib.rs b/libs/ux-api/src/lib.rs index d8195e65f..30a2c7aee 100644 --- a/libs/ux-api/src/lib.rs +++ b/libs/ux-api/src/lib.rs @@ -5,7 +5,7 @@ pub mod cursor; mod fontmap; pub mod minigfx; pub mod platform; -#[cfg(all(feature = "std", any(feature = "cramium-soc", feature = "hosted-baosec")))] +#[cfg(all(feature = "std", any(feature = "board-baosec", feature = "hosted-baosec")))] pub mod widgets; #[cfg(feature = "std")] pub mod wordwrap; From 4c16d42d457c441528612e1c3e37275e88003464 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 01:27:37 +0800 Subject: [PATCH 16/35] fixup features --- services/modals/src/api.rs | 4 ++-- services/modals/src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/modals/src/api.rs b/services/modals/src/api.rs index 69fbb947f..35e82a3b7 100644 --- a/services/modals/src/api.rs +++ b/services/modals/src/api.rs @@ -1,8 +1,8 @@ #[cfg(feature = "ditherpunk")] use gam::Tile; -#[cfg(not(any(feature = "hosted-baosec", feature = "cramium-soc")))] +#[cfg(not(any(feature = "hosted-baosec", feature = "board-baosec")))] use gam::modal::*; -#[cfg(any(feature = "hosted-baosec", feature = "cramium-soc"))] +#[cfg(any(feature = "hosted-baosec", feature = "board-baosec"))] use ux_api::widgets::*; pub(crate) const SERVER_NAME_MODALS: &str = "_Modal Dialog Server_"; diff --git a/services/modals/src/lib.rs b/services/modals/src/lib.rs index 001c17008..137012c06 100644 --- a/services/modals/src/lib.rs +++ b/services/modals/src/lib.rs @@ -16,12 +16,12 @@ use bit_field::BitField; use cram_hal_service::trng::Trng; #[cfg(feature = "hosted-baosec")] use cramium_emu::trng::Trng; -#[cfg(not(any(feature = "hosted-baosec", feature = "cramium-soc")))] +#[cfg(not(any(feature = "hosted-baosec", feature = "board-baosec")))] use gam::*; use num_traits::*; #[cfg(all(not(feature = "cramium-soc"), not(feature = "doc-deps"), not(feature = "hosted-baosec")))] use trng::Trng; -#[cfg(any(feature = "hosted-baosec", feature = "cramium-soc"))] +#[cfg(any(feature = "hosted-baosec", feature = "board-baosec"))] use ux_api::widgets::*; use xous::{CID, Message, send_message}; use xous_ipc::Buffer; From 3be69cf8d15d65f8595749b0380512a81ff9df79 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 01:28:38 +0800 Subject: [PATCH 17/35] cleanup dependencies and inter-dependent features --- services/bao-console/Cargo.toml | 6 ++++-- services/cram-console/Cargo.toml | 12 +++++++----- services/cram-hal-service/Cargo.toml | 6 +++--- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/services/bao-console/Cargo.toml b/services/bao-console/Cargo.toml index b5aeb2413..54f0400e5 100644 --- a/services/bao-console/Cargo.toml +++ b/services/bao-console/Cargo.toml @@ -18,8 +18,10 @@ cramium-hal = { path = "../../libs/cramium-hal", features = [ cramium-api = { path = "../../libs/cramium-api" } cram-hal-service = { path = "../cram-hal-service", optional = true } cramium-emu = { path = "../cramium-emu", optional = true } -usb-cramium = { path = "../usb-cramium" } -modals = { path = "../modals" } +usb-cramium = { path = "../usb-cramium", features = ["cramium-soc"] } +modals = { path = "../modals", default-features = false, features = [ + "cramium-soc", +] } [features] cramium-soc = ["utralib/cramium-soc"] diff --git a/services/cram-console/Cargo.toml b/services/cram-console/Cargo.toml index ab06c4980..355e60b4d 100644 --- a/services/cram-console/Cargo.toml +++ b/services/cram-console/Cargo.toml @@ -17,11 +17,10 @@ num-derive = { version = "0.4.2", default-features = false } num-traits = { version = "0.2.14", default-features = false } # xous-pio = { path = "../../libs/xous-pio", optional = true } -xous-pl230 = { path = "../../libs/xous-pl230", optional = true } +# xous-pl230 = { path = "../../libs/xous-pl230", optional = true } # this is temporary, until we get GAM/modals pulled in ux-api = { path = "../../libs/ux-api" } modals = { path = "../modals", features = ["cramium-soc"] } -gam = { path = "../gam" } locales = { path = "../../locales" } usb-device-xous = { path = "../usb-device-xous", default-features = false, optional = true } cramium-hal = { path = "../../libs/cramium-hal", features = ["std"] } @@ -30,8 +29,11 @@ ime-plugin-shell = { path = "../ime-plugin-shell" } xous-swapper = { path = "../xous-swapper" } [features] -"cramium-fpga" = ["utralib/cramium-fpga", "xous-pl230/cramium-fpga"] -"cramium-soc" = ["utralib/cramium-soc", "xous-pl230/cramium-soc"] +"cramium-fpga" = ["utralib/cramium-fpga"] +"cramium-soc" = ["utralib/cramium-soc"] +"board-baosec" = ["modals/board-baosec"] +"hosted-baosec" = ["modals/hosted-baosec"] + "hwsim" = [] "shellperf" = [] "mass-storage" = [] @@ -39,7 +41,7 @@ xous-swapper = { path = "../xous-swapper" } "tts" = [] "early-ball" = [] # "pio-test" = ["xous-pio"] -"pl230-test" = ["xous-pl230"] +# "pl230-test" = ["xous-pl230"] "hwtest" = [] "pddb" = [] "gam" = [] diff --git a/services/cram-hal-service/Cargo.toml b/services/cram-hal-service/Cargo.toml index 2ff1207ee..f259059e5 100644 --- a/services/cram-hal-service/Cargo.toml +++ b/services/cram-hal-service/Cargo.toml @@ -23,12 +23,12 @@ cramium-api = { path = "../../libs/cramium-api", features = [ "derive-rkyv", "std", ] } -xous-pl230 = { path = "../../libs/xous-pl230", features = ["cramium-soc"] } +# xous-pl230 = { path = "../../libs/xous-pl230", features = ["cramium-soc"] } # xous-pio = { path = "../../libs/xous-pio", default-features = false, features = [ # "cramium-soc", # ] } -pio-proc = "0.2.2" -pio = "0.2.1" +# pio-proc = "0.2.2" +# pio = "0.2.1" rand_core = "0.6.4" rand_chacha = "0.3.1" bitfield = "0.13.2" From a013720b6f1bb9a62a8855d0842c53458322b0c5 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 01:29:13 +0800 Subject: [PATCH 18/35] more dependency fixup --- services/modals/Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/modals/Cargo.toml b/services/modals/Cargo.toml index f30343126..c8e971e07 100644 --- a/services/modals/Cargo.toml +++ b/services/modals/Cargo.toml @@ -28,6 +28,7 @@ bit_field = "0.9.0" cramium-hal = { path = "../../libs/cramium-hal", optional = true, features = [ "std", ] } +cramium-api = { path = "../../libs/cramium-api", optional = true } cramium-emu = { path = "../cramium-emu", optional = true } blitstr2 = { path = "../../libs/blitstr2" } ux-api = { path = "../../libs/ux-api", optional = true, features = ["std"] } @@ -40,8 +41,8 @@ hosted = ["utralib/hosted", "trng", "gam", "tts-frontend"] renode = ["utralib/renode", "trng", "gam", "tts-frontend"] cramium-soc = ["utralib/cramium-soc", "cram-hal-service"] -hosted-baosec = ["ux-api/hosted-baosec", "cramium-emu"] -board-baosec = ["ux-api/board-baosec"] +hosted-baosec = ["ux-api/hosted-baosec", "cramium-emu", "cramium-api"] +board-baosec = ["ux-api/board-baosec", "cramium-api"] doc-deps = ["gam", "ux-api/doc-deps"] modal-testing = [] From aafec72e6436ad46263195e267e6257f51d0c4d4 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 02:04:22 +0800 Subject: [PATCH 19/35] add keyboard listener to modals in cramium targets --- services/modals/src/main.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/services/modals/src/main.rs b/services/modals/src/main.rs index b95a66f59..8197bfb40 100644 --- a/services/modals/src/main.rs +++ b/services/modals/src/main.rs @@ -36,14 +36,14 @@ use cram_hal_service::trng::Trng; use cramium_emu::trng::Trng; #[cfg(feature = "ditherpunk")] use gam::Bitmap; -#[cfg(not(any(feature = "hosted-baosec", feature = "cramium-soc")))] +#[cfg(not(any(feature = "hosted-baosec", feature = "board-baosec")))] use gam::modal::*; use locales::t; #[cfg(all(not(feature = "cramium-soc"), not(feature = "doc-deps"), not(feature = "hosted-baosec")))] use trng::Trng; #[cfg(feature = "tts")] use tts_frontend::TtsFrontend; -#[cfg(any(feature = "hosted-baosec", feature = "cramium-soc"))] +#[cfg(any(feature = "hosted-baosec", feature = "board-baosec"))] use ux_api::widgets::*; use xous::{Message, msg_blocking_scalar_unpack, msg_scalar_unpack, send_message}; use xous_ipc::Buffer; @@ -144,6 +144,11 @@ fn wrapped_main() -> ! { #[cfg(any(feature = "hosted-baosec", feature = "cramium-soc"))] let mut renderer_modal = Modal::new(ActionType::TextEntry(text_action.clone()), Some("Placeholder"), None, DEFAULT_STYLE, 8); + #[cfg(any(feature = "hosted-baosec", feature = "cramium-soc"))] + { + let kbd = cramium_api::keyboard::Keyboard::new(&xns).unwrap(); + kbd.register_listener(api::SERVER_NAME_MODALS, Opcode::ModalKeypress.to_u32().unwrap() as usize); + } #[cfg(not(any(feature = "hosted-baosec", feature = "cramium-soc")))] renderer_modal.spawn_helper( modals_sid, From 0790a2dc16f0ec4f912d308f17b9f31e5129d858 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 02:04:50 +0800 Subject: [PATCH 20/35] more feature wrangling --- services/bao-console/Cargo.toml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/services/bao-console/Cargo.toml b/services/bao-console/Cargo.toml index 54f0400e5..f1885d894 100644 --- a/services/bao-console/Cargo.toml +++ b/services/bao-console/Cargo.toml @@ -18,13 +18,15 @@ cramium-hal = { path = "../../libs/cramium-hal", features = [ cramium-api = { path = "../../libs/cramium-api" } cram-hal-service = { path = "../cram-hal-service", optional = true } cramium-emu = { path = "../cramium-emu", optional = true } -usb-cramium = { path = "../usb-cramium", features = ["cramium-soc"] } -modals = { path = "../modals", default-features = false, features = [ - "cramium-soc", -] } +usb-cramium = { path = "../usb-cramium" } +modals = { path = "../modals", default-features = false } [features] -cramium-soc = ["utralib/cramium-soc"] +cramium-soc = [ + "utralib/cramium-soc", + "modals/cramium-soc", + "usb-cramium/cramium-soc", +] board-baosec = [ "modals/board-baosec", "cram-hal-service", From 938e8388e01c549ed2cad1319943c51e3c204f2c Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 02:05:21 +0800 Subject: [PATCH 21/35] add modals acquire/release logic to the graphics layer --- services/bao-video/src/main.rs | 52 +++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/services/bao-video/src/main.rs b/services/bao-video/src/main.rs index 9e5c129c7..0b95aa2e1 100644 --- a/services/bao-video/src/main.rs +++ b/services/bao-video/src/main.rs @@ -9,9 +9,12 @@ mod qr; #[cfg(feature = "gfx-testing")] mod testing; -use std::sync::{ - Arc, - atomic::{AtomicBool, Ordering}, +use std::{ + collections::VecDeque, + sync::{ + Arc, + atomic::{AtomicBool, Ordering}, + }, }; use blitstr2::fontmap; @@ -37,6 +40,7 @@ use utralib::utra; use ux_api::minigfx::{self, FrameBuffer}; use ux_api::service::api::*; use xous::MemoryRange; +use xous::sender::Sender; use xous_ipc::Buffer; // Scope of this crate: *No calls to modals* this can create dependency lockups. @@ -189,7 +193,7 @@ fn main() -> ! { pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! { log_server::init_wait().unwrap(); - log::set_max_level(log::LevelFilter::Info); + log::set_max_level(log::LevelFilter::Trace); log::info!("my PID is {}", xous::process::id()); // ---- Xous setup @@ -299,6 +303,7 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! { cam.capture_async(); } + let mut modal_queue = VecDeque::::new(); let mut frames = 0; let mut frame = [0u8; IMAGE_WIDTH * IMAGE_HEIGHT]; let mut decode_success; @@ -636,6 +641,41 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! { log::error!("Invalid call to bao video server: {:?}", msg); } + // ---- v2 graphics API + GfxOpcode::AcquireModal => { + if let Some(scalar) = msg.body.scalar_message_mut() { + let sender = msg.sender; + log::debug!("Acquirer Sender: {:x?}", sender); + modal_queue.push_back(sender); + if modal_queue.len() > 1 { + // Prevents `msg` from being "dropped" which would cause the blocking scalar to + // return + core::mem::forget(msg_opt.take()); + } else { + scalar.arg1 = 0; + // the message is responded to, which allows the caller to unblock + } + } + } + GfxOpcode::ReleaseModal => { + if let Some(_scalar) = msg.body.scalar_message() { + let sender = msg.sender; + log::debug!("Release Sender: {:x?}", sender); + if let Some(pos) = modal_queue + .iter() + .position(|x| x.to_usize() & 0xffff_0000 == sender.to_usize() & 0xffff_0000) + { + modal_queue.remove(pos); + } else { + log::error!("Release modal called but sender {:x?} was not found", sender); + }; + if let Some(sender) = modal_queue.front() { + // Notify the waiter that it is allowed to run + xous::return_scalar(*sender, 0).unwrap(); + } + } + } + // ---- "regular" graphics API GfxOpcode::DrawClipObject => { minigfx::handlers::draw_clip_object(&mut display, msg); @@ -651,9 +691,7 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! { display.redraw(); } GfxOpcode::Clear => { - let mut r = Rectangle::full_screen(); - r.style = DrawStyle::new(PixelColor::Light, PixelColor::Light, 0); - op::rectangle(&mut display, r, screen_clip.into(), false) + display.clear(); } GfxOpcode::Line => { minigfx::handlers::line(&mut display, screen_clip.into(), msg); From 8602af4d18a2ba00f8de07bef33b1dd08d0ea5dc Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 02:07:17 +0800 Subject: [PATCH 22/35] add modals adapter code initial take just lifted directly from the gam version; layout details needs adapting for small screen still --- libs/ux-api/src/widgets/modal.rs | 63 +++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/libs/ux-api/src/widgets/modal.rs b/libs/ux-api/src/widgets/modal.rs index f230c1c98..0cb447367 100644 --- a/libs/ux-api/src/widgets/modal.rs +++ b/libs/ux-api/src/widgets/modal.rs @@ -6,8 +6,20 @@ use xous_ipc::Buffer; use super::*; use crate::minigfx::*; use crate::platform::{LINES, WIDTH}; +use crate::service::api::Gid; +use crate::service::gfx::Gfx; + +#[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive)] +pub enum ModalOpcode { + // if changes are made here, also update MenuOpcode + Redraw = 0x4000_0000, /* set the high bit so that "standard" enums don't conflict with the + * Modal-specific opcodes */ + Rawkeys, + Quit, +} pub struct Modal<'a> { + gfx: Gfx, pub top_text: Option, pub bot_text: Option, pub action: ActionType, @@ -56,7 +68,9 @@ impl<'a> Modal<'a> { // we now have a canvas that is some minimal height, but with the final width as allowed by the GAM. // compute the final height based upon the contents within. + let xns = xous_names::XousNames::new().unwrap(); let mut modal = Modal { + gfx: Gfx::new(&xns).unwrap(), top_text: None, bot_text: None, action, @@ -83,17 +97,29 @@ impl<'a> Modal<'a> { } pub fn activate(&self) { - todo!(); + self.gfx.acquire_modal().expect("Couldn't acquire lock on graphics subsystem"); + self.gfx.clear().ok(); + self.redraw(); + self.gfx.flush().ok(); } - pub fn redraw(&mut self) { + pub fn redraw(&self) { let action_resolver: Box<&dyn ActionApi> = Box::new(&self.action); action_resolver.redraw(0, &self); - todo!(); } pub fn key_event(&mut self, keys: [char; 4]) { - todo!(); + for &k in keys.iter() { + if k != '\u{0}' { + log::debug!("got key '{}'", k); + let action_resolver: Box<&mut dyn ActionApi> = Box::new(&mut self.action); + let err = action_resolver.key_action(k); + if let Some(err_msg) = err { + self.modify(None, None, false, Some(&err_msg), false, None); + } + } + } + self.redraw(); } /// This empowers an action within a modal to potentially consume all the available height in a canvas @@ -175,8 +201,6 @@ impl<'a> Modal<'a> { pub fn screen_bounds() -> Point { Point::new(WIDTH as isize, LINES as isize) } // comment this out while I figure out how to shim in the HAL layer for the graphics emulation -fn layout(modal: &mut Modal, top_text: Option<&str>, bot_text: Option<&str>, style: GlyphStyle) {} -/* fn layout(modal: &mut Modal, top_text: Option<&str>, bot_text: Option<&str>, style: GlyphStyle) { // we need to set a "max" size to our modal box, so that the text computations don't fail later on let current_bounds = screen_bounds(); @@ -194,10 +218,13 @@ fn layout(modal: &mut Modal, top_text: Option<&str>, bot_text: Option<&str>, sty log::trace!("step 0 total_height: {}", total_height); // compute height of top_text, if any if let Some(top_str) = top_text { - let mut top_tv = TextView::new(TextBounds::GrowableFromTl( - Point::new(modal.margin, modal.margin), - (modal.canvas_width - modal.margin * 2) as u16, - )); + let mut top_tv = TextView::new( + Gid::dummy(), + TextBounds::GrowableFromTl( + Point::new(modal.margin, modal.margin), + (modal.canvas_width - modal.margin * 2) as u16, + ), + ); top_tv.draw_border = false; top_tv.style = style; top_tv.margin = Point::new(0, 0); // all margin already accounted for in the raw bounds of the text drawing @@ -212,7 +239,7 @@ fn layout(modal: &mut Modal, top_text: Option<&str>, bot_text: Option<&str>, sty write!(top_tv.text, "{}", top_str).unwrap(); log::trace!("posting top tv: {:?}", top_tv); - modal.gam.bounds_compute_textview(&mut top_tv).expect("couldn't simulate top text size"); + modal.gfx.bounds_compute_textview(&mut top_tv).expect("couldn't simulate top text size"); if let Some(bounds) = top_tv.bounds_computed { log::trace!("top_tv bounds computed {}", bounds.br.y - bounds.tl.y); total_height += bounds.br.y - bounds.tl.y; @@ -235,10 +262,13 @@ fn layout(modal: &mut Modal, top_text: Option<&str>, bot_text: Option<&str>, sty // compute height of bot_text, if any log::trace!("step 2 total_height: {}", total_height); if let Some(bot_str) = bot_text { - let mut bot_tv = TextView::new(TextBounds::GrowableFromTl( - Point::new(modal.margin, total_height), - (modal.canvas_width - modal.margin * 2) as u16, - )); + let mut bot_tv = TextView::new( + Gid::dummy(), + TextBounds::GrowableFromTl( + Point::new(modal.margin, total_height), + (modal.canvas_width - modal.margin * 2) as u16, + ), + ); bot_tv.draw_border = false; bot_tv.style = style; bot_tv.margin = Point::new(0, 0); // all margin already accounted for in the raw bounds of the text drawing @@ -253,7 +283,7 @@ fn layout(modal: &mut Modal, top_text: Option<&str>, bot_text: Option<&str>, sty write!(bot_tv.text, "{}", bot_str).unwrap(); log::trace!("posting bot tv: {:?}", bot_tv); - modal.gam.bounds_compute_textview(&mut bot_tv).expect("couldn't simulate bot text size"); + modal.gfx.bounds_compute_textview(&mut bot_tv).expect("couldn't simulate bot text size"); if let Some(bounds) = bot_tv.bounds_computed { total_height += bounds.br.y - bounds.tl.y; } else { @@ -265,4 +295,3 @@ fn layout(modal: &mut Modal, top_text: Option<&str>, bot_text: Option<&str>, sty } log::trace!("step 3 total_height: {}", total_height); } -*/ From 5d4d009e64a430a3aff6c2b5549903b90d7e3287 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 19:00:33 +0800 Subject: [PATCH 23/35] add object list draw routine this can accelerate drawing of complicated objects, such as QR codes, that are composed of many rectangles. there are some warts...in theory, this should be able to seralize arbitrary-sized lists, but there must be some bug somewhere that is preventing it from happening. For now, the list length is clipped to the capacity of a page of memory. --- libs/ux-api/src/minigfx/clip.rs | 30 +++++++++++++++++++++++++++++ libs/ux-api/src/minigfx/handlers.rs | 27 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/libs/ux-api/src/minigfx/clip.rs b/libs/ux-api/src/minigfx/clip.rs index aab79a69c..643948155 100644 --- a/libs/ux-api/src/minigfx/clip.rs +++ b/libs/ux-api/src/minigfx/clip.rs @@ -41,3 +41,33 @@ impl ClipObjectList { } } } + +#[cfg_attr(feature = "derive-rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[derive(Debug, Clone)] +/// This API relies on an upgraded version of rkyv that was not available when the ClipObjectList +/// API was defined: we can now just use a `Vec` to do the trick. +pub struct ObjectList { + pub list: Vec, +} +impl ObjectList { + pub fn new() -> Self { Self { list: Vec::new() } } + + /// The intent was for the push() to be infalliable, but in practice, draw lists could get + /// arbitrarily large and some back-pressure is needed to keep the memory allocation within + /// bounds that the system can handle. Thus, this method can fail returning the pushed object, + /// at which point one should send the draw list to the graphics engine, and retry the push. + pub fn push(&mut self, item: ClipObjectType) -> Result<(), ClipObjectType> { + // TODO: export the capacity limit of a buffer. The origin of the capacity limit is equal to + // the size of a page of memory, plus 256 bytes for "scratch" area for rkyv to work in. I did + // try to use the .replace() method with an allocation of a large enough buffer to hold the whole + // Vec, but it seems to fail. I think it could be that the scratch space hard-coded into the IPC + // library is not big enough... + if self.list.capacity() * size_of::() + size_of::>() < 4096 - 256 + { + self.list.push(item); + Ok(()) + } else { + Err(item) + } + } +} diff --git a/libs/ux-api/src/minigfx/handlers.rs b/libs/ux-api/src/minigfx/handlers.rs index 2e5123e9c..0282f977d 100644 --- a/libs/ux-api/src/minigfx/handlers.rs +++ b/libs/ux-api/src/minigfx/handlers.rs @@ -70,6 +70,33 @@ pub fn draw_clip_object_list(display: &mut T, msg: &mut xous::en } } +pub fn draw_object_list(display: &mut T, msg: &mut xous::envelope::Envelope) { + let buffer = unsafe { Buffer::from_memory_message(msg.body.memory_message().unwrap()) }; + let list_ipc = buffer.to_original::().unwrap(); + for &item in list_ipc.list.iter() { + match item { + ClipObjectType::Line(line) => { + op::line(display, line, None, false); + } + ClipObjectType::XorLine(line) => { + op::line(display, line, None, true); + } + ClipObjectType::Circ(circ) => { + op::circle(display, circ, None); + } + ClipObjectType::Rect(rect) => { + op::rectangle(display, rect, None, false); + } + ClipObjectType::RoundRect(rr) => { + op::rounded_rectangle(display, rr, None); + } + _ => { + unimplemented!("Object type not yet implemented"); + } + } + } +} + pub fn line( display: &mut T, screen_clip: Option, From 15f41a5f5bcccc213b821244aecb60a9eed25359 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 19:01:47 +0800 Subject: [PATCH 24/35] object list API elements --- libs/ux-api/src/service/api.rs | 2 ++ libs/ux-api/src/service/gfx.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/libs/ux-api/src/service/api.rs b/libs/ux-api/src/service/api.rs index 3193e4205..c64cd4f43 100644 --- a/libs/ux-api/src/service/api.rs +++ b/libs/ux-api/src/service/api.rs @@ -94,6 +94,8 @@ pub enum GfxOpcode { /// V2 API for claiming ownership of screen for modal operation AcquireModal, ReleaseModal, + /// V2 API for fast drawing of multiple objects + UnclippedObjectList, /// Gutter for invalid calls InvalidCall, diff --git a/libs/ux-api/src/service/gfx.rs b/libs/ux-api/src/service/gfx.rs index b75a448a8..3f3b20a0b 100644 --- a/libs/ux-api/src/service/gfx.rs +++ b/libs/ux-api/src/service/gfx.rs @@ -705,6 +705,18 @@ impl Gfx { pub fn release_modal(&self) -> Result { send_message(self.conn, Message::new_scalar(GfxOpcode::ReleaseModal.to_usize().unwrap(), 0, 0, 0, 0)) } + + /// V2-only API + pub fn draw_object_list(&self, list: ObjectList) -> Result<(), xous::Error> { + let buf = match Buffer::into_buf(list) { + Ok(b) => b, + Err(e) => { + log::error!("err: {:?}", e); + panic!("error") + } + }; + buf.lend(self.conn, GfxOpcode::UnclippedObjectList.to_u32().unwrap()).map(|_| ()) + } } use core::sync::atomic::{AtomicU32, Ordering}; From 1a2d6bf7728d4ca495bdc42f741c55413b3694c7 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 19:02:15 +0800 Subject: [PATCH 25/35] intercept and warn on more v2 calls in v1 implementation --- services/graphics-server/src/main.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/services/graphics-server/src/main.rs b/services/graphics-server/src/main.rs index 9ddd35545..7b5fc5c05 100644 --- a/services/graphics-server/src/main.rs +++ b/services/graphics-server/src/main.rs @@ -471,10 +471,8 @@ fn wrapped_main(main_thread_token: backend::MainThreadToken) -> ! { } // no failure if it's not } - GfxOpcode::AcquireModal | GfxOpcode::ReleaseModal => { - unimplemented!( - "Acquire/Release modal is not supported for this target. Use the GAM instead." - ); + GfxOpcode::AcquireModal | GfxOpcode::ReleaseModal | GfxOpcode::UnclippedObjectList => { + unimplemented!("{:?} is not supported for this target. Use the GAM instead.", op); } GfxOpcode::Quit => break, GfxOpcode::InvalidCall => { From 3c5975715d4a4919b27fc69ca25b90965ad8da4c Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 19:02:45 +0800 Subject: [PATCH 26/35] kind of hack to make font rendering work on oled displays so, there is a fundamental abstraction mismatch in the blitstr library. In Precursor, I created a rule that you can't draw text on black backgrounds without some special permissions in highly trusted contexts. For baosec, this restriction is removed because the whole OS can run inside a single chip (i.e. we have eliminated most easy RAM modification attacks, requires probing/glitching; but as a trade-off, we don't have as much RAM so we can't afford the extra abstractions). The compromise is for OLED-type platforms where inverse drawing is very common to save power, all text ops are assumed to be XOR operations (so you're always flipping pixels instead of drawing at a defined color). --- libs/ux-api/src/wordwrap.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/libs/ux-api/src/wordwrap.rs b/libs/ux-api/src/wordwrap.rs index e0057a89e..d9a5ebcda 100644 --- a/libs/ux-api/src/wordwrap.rs +++ b/libs/ux-api/src/wordwrap.rs @@ -160,18 +160,20 @@ impl ComposedType { let cr = ClipRect::new(clip_rect.tl().x, clip_rect.tl().y, clip_rect.br().x, clip_rect.br().y); let rawbuf = unsafe { frbuf.raw_mut() }; - if glyph.large { - blitstr2::xor_glyph_large( - rawbuf, - (maybe_x, maybe_y), - glyph, - glyph.invert ^ invert, - cr, - ); - } else if !glyph.double { - blitstr2::xor_glyph(rawbuf, (maybe_x, maybe_y), glyph, glyph.invert ^ invert, cr); + let char_invert = if cfg!(feature = "hosted-baosec") || cfg!(feature = "board-baosec") { + // OLED display background is black for power savings, so all polarities of drawing + // need to be allowed + true } else { - blitstr2::xor_glyph_2x(rawbuf, (maybe_x, maybe_y), glyph, glyph.invert ^ invert, cr); + glyph.invert ^ invert + }; + + if glyph.large { + blitstr2::xor_glyph_large(rawbuf, (maybe_x, maybe_y), glyph, char_invert, cr); + } else if !glyph.double { + blitstr2::xor_glyph(rawbuf, (maybe_x, maybe_y), glyph, char_invert, cr); + } else { + blitstr2::xor_glyph_2x(rawbuf, (maybe_x, maybe_y), glyph, char_invert, cr); } // Platform-dependent cursor inversion because the color scheme for sh1107 doesn't comply // to precursor norms From 1e9e02435b951be8d313c7e52cef9391f5e5775e Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 19:05:41 +0800 Subject: [PATCH 27/35] add missing model boilerplate a lot of routines were left blank in the initial port to get through the core graphics primitives porting. This commit fills in those missing bits in a fashion that optimizes the modals for drawing on small displays. --- libs/ux-api/src/widgets/modal.rs | 130 +++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 33 deletions(-) diff --git a/libs/ux-api/src/widgets/modal.rs b/libs/ux-api/src/widgets/modal.rs index 0cb447367..275fe9b3e 100644 --- a/libs/ux-api/src/widgets/modal.rs +++ b/libs/ux-api/src/widgets/modal.rs @@ -4,6 +4,7 @@ use blitstr2::{GlyphStyle, glyph_height_hint}; use xous_ipc::Buffer; use super::*; +use crate::minigfx::op::HEIGHT; use crate::minigfx::*; use crate::platform::{LINES, WIDTH}; use crate::service::api::Gid; @@ -19,7 +20,7 @@ pub enum ModalOpcode { } pub struct Modal<'a> { - gfx: Gfx, + pub gfx: Gfx, pub top_text: Option, pub bot_text: Option, pub action: ActionType, @@ -96,16 +97,74 @@ impl<'a> Modal<'a> { modal } - pub fn activate(&self) { + pub fn activate(&mut self) { self.gfx.acquire_modal().expect("Couldn't acquire lock on graphics subsystem"); self.gfx.clear().ok(); self.redraw(); self.gfx.flush().ok(); } - pub fn redraw(&self) { + pub fn redraw(&mut self) { + let do_redraw = self.top_dirty || self.bot_dirty || self.inverted; + if do_redraw { + self.gfx.clear().unwrap(); + } + let mut cur_height = self.margin; + if let Some(mut tv) = self.top_text.as_mut() { + if do_redraw { + self.gfx.draw_textview(&mut tv).expect("couldn't draw text"); + if let Some(bounds) = tv.bounds_computed { + let y = bounds.br.y - bounds.tl.y; + let y_clip = if y > HEIGHT - self.line_height * 3 { + log::warn!("overside text, clipping back {}", HEIGHT - (self.line_height * 2)); + HEIGHT - (self.line_height * 2) + } else { + y + }; + cur_height += y_clip; + log::trace!("top_tv height: {}", y_clip); + self.top_memoized_height = Some(y_clip); + } else { + log::warn!("text bounds didn't compute setting to max"); + self.top_memoized_height = Some(HEIGHT - (self.line_height * 2)); + } + self.top_dirty = false; + } else { + cur_height += + self.top_memoized_height.expect("internal error: memoization didn't work correctly"); + } + } else { + self.top_dirty = false; + } + + let action_height = self.action.height(self.line_height, self.margin, &self); + if !do_redraw { + self.gfx.clear().unwrap(); + } + let action_resolver: Box<&dyn ActionApi> = Box::new(&self.action); - action_resolver.redraw(0, &self); + action_resolver.redraw(cur_height, &self); + + cur_height += action_height; + + if let Some(mut tv) = self.bot_text.as_mut() { + if do_redraw { + self.gfx.draw_textview(&mut tv).expect("couldn't draw text"); + if let Some(bounds) = tv.bounds_computed { + cur_height += bounds.br.y - bounds.tl.y; + self.bot_memoized_height = Some(bounds.br.y - bounds.tl.y); + } + self.bot_dirty = false; + } else { + cur_height += + self.bot_memoized_height.expect("internal error: memoization didn't work correctly"); + } + } else { + self.bot_dirty = false; + } + log::trace!("total height: {}", cur_height); + log::trace!("modal redraw##"); + self.gfx.flush().unwrap(); } pub fn key_event(&mut self, keys: [char; 4]) { @@ -195,6 +254,7 @@ impl<'a> Modal<'a> { } else { self.style }; + layout(self, top_text, bot_text, style); } } @@ -218,37 +278,41 @@ fn layout(modal: &mut Modal, top_text: Option<&str>, bot_text: Option<&str>, sty log::trace!("step 0 total_height: {}", total_height); // compute height of top_text, if any if let Some(top_str) = top_text { - let mut top_tv = TextView::new( - Gid::dummy(), - TextBounds::GrowableFromTl( - Point::new(modal.margin, modal.margin), - (modal.canvas_width - modal.margin * 2) as u16, - ), - ); - top_tv.draw_border = false; - top_tv.style = style; - top_tv.margin = Point::new(0, 0); // all margin already accounted for in the raw bounds of the text drawing - top_tv.ellipsis = false; - top_tv.invert = modal.inverted; - // specify a clip rect that's the biggest possible allowed. If we don't do this, the current canvas - // bounds are used, and the operation will fail if the text has to get bigger. - top_tv.clip_rect = Some(Rectangle::new( - Point::new(0, 0), - Point::new(current_bounds.x, LINES - 2 * modal.line_height), - )); - write!(top_tv.text, "{}", top_str).unwrap(); + if top_str.len() > 0 { + let mut top_tv = TextView::new( + Gid::dummy(), + TextBounds::GrowableFromTl( + Point::new(modal.margin, modal.margin), + (modal.canvas_width - modal.margin * 2) as u16, + ), + ); + top_tv.draw_border = false; + top_tv.style = style; + top_tv.margin = Point::new(0, 0); // all margin already accounted for in the raw bounds of the text drawing + top_tv.ellipsis = false; + top_tv.invert = true; + // specify a clip rect that's the biggest possible allowed. If we don't do this, the current + // canvas bounds are used, and the operation will fail if the text has to get bigger. + top_tv.clip_rect = Some(Rectangle::new( + Point::new(0, 0), + Point::new(current_bounds.x, LINES - 2 * modal.line_height), + )); + write!(top_tv.text, "{}", top_str).unwrap(); - log::trace!("posting top tv: {:?}", top_tv); - modal.gfx.bounds_compute_textview(&mut top_tv).expect("couldn't simulate top text size"); - if let Some(bounds) = top_tv.bounds_computed { - log::trace!("top_tv bounds computed {}", bounds.br.y - bounds.tl.y); - total_height += bounds.br.y - bounds.tl.y; + log::trace!("posting top tv: {:?}", top_tv); + modal.gfx.bounds_compute_textview(&mut top_tv).expect("couldn't simulate top text size"); + if let Some(bounds) = top_tv.bounds_computed { + log::trace!("top_tv bounds computed {}", bounds.br.y - bounds.tl.y); + total_height += bounds.br.y - bounds.tl.y; + } else { + log::warn!("couldn't compute height for modal top_text: {:?}", top_tv); + // probably should find a better way to deal with this. + total_height += LINES - (modal.line_height * 2); + } + modal.top_text = Some(top_tv); } else { - log::warn!("couldn't compute height for modal top_text: {:?}", top_tv); - // probably should find a better way to deal with this. - total_height += LINES - (modal.line_height * 2); + modal.top_text = None; } - modal.top_text = Some(top_tv); } total_height += modal.margin; @@ -273,7 +337,7 @@ fn layout(modal: &mut Modal, top_text: Option<&str>, bot_text: Option<&str>, sty bot_tv.style = style; bot_tv.margin = Point::new(0, 0); // all margin already accounted for in the raw bounds of the text drawing bot_tv.ellipsis = false; - bot_tv.invert = modal.inverted; + bot_tv.invert = true; // specify a clip rect that's the biggest possible allowed. If we don't do this, the current canvas // bounds are used, and the operation will fail if the text has to get bigger. bot_tv.clip_rect = Some(Rectangle::new( From 63a9a2b0a05feae408436e6aaab0beab00e00cd5 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 19:06:41 +0800 Subject: [PATCH 28/35] implement notification models for small screens --- libs/ux-api/src/widgets/notification.rs | 141 +++++++++++++++++++++++- 1 file changed, 135 insertions(+), 6 deletions(-) diff --git a/libs/ux-api/src/widgets/notification.rs b/libs/ux-api/src/widgets/notification.rs index 2598ba613..0452fe2d9 100644 --- a/libs/ux-api/src/widgets/notification.rs +++ b/libs/ux-api/src/widgets/notification.rs @@ -4,12 +4,16 @@ use locales::t; use qrcode::{Color, QrCode}; use super::*; +use crate::minigfx::op::{HEIGHT, WIDTH}; use crate::minigfx::*; +use crate::service::api::*; +use crate::service::gfx::Gfx; -pub(crate) const QUIET_MODULES: i16 = 2; +pub(crate) const QUIET_MODULES: isize = 2; #[derive(Debug)] pub struct Notification { + pub gfx: Gfx, pub action_conn: xous::CID, pub action_opcode: u32, pub is_password: bool, @@ -19,7 +23,9 @@ pub struct Notification { } impl Notification { pub fn new(action_conn: xous::CID, action_opcode: u32) -> Self { + let xns = xous_names::XousNames::new().unwrap(); Notification { + gfx: Gfx::new(&xns).unwrap(), action_conn, action_opcode, is_password: false, @@ -62,14 +68,137 @@ impl Notification { } } - fn draw_text(&self, at_height: i16, modal: &Modal) { - todo!(); + fn draw_text(&self, at_height: isize, modal: &Modal) { + // prime a textview with the correct general style parameters + let mut tv = TextView::new(Gid::dummy(), TextBounds::BoundingBox(Rectangle::new_coords(0, 0, 1, 1))); + tv.ellipsis = true; + tv.style = modal.style; + tv.invert = true; + tv.draw_border = false; + tv.margin = Point::new(0, 0); + tv.insertion = None; + + tv.bounds_computed = None; + tv.bounds_hint = TextBounds::GrowableFromTl( + Point::new(modal.margin, at_height + modal.margin * 2), + (modal.canvas_width - modal.margin * 2) as u16, + ); + write!(tv, "{}", t!("notification.dismiss", locales::LANG)).unwrap(); + self.gfx.bounds_compute_textview(&mut tv).expect("couldn't simulate text size"); + let textwidth = if let Some(bounds) = tv.bounds_computed { + bounds.br.x - bounds.tl.x + } else { + modal.canvas_width - modal.margin * 2 + }; + let offset = (modal.canvas_width - textwidth) / 2; + tv.bounds_computed = None; + tv.bounds_hint = TextBounds::BoundingBox(Rectangle::new( + Point::new(offset, at_height + modal.margin * 2), + Point::new(modal.canvas_width - modal.margin, at_height + modal.line_height + modal.margin * 2), + )); + self.gfx.draw_textview(&mut tv).expect("couldn't post tv"); } - fn draw_qrcode(&self, at_height: i16, modal: &Modal) { - todo!(); + fn draw_qrcode(&self, at_height: isize, modal: &Modal) { + // calculate pixel size of each module in the qrcode + let qrcode_modules: isize = self.qrwidth.try_into().unwrap(); + let modules: isize = qrcode_modules + 2 * QUIET_MODULES; + let canvas_width = modal.canvas_width - 2 * modal.margin; + let mod_size_px: isize = canvas_width / modules; + let qrcode_width_px = qrcode_modules * mod_size_px; + let quiet_px: isize = (canvas_width - qrcode_width_px) / 2; + + // Iterate thru qrcode and stamp each square module like a typewriter + let black = DrawStyle::new(PixelColor::Dark, PixelColor::Dark, 1); + let top = at_height + quiet_px; + + // draw a white background over the whole screen + self.gfx + .draw_rectangle(Rectangle::new_with_style( + Point::new(0, at_height), + Point::new(WIDTH, HEIGHT), + DrawStyle::new(PixelColor::Light, PixelColor::Light, 1), + )) + .unwrap(); + + let left = modal.margin + quiet_px; + let right = left + qrcode_modules * mod_size_px; + let mut module = + Rectangle::new_with_style(Point::new(0, 0), Point::new(mod_size_px - 1, mod_size_px - 1), black); + let step = Point::new(mod_size_px, 0); + let cr_lf = Point::new(-qrcode_modules * mod_size_px, mod_size_px); + let mut j: isize; + let mut obj_list = ObjectList::new(); + module.translate(Point::new(right, top)); + for (i, stamp) in self.qrcode.iter().enumerate() { + j = i.try_into().unwrap(); + if j % qrcode_modules == 0 { + module.translate(cr_lf); + } + if *stamp { + if obj_list.push(ClipObjectType::Rect(module.clone())).is_err() { + // the capacity of the list has exceeded the capacity of the buffer, send it and make a + // new one + self.gfx.draw_object_list(obj_list).expect("couldn't draw qrcode module"); + obj_list = ObjectList::new(); + obj_list.push(ClipObjectType::Rect(module.clone())).unwrap(); + }; + } + module.translate(step); + } + self.gfx.draw_object_list(obj_list).expect("couldn't draw qrcode module"); } } use crate::widgets::ActionApi; -impl ActionApi for Notification {} +impl ActionApi for Notification { + fn redraw(&self, at_height: isize, modal: &Modal) { + if self.qrwidth > 0 { + self.draw_qrcode(at_height, modal); + } else { + if self.manual_dismiss { + self.draw_text(at_height, modal); + modal + .gfx + .draw_line(Line::new_with_style( + Point::new(modal.margin, at_height + modal.margin), + Point::new(modal.canvas_width - modal.margin, at_height + modal.margin), + DrawStyle::new(PixelColor::Light, PixelColor::Light, 1), + )) + .expect("couldn't draw entry line"); + } + } + } + + fn key_action(&mut self, k: char) -> Option { + log::trace!("key_action: {}", k); + match k { + '\u{0}' => { + // ignore null messages + } + _ => { + // relinquish focus before returning the result + if self.manual_dismiss { + self.gfx.release_modal().unwrap(); + xous::yield_slice(); + } + + xous::send_message( + self.action_conn, + xous::Message::new_scalar(self.action_opcode as usize, k as u32 as usize, 0, 0, 0), + ) + .expect("couldn't pass on dismissal"); + if self.manual_dismiss { + return None; + } + } + } + None + } + + fn height(&self, _glyph_height: isize, margin: isize, _modal: &Modal) -> isize { + if self.qrwidth > 0 { HEIGHT } else { margin } + } + + fn set_action_opcode(&mut self, op: u32) { self.action_opcode = op } +} From 7f860e058c846015fab084e165cdb2df7d1b1390 Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 19:07:10 +0800 Subject: [PATCH 29/35] add handler for object list drawing --- services/bao-video/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/bao-video/src/main.rs b/services/bao-video/src/main.rs index 0b95aa2e1..d4a060a96 100644 --- a/services/bao-video/src/main.rs +++ b/services/bao-video/src/main.rs @@ -683,6 +683,9 @@ pub fn wrapped_main(main_thread_token: MainThreadToken) -> ! { GfxOpcode::DrawClipObjectList => { minigfx::handlers::draw_clip_object_list(&mut display, msg); } + GfxOpcode::UnclippedObjectList => { + minigfx::handlers::draw_object_list(&mut display, msg); + } GfxOpcode::DrawTextView => { minigfx::handlers::draw_text_view(&mut display, msg); } From 57a4f24a027f23020771dd81dc4a79c9607a795c Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 4 Mar 2025 19:11:23 +0800 Subject: [PATCH 30/35] restrict ObjectList to `std` targets only --- libs/ux-api/src/minigfx/clip.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/ux-api/src/minigfx/clip.rs b/libs/ux-api/src/minigfx/clip.rs index 643948155..8a57cb714 100644 --- a/libs/ux-api/src/minigfx/clip.rs +++ b/libs/ux-api/src/minigfx/clip.rs @@ -46,9 +46,11 @@ impl ClipObjectList { #[derive(Debug, Clone)] /// This API relies on an upgraded version of rkyv that was not available when the ClipObjectList /// API was defined: we can now just use a `Vec` to do the trick. +#[cfg(feature = "std")] pub struct ObjectList { pub list: Vec, } +#[cfg(feature = "std")] impl ObjectList { pub fn new() -> Self { Self { list: Vec::new() } } From 77d18530f75b83e42e62cda4e492535fdce45b93 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 5 Mar 2025 20:47:51 +0800 Subject: [PATCH 31/35] match dependencies on modals tests to platform --- services/modals/src/tests.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/modals/src/tests.rs b/services/modals/src/tests.rs index 3a8b7cf91..1064c60e3 100644 --- a/services/modals/src/tests.rs +++ b/services/modals/src/tests.rs @@ -2,7 +2,10 @@ use std::thread; #[cfg(feature = "ditherpunk")] use bitmap::PixelType; +#[cfg(not(any(feature = "hosted-baosec", feature = "cramium-soc")))] use gam::*; +#[cfg(any(feature = "hosted-baosec", feature = "cramium-soc"))] +use ux_api::widgets::*; use xous_names::XousNames; use super::*; From 5a36ebbb58d3d8093488c9857dccc20b6665843e Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 5 Mar 2025 20:50:09 +0800 Subject: [PATCH 32/35] setup modals testing feature in the console runner --- services/bao-console/Cargo.toml | 1 + services/bao-console/src/main.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/services/bao-console/Cargo.toml b/services/bao-console/Cargo.toml index f1885d894..cc52dbf19 100644 --- a/services/bao-console/Cargo.toml +++ b/services/bao-console/Cargo.toml @@ -39,4 +39,5 @@ hosted-baosec = [ "cramium-emu", ] +modal-testing = [] default = [] diff --git a/services/bao-console/src/main.rs b/services/bao-console/src/main.rs index 6758ad36d..ec8034a13 100644 --- a/services/bao-console/src/main.rs +++ b/services/bao-console/src/main.rs @@ -12,6 +12,12 @@ fn main() { let tt = ticktimer::Ticktimer::new().unwrap(); shell::start_shell(); + #[cfg(feature = "modal-testing")] + { + log::set_max_level(log::LevelFilter::Debug); + modals::tests::spawn_test(); + } + loop { // just sleep as this is the parent thread tt.sleep_ms(120_000).ok(); From f91ff92b3a02bd9aaf0255fa2e40ec2fa97edabb Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 5 Mar 2025 20:50:34 +0800 Subject: [PATCH 33/35] add modals to the baosec emulation target --- xtask/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 47bd652ac..b21b2955b 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -606,7 +606,7 @@ fn main() -> Result<(), Box> { // - [planned] pddb server // - [planned] vault application let bao_rram_pkgs = ["xous-ticktimer", "xous-log", "xous-names" /* "usb-cramium" */].to_vec(); /* "usb-cramium" */ - let bao_swap_pkgs = ["cram-hal-service", "bao-console", "bao-video"].to_vec(); /* "bao-video" */ + let bao_swap_pkgs = ["cram-hal-service", "bao-console", "bao-video", "modals"].to_vec(); /* "bao-video" */ if !builder.is_swap_set() { builder.set_swap(0, 8 * 1024 * 1024); } From 48d58576813f951d6ffd4d0e288b1f4f31534151 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 5 Mar 2025 20:51:00 +0800 Subject: [PATCH 34/35] reflect updated dependencies --- Cargo.lock | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37dfacb36..b1813956d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -336,8 +336,10 @@ version = "0.1.0" dependencies = [ "cram-hal-service", "cramium-api", + "cramium-emu", "cramium-hal", "log", + "modals", "num-derive 0.4.2", "num-traits", "usb-cramium", @@ -953,7 +955,6 @@ version = "0.1.0" dependencies = [ "cram-hal-service", "cramium-hal", - "gam", "ime-plugin-shell", "locales", "log", @@ -968,7 +969,6 @@ dependencies = [ "xous-api-names", "xous-api-ticktimer", "xous-ipc 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", - "xous-pl230", "xous-swapper", ] @@ -982,8 +982,6 @@ dependencies = [ "log", "num-derive 0.4.2", "num-traits", - "pio", - "pio-proc", "rand_chacha 0.3.1", "rand_core 0.6.4", "rkyv 0.8.8", @@ -993,7 +991,6 @@ dependencies = [ "xous-api-names", "xous-api-ticktimer", "xous-ipc 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", - "xous-pl230", ] [[package]] @@ -3241,6 +3238,7 @@ dependencies = [ "bit_field", "blitstr2", "cram-hal-service", + "cramium-api", "cramium-emu", "cramium-hal", "gam", @@ -5865,6 +5863,7 @@ name = "usb-cramium" version = "0.1.0" dependencies = [ "cram-hal-service", + "cramium-emu", "cramium-hal", "frunk_core", "locales", From c49c41699b6b2390334082577307fd47dba99957 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 5 Mar 2025 20:54:03 +0800 Subject: [PATCH 35/35] cleanup warning --- libs/ux-api/src/minigfx/handlers.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/ux-api/src/minigfx/handlers.rs b/libs/ux-api/src/minigfx/handlers.rs index 0282f977d..8ee397ddf 100644 --- a/libs/ux-api/src/minigfx/handlers.rs +++ b/libs/ux-api/src/minigfx/handlers.rs @@ -90,6 +90,7 @@ pub fn draw_object_list(display: &mut T, msg: &mut xous::envelop ClipObjectType::RoundRect(rr) => { op::rounded_rectangle(display, rr, None); } + #[cfg(feature = "ditherpunk")] _ => { unimplemented!("Object type not yet implemented"); }