Merge branch 'main' into pddb-feat-down

This commit is contained in:
bunnie 2025-03-05 20:57:35 +08:00 committed by GitHub
commit d1860fee56
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 600 additions and 98 deletions

9
Cargo.lock generated
View file

@ -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",
@ -5866,6 +5864,7 @@ name = "usb-cramium"
version = "0.1.0"
dependencies = [
"cram-hal-service",
"cramium-emu",
"cramium-hal",
"frunk_core",
"locales",

View file

@ -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) {

View file

@ -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;

View file

@ -41,3 +41,35 @@ 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.
#[cfg(feature = "std")]
pub struct ObjectList {
pub list: Vec<ClipObjectType>,
}
#[cfg(feature = "std")]
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::<ClipObjectType>() + size_of::<Vec<ClipObjectType>>() < 4096 - 256
{
self.list.push(item);
Ok(())
} else {
Err(item)
}
}
}

View file

@ -70,6 +70,34 @@ pub fn draw_clip_object_list<T: FrameBuffer>(display: &mut T, msg: &mut xous::en
}
}
pub fn draw_object_list<T: FrameBuffer>(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::<ObjectList, _>().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);
}
#[cfg(feature = "ditherpunk")]
_ => {
unimplemented!("Object type not yet implemented");
}
}
}
}
pub fn line<T: FrameBuffer>(
display: &mut T,
screen_clip: Option<Rectangle>,
@ -166,7 +194,12 @@ pub fn draw_text_view<T: FrameBuffer>(display: &mut T, msg: &mut xous::envelope:
let mut tv = buffer.to_original::<TextView, _>().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

View file

@ -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<H>(&self, state: &mut H)
@ -89,6 +91,12 @@ pub enum GfxOpcode {
/// Handle Camera IRQs
CamIrq,
/// 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,

View file

@ -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::<TextView, _>().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,33 @@ 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<xous::Result, xous::Error> {
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<xous::Result, xous::Error> {
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};

View file

@ -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 '∴' | '←' | '→' | '↑' | '↓'

View file

@ -4,10 +4,23 @@ 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;
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> {
pub gfx: Gfx,
pub top_text: Option<TextView>,
pub bot_text: Option<TextView>,
pub action: ActionType,
@ -56,7 +69,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,
@ -82,18 +97,88 @@ impl<'a> Modal<'a> {
modal
}
pub fn activate(&self) {
todo!();
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(&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);
todo!();
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]) {
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
@ -169,14 +254,13 @@ impl<'a> Modal<'a> {
} else {
self.style
};
layout(self, top_text, bot_text, style);
}
}
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,34 +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(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.gam.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;
@ -235,15 +326,18 @@ 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
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(
@ -253,7 +347,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 +359,3 @@ fn layout(modal: &mut Modal, top_text: Option<&str>, bot_text: Option<&str>, sty
}
log::trace!("step 3 total_height: {}", total_height);
}
*/

View file

@ -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<ValidatorErr> {
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 }
}

View file

@ -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

View file

@ -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);

View file

@ -12,11 +12,32 @@ 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", 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",
"cramium-hal",
"usb-cramium/board-baosec",
]
hosted-baosec = [
"modals/hosted-baosec",
"usb-cramium/hosted-baosec",
"cramium-emu",
]
modal-testing = []
default = []

View file

@ -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

View file

@ -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();

View file

@ -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::<Sender>::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);
@ -643,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);
}
@ -651,9 +694,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);

View file

@ -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" = []

View file

@ -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"

View file

@ -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,
}
}

View file

@ -471,6 +471,9 @@ fn wrapped_main(main_thread_token: backend::MainThreadToken) -> ! {
}
// no failure if it's not
}
GfxOpcode::AcquireModal | GfxOpcode::ReleaseModal | GfxOpcode::UnclippedObjectList => {
unimplemented!("{:?} is not supported for this target. Use the GAM instead.", op);
}
GfxOpcode::Quit => break,
GfxOpcode::InvalidCall => {
log::error!("Received invalid GfxOpcode. Ignoring.");

View file

@ -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,9 +41,10 @@ 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 = []
tts = []
ditherpunk = []

View file

@ -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_";

View file

@ -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;
@ -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;

View file

@ -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,

View file

@ -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::*;

View file

@ -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"]

View file

@ -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::*;

View file

@ -365,7 +365,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.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()
@ -598,7 +606,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// - [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", "pddb"].to_vec(); /* "bao-video" */
let bao_swap_pkgs = ["cram-hal-service", "bao-console", "bao-video", "modals", "pddb"].to_vec(); /* "bao-video" */
if !builder.is_swap_set() {
builder.set_swap(0, 8 * 1024 * 1024);
}
@ -676,7 +684,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
"default = []",
)
.expect("couldn't patch AES");
builder::search_and_replace_in_file(
builder::search_and_replace_in_file(WIP modals refactoring
"Cargo.toml",
"# [patch.crates-io.curve25519-dalek]",
"[patch.crates-io.curve25519-dalek]",