1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-08 22:40:59 +00:00

feat(core/rust): model R bootloader implementation in rust

This commit is contained in:
tychovrahe 2022-10-19 23:03:59 +02:00 committed by Martin Milata
parent 659c939080
commit d633fa885b
14 changed files with 991 additions and 0 deletions

View File

@ -0,0 +1,118 @@
use crate::ui::{
component::{
text::paragraphs::{ParagraphVecShort, Paragraphs},
Child, Component, Event, EventCtx, Pad,
},
constant::screen,
display::{Color, Icon},
geometry::{Point, Rect, CENTER},
model_tr::{
component::{Button, ButtonMsg::Clicked},
constant::{HEIGHT, WIDTH},
theme::WHITE,
},
};
use super::ReturnToC;
#[derive(Copy, Clone)]
pub enum ConfirmMsg {
Cancel = 1,
Confirm = 2,
}
impl ReturnToC for ConfirmMsg {
fn return_to_c(self) -> u32 {
self as u32
}
}
pub struct Confirm {
bg: Pad,
bg_color: Color,
icon: Option<Icon>,
message: Child<Paragraphs<ParagraphVecShort<&'static str>>>,
left: Child<Button<&'static str>>,
right: Child<Button<&'static str>>,
confirm_left: bool,
}
impl Confirm {
pub fn new(
bg_color: Color,
icon: Option<Icon>,
message: Paragraphs<ParagraphVecShort<&'static str>>,
left: Button<&'static str>,
right: Button<&'static str>,
confirm_left: bool,
) -> Self {
let mut instance = Self {
bg: Pad::with_background(bg_color),
bg_color,
icon,
message: Child::new(message),
left: Child::new(left),
right: Child::new(right),
confirm_left,
};
instance.bg.clear();
instance
}
}
impl Component for Confirm {
type Msg = ConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.bg
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
self.message
.place(Rect::new(Point::new(10, 0), Point::new(118, 50)));
let button_area = bounds.split_bottom(12).1;
self.left.place(button_area);
self.right.place(button_area);
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if let Some(Clicked) = self.left.event(ctx, event) {
return if self.confirm_left {
Some(Self::Msg::Confirm)
} else {
Some(Self::Msg::Cancel)
};
};
if let Some(Clicked) = self.right.event(ctx, event) {
return if self.confirm_left {
Some(Self::Msg::Cancel)
} else {
Some(Self::Msg::Confirm)
};
};
None
}
fn paint(&mut self) {
self.bg.paint();
if let Some(icon) = self.icon {
icon.draw(
Point::new(screen().center().x, 45),
CENTER,
WHITE,
self.bg_color,
);
}
self.message.paint();
self.left.paint();
self.right.paint();
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.left.bounds(sink);
self.right.bounds(sink);
}
}

View File

