You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/embed/rust/src/ui/layout/util.rs

175 lines
5.0 KiB

use crate::{
error::Error,
micropython::{
buffer::{hexlify_bytes, StrBuffer},
gc::Gc,
list::List,
obj::Obj,
util::{iter_into_array, try_or_raise},
},
storage::{get_avatar_len, load_avatar},
ui::{
component::text::{
paragraphs::{Paragraph, ParagraphSource},
TextStyle,
},
util::set_animation_disabled,
},
};
/// Maximum number of characters that can be displayed on screen at once. Used
/// for on-the-fly conversion of binary data to hexadecimal representation.
/// NOTE: can be fine-tuned for particular model screen to decrease memory
/// consumption and conversion time.
pub const MAX_HEX_CHARS_ON_SCREEN: usize = 256;
pub enum StrOrBytes {
Str(StrBuffer),
Bytes(Obj),
}
impl StrOrBytes {
pub fn as_str_offset(&self, offset: usize) -> StrBuffer {
match self {
StrOrBytes::Str(x) => x.skip_prefix(offset),
StrOrBytes::Bytes(x) => hexlify_bytes(*x, offset, MAX_HEX_CHARS_ON_SCREEN)
.unwrap_or_else(|_| StrBuffer::from("ERROR")),
}
}
}
impl TryFrom<Obj> for StrOrBytes {
type Error = Error;
fn try_from(obj: Obj) -> Result<Self, Error> {
if obj.is_str() {
Ok(StrOrBytes::Str(obj.try_into()?))
} else if obj.is_bytes() {
Ok(StrOrBytes::Bytes(obj))
} else {
Err(Error::TypeError)
}
}
}
pub struct ConfirmBlob {
pub description: StrBuffer,
pub extra: StrBuffer,
pub data: StrOrBytes,
pub description_font: &'static TextStyle,
pub extra_font: &'static TextStyle,
pub data_font: &'static TextStyle,
}
impl ParagraphSource<'static> for ConfirmBlob {
fn at(&self, index: usize, offset: usize) -> Paragraph<'static> {
match index {
0 => Paragraph::new(self.description_font, self.description.skip_prefix(offset)),
1 => Paragraph::new(self.extra_font, self.extra.skip_prefix(offset)),
2 => Paragraph::new(self.data_font, self.data.as_str_offset(offset)),
_ => unreachable!(),
}
}
fn size(&self) -> usize {
3
}
}
pub struct PropsList {
items: Gc<List>,
key_font: &'static TextStyle,
value_font: &'static TextStyle,
value_mono_font: &'static TextStyle,
}
impl PropsList {
pub fn new(
obj: Obj,
key_font: &'static TextStyle,
value_font: &'static TextStyle,
value_mono_font: &'static TextStyle,
) -> Result<Self, Error> {
Ok(Self {
items: obj.try_into()?,
key_font,
value_font,
value_mono_font,
})
}
}
impl ParagraphSource<'static> for PropsList {
fn at(&self, index: usize, offset: usize) -> Paragraph<'static> {
let block = move || {
let entry = self.items.get(index / 2)?;
let [key, value, value_is_mono]: [Obj; 3] = iter_into_array(entry)?;
let value_is_mono: bool = bool::try_from(value_is_mono)?;
let obj: Obj;
let style: &TextStyle;
if index % 2 == 0 {
if !key.is_str() && key != Obj::const_none() {
return Err(Error::TypeError);
}
style = self.key_font;
obj = key;
} else {
if value_is_mono {
style = self.value_mono_font;
} else {
if value.is_bytes() {
return Err(Error::TypeError);
}
style = self.value_font;
}
obj = value;
};
if obj == Obj::const_none() {
return Ok(Paragraph::new(style, StrBuffer::empty()));
}
let para = if obj.is_str() {
let content: StrBuffer = obj.try_into()?;
Paragraph::new(style, content.skip_prefix(offset))
} else if obj.is_bytes() {
let content = hexlify_bytes(obj, offset, MAX_HEX_CHARS_ON_SCREEN)?;
Paragraph::new(style, content)
} else {
return Err(Error::TypeError);
};
if obj == key && value != Obj::const_none() {
Ok(para.no_break())
} else {
Ok(para)
}
};
match block() {
Ok(para) => para,
Err(_) => Paragraph::new(self.value_font, StrBuffer::from("ERROR")),
}
}
fn size(&self) -> usize {
2 * self.items.len()
}
}
pub extern "C" fn upy_disable_animation(disable: Obj) -> Obj {
let block = || {
set_animation_disabled(disable.try_into()?);
Ok(Obj::const_none())
};
unsafe { try_or_raise(block) }
}
pub fn get_user_custom_image() -> Result<Gc<[u8]>, Error> {
let len = get_avatar_len()?;
let mut data = Gc::<[u8]>::new_slice(len)?;
// SAFETY: buffer is freshly allocated so nobody else has it.
load_avatar(unsafe { Gc::<[u8]>::as_mut(&mut data) })?;
Ok(data)
}