mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-18 05:28:40 +00:00
feat(core/rust): model R bootloader implementation in rust
This commit is contained in:
parent
e546346333
commit
7626a99a0f
116
core/embed/rust/src/ui/model_tr/bootloader/confirm.rs
Normal file
116
core/embed/rust/src/ui/model_tr/bootloader/confirm.rs
Normal file
@ -0,0 +1,116 @@
|
||||
use crate::ui::{
|
||||
component::{text::paragraphs::Paragraphs, Child, Component, Event, EventCtx, Pad},
|
||||
constant::screen,
|
||||
display,
|
||||
display::Color,
|
||||
geometry::{Point, Rect},
|
||||
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<&'static [u8]>,
|
||||
message: Child<Paragraphs<&'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<&'static [u8]>,
|
||||
message: Paragraphs<&'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 {
|
||||
display::icon(
|
||||
Point::new(screen().center().x, 45),
|
||||
icon,
|
||||
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);
|
||||
}
|
||||
}
|
110
core/embed/rust/src/ui/model_tr/bootloader/intro.rs
Normal file
110
core/embed/rust/src/ui/model_tr/bootloader/intro.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use crate::ui::{
|
||||
component::{text::paragraphs::Paragraphs, 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<&'static str>>,
|
||||
}
|
||||
|
||||
impl Intro {
|
||||
pub fn new(bld_version: &'static str, vendor: &'static str, version: &'static str) -> Self {
|
||||
let p1 = Paragraphs::new()
|
||||
.add(TEXT_NORMAL, version)
|
||||
.add(TEXT_NORMAL, vendor)
|
||||
.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);
|
||||
}
|
||||
}
|
104
core/embed/rust/src/ui/model_tr/bootloader/menu.rs
Normal file
104
core/embed/rust/src/ui/model_tr/bootloader/menu.rs
Normal file
@ -0,0 +1,104 @@
|
||||
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>,
|
||||
// close: Child<Button<&'static str>>,
|
||||
// reboot: Child<Button<&'static str>>,
|
||||
// reset: Child<Button<&'static str>>,
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn new(bld_version: &'static str) -> Self {
|
||||
// let content_reboot = IconText::new("REBOOT", REBOOT);
|
||||
// let content_reset = IconText::new("FACTORY RESET", ERASE);
|
||||
|
||||
let mut instance = Self {
|
||||
bg: Pad::with_background(BLD_BG),
|
||||
title: Child::new(Title::new(bld_version)),
|
||||
// close: Child::new(
|
||||
// Button::with_icon(CLOSE)
|
||||
// .styled(button_bld_menu())
|
||||
// .with_expanded_touch_area(Insets::uniform(13)),
|
||||
// ),
|
||||
// reboot: Child::new(
|
||||
// Button::with_icon_and_text(content_reboot).styled(button_bld_menu_item()),
|
||||
// ),
|
||||
// reset: Child::new(
|
||||
// Button::with_icon_and_text(content_reset).styled(button_bld_menu_item()),
|
||||
// ),
|
||||
};
|
||||
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)));
|
||||
// self.close.place(Rect::new(
|
||||
// Point::new(187, 15),
|
||||
// Point::new(187 + 38, 15 + 38),
|
||||
// ));
|
||||
// self.reboot
|
||||
// .place(Rect::new(Point::new(16, 66), Point::new(16 + 209, 66 + 48)));
|
||||
// self.reset.place(Rect::new(
|
||||
// Point::new(16, 122),
|
||||
// Point::new(16 + 209, 122 + 48),
|
||||
// ));
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
// if let Some(Clicked) = self.close.event(ctx, event) {
|
||||
// return Some(Self::Msg::Close);
|
||||
// }
|
||||
// if let Some(Clicked) = self.reboot.event(ctx, event) {
|
||||
// return Some(Self::Msg::Reboot);
|
||||
// }
|
||||
// if let Some(Clicked) = self.reset.event(ctx, event) {
|
||||
// return Some(Self::Msg::FactoryReset);
|
||||
// }
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.bg.paint();
|
||||
self.title.paint();
|
||||
// self.close.paint();
|
||||
// self.reboot.paint();
|
||||
// self.reset.paint();
|
||||
}
|
||||
|
||||
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {
|
||||
// self.close.bounds(sink);
|
||||
// self.reboot.bounds(sink);
|
||||
// self.reset.bounds(sink);
|
||||
}
|
||||
}
|
393
core/embed/rust/src/ui/model_tr/bootloader/mod.rs
Normal file
393
core/embed/rust/src/ui/model_tr/bootloader/mod.rs
Normal file
@ -0,0 +1,393 @@
|
||||
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::Paragraphs, Event, EventCtx},
|
||||
constant::{screen, BACKLIGHT_NORMAL, WIDTH},
|
||||
display::{fade_backlight_duration, Color, TextOverlay},
|
||||
event::ButtonEvent,
|
||||
geometry::{LinearPlacement, Offset, Rect},
|
||||
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::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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_install_confirm(
|
||||
vendor_str: *const cty::c_char,
|
||||
vendor_str_len: u8,
|
||||
version: *const cty::c_char,
|
||||
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) });
|
||||
|
||||
const ICON: Option<&'static [u8]> = None; //Some(RECEIVE);
|
||||
|
||||
let msg = if downgrade {
|
||||
"Downgrade firmware by"
|
||||
} else if vendor {
|
||||
"Change vendor to"
|
||||
} else {
|
||||
"Update firmware by"
|
||||
};
|
||||
|
||||
let mut message = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, msg)
|
||||
.centered()
|
||||
.add(theme::TEXT_NORMAL, text)
|
||||
.centered()
|
||||
.add(theme::TEXT_NORMAL, version)
|
||||
.centered();
|
||||
|
||||
if vendor || downgrade {
|
||||
message = message
|
||||
.add(theme::TEXT_BOLD, "Seed will be erased!")
|
||||
.centered();
|
||||
}
|
||||
|
||||
message = message.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
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, message, left, right, false);
|
||||
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_confirm() -> u32 {
|
||||
const ICON: Option<&'static [u8]> = None; //Some(ERASE_BIG);
|
||||
|
||||
let message = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, "Do you really want to wipe the device?")
|
||||
.centered()
|
||||
.add(theme::TEXT_BOLD, "Seed will be erased!")
|
||||
.centered()
|
||||
.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)>,
|
||||
) -> u32 {
|
||||
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::text_center(
|
||||
// Point::new(constant::WIDTH / 2, 100),
|
||||
// text,
|
||||
// Font::NORMAL,
|
||||
// fg_color,
|
||||
// bg_color,
|
||||
// );
|
||||
// display::loader(progress, -20, fg_color, bg_color, icon);
|
||||
0
|
||||
}
|
||||
|
||||
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,
|
||||
) -> u32 {
|
||||
screen_progress(
|
||||
"INSTALLING FIRMWARE",
|
||||
progress,
|
||||
initialize,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
None, //Some((theme::RECEIVE, fg_color)),
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) -> u32 {
|
||||
screen_progress(
|
||||
"WIPING DEVICE",
|
||||
progress,
|
||||
initialize,
|
||||
theme::BLD_FG,
|
||||
BLD_BG,
|
||||
None, //Some((theme::ERASE_BIG, theme::BLD_FG)),
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_connect() -> u32 {
|
||||
let mut frame = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, "Waiting for host...")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
frame.place(SCREEN_ADJ);
|
||||
frame.paint();
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_success() -> u32 {
|
||||
let m_top = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, "Device wiped")
|
||||
.centered()
|
||||
.add(theme::TEXT_NORMAL, "successfully.")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let m_bottom = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, "PLEASE RECONNECT")
|
||||
.centered()
|
||||
.add(theme::TEXT_NORMAL, "THE DEVICE")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, m_top, m_bottom, true);
|
||||
frame.place(SCREEN_ADJ);
|
||||
frame.paint();
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_fail() -> u32 {
|
||||
let m_top = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, "Device wipe was")
|
||||
.centered()
|
||||
.add(theme::TEXT_NORMAL, "not successful.")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let m_bottom = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, "PLEASE RECONNECT")
|
||||
.centered()
|
||||
.add(theme::TEXT_NORMAL, "THE DEVICE")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, m_top, m_bottom, true);
|
||||
frame.place(SCREEN_ADJ);
|
||||
frame.paint();
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_boot_empty(_firmware_present: bool) {
|
||||
display::icon(SCREEN_ADJ.center(), LOGO_EMPTY, BLD_FG, BLD_BG);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_install_fail() -> u32 {
|
||||
let m_top = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, "Firmware installation was")
|
||||
.centered()
|
||||
.add(theme::TEXT_NORMAL, "not successful.")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let m_bottom = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, "PLEASE RECONNECT")
|
||||
.centered()
|
||||
.add(theme::TEXT_NORMAL, "THE DEVICE")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, m_top, m_bottom, true);
|
||||
frame.place(SCREEN_ADJ);
|
||||
frame.paint();
|
||||
0
|
||||
}
|
||||
|
||||
fn screen_install_success_bld(msg: &'static str, complete_draw: bool) -> u32 {
|
||||
let m_top = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, "Firmware installed")
|
||||
.centered()
|
||||
.add(theme::TEXT_NORMAL, "successfully.")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let m_bottom = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, msg)
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, m_top, m_bottom, complete_draw);
|
||||
frame.place(SCREEN_ADJ);
|
||||
frame.paint();
|
||||
0
|
||||
}
|
||||
|
||||
fn screen_install_success_initial(msg: &'static str, complete_draw: bool) -> u32 {
|
||||
let m_top = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, "Firmware installed")
|
||||
.centered()
|
||||
.add(theme::TEXT_NORMAL, "successfully.")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let m_bottom = Paragraphs::new()
|
||||
.add(theme::TEXT_NORMAL, msg)
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, m_top, m_bottom, complete_draw);
|
||||
frame.place(SCREEN_ADJ);
|
||||
frame.paint();
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_install_success(
|
||||
reboot_msg: *const cty::c_char,
|
||||
initial_setup: bool,
|
||||
complete_draw: bool,
|
||||
) -> u32 {
|
||||
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() -> u32 {
|
||||
let mut frame = Paragraphs::new()
|
||||
.add(theme::TEXT_BOLD, "Get started with")
|
||||
.centered()
|
||||
.add(theme::TEXT_BOLD, "your trezor at")
|
||||
.centered()
|
||||
.add(theme::TEXT_BOLD, "trezor.io/start")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
frame.place(SCREEN_ADJ);
|
||||
frame.paint();
|
||||
0
|
||||
}
|
50
core/embed/rust/src/ui/model_tr/bootloader/theme.rs
Normal file
50
core/embed/rust/src/ui/model_tr/bootloader/theme.rs
Normal 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);
|
52
core/embed/rust/src/ui/model_tr/bootloader/title.rs
Normal file
52
core/embed/rust/src/ui/model_tr/bootloader/title.rs
Normal 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)) {}
|
||||
}
|
@ -15,6 +15,7 @@ mod loader;
|
||||
mod page;
|
||||
mod passphrase;
|
||||
mod pin;
|
||||
mod result;
|
||||
mod result_anim;
|
||||
mod result_popup;
|
||||
mod scrollbar;
|
||||
@ -43,6 +44,7 @@ pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
|
||||
pub use page::ButtonPage;
|
||||
pub use passphrase::{PassphraseEntry, PassphraseEntryMsg};
|
||||
pub use pin::{PinEntry, PinEntryMsg};
|
||||
pub use result::ResultScreen;
|
||||
pub use result_anim::{ResultAnim, ResultAnimMsg};
|
||||
pub use result_popup::{ResultPopup, ResultPopupMsg};
|
||||
pub use scrollbar::ScrollBar;
|
||||
|
84
core/embed/rust/src/ui/model_tr/component/result.rs
Normal file
84
core/embed/rust/src/ui/model_tr/component/result.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use crate::ui::{
|
||||
component::{text::paragraphs::Paragraphs, Child, Component, Event, EventCtx, Never, Pad},
|
||||
constant::{HEIGHT, WIDTH},
|
||||
display::Color,
|
||||
geometry::{Point, Rect},
|
||||
};
|
||||
|
||||
pub struct ResultScreen {
|
||||
bg: Pad,
|
||||
small_pad: Pad,
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
message_top: Child<Paragraphs<&'static str>>,
|
||||
message_bottom: Child<Paragraphs<&'static str>>,
|
||||
}
|
||||
|
||||
impl ResultScreen {
|
||||
pub fn new(
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
message_top: Paragraphs<&'static str>,
|
||||
message_bottom: Paragraphs<&'static str>,
|
||||
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,
|
||||
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 Component for ResultScreen {
|
||||
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, 0), Point::new(WIDTH, 30)));
|
||||
|
||||
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();
|
||||
|
||||
// display::icon(
|
||||
// Point::new(screen().center().x, 45),
|
||||
// self.icon,
|
||||
// self.fg_color,
|
||||
// self.bg_color,
|
||||
// );
|
||||
// display::rect_fill(
|
||||
// Rect::from_top_left_and_size(Point::new(12, 149), Offset::new(216, 1)),
|
||||
// self.fg_color,
|
||||
// );
|
||||
self.message_top.paint();
|
||||
self.message_bottom.paint();
|
||||
}
|
||||
|
||||
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
|
||||
}
|
@ -9,6 +9,8 @@ pub const LOADER_OUTER: f32 = 20_f32;
|
||||
pub const LOADER_INNER: f32 = 14_f32;
|
||||
pub const LOADER_ICON_MAX_SIZE: i16 = 24;
|
||||
|
||||
pub const BACKLIGHT_NORMAL: i32 = 150;
|
||||
|
||||
pub const fn size() -> Offset {
|
||||
Offset::new(WIDTH, HEIGHT)
|
||||
}
|
||||
|
@ -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;
|
||||
|
BIN
core/embed/rust/src/ui/model_tr/res/trezor_empty.toif
Normal file
BIN
core/embed/rust/src/ui/model_tr/res/trezor_empty.toif
Normal file
Binary file not shown.
76
core/embed/rust/src/ui/model_tr/screens.rs
Normal file
76
core/embed/rust/src/ui/model_tr/screens.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use crate::ui::{
|
||||
component::{text::paragraphs::Paragraphs, Component},
|
||||
geometry::LinearPlacement,
|
||||
model_tr::{
|
||||
component::ResultScreen,
|
||||
constant,
|
||||
theme::{BLACK, TEXT_BOLD, TEXT_NORMAL, WHITE},
|
||||
},
|
||||
util::from_c_str,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_fatal_error(msg: *const cty::c_char, file: *const cty::c_char) -> u32 {
|
||||
let m_top = if msg.is_null() {
|
||||
Paragraphs::new()
|
||||
.add(TEXT_BOLD, "FATAL ERROR!")
|
||||
.centered()
|
||||
// .add(theme::TEXT_WIPE_NORMAL, unwrap!(unsafe { from_c_str(expr) }))
|
||||
// .centered()
|
||||
.add(TEXT_NORMAL, unwrap!(unsafe { from_c_str(file) }))
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center())
|
||||
} else {
|
||||
let msg = unwrap!(unsafe { from_c_str(msg) });
|
||||
Paragraphs::new()
|
||||
.add(TEXT_BOLD, "FATAL ERROR!")
|
||||
.centered()
|
||||
.add(TEXT_NORMAL, msg)
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center())
|
||||
};
|
||||
|
||||
let m_bottom = Paragraphs::new()
|
||||
.add(TEXT_BOLD, "PLEASE CONTACT")
|
||||
.centered()
|
||||
.add(TEXT_BOLD, "TREZOR SUPPORT")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(WHITE, BLACK, m_top, m_bottom, true);
|
||||
frame.place(constant::screen());
|
||||
frame.paint();
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_error_shutdown(label: *const cty::c_char, msg: *const cty::c_char) -> u32 {
|
||||
let label = unwrap!(unsafe { from_c_str(label) });
|
||||
|
||||
let m_top = if msg.is_null() {
|
||||
Paragraphs::new()
|
||||
.add(TEXT_BOLD, label)
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center())
|
||||
} else {
|
||||
let msg = unwrap!(unsafe { from_c_str(msg) });
|
||||
Paragraphs::new()
|
||||
.add(TEXT_BOLD, label)
|
||||
.centered()
|
||||
.add(TEXT_NORMAL, msg)
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center())
|
||||
};
|
||||
|
||||
let m_bottom = Paragraphs::new()
|
||||
.add(TEXT_BOLD, "PLEASE UNPLUG")
|
||||
.centered()
|
||||
.add(TEXT_BOLD, "THE DEVICE")
|
||||
.centered()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(WHITE, BLACK, m_top, m_bottom, true);
|
||||
frame.place(constant::screen());
|
||||
frame.paint();
|
||||
0
|
||||
}
|
@ -14,7 +14,7 @@ mod connect;
|
||||
mod fwinfo;
|
||||
pub mod intro;
|
||||
pub mod menu;
|
||||
mod theme;
|
||||
pub mod theme;
|
||||
mod title;
|
||||
|
||||
use crate::ui::{
|
||||
|
Loading…
Reference in New Issue
Block a user