@ -0,0 +1,116 @@
use crate::ui::{
component::{
text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt},
Child, Component, Event, EventCtx, Pad,
},
geometry::{LinearPlacement, Point, Rect},
model_tr::{
bootloader::{
theme::{BLD_BG, TEXT_NORMAL},
title::Title,
ReturnToC,
},
component::ButtonMsg::Clicked,
},
};
use crate::ui::model_tr::{
bootloader::theme::bld_button_default,
component::{Button, ButtonPos},
constant::{HEIGHT, WIDTH},
};
#[repr(u32)]
#[derive(Copy, Clone)]
pub enum IntroMsg {
Menu = 1,
Host = 2,
}
impl ReturnToC for IntroMsg {
fn return_to_c(self) -> u32 {
self as u32
}
}
pub struct Intro {
bg: Pad,
title: Child<Title>,
host: Child<Button<&'static str>>,
menu: Child<Button<&'static str>>,
text: Child<Paragraphs<ParagraphVecShort<&'static str>>>,
}
impl Intro {
pub fn new(bld_version: &'static str, vendor: &'static str, version: &'static str) -> Self {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_NORMAL, version));
messages.add(Paragraph::new(&TEXT_NORMAL, vendor));
let p1 =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_start());
let mut instance = Self {
bg: Pad::with_background(BLD_BG),
title: Child::new(Title::new(bld_version)),
host: Child::new(Button::with_text(
ButtonPos::Left,
"INSTALL FIRMWARE",
bld_button_default(),
)),
menu: Child::new(Button::with_text(
ButtonPos::Right,
"MENU",
bld_button_default(),
)),
text: Child::new(p1),
};
instance.bg.clear();
instance
}
}
impl Component for Intro {
type Msg = IntroMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.bg
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
self.title
.place(Rect::new(Point::new(10, 0), Point::new(128, 8)));
let button_area = bounds.split_bottom(12).1;
self.host.place(button_area);
self.menu.place(button_area);
self.text
.place(Rect::new(Point::new(10, 20), Point::new(118, 50)));
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if let Some(Clicked) = self.menu.event(ctx, event) {
return Some(Self::Msg::Menu);
};
if let Some(Clicked) = self.host.event(ctx, event) {
return Some(Self::Msg::Host);
};
None
}
fn paint(&mut self) {
self.bg.paint();
self.title.paint();
self.text.paint();
self.host.paint();
self.menu.paint();
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.title.bounds(sink);
self.text.bounds(sink);
self.host.bounds(sink);
self.menu.bounds(sink);
}
}

View File

@ -0,0 +1,58 @@
use crate::ui::{
component::{Child, Component, Event, EventCtx, Pad},
geometry::{Point, Rect},
model_tr::{
bootloader::{theme::BLD_BG, title::Title, ReturnToC},
constant::{HEIGHT, WIDTH},
},
};
#[repr(u32)]
#[derive(Copy, Clone)]
pub enum MenuMsg {
Close = 1,
Reboot = 2,
FactoryReset = 3,
}
impl ReturnToC for MenuMsg {
fn return_to_c(self) -> u32 {
self as u32
}
}
pub struct Menu {
bg: Pad,
title: Child<Title>,
}
impl Menu {
pub fn new(bld_version: &'static str) -> Self {
let mut instance = Self {
bg: Pad::with_background(BLD_BG),
title: Child::new(Title::new(bld_version)),
};
instance.bg.clear();
instance
}
}
impl Component for Menu {
type Msg = MenuMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.bg
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
self.title
.place(Rect::new(Point::new(10, 0), Point::new(128, 8)));
bounds
}
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
None
}
fn paint(&mut self) {
self.bg.paint();
self.title.paint();
}
}

View File

@ -0,0 +1,409 @@
use crate::{
trezorhal::io::io_button_read,
ui::{
component::{Component, Never},
display::{self, Font},
geometry::Point,
model_tr::constant,
},
};
mod confirm;
mod intro;
mod menu;
mod theme;
mod title;
use crate::ui::{
component::{
text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt},
Event, EventCtx,
},
constant::{screen, BACKLIGHT_NORMAL, WIDTH},
display::{fade_backlight_duration, Color, Icon, TextOverlay},
event::ButtonEvent,
geometry::{LinearPlacement, Offset, Rect, CENTER},
model_tr::{
bootloader::{
confirm::Confirm,
intro::Intro,
menu::Menu,
theme::{bld_button_cancel, bld_button_default, BLD_BG, BLD_FG},
},
component::{Button, ButtonPos, ResultScreen},
theme::{ICON_FAIL, ICON_SUCCESS, LOGO_EMPTY},
},
util::{from_c_array, from_c_str},
};
const SCREEN_ADJ: Rect = screen().split_top(64).0;
pub trait ReturnToC {
fn return_to_c(self) -> u32;
}
impl ReturnToC for Never {
fn return_to_c(self) -> u32 {
unreachable!()
}
}
impl ReturnToC for () {
fn return_to_c(self) -> u32 {
0
}
}
fn button_eval() -> Option<ButtonEvent> {
let event = io_button_read();
if event == 0 {
return None;
}
let event_type = event >> 24;
let event_btn = event & 0xFFFFFF;
let event = ButtonEvent::new(event_type, event_btn);
if let Ok(event) = event {
return Some(event);
}
None
}
fn run<F>(frame: &mut F) -> u32
where
F: Component,
F::Msg: ReturnToC,
{
frame.place(SCREEN_ADJ);
frame.paint();
fade_backlight_duration(BACKLIGHT_NORMAL as _, 500);
while button_eval().is_some() {}
loop {
let event = button_eval();
if let Some(e) = event {
let mut ctx = EventCtx::new();
let msg = frame.event(&mut ctx, Event::Button(e));
if let Some(message) = msg {
return message.return_to_c();
}
frame.paint();
}
}
}
fn show<F>(frame: &mut F)
where
F: Component,
{
frame.place(SCREEN_ADJ);
display::sync();
frame.paint();
display::refresh();
}
#[no_mangle]
extern "C" fn screen_install_confirm(
vendor_str: *const cty::c_char,
vendor_str_len: u8,
version: *const cty::c_char,
_fingerprint: *const cty::uint8_t,
downgrade: bool,
vendor: bool,
) -> u32 {
let text = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
let version = unwrap!(unsafe { from_c_str(version) });
let icon: Option<Icon> = None;
let msg = if downgrade {
"Downgrade firmware by"
} else if vendor {
"Change vendor to"
} else {
"Update firmware by"
};
let mut message = ParagraphVecShort::new();
message.add(Paragraph::new(&theme::TEXT_NORMAL, msg).centered());
message.add(Paragraph::new(&theme::TEXT_NORMAL, text).centered());
message.add(Paragraph::new(&theme::TEXT_NORMAL, version).centered());
if vendor || downgrade {
message.add(Paragraph::new(&theme::TEXT_BOLD, "Seed will be erased!").centered());
}
let left = Button::with_text(ButtonPos::Left, "CANCEL", bld_button_cancel());
let right = Button::with_text(ButtonPos::Right, "INSTALL", bld_button_default());
let mut frame = Confirm::new(
BLD_BG,
icon,
Paragraphs::new(message).with_placement(LinearPlacement::vertical().align_at_center()),
left,
right,
false,
);
run(&mut frame)
}
#[no_mangle]
extern "C" fn screen_wipe_confirm() -> u32 {
let icon: Option<Icon> = None; //Some(ERASE_BIG);
let mut messages = ParagraphVecShort::new();
messages.add(
Paragraph::new(
&theme::TEXT_NORMAL,
"Do you really want to wipe the device?",
)
.centered(),
);
messages.add(Paragraph::new(&theme::TEXT_BOLD, "Seed will be erased!").centered());
let message =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let left = Button::with_text(ButtonPos::Left, "WIPE", bld_button_default());
let right = Button::with_text(ButtonPos::Right, "CANCEL", bld_button_cancel());
let mut frame = Confirm::new(BLD_BG, icon, message, left, right, true);
run(&mut frame)
}
#[no_mangle]
extern "C" fn screen_menu(bld_version: *const cty::c_char) -> u32 {
let bld_version = unwrap!(unsafe { from_c_str(bld_version) });
run(&mut Menu::new(bld_version))
}
#[no_mangle]
extern "C" fn screen_intro(
bld_version: *const cty::c_char,
vendor_str: *const cty::c_char,
vendor_str_len: u8,
version: *const cty::c_char,
) -> u32 {
let vendor = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
let version = unwrap!(unsafe { from_c_str(version) });
let bld_version = unwrap!(unsafe { from_c_str(bld_version) });
run(&mut Intro::new(bld_version, vendor, version))
}
fn screen_progress(
text: &str,
progress: u16,
initialize: bool,
fg_color: Color,
bg_color: Color,
_icon: Option<(&[u8], Color)>,
) {
if initialize {
display::rect_fill(constant::screen(), bg_color);
}
let loader_area = Rect::new(Point::new(5, 24), Point::new(WIDTH - 5, 24 + 16));
let mut text = TextOverlay::new(text, Font::NORMAL);
text.place(loader_area.center() + Offset::y(Font::NORMAL.text_height() / 2));
let fill_to = (loader_area.width() as u32 * progress as u32) / 1000;
display::bar_with_text_and_fill(loader_area, Some(text), fg_color, bg_color, 0, fill_to as _);
display::refresh();
}
const INITIAL_INSTALL_LOADER_COLOR: Color = Color::rgb(0x4A, 0x90, 0xE2);
#[no_mangle]
extern "C" fn screen_install_progress(progress: u16, initialize: bool, _initial_setup: bool) {
screen_progress(
"INSTALLING FIRMWARE",
progress,
initialize,
BLD_FG,
BLD_BG,
None,
)
}
#[no_mangle]
extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) {
screen_progress(
"WIPING DEVICE",
progress,
initialize,
theme::BLD_FG,
BLD_BG,
None,
)
}
#[no_mangle]
extern "C" fn screen_connect() {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Waiting for host...").centered());
let mut frame =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
show(&mut frame);
}
#[no_mangle]
extern "C" fn screen_wipe_success() {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Device wiped").centered());
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "successfully.").centered());
let m_top =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "PLEASE RECONNECT").centered());
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "THE DEVICE").centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
BLD_FG,
BLD_BG,
Icon::new(ICON_SUCCESS),
m_top,
m_bottom,
true,
);
show(&mut frame);
}
#[no_mangle]
extern "C" fn screen_wipe_fail() {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Device wipe was").centered());
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "not successful.").centered());
let m_top =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "PLEASE RECONNECT").centered());
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "THE DEVICE").centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, Icon::new(ICON_FAIL), m_top, m_bottom, true);
show(&mut frame);
}
#[no_mangle]
extern "C" fn screen_boot_empty(_firmware_present: bool, _fading: bool) {
Icon::new(LOGO_EMPTY).draw(SCREEN_ADJ.center(), CENTER, BLD_FG, BLD_BG);
}
#[no_mangle]
extern "C" fn screen_install_fail() {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Firmware installation was").centered());
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "not successful.").centered());
let m_top =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "PLEASE RECONNECT").centered());
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "THE DEVICE").centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, Icon::new(ICON_FAIL), m_top, m_bottom, true);
show(&mut frame);
}
fn screen_install_success_bld(msg: &'static str, complete_draw: bool) {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Firmware installed").centered());
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "successfully.").centered());
let m_top =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, msg).centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
BLD_FG,
BLD_BG,
Icon::new(ICON_SUCCESS),
m_top,
m_bottom,
complete_draw,
);
show(&mut frame);
}
fn screen_install_success_initial(msg: &'static str, complete_draw: bool) {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Firmware installed").centered());
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "successfully.").centered());
let m_top =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, msg).centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
BLD_FG,
BLD_BG,
Icon::new(ICON_SUCCESS),
m_top,
m_bottom,
complete_draw,
);
show(&mut frame);
}
#[no_mangle]
extern "C" fn screen_install_success(
reboot_msg: *const cty::c_char,
initial_setup: bool,
complete_draw: bool,
) {
let msg = unwrap!(unsafe { from_c_str(reboot_msg) });
if initial_setup {
screen_install_success_initial(msg, complete_draw)
} else {
screen_install_success_bld(msg, complete_draw)
}
}
#[no_mangle]
extern "C" fn screen_welcome() {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_BOLD, "Get started with").centered());
messages.add(Paragraph::new(&theme::TEXT_BOLD, "your trezor at").centered());
messages.add(Paragraph::new(&theme::TEXT_BOLD, "trezor.io/start").centered());
let mut frame =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
show(&mut frame);
}

View File

@ -0,0 +1,50 @@
use crate::ui::{
component::text::TextStyle,
display::{Color, Font},
model_tr::{
component::{ButtonStyle, ButtonStyleSheet},
theme::{BG, BLACK, FG, WHITE},
},
};
pub const BLD_BG: Color = BLACK;
pub const BLD_FG: Color = WHITE;
// Commonly used corner radius (i.e. for buttons).
pub const RADIUS: u8 = 2;
// Size of icons in the UI (i.e. inside buttons).
pub const ICON_SIZE: i32 = 16;
pub fn bld_button_default() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::NORMAL,
text_color: BG,
border_horiz: true,
},
active: &ButtonStyle {
font: Font::NORMAL,
text_color: FG,
border_horiz: true,
},
}
}
pub fn bld_button_cancel() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::NORMAL,
text_color: FG,
border_horiz: false,
},
active: &ButtonStyle {
font: Font::NORMAL,
text_color: BG,
border_horiz: false,
},
}
}
pub const TEXT_NORMAL: TextStyle = TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG);
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG);

View File

@ -0,0 +1,52 @@
use crate::ui::{
component::{Component, Event, EventCtx, Never},
display::{self, Font},
geometry::{Point, Rect},
model_tr::bootloader::theme::{BLD_BG, BLD_FG},
};
pub struct Title {
version: &'static str,
area: Rect,
}
impl Title {
pub fn new(version: &'static str) -> Self {
Self {
version,
area: Rect::zero(),
}
}
}
impl Component for Title {
type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect {
self.area = bounds;
bounds
}
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
None
}
fn paint(&mut self) {
display::text_top_left(
self.area.top_left(),
"BOOTLOADER",
Font::NORMAL,
BLD_FG,
BLD_BG,
);
display::text_top_left(
Point::new(self.area.top_left().x + 65, self.area.top_left().y),
self.version,
Font::NORMAL,
BLD_FG,
BLD_BG,
);
}
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
}

View File

@ -4,6 +4,7 @@ mod dialog;
mod frame;
mod loader;
mod page;
mod result;
mod result_anim;
mod result_popup;
@ -15,5 +16,6 @@ pub use dialog::{Dialog, DialogMsg};
pub use frame::Frame;
pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
pub use page::ButtonPage;
pub use result::ResultScreen;
pub use result_anim::{ResultAnim, ResultAnimMsg};
pub use result_popup::{ResultPopup, ResultPopupMsg};

View File

@ -0,0 +1,85 @@
use crate::ui::{
component::{
text::paragraphs::{ParagraphStrType, ParagraphVecShort, Paragraphs},
Child, Component, Event, EventCtx, Never, Pad,
},
constant::{screen, HEIGHT, WIDTH},
display::{Color, Icon},
geometry::{Offset, Point, Rect, CENTER},
};
pub struct ResultScreen<T> {
bg: Pad,
small_pad: Pad,
fg_color: Color,
bg_color: Color,
icon: Icon,
message_top: Child<Paragraphs<ParagraphVecShort<T>>>,
message_bottom: Child<Paragraphs<ParagraphVecShort<T>>>,
}
impl<T: ParagraphStrType> ResultScreen<T> {
pub fn new(
fg_color: Color,
bg_color: Color,
icon: Icon,
message_top: Paragraphs<ParagraphVecShort<T>>,
message_bottom: Paragraphs<ParagraphVecShort<T>>,
complete_draw: bool,
) -> Self {
let mut instance = Self {
bg: Pad::with_background(bg_color),
small_pad: Pad::with_background(bg_color),
fg_color,
bg_color,
icon,
message_top: Child::new(message_top),
message_bottom: Child::new(message_bottom),
};
if complete_draw {
instance.bg.clear();
} else {
instance.small_pad.clear();
}
instance
}
}
impl<T: ParagraphStrType> Component for ResultScreen<T> {
type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect {
self.bg
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
self.message_top
.place(Rect::new(Point::new(0, 26), Point::new(WIDTH, 40)));
let bottom_area = Rect::new(Point::new(0, 40), Point::new(WIDTH, HEIGHT));
self.small_pad.place(bottom_area);
self.message_bottom.place(bottom_area);
bounds
}
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
None
}
fn paint(&mut self) {
self.bg.paint();
self.small_pad.paint();
self.icon.draw(
screen().top_center() + Offset::y(12),
CENTER,
self.fg_color,
self.bg_color,
);
self.message_top.paint();
self.message_bottom.paint();
}
}

View File

@ -9,6 +9,8 @@ pub const LOADER_OUTER: f32 = 32_f32;
pub const LOADER_INNER: f32 = 18_f32;
pub const LOADER_ICON_MAX_SIZE: i16 = 8;
pub const BACKLIGHT_NORMAL: i32 = 150;
pub const fn size() -> Offset {
Offset::new(WIDTH, HEIGHT)
}

View File

@ -1,6 +1,9 @@
#[cfg(feature = "bootloader")]
pub mod bootloader;
pub mod component;
pub mod constant;
pub mod theme;
#[cfg(feature = "micropython")]
pub mod layout;
pub mod screens;

Binary file not shown.

View File

@ -0,0 +1,93 @@
#[cfg(feature = "micropython")]
use crate::micropython::buffer::StrBuffer;
use crate::ui::{
component::{
text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt},
Component,
},
display::Icon,
geometry::LinearPlacement,
model_tr::{
component::ResultScreen,
constant,
theme::{BLACK, ICON_FAIL, TEXT_BOLD, TEXT_NORMAL, WHITE},
},
};
#[cfg(not(feature = "micropython"))]
// SAFETY: Actually safe but see below
unsafe fn get_str(text: &str) -> &str {
text
}
#[cfg(feature = "micropython")]
// SAFETY: The caller is responsible for ensuring that the StrBuffer does not
// escape the lifetime of the original &str.
unsafe fn get_str(text: &str) -> StrBuffer {
unsafe { StrBuffer::from_ptr_and_len(text.as_ptr(), text.len()) }
}
pub fn screen_fatal_error(msg: Option<&str>, file: &str) {
// SAFETY: these will get placed into `frame` which does not outlive this
// function
let msg = msg.map(|s| unsafe { get_str(s) });
let file = unsafe { get_str(file) };
let m_top = if let Some(msg) = msg {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_BOLD, "FATAL ERROR!".into()).centered());
messages.add(Paragraph::new(&TEXT_NORMAL, msg).centered());
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center())
} else {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_BOLD, "FATAL ERROR!".into()).centered());
messages.add(Paragraph::new(&TEXT_NORMAL, file).centered());
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center())
};
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_BOLD, "PLEASE CONTACT".into()).centered());
messages.add(Paragraph::new(&TEXT_BOLD, "TREZOR SUPPORT".into()).centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(WHITE, BLACK, Icon::new(ICON_FAIL), m_top, m_bottom, true);
frame.place(constant::screen());
frame.paint();
}
pub fn screen_error_shutdown(msg: Option<&str>, label: &str) {
// SAFETY: these will get placed into `frame` which does not outlive this
// function
let msg = msg.map(|s| unsafe { get_str(s) });
let label = unsafe { get_str(label) };
let m_top = if let Some(msg) = msg {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_BOLD, label).centered());
messages.add(Paragraph::new(&TEXT_NORMAL, msg).centered());
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center())
} else {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_BOLD, label).centered());
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center())
};
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_BOLD, "PLEASE UNPLUG".into()).centered());
messages.add(Paragraph::new(&TEXT_BOLD, "THE DEVICE".into()).centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(WHITE, BLACK, Icon::new(ICON_FAIL), m_top, m_bottom, true);
frame.place(constant::screen());
frame.paint();
}

View File

@ -16,6 +16,9 @@ pub const BG: Color = BLACK; // Default background color.
pub const ICON_SUCCESS: &[u8] = include_res!("model_tr/res/success.toif");
pub const ICON_FAIL: &[u8] = include_res!("model_tr/res/fail.toif");
// BLD icons
pub const LOGO_EMPTY: &[u8] = include_res!("model_tr/res/trezor_empty.toif");
pub fn button_default() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {