mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-01 20:32:35 +00:00
WIP - progress, homescreen, Popup, draw_simple, device tests
This commit is contained in:
parent
cf3687263f
commit
c5ad2937c1
@ -31,7 +31,6 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_confirm_output_r;
|
MP_QSTR_confirm_output_r;
|
||||||
MP_QSTR_confirm_payment_request;
|
MP_QSTR_confirm_payment_request;
|
||||||
MP_QSTR_confirm_reset_device;
|
MP_QSTR_confirm_reset_device;
|
||||||
MP_QSTR_confirm_recovery;
|
|
||||||
MP_QSTR_confirm_text;
|
MP_QSTR_confirm_text;
|
||||||
MP_QSTR_confirm_total;
|
MP_QSTR_confirm_total;
|
||||||
MP_QSTR_confirm_total_r;
|
MP_QSTR_confirm_total_r;
|
||||||
@ -39,16 +38,10 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_confirm_with_info;
|
MP_QSTR_confirm_with_info;
|
||||||
MP_QSTR_confirm_more;
|
MP_QSTR_confirm_more;
|
||||||
MP_QSTR_confirm_recovery;
|
MP_QSTR_confirm_recovery;
|
||||||
MP_QSTR_show_checklist;
|
|
||||||
MP_QSTR_show_error;
|
|
||||||
MP_QSTR_show_qr;
|
|
||||||
MP_QSTR_show_success;
|
MP_QSTR_show_success;
|
||||||
MP_QSTR_show_warning;
|
MP_QSTR_show_warning;
|
||||||
MP_QSTR_show_info;
|
MP_QSTR_show_info;
|
||||||
MP_QSTR_show_simple;
|
MP_QSTR_show_simple;
|
||||||
MP_QSTR_request_number;
|
|
||||||
MP_QSTR_request_pin;
|
|
||||||
MP_QSTR_request_passphrase;
|
|
||||||
MP_QSTR_confirm_word;
|
MP_QSTR_confirm_word;
|
||||||
MP_QSTR_request_bip39;
|
MP_QSTR_request_bip39;
|
||||||
MP_QSTR_request_number;
|
MP_QSTR_request_number;
|
||||||
@ -58,31 +51,19 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_select_word;
|
MP_QSTR_select_word;
|
||||||
MP_QSTR_select_word_count;
|
MP_QSTR_select_word_count;
|
||||||
MP_QSTR_show_busyscreen;
|
MP_QSTR_show_busyscreen;
|
||||||
MP_QSTR_show_group_share_success;
|
|
||||||
MP_QSTR_show_homescreen;
|
MP_QSTR_show_homescreen;
|
||||||
MP_QSTR_show_lockscreen;
|
MP_QSTR_show_lockscreen;
|
||||||
MP_QSTR_share_words;
|
MP_QSTR_share_words;
|
||||||
MP_QSTR_show_checklist;
|
MP_QSTR_show_checklist;
|
||||||
MP_QSTR_show_error;
|
MP_QSTR_show_error;
|
||||||
MP_QSTR_show_group_share_success;
|
MP_QSTR_show_group_share_success;
|
||||||
MP_QSTR_show_info;
|
|
||||||
MP_QSTR_show_qr;
|
MP_QSTR_show_qr;
|
||||||
MP_QSTR_show_remaining_shares;
|
MP_QSTR_show_remaining_shares;
|
||||||
MP_QSTR_show_success;
|
|
||||||
MP_QSTR_show_simple;
|
|
||||||
MP_QSTR_show_warning;
|
|
||||||
MP_QSTR_show_share_words;
|
MP_QSTR_show_share_words;
|
||||||
MP_QSTR_show_progress;
|
MP_QSTR_show_progress;
|
||||||
|
|
||||||
MP_QSTR_attach_timer_fn;
|
|
||||||
MP_QSTR_touch_event;
|
|
||||||
MP_QSTR_button_event;
|
|
||||||
MP_QSTR_progress_event;
|
MP_QSTR_progress_event;
|
||||||
MP_QSTR_usb_event;
|
MP_QSTR_usb_event;
|
||||||
MP_QSTR_timer;
|
|
||||||
MP_QSTR_paint;
|
|
||||||
MP_QSTR_request_complete_repaint;
|
|
||||||
MP_QSTR_trace;
|
|
||||||
MP_QSTR_request_word_count;
|
MP_QSTR_request_word_count;
|
||||||
MP_QSTR_request_word_bip39;
|
MP_QSTR_request_word_bip39;
|
||||||
MP_QSTR_tutorial;
|
MP_QSTR_tutorial;
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
use super::button::{Button, ButtonMsg::Clicked};
|
|
||||||
use crate::ui::{
|
|
||||||
component::{Child, Component, Event, EventCtx},
|
|
||||||
geometry::Rect,
|
|
||||||
model_tr::theme,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub enum DialogMsg<T> {
|
|
||||||
Content(T),
|
|
||||||
LeftClicked,
|
|
||||||
RightClicked,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Dialog<T, U> {
|
|
||||||
content: Child<T>,
|
|
||||||
left_btn: Option<Child<Button<U>>>,
|
|
||||||
right_btn: Option<Child<Button<U>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> Dialog<T, U>
|
|
||||||
where
|
|
||||||
T: Component,
|
|
||||||
U: AsRef<str>,
|
|
||||||
{
|
|
||||||
pub fn new(content: T, left: Option<Button<U>>, right: Option<Button<U>>) -> Self {
|
|
||||||
Self {
|
|
||||||
content: Child::new(content),
|
|
||||||
left_btn: left.map(Child::new),
|
|
||||||
right_btn: right.map(Child::new),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inner(&self) -> &T {
|
|
||||||
self.content.inner()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> Component for Dialog<T, U>
|
|
||||||
where
|
|
||||||
T: Component,
|
|
||||||
U: AsRef<str>,
|
|
||||||
{
|
|
||||||
type Msg = DialogMsg<T::Msg>;
|
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
|
||||||
let button_height = theme::FONT_BUTTON.line_height() + 2;
|
|
||||||
let (content_area, button_area) = bounds.split_bottom(button_height);
|
|
||||||
self.content.place(content_area);
|
|
||||||
self.left_btn.as_mut().map(|b| b.place(button_area));
|
|
||||||
self.right_btn.as_mut().map(|b| b.place(button_area));
|
|
||||||
bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
|
||||||
if let Some(msg) = self.content.event(ctx, event) {
|
|
||||||
Some(DialogMsg::Content(msg))
|
|
||||||
} else if let Some(Clicked) = self.left_btn.as_mut().and_then(|b| b.event(ctx, event)) {
|
|
||||||
Some(DialogMsg::LeftClicked)
|
|
||||||
} else if let Some(Clicked) = self.right_btn.as_mut().and_then(|b| b.event(ctx, event)) {
|
|
||||||
Some(DialogMsg::RightClicked)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(&mut self) {
|
|
||||||
self.content.paint();
|
|
||||||
if let Some(b) = self.left_btn.as_mut() {
|
|
||||||
b.paint();
|
|
||||||
}
|
|
||||||
if let Some(b) = self.right_btn.as_mut() {
|
|
||||||
b.paint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
|
||||||
impl<T, U> crate::trace::Trace for Dialog<T, U>
|
|
||||||
where
|
|
||||||
T: crate::trace::Trace,
|
|
||||||
U: crate::trace::Trace + AsRef<str>,
|
|
||||||
{
|
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
|
||||||
t.open("Dialog");
|
|
||||||
t.field("content", &self.content);
|
|
||||||
if let Some(label) = &self.left_btn {
|
|
||||||
t.field("left", label);
|
|
||||||
}
|
|
||||||
if let Some(label) = &self.right_btn {
|
|
||||||
t.field("right", label);
|
|
||||||
}
|
|
||||||
t.close();
|
|
||||||
}
|
|
||||||
}
|
|
@ -349,7 +349,7 @@ impl TextLayout {
|
|||||||
|
|
||||||
// TODO: draw the arrow icon if we are in the middle of a string
|
// TODO: draw the arrow icon if we are in the middle of a string
|
||||||
|
|
||||||
// TODO: have the variable `is_last` and in that case tell it to
|
// TODO: have the variable `is_last` and in that case tell it to
|
||||||
// `fit_horizontally`, which will then account for the ellipsis icon
|
// `fit_horizontally`, which will then account for the ellipsis icon
|
||||||
// instead of the hyphen (have it `ellipsis_length`)
|
// instead of the hyphen (have it `ellipsis_length`)
|
||||||
|
|
||||||
|
156
core/embed/rust/src/ui/model_tr/component/homescreen.rs
Normal file
156
core/embed/rust/src/ui/model_tr/component/homescreen.rs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
use crate::ui::{
|
||||||
|
component::{Component, Event, EventCtx, Pad},
|
||||||
|
display::Font,
|
||||||
|
event::{ButtonEvent, USBEvent},
|
||||||
|
geometry::{Offset, Point, Rect},
|
||||||
|
model_tr::constant,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{common::display_center, theme};
|
||||||
|
|
||||||
|
const AREA: Rect = constant::screen();
|
||||||
|
const TOP_CENTER: Point = AREA.top_center();
|
||||||
|
const LABEL_Y: i16 = 62;
|
||||||
|
const LOCKED_Y: i16 = 32;
|
||||||
|
const TAP_Y: i16 = 47;
|
||||||
|
|
||||||
|
pub struct Homescreen<T> {
|
||||||
|
label: T,
|
||||||
|
notification: Option<(T, u8)>,
|
||||||
|
usb_connected: bool,
|
||||||
|
pad: Pad,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum HomescreenMsg {
|
||||||
|
Dismissed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Homescreen<T>
|
||||||
|
where
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
pub fn new(label: T, notification: Option<(T, u8)>) -> Self {
|
||||||
|
Self {
|
||||||
|
label,
|
||||||
|
notification,
|
||||||
|
usb_connected: true,
|
||||||
|
pad: Pad::with_background(theme::BG),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint_notification(&self) {
|
||||||
|
let baseline = TOP_CENTER + Offset::y(Font::MONO.line_height());
|
||||||
|
if !self.usb_connected {
|
||||||
|
display_center(baseline, &"NO USB CONNECTION", Font::MONO);
|
||||||
|
} else if let Some((notification, _level)) = &self.notification {
|
||||||
|
display_center(baseline, ¬ification.as_ref(), Font::MONO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_usb(&mut self, ctx: &mut EventCtx, event: Event) {
|
||||||
|
if let Event::USB(USBEvent::Connected(is_connected)) = event {
|
||||||
|
if self.usb_connected != is_connected {
|
||||||
|
self.usb_connected = is_connected;
|
||||||
|
ctx.request_paint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Component for Homescreen<T>
|
||||||
|
where
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
type Msg = HomescreenMsg;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.pad.place(AREA);
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
Self::event_usb(self, ctx, event);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self) {
|
||||||
|
self.pad.paint();
|
||||||
|
self.paint_notification();
|
||||||
|
display_center(
|
||||||
|
TOP_CENTER + Offset::y(LABEL_Y),
|
||||||
|
&self.label.as_ref(),
|
||||||
|
Font::BOLD,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||||
|
sink(self.pad.area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl<T: AsRef<str>> crate::trace::Trace for Homescreen<T> {
|
||||||
|
fn trace(&self, d: &mut dyn crate::trace::Tracer) {
|
||||||
|
d.open("Homescreen");
|
||||||
|
d.kw_pair("active_page", "0");
|
||||||
|
d.kw_pair("page_count", "1");
|
||||||
|
d.field("label", &self.label.as_ref());
|
||||||
|
d.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Lockscreen<T> {
|
||||||
|
label: T,
|
||||||
|
bootscreen: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Lockscreen<T> {
|
||||||
|
pub fn new(label: T, bootscreen: bool) -> Self {
|
||||||
|
Lockscreen { label, bootscreen }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Component for Lockscreen<T>
|
||||||
|
where
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
type Msg = HomescreenMsg;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, _ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Event::Button(ButtonEvent::ButtonReleased(_)) = event {
|
||||||
|
return Some(HomescreenMsg::Dismissed);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self) {
|
||||||
|
let (locked, tap) = if self.bootscreen {
|
||||||
|
("NOT CONNECTED", "Click to connect")
|
||||||
|
} else {
|
||||||
|
("LOCKED", "Click to unlock")
|
||||||
|
};
|
||||||
|
display_center(TOP_CENTER + Offset::y(LOCKED_Y), &locked, Font::MONO);
|
||||||
|
display_center(TOP_CENTER + Offset::y(TAP_Y), &tap, Font::MONO);
|
||||||
|
display_center(
|
||||||
|
TOP_CENTER + Offset::y(LABEL_Y),
|
||||||
|
&self.label.as_ref(),
|
||||||
|
Font::BOLD,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl<T> crate::trace::Trace for Lockscreen<T>
|
||||||
|
where
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
fn trace(&self, d: &mut dyn crate::trace::Tracer) {
|
||||||
|
d.open("Lockscreen");
|
||||||
|
d.field("label", &self.label.as_ref());
|
||||||
|
d.close();
|
||||||
|
}
|
||||||
|
}
|
@ -6,15 +6,17 @@ mod choice;
|
|||||||
mod choice_item;
|
mod choice_item;
|
||||||
mod common;
|
mod common;
|
||||||
mod confirm;
|
mod confirm;
|
||||||
mod dialog;
|
|
||||||
mod flow;
|
mod flow;
|
||||||
mod flow_pages;
|
mod flow_pages;
|
||||||
mod flow_pages_poc_helpers;
|
mod flow_pages_poc_helpers;
|
||||||
mod frame;
|
mod frame;
|
||||||
|
mod homescreen;
|
||||||
mod loader;
|
mod loader;
|
||||||
|
mod no_btn_dialog;
|
||||||
mod page;
|
mod page;
|
||||||
mod passphrase;
|
mod passphrase;
|
||||||
mod pin;
|
mod pin;
|
||||||
|
mod progress;
|
||||||
mod qr_code;
|
mod qr_code;
|
||||||
mod result_anim;
|
mod result_anim;
|
||||||
mod result_popup;
|
mod result_popup;
|
||||||
@ -35,15 +37,17 @@ pub use button_controller::{ButtonController, ButtonControllerMsg};
|
|||||||
pub use changing_text::ChangingTextLine;
|
pub use changing_text::ChangingTextLine;
|
||||||
pub use choice::{Choice, ChoiceFactory, ChoicePage, ChoicePageMsg};
|
pub use choice::{Choice, ChoiceFactory, ChoicePage, ChoicePageMsg};
|
||||||
pub use choice_item::ChoiceItem;
|
pub use choice_item::ChoiceItem;
|
||||||
pub use dialog::{Dialog, DialogMsg};
|
|
||||||
pub use flow::{Flow, FlowMsg};
|
pub use flow::{Flow, FlowMsg};
|
||||||
pub use flow_pages::{FlowPages, Page};
|
pub use flow_pages::{FlowPages, Page};
|
||||||
pub use flow_pages_poc_helpers::LineAlignment;
|
pub use flow_pages_poc_helpers::LineAlignment;
|
||||||
pub use frame::Frame;
|
pub use frame::Frame;
|
||||||
|
pub use homescreen::{Homescreen, HomescreenMsg, Lockscreen};
|
||||||
pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
|
pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
|
||||||
|
pub use no_btn_dialog::{NoBtnDialog, NoBtnDialogMsg};
|
||||||
pub use page::ButtonPage;
|
pub use page::ButtonPage;
|
||||||
pub use passphrase::{PassphraseEntry, PassphraseEntryMsg};
|
pub use passphrase::{PassphraseEntry, PassphraseEntryMsg};
|
||||||
pub use pin::{PinEntry, PinEntryMsg};
|
pub use pin::{PinEntry, PinEntryMsg};
|
||||||
|
pub use progress::Progress;
|
||||||
pub use qr_code::{QRCodePage, QRCodePageMessage};
|
pub use qr_code::{QRCodePage, QRCodePageMessage};
|
||||||
pub use result_anim::{ResultAnim, ResultAnimMsg};
|
pub use result_anim::{ResultAnim, ResultAnimMsg};
|
||||||
pub use result_popup::{ResultPopup, ResultPopupMsg};
|
pub use result_popup::{ResultPopup, ResultPopupMsg};
|
||||||
|
69
core/embed/rust/src/ui/model_tr/component/no_btn_dialog.rs
Normal file
69
core/embed/rust/src/ui/model_tr/component/no_btn_dialog.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
use crate::ui::{
|
||||||
|
component::{Child, Component, Event, EventCtx},
|
||||||
|
geometry::Rect,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum NoBtnDialogMsg<T> {
|
||||||
|
Controls(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used for simple displaying of information without user interaction.
|
||||||
|
/// Suitable for just showing a message, or having a timeout after which
|
||||||
|
/// the dialog is dismissed.
|
||||||
|
pub struct NoBtnDialog<T, U> {
|
||||||
|
content: Child<T>,
|
||||||
|
controls: Child<U>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> NoBtnDialog<T, U>
|
||||||
|
where
|
||||||
|
T: Component,
|
||||||
|
U: Component,
|
||||||
|
{
|
||||||
|
pub fn new(content: T, controls: U) -> Self {
|
||||||
|
Self {
|
||||||
|
content: Child::new(content),
|
||||||
|
controls: Child::new(controls),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner(&self) -> &T {
|
||||||
|
self.content.inner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> Component for NoBtnDialog<T, U>
|
||||||
|
where
|
||||||
|
T: Component,
|
||||||
|
U: Component,
|
||||||
|
{
|
||||||
|
type Msg = NoBtnDialogMsg<U::Msg>;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.controls.place(bounds);
|
||||||
|
self.content.place(bounds);
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
self.controls.event(ctx, event).map(Self::Msg::Controls)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self) {
|
||||||
|
self.content.paint();
|
||||||
|
self.controls.paint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl<T, U> crate::trace::Trace for NoBtnDialog<T, U>
|
||||||
|
where
|
||||||
|
T: crate::trace::Trace,
|
||||||
|
U: crate::trace::Trace,
|
||||||
|
{
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.open("NoBtnDialog");
|
||||||
|
self.content.trace(t);
|
||||||
|
t.close();
|
||||||
|
}
|
||||||
|
}
|
137
core/embed/rust/src/ui/model_tr/component/progress.rs
Normal file
137
core/embed/rust/src/ui/model_tr/component/progress.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::Error,
|
||||||
|
ui::{
|
||||||
|
component::{
|
||||||
|
base::ComponentExt,
|
||||||
|
paginated::Paginate,
|
||||||
|
text::paragraphs::{Paragraph, ParagraphStrType, Paragraphs},
|
||||||
|
Child, Component, Event, EventCtx, Label, Never, Pad,
|
||||||
|
},
|
||||||
|
display::{self, Font},
|
||||||
|
geometry::Rect,
|
||||||
|
model_tr::constant,
|
||||||
|
util::animation_disabled,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::theme;
|
||||||
|
|
||||||
|
pub struct Progress<T> {
|
||||||
|
title: Child<Label<T>>,
|
||||||
|
value: u16,
|
||||||
|
loader_y_offset: i16,
|
||||||
|
indeterminate: bool,
|
||||||
|
description: Child<Paragraphs<Paragraph<T>>>,
|
||||||
|
description_pad: Pad,
|
||||||
|
update_description: fn(&str) -> Result<T, Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Progress<T>
|
||||||
|
where
|
||||||
|
T: ParagraphStrType,
|
||||||
|
{
|
||||||
|
const AREA: Rect = constant::screen();
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
title: T,
|
||||||
|
indeterminate: bool,
|
||||||
|
description: T,
|
||||||
|
update_description: fn(&str) -> Result<T, Error>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
title: Label::centered(title, theme::TEXT_HEADER).into_child(),
|
||||||
|
value: 0,
|
||||||
|
loader_y_offset: 0,
|
||||||
|
indeterminate,
|
||||||
|
description: Paragraphs::new(
|
||||||
|
Paragraph::new(&theme::TEXT_NORMAL, description).centered(),
|
||||||
|
)
|
||||||
|
.into_child(),
|
||||||
|
description_pad: Pad::with_background(theme::BG),
|
||||||
|
update_description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Component for Progress<T>
|
||||||
|
where
|
||||||
|
T: ParagraphStrType,
|
||||||
|
{
|
||||||
|
type Msg = Never;
|
||||||
|
|
||||||
|
fn place(&mut self, _bounds: Rect) -> Rect {
|
||||||
|
let description_lines = 1 + self
|
||||||
|
.description
|
||||||
|
.inner()
|
||||||
|
.inner()
|
||||||
|
.content()
|
||||||
|
.as_ref()
|
||||||
|
.chars()
|
||||||
|
.filter(|c| *c == '\n')
|
||||||
|
.count() as i16;
|
||||||
|
let (title, rest) = Self::AREA.split_top(self.title.inner().max_size().y);
|
||||||
|
let (loader, description) =
|
||||||
|
rest.split_bottom(Font::NORMAL.line_height() * description_lines);
|
||||||
|
self.title.place(title);
|
||||||
|
self.loader_y_offset = loader.center().y - constant::screen().center().y;
|
||||||
|
self.description.place(description);
|
||||||
|
self.description_pad.place(description);
|
||||||
|
Self::AREA
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Event::Progress(new_value, new_description) = event {
|
||||||
|
if mem::replace(&mut self.value, new_value) != new_value {
|
||||||
|
if !animation_disabled() {
|
||||||
|
ctx.request_paint();
|
||||||
|
}
|
||||||
|
self.description.mutate(ctx, |ctx, para| {
|
||||||
|
if para.inner_mut().content().as_ref() != new_description {
|
||||||
|
let new_description = unwrap!((self.update_description)(new_description));
|
||||||
|
para.inner_mut().update(new_description);
|
||||||
|
para.change_page(0); // Recompute bounding box.
|
||||||
|
ctx.request_paint();
|
||||||
|
self.description_pad.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self) {
|
||||||
|
self.title.paint();
|
||||||
|
if self.indeterminate {
|
||||||
|
display::loader_indeterminate(
|
||||||
|
self.value,
|
||||||
|
self.loader_y_offset,
|
||||||
|
theme::FG,
|
||||||
|
theme::BG,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
display::loader(self.value, self.loader_y_offset, theme::FG, theme::BG, None);
|
||||||
|
}
|
||||||
|
self.description_pad.paint();
|
||||||
|
self.description.paint();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||||
|
sink(Self::AREA);
|
||||||
|
self.title.bounds(sink);
|
||||||
|
self.description.bounds(sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl<T> crate::trace::Trace for Progress<T>
|
||||||
|
where
|
||||||
|
T: ParagraphStrType,
|
||||||
|
{
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.open("Progress");
|
||||||
|
t.close();
|
||||||
|
}
|
||||||
|
}
|
@ -19,8 +19,10 @@ use crate::{
|
|||||||
base::Component,
|
base::Component,
|
||||||
paginated::{PageMsg, Paginate},
|
paginated::{PageMsg, Paginate},
|
||||||
painter,
|
painter,
|
||||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecLong, Paragraphs, VecExt},
|
text::paragraphs::{
|
||||||
FormattedText,
|
Paragraph, ParagraphSource, ParagraphStrType, ParagraphVecLong, Paragraphs, VecExt,
|
||||||
|
},
|
||||||
|
ComponentExt, Empty, FormattedText, Timeout, TimeoutMsg,
|
||||||
},
|
},
|
||||||
display::Font,
|
display::Font,
|
||||||
layout::{
|
layout::{
|
||||||
@ -35,12 +37,42 @@ use crate::{
|
|||||||
use super::{
|
use super::{
|
||||||
component::{
|
component::{
|
||||||
Bip39Entry, Bip39EntryMsg, ButtonActions, ButtonDetails, ButtonLayout, ButtonPage, Flow,
|
Bip39Entry, Bip39EntryMsg, ButtonActions, ButtonDetails, ButtonLayout, ButtonPage, Flow,
|
||||||
FlowMsg, FlowPages, Frame, Page, PassphraseEntry, PassphraseEntryMsg, PinEntry,
|
FlowMsg, FlowPages, Frame, Homescreen, HomescreenMsg, Lockscreen, NoBtnDialog,
|
||||||
PinEntryMsg, QRCodePage, QRCodePageMessage, ShareWords, SimpleChoice, SimpleChoiceMsg,
|
NoBtnDialogMsg, Page, PassphraseEntry, PassphraseEntryMsg, PinEntry, PinEntryMsg, Progress,
|
||||||
|
QRCodePage, QRCodePageMessage, ShareWords, SimpleChoice, SimpleChoiceMsg,
|
||||||
},
|
},
|
||||||
theme,
|
theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub enum CancelConfirmMsg {
|
||||||
|
Cancelled,
|
||||||
|
Confirmed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<CancelConfirmMsg> for Obj {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: CancelConfirmMsg) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
CancelConfirmMsg::Cancelled => Ok(CANCELLED.as_obj()),
|
||||||
|
CancelConfirmMsg::Confirmed => Ok(CONFIRMED.as_obj()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> ComponentMsgObj for NoBtnDialog<T, U>
|
||||||
|
where
|
||||||
|
T: Component,
|
||||||
|
U: Component,
|
||||||
|
<U as Component>::Msg: TryInto<Obj, Error = Error>,
|
||||||
|
{
|
||||||
|
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
|
match msg {
|
||||||
|
NoBtnDialogMsg::Controls(msg) => msg.try_into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<S, T> ComponentMsgObj for ButtonPage<S, T>
|
impl<S, T> ComponentMsgObj for ButtonPage<S, T>
|
||||||
where
|
where
|
||||||
T: Component + Paginate,
|
T: Component + Paginate,
|
||||||
@ -133,6 +165,37 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> ComponentMsgObj for Progress<T>
|
||||||
|
where
|
||||||
|
T: ParagraphStrType,
|
||||||
|
{
|
||||||
|
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ComponentMsgObj for Homescreen<T>
|
||||||
|
where
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
|
match msg {
|
||||||
|
HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ComponentMsgObj for Lockscreen<T>
|
||||||
|
where
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
|
match msg {
|
||||||
|
HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
@ -202,7 +265,8 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn new_confirm_text(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_confirm_text(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
// TODO: should be deleted and replaced by new_confirm_action with some default parameters
|
// TODO: should be deleted and replaced by confirm_action with some default
|
||||||
|
// parameters
|
||||||
|
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
@ -231,7 +295,7 @@ extern "C" fn new_confirm_text(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn confirm_properties(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
||||||
@ -269,7 +333,7 @@ extern "C" fn confirm_properties(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?;
|
let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?;
|
||||||
// Getting this from micropython so it is also a `StrBuffer`, not having
|
// Getting this from micropython so it is also a `StrBuffer`, not having
|
||||||
@ -325,7 +389,7 @@ extern "C" fn confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
let total_amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_amount)?.try_into()?;
|
let total_amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_amount)?.try_into()?;
|
||||||
@ -369,7 +433,7 @@ extern "C" fn confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn show_qr(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_show_qr(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?;
|
let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?;
|
||||||
@ -392,6 +456,36 @@ extern "C" fn show_qr(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
|
let description: StrBuffer =
|
||||||
|
kwargs.get_or(Qstr::MP_QSTR_description, StrBuffer::empty())?;
|
||||||
|
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
|
||||||
|
|
||||||
|
let content = Paragraphs::new([
|
||||||
|
Paragraph::new(&theme::TEXT_MONO, title),
|
||||||
|
Paragraph::new(&theme::TEXT_MONO, description),
|
||||||
|
]);
|
||||||
|
let obj = if time_ms == 0 {
|
||||||
|
// No timer, used when we only want to draw the dialog once and
|
||||||
|
// then throw away the layout object.
|
||||||
|
LayoutObj::new(NoBtnDialog::new(content, Empty))?
|
||||||
|
} else {
|
||||||
|
// Timeout.
|
||||||
|
LayoutObj::new(NoBtnDialog::new(
|
||||||
|
content,
|
||||||
|
Timeout::new(time_ms).map(|msg| {
|
||||||
|
(matches!(msg, TimeoutMsg::TimedOut)).then(|| CancelConfirmMsg::Confirmed)
|
||||||
|
}),
|
||||||
|
))?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(obj.into())
|
||||||
|
};
|
||||||
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: not supplying tuple of data, supply data itself without unpacking
|
// TODO: not supplying tuple of data, supply data itself without unpacking
|
||||||
/// General pattern of most tutorial screens.
|
/// General pattern of most tutorial screens.
|
||||||
/// (title, text, btn_layout, btn_actions)
|
/// (title, text, btn_layout, btn_actions)
|
||||||
@ -507,7 +601,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
||||||
let subprompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?;
|
let subprompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?;
|
||||||
@ -520,7 +614,7 @@ extern "C" fn request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map) ->
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?;
|
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?;
|
||||||
let share_words: Vec<StrBuffer, 24> = iter_into_vec(share_words_obj)?;
|
let share_words: Vec<StrBuffer, 24> = iter_into_vec(share_words_obj)?;
|
||||||
@ -536,7 +630,7 @@ extern "C" fn show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn select_word(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
|
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
|
||||||
@ -551,7 +645,7 @@ extern "C" fn select_word(n_args: usize, args: *const Obj, kwargs: *mut Map) ->
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn request_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_request_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
|
|
||||||
@ -563,7 +657,7 @@ extern "C" fn request_word_count(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn request_word_bip39(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_request_word_bip39(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
||||||
|
|
||||||
@ -573,7 +667,7 @@ extern "C" fn request_word_bip39(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn request_passphrase(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = |_args: &[Obj], kwargs: &Map| {
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
||||||
let _max_len: u8 = kwargs.get(Qstr::MP_QSTR_max_len)?.try_into()?;
|
let _max_len: u8 = kwargs.get(Qstr::MP_QSTR_max_len)?.try_into()?;
|
||||||
@ -584,6 +678,87 @@ extern "C" fn request_passphrase(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn new_show_progress(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
|
let indeterminate: bool = kwargs.get_or(Qstr::MP_QSTR_indeterminate, false)?;
|
||||||
|
let description: StrBuffer =
|
||||||
|
kwargs.get_or(Qstr::MP_QSTR_description, StrBuffer::empty())?;
|
||||||
|
|
||||||
|
// Description updates are received as &str and we need to provide a way to
|
||||||
|
// convert them to StrBuffer.
|
||||||
|
let obj = LayoutObj::new(Progress::new(
|
||||||
|
title,
|
||||||
|
indeterminate,
|
||||||
|
description,
|
||||||
|
StrBuffer::alloc,
|
||||||
|
))?;
|
||||||
|
Ok(obj.into())
|
||||||
|
};
|
||||||
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn new_show_homescreen(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
|
let label: StrBuffer = kwargs.get(Qstr::MP_QSTR_label)?.try_into()?;
|
||||||
|
let notification: Option<StrBuffer> =
|
||||||
|
kwargs.get(Qstr::MP_QSTR_notification)?.try_into_option()?;
|
||||||
|
let notification_level: u8 = kwargs.get_or(Qstr::MP_QSTR_notification_level, 0)?;
|
||||||
|
let _hold: bool = kwargs.get(Qstr::MP_QSTR_hold)?.try_into()?;
|
||||||
|
let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?;
|
||||||
|
|
||||||
|
let notification = notification.map(|w| (w, notification_level));
|
||||||
|
let obj = LayoutObj::new(Homescreen::new(label, notification))?;
|
||||||
|
if skip_first_paint {
|
||||||
|
obj.skip_first_paint();
|
||||||
|
}
|
||||||
|
Ok(obj.into())
|
||||||
|
};
|
||||||
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn new_show_lockscreen(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
|
let label: StrBuffer = kwargs.get(Qstr::MP_QSTR_label)?.try_into()?;
|
||||||
|
let bootscreen: bool = kwargs.get(Qstr::MP_QSTR_bootscreen)?.try_into()?;
|
||||||
|
let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?;
|
||||||
|
|
||||||
|
let obj = LayoutObj::new(Lockscreen::new(label, bootscreen))?;
|
||||||
|
if skip_first_paint {
|
||||||
|
obj.skip_first_paint();
|
||||||
|
}
|
||||||
|
Ok(obj.into())
|
||||||
|
};
|
||||||
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// extern "C" fn new_show_busyscreen(n_args: usize, args: *const Obj, kwargs:
|
||||||
|
// *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
|
// let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
|
// let description: StrBuffer =
|
||||||
|
// kwargs.get(Qstr::MP_QSTR_description)?.try_into()?; let time_ms: u32
|
||||||
|
// = kwargs.get(Qstr::MP_QSTR_time_ms)?.try_into()?; let
|
||||||
|
// skip_first_paint: bool =
|
||||||
|
// kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?;
|
||||||
|
|
||||||
|
// let obj = LayoutObj::new(Frame::left_aligned(
|
||||||
|
// theme::label_title(),
|
||||||
|
// title,
|
||||||
|
// Dialog::new(
|
||||||
|
// Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL,
|
||||||
|
// description).centered()), Timeout::new(time_ms).map(|msg| {
|
||||||
|
// (matches!(msg, TimeoutMsg::TimedOut)).then(||
|
||||||
|
// CancelConfirmMsg::Cancelled) }),
|
||||||
|
// ),
|
||||||
|
// ))?;
|
||||||
|
// if skip_first_paint {
|
||||||
|
// obj.skip_first_paint();
|
||||||
|
// }
|
||||||
|
// Ok(obj.into())
|
||||||
|
// };
|
||||||
|
// unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
// }
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub static mp_module_trezorui2: Module = obj_module! {
|
pub static mp_module_trezorui2: Module = obj_module! {
|
||||||
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
|
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
|
||||||
@ -624,7 +799,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """Confirm list of key-value pairs. The third component in the tuple should be True if
|
/// """Confirm list of key-value pairs. The third component in the tuple should be True if
|
||||||
/// the value is to be rendered as binary with monospace font, False otherwise.
|
/// the value is to be rendered as binary with monospace font, False otherwise.
|
||||||
/// This only concerns the text style, you need to decode the value to UTF-8 in python."""
|
/// This only concerns the text style, you need to decode the value to UTF-8 in python."""
|
||||||
Qstr::MP_QSTR_confirm_properties => obj_fn_kw!(0, confirm_properties).as_obj(),
|
Qstr::MP_QSTR_confirm_properties => obj_fn_kw!(0, new_confirm_properties).as_obj(),
|
||||||
|
|
||||||
/// def confirm_output_r(
|
/// def confirm_output_r(
|
||||||
/// *,
|
/// *,
|
||||||
@ -633,7 +808,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// amount: str,
|
/// amount: str,
|
||||||
/// ) -> object:
|
/// ) -> object:
|
||||||
/// """Confirm output. Specific for model R."""
|
/// """Confirm output. Specific for model R."""
|
||||||
Qstr::MP_QSTR_confirm_output_r => obj_fn_kw!(0, confirm_output).as_obj(),
|
Qstr::MP_QSTR_confirm_output_r => obj_fn_kw!(0, new_confirm_output).as_obj(),
|
||||||
|
|
||||||
/// def confirm_total_r(
|
/// def confirm_total_r(
|
||||||
/// *,
|
/// *,
|
||||||
@ -645,7 +820,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// fee_label: str,
|
/// fee_label: str,
|
||||||
/// ) -> object:
|
/// ) -> object:
|
||||||
/// """Confirm summary of a transaction. Specific for model R."""
|
/// """Confirm summary of a transaction. Specific for model R."""
|
||||||
Qstr::MP_QSTR_confirm_total_r => obj_fn_kw!(0, confirm_total).as_obj(),
|
Qstr::MP_QSTR_confirm_total_r => obj_fn_kw!(0, new_confirm_total).as_obj(),
|
||||||
|
|
||||||
/// def show_qr(
|
/// def show_qr(
|
||||||
/// *,
|
/// *,
|
||||||
@ -655,7 +830,16 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// case_sensitive: bool,
|
/// case_sensitive: bool,
|
||||||
/// ) -> object:
|
/// ) -> object:
|
||||||
/// """Show QR code."""
|
/// """Show QR code."""
|
||||||
Qstr::MP_QSTR_show_qr => obj_fn_kw!(0, show_qr).as_obj(),
|
Qstr::MP_QSTR_show_qr => obj_fn_kw!(0, new_show_qr).as_obj(),
|
||||||
|
|
||||||
|
/// def show_info(
|
||||||
|
/// *,
|
||||||
|
/// title: str,
|
||||||
|
/// description: str = "",
|
||||||
|
/// time_ms: int = 0,
|
||||||
|
/// ) -> object:
|
||||||
|
/// """Info modal."""
|
||||||
|
Qstr::MP_QSTR_show_info => obj_fn_kw!(0, new_show_info).as_obj(),
|
||||||
|
|
||||||
/// def tutorial() -> object:
|
/// def tutorial() -> object:
|
||||||
/// """Show user how to interact with the device."""
|
/// """Show user how to interact with the device."""
|
||||||
@ -668,7 +852,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// allow_cancel: bool | None = None,
|
/// allow_cancel: bool | None = None,
|
||||||
/// ) -> str | object:
|
/// ) -> str | object:
|
||||||
/// """Request pin on device."""
|
/// """Request pin on device."""
|
||||||
Qstr::MP_QSTR_request_pin => obj_fn_kw!(0, request_pin).as_obj(),
|
Qstr::MP_QSTR_request_pin => obj_fn_kw!(0, new_request_pin).as_obj(),
|
||||||
|
|
||||||
/// def confirm_text(
|
/// def confirm_text(
|
||||||
/// *,
|
/// *,
|
||||||
@ -684,7 +868,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// share_words: Iterable[str],
|
/// share_words: Iterable[str],
|
||||||
/// ) -> None:
|
/// ) -> None:
|
||||||
/// """Shows a backup seed."""
|
/// """Shows a backup seed."""
|
||||||
Qstr::MP_QSTR_show_share_words => obj_fn_kw!(0, show_share_words).as_obj(),
|
Qstr::MP_QSTR_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(),
|
||||||
|
|
||||||
/// def select_word(
|
/// def select_word(
|
||||||
/// *,
|
/// *,
|
||||||
@ -692,21 +876,21 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// words: Iterable[str],
|
/// words: Iterable[str],
|
||||||
/// ) -> str:
|
/// ) -> str:
|
||||||
/// """Select a word from a list. TODO: should return int, to be consistent with TT's select_word"""
|
/// """Select a word from a list. TODO: should return int, to be consistent with TT's select_word"""
|
||||||
Qstr::MP_QSTR_select_word => obj_fn_kw!(0, select_word).as_obj(),
|
Qstr::MP_QSTR_select_word => obj_fn_kw!(0, new_select_word).as_obj(),
|
||||||
|
|
||||||
/// def request_word_count(
|
/// def request_word_count(
|
||||||
/// *,
|
/// *,
|
||||||
/// title: str,
|
/// title: str,
|
||||||
/// ) -> str: # TODO: make it return int
|
/// ) -> str: # TODO: make it return int
|
||||||
/// """Get word count for recovery."""
|
/// """Get word count for recovery."""
|
||||||
Qstr::MP_QSTR_request_word_count => obj_fn_kw!(0, request_word_count).as_obj(),
|
Qstr::MP_QSTR_request_word_count => obj_fn_kw!(0, new_request_word_count).as_obj(),
|
||||||
|
|
||||||
/// def request_word_bip39(
|
/// def request_word_bip39(
|
||||||
/// *,
|
/// *,
|
||||||
/// prompt: str,
|
/// prompt: str,
|
||||||
/// ) -> str:
|
/// ) -> str:
|
||||||
/// """Get recovery word for BIP39."""
|
/// """Get recovery word for BIP39."""
|
||||||
Qstr::MP_QSTR_request_word_bip39 => obj_fn_kw!(0, request_word_bip39).as_obj(),
|
Qstr::MP_QSTR_request_word_bip39 => obj_fn_kw!(0, new_request_word_bip39).as_obj(),
|
||||||
|
|
||||||
/// def request_passphrase(
|
/// def request_passphrase(
|
||||||
/// *,
|
/// *,
|
||||||
@ -714,7 +898,48 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// max_len: int,
|
/// max_len: int,
|
||||||
/// ) -> str:
|
/// ) -> str:
|
||||||
/// """Get passphrase."""
|
/// """Get passphrase."""
|
||||||
Qstr::MP_QSTR_request_passphrase => obj_fn_kw!(0, request_passphrase).as_obj(),
|
Qstr::MP_QSTR_request_passphrase => obj_fn_kw!(0, new_request_passphrase).as_obj(),
|
||||||
|
|
||||||
|
/// def show_progress(
|
||||||
|
/// *,
|
||||||
|
/// title: str,
|
||||||
|
/// indeterminate: bool = False,
|
||||||
|
/// description: str | None = None,
|
||||||
|
/// ) -> object:
|
||||||
|
/// """Show progress loader. Please note that the number of lines reserved on screen for
|
||||||
|
/// description is determined at construction time. If you want multiline descriptions
|
||||||
|
/// make sure the initial description has at least that amount of lines."""
|
||||||
|
Qstr::MP_QSTR_show_progress => obj_fn_kw!(0, new_show_progress).as_obj(),
|
||||||
|
|
||||||
|
/// def show_homescreen(
|
||||||
|
/// *,
|
||||||
|
/// label: str,
|
||||||
|
/// hold: bool,
|
||||||
|
/// notification: str | None,
|
||||||
|
/// notification_level: int = 0,
|
||||||
|
/// skip_first_paint: bool,
|
||||||
|
/// ) -> CANCELLED:
|
||||||
|
/// """Idle homescreen."""
|
||||||
|
Qstr::MP_QSTR_show_homescreen => obj_fn_kw!(0, new_show_homescreen).as_obj(),
|
||||||
|
|
||||||
|
/// def show_lockscreen(
|
||||||
|
/// *,
|
||||||
|
/// label: str,
|
||||||
|
/// bootscreen: bool,
|
||||||
|
/// skip_first_paint: bool,
|
||||||
|
/// ) -> CANCELLED:
|
||||||
|
/// """Homescreen for locked device."""
|
||||||
|
Qstr::MP_QSTR_show_lockscreen => obj_fn_kw!(0, new_show_lockscreen).as_obj(),
|
||||||
|
|
||||||
|
// /// def show_busyscreen(
|
||||||
|
// /// *,
|
||||||
|
// /// title: str,
|
||||||
|
// /// description: str,
|
||||||
|
// /// time_ms: int,
|
||||||
|
// /// skip_first_paint: bool,
|
||||||
|
// /// ) -> CANCELLED:
|
||||||
|
// /// """Homescreen used for indicating coinjoin in progress."""
|
||||||
|
// Qstr::MP_QSTR_show_busyscreen => obj_fn_kw!(0, new_show_busyscreen).as_obj(),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -21,6 +21,8 @@ pub const TEXT_BOLD: TextStyle =
|
|||||||
TextStyle::new(Font::BOLD, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0);
|
TextStyle::new(Font::BOLD, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0);
|
||||||
pub const TEXT_MONO: TextStyle =
|
pub const TEXT_MONO: TextStyle =
|
||||||
TextStyle::new(Font::MONO, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0);
|
TextStyle::new(Font::MONO, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0);
|
||||||
|
// Header does not have the ellipsis
|
||||||
|
pub const TEXT_HEADER: TextStyle = TextStyle::new(Font::BOLD, FG, BG, FG, FG);
|
||||||
|
|
||||||
pub const FORMATTED: FormattedFonts = FormattedFonts {
|
pub const FORMATTED: FormattedFonts = FormattedFonts {
|
||||||
normal: Font::NORMAL,
|
normal: Font::NORMAL,
|
||||||
|
@ -1598,7 +1598,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// ) -> object:
|
/// ) -> object:
|
||||||
/// """Show progress loader. Please note that the number of lines reserved on screen for
|
/// """Show progress loader. Please note that the number of lines reserved on screen for
|
||||||
/// description is determined at construction time. If you want multiline descriptions
|
/// description is determined at construction time. If you want multiline descriptions
|
||||||
/// make sure the initial desciption has at least that amount of lines."""
|
/// make sure the initial description has at least that amount of lines."""
|
||||||
Qstr::MP_QSTR_show_progress => obj_fn_kw!(0, new_show_progress).as_obj(),
|
Qstr::MP_QSTR_show_progress => obj_fn_kw!(0, new_show_progress).as_obj(),
|
||||||
|
|
||||||
/// def show_homescreen(
|
/// def show_homescreen(
|
||||||
|
@ -101,6 +101,16 @@ def show_qr(
|
|||||||
"""Show QR code."""
|
"""Show QR code."""
|
||||||
|
|
||||||
|
|
||||||
|
# rust/src/ui/model_tr/layout.rs
|
||||||
|
def show_info(
|
||||||
|
*,
|
||||||
|
title: str,
|
||||||
|
description: str = "",
|
||||||
|
time_ms: int = 0,
|
||||||
|
) -> object:
|
||||||
|
"""Info modal."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tr/layout.rs
|
# rust/src/ui/model_tr/layout.rs
|
||||||
def tutorial() -> object:
|
def tutorial() -> object:
|
||||||
"""Show user how to interact with the device."""
|
"""Show user how to interact with the device."""
|
||||||
@ -166,6 +176,40 @@ def request_passphrase(
|
|||||||
max_len: int,
|
max_len: int,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Get passphrase."""
|
"""Get passphrase."""
|
||||||
|
|
||||||
|
|
||||||
|
# rust/src/ui/model_tr/layout.rs
|
||||||
|
def show_progress(
|
||||||
|
*,
|
||||||
|
title: str,
|
||||||
|
indeterminate: bool = False,
|
||||||
|
description: str | None = None,
|
||||||
|
) -> object:
|
||||||
|
"""Show progress loader. Please note that the number of lines reserved on screen for
|
||||||
|
description is determined at construction time. If you want multiline descriptions
|
||||||
|
make sure the initial description has at least that amount of lines."""
|
||||||
|
|
||||||
|
|
||||||
|
# rust/src/ui/model_tr/layout.rs
|
||||||
|
def show_homescreen(
|
||||||
|
*,
|
||||||
|
label: str,
|
||||||
|
hold: bool,
|
||||||
|
notification: str | None,
|
||||||
|
notification_level: int = 0,
|
||||||
|
skip_first_paint: bool,
|
||||||
|
) -> CANCELLED:
|
||||||
|
"""Idle homescreen."""
|
||||||
|
|
||||||
|
|
||||||
|
# rust/src/ui/model_tr/layout.rs
|
||||||
|
def show_lockscreen(
|
||||||
|
*,
|
||||||
|
label: str,
|
||||||
|
bootscreen: bool,
|
||||||
|
skip_first_paint: bool,
|
||||||
|
) -> CANCELLED:
|
||||||
|
"""Homescreen for locked device."""
|
||||||
CONFIRMED: object
|
CONFIRMED: object
|
||||||
CANCELLED: object
|
CANCELLED: object
|
||||||
INFO: object
|
INFO: object
|
||||||
@ -505,7 +549,7 @@ def show_progress(
|
|||||||
) -> object:
|
) -> object:
|
||||||
"""Show progress loader. Please note that the number of lines reserved on screen for
|
"""Show progress loader. Please note that the number of lines reserved on screen for
|
||||||
description is determined at construction time. If you want multiline descriptions
|
description is determined at construction time. If you want multiline descriptions
|
||||||
make sure the initial desciption has at least that amount of lines."""
|
make sure the initial description has at least that amount of lines."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tt/layout.rs
|
# rust/src/ui/model_tt/layout.rs
|
||||||
|
@ -167,6 +167,8 @@ trezor.ui.layouts.tr.altcoin
|
|||||||
import trezor.ui.layouts.tr.altcoin
|
import trezor.ui.layouts.tr.altcoin
|
||||||
trezor.ui.layouts.tr.fido
|
trezor.ui.layouts.tr.fido
|
||||||
import trezor.ui.layouts.tr.fido
|
import trezor.ui.layouts.tr.fido
|
||||||
|
trezor.ui.layouts.tr.homescreen
|
||||||
|
import trezor.ui.layouts.tr.homescreen
|
||||||
trezor.ui.layouts.tr.recovery
|
trezor.ui.layouts.tr.recovery
|
||||||
import trezor.ui.layouts.tr.recovery
|
import trezor.ui.layouts.tr.recovery
|
||||||
trezor.ui.layouts.tr.reset
|
trezor.ui.layouts.tr.reset
|
||||||
|
@ -108,7 +108,9 @@ async def _request_wipe_code_confirm(ctx: Context, pin: str) -> str:
|
|||||||
# _wipe_code_invalid
|
# _wipe_code_invalid
|
||||||
await show_popup(
|
await show_popup(
|
||||||
"Invalid wipe code",
|
"Invalid wipe code",
|
||||||
"The wipe code must be\ndifferent from your PIN.\n\nPlease try again.",
|
text_r(
|
||||||
|
"The wipe code must be\ndifferent from your PIN.\n\nPlease try again."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -118,7 +120,5 @@ async def _request_wipe_code_confirm(ctx: Context, pin: str) -> str:
|
|||||||
# _wipe_code_mismatch
|
# _wipe_code_mismatch
|
||||||
await show_popup(
|
await show_popup(
|
||||||
"Code mismatch",
|
"Code mismatch",
|
||||||
text_r(
|
text_r("The wipe codes you\nentered do not match.\n\nPlease try again."),
|
||||||
"The wipe code must be\ndifferent from your PIN.\n\nPlease try again."
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
@ -65,7 +65,7 @@ async def _continue_recovery_process(ctx: GenericContext) -> Success:
|
|||||||
if is_first_step:
|
if is_first_step:
|
||||||
# If we are starting recovery, ask for word count first...
|
# If we are starting recovery, ask for word count first...
|
||||||
# _request_word_count
|
# _request_word_count
|
||||||
await layout.homescreen_dialog(ctx, "Select", "Select the number of words")
|
await layout.homescreen_dialog(ctx, "Select", "Select number of words")
|
||||||
# ask for the number of words
|
# ask for the number of words
|
||||||
word_count = await layout.request_word_count(ctx, dry_run)
|
word_count = await layout.request_word_count(ctx, dry_run)
|
||||||
# ...and only then show the starting screen with word count.
|
# ...and only then show the starting screen with word count.
|
||||||
|
@ -1 +1,6 @@
|
|||||||
from .tt_v2.homescreen import * # noqa: F401,F403
|
from trezor import utils
|
||||||
|
|
||||||
|
if utils.MODEL in ("T",):
|
||||||
|
from .tt_v2.homescreen import * # noqa: F401,F403
|
||||||
|
elif utils.MODEL in ("R",):
|
||||||
|
from .tr.homescreen import * # noqa: F401,F403
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
from typing import TYPE_CHECKING, Sequence
|
from typing import TYPE_CHECKING, Sequence
|
||||||
|
|
||||||
from trezor import io, log, loop, ui, wire, workflow
|
from trezor import io, log, loop, ui, workflow
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
from trezor.utils import DISABLE_ANIMATION
|
from trezor.utils import DISABLE_ANIMATION
|
||||||
|
from trezor.wire import ActionCancelled
|
||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
|
||||||
from ..common import button_request, interact
|
from ..common import button_request, interact
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Any, NoReturn, Type, Awaitable, Iterable, TypeVar
|
from typing import Any, NoReturn, Awaitable, Iterable, TypeVar
|
||||||
|
|
||||||
from trezor.wire import GenericContext, Context
|
from trezor.wire import GenericContext, Context
|
||||||
from ..common import PropertyType
|
from ..common import PropertyType, ExceptionType, ProgressLayout
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
ExceptionType = BaseException | Type[BaseException]
|
|
||||||
|
|
||||||
|
|
||||||
BR_TYPE_OTHER = ButtonRequestType.Other # global_import_cache
|
BR_TYPE_OTHER = ButtonRequestType.Other # global_import_cache
|
||||||
|
|
||||||
@ -56,11 +55,11 @@ class RustLayoutContent:
|
|||||||
|
|
||||||
def active_page(self) -> int:
|
def active_page(self) -> int:
|
||||||
"""Current index of the active page. Should always be there."""
|
"""Current index of the active page. Should always be there."""
|
||||||
return self.kw_pair_int_compulsory("active_page")
|
return self.kw_pair_int("active_page") or 0
|
||||||
|
|
||||||
def page_count(self) -> int:
|
def page_count(self) -> int:
|
||||||
"""Overall number of pages in this screen. Should always be there."""
|
"""Overall number of pages in this screen. Should always be there."""
|
||||||
return self.kw_pair_int_compulsory("page_count")
|
return self.kw_pair_int("page_count") or 1
|
||||||
|
|
||||||
def in_flow(self) -> bool:
|
def in_flow(self) -> bool:
|
||||||
"""Whether we are in flow."""
|
"""Whether we are in flow."""
|
||||||
@ -136,22 +135,20 @@ class RustLayoutContent:
|
|||||||
|
|
||||||
def buttons_content(self) -> tuple[str, str, str]:
|
def buttons_content(self) -> tuple[str, str, str]:
|
||||||
"""Getting visual details for all three buttons. They should always be there."""
|
"""Getting visual details for all three buttons. They should always be there."""
|
||||||
|
if self.BTN_TAG not in self.str_content:
|
||||||
|
return ("None", "None", "None")
|
||||||
btns = self._get_strings_inside_tag(self.str_content, self.BTN_TAG)
|
btns = self._get_strings_inside_tag(self.str_content, self.BTN_TAG)
|
||||||
assert len(btns) == 3
|
assert len(btns) == 3
|
||||||
return btns[0], btns[1], btns[2]
|
return btns[0], btns[1], btns[2]
|
||||||
|
|
||||||
def button_actions(self) -> tuple[str, str, str]:
|
def button_actions(self) -> tuple[str, str, str]:
|
||||||
"""Getting actions for all three buttons. They should always be there."""
|
"""Getting actions for all three buttons. They should always be there."""
|
||||||
|
if "_action" not in self.str_content:
|
||||||
|
return ("None", "None", "None")
|
||||||
action_ids = ("left_action", "middle_action", "right_action")
|
action_ids = ("left_action", "middle_action", "right_action")
|
||||||
assert len(action_ids) == 3
|
assert len(action_ids) == 3
|
||||||
return tuple(self.kw_pair_compulsory(action) for action in action_ids)
|
return tuple(self.kw_pair_compulsory(action) for action in action_ids)
|
||||||
|
|
||||||
def kw_pair_int_compulsory(self, key: str) -> int:
|
|
||||||
"""Getting integer that cannot be missing."""
|
|
||||||
val = self.kw_pair_int(key)
|
|
||||||
assert val is not None
|
|
||||||
return val
|
|
||||||
|
|
||||||
def kw_pair_int(self, key: str) -> int | None:
|
def kw_pair_int(self, key: str) -> int | None:
|
||||||
"""Getting the value of a key-value pair as an integer. None if missing."""
|
"""Getting the value of a key-value pair as an integer. None if missing."""
|
||||||
val = self.kw_pair(key)
|
val = self.kw_pair(key)
|
||||||
@ -196,6 +193,35 @@ class RustLayout(ui.Layout):
|
|||||||
def set_timer(self, token: int, deadline: int) -> None:
|
def set_timer(self, token: int, deadline: int) -> None:
|
||||||
self.timer.schedule(deadline, token)
|
self.timer.schedule(deadline, token)
|
||||||
|
|
||||||
|
def request_complete_repaint(self) -> None:
|
||||||
|
msg = self.layout.request_complete_repaint()
|
||||||
|
assert msg is None
|
||||||
|
|
||||||
|
def _paint(self) -> None:
|
||||||
|
import storage.cache as storage_cache
|
||||||
|
|
||||||
|
painted = self.layout.paint()
|
||||||
|
if storage_cache.homescreen_shown is not None and painted:
|
||||||
|
storage_cache.homescreen_shown = None
|
||||||
|
|
||||||
|
def _first_paint(self) -> None:
|
||||||
|
# Clear the screen of any leftovers.
|
||||||
|
ui.backlight_fade(ui.style.BACKLIGHT_DIM)
|
||||||
|
ui.display.clear()
|
||||||
|
self._paint()
|
||||||
|
|
||||||
|
if __debug__ and self.should_notify_layout_change:
|
||||||
|
from apps.debug import notify_layout_change
|
||||||
|
|
||||||
|
# notify about change and do not notify again until next await.
|
||||||
|
# (handle_rendering might be called multiple times in a single await,
|
||||||
|
# because of the endless loop in __iter__)
|
||||||
|
self.should_notify_layout_change = False
|
||||||
|
notify_layout_change(self)
|
||||||
|
|
||||||
|
# Turn the brightness on again.
|
||||||
|
ui.backlight_fade(self.BACKLIGHT_LEVEL)
|
||||||
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
from trezor.enums import DebugPhysicalButton
|
from trezor.enums import DebugPhysicalButton
|
||||||
|
|
||||||
@ -394,6 +420,19 @@ class RustLayout(ui.Layout):
|
|||||||
self.layout.paint()
|
self.layout.paint()
|
||||||
|
|
||||||
|
|
||||||
|
def draw_simple(layout: Any) -> None:
|
||||||
|
# Simple drawing not supported for layouts that set timers.
|
||||||
|
def dummy_set_timer(token: int, deadline: int) -> None:
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
layout.attach_timer_fn(dummy_set_timer)
|
||||||
|
ui.backlight_fade(ui.style.BACKLIGHT_DIM)
|
||||||
|
ui.display.clear()
|
||||||
|
layout.paint()
|
||||||
|
ui.refresh()
|
||||||
|
ui.backlight_fade(ui.style.BACKLIGHT_NORMAL)
|
||||||
|
|
||||||
|
|
||||||
# Temporary function, so we know where it is used
|
# Temporary function, so we know where it is used
|
||||||
# Should be gradually replaced by custom designs/layouts
|
# Should be gradually replaced by custom designs/layouts
|
||||||
async def _placeholder_confirm(
|
async def _placeholder_confirm(
|
||||||
@ -448,7 +487,7 @@ async def get_bool(
|
|||||||
return result is trezorui2.CONFIRMED
|
return result is trezorui2.CONFIRMED
|
||||||
|
|
||||||
|
|
||||||
async def raise_if_cancelled(a: Awaitable[T], exc: Any = wire.ActionCancelled) -> T:
|
async def raise_if_cancelled(a: Awaitable[T], exc: Any = ActionCancelled) -> T:
|
||||||
result = await a
|
result = await a
|
||||||
if result is trezorui2.CANCELLED:
|
if result is trezorui2.CANCELLED:
|
||||||
raise exc
|
raise exc
|
||||||
@ -469,8 +508,9 @@ async def confirm_action(
|
|||||||
verb: str = "CONFIRM",
|
verb: str = "CONFIRM",
|
||||||
verb_cancel: str | None = None,
|
verb_cancel: str | None = None,
|
||||||
hold: bool = False,
|
hold: bool = False,
|
||||||
|
hold_danger: bool = False,
|
||||||
reverse: bool = False,
|
reverse: bool = False,
|
||||||
exc: ExceptionType = wire.ActionCancelled,
|
exc: ExceptionType = ActionCancelled,
|
||||||
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||||
) -> None:
|
) -> None:
|
||||||
if verb_cancel is not None:
|
if verb_cancel is not None:
|
||||||
@ -584,7 +624,7 @@ async def confirm_path_warning(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _show_xpub(xpub: str, title: str, cancel: str) -> ui.Layout:
|
def _show_xpub(xpub: str, title: str, cancel: str | None) -> ui.Layout:
|
||||||
content = RustLayout(
|
content = RustLayout(
|
||||||
trezorui2.confirm_text(
|
trezorui2.confirm_text(
|
||||||
title=title.upper(),
|
title=title.upper(),
|
||||||
@ -596,11 +636,11 @@ def _show_xpub(xpub: str, title: str, cancel: str) -> ui.Layout:
|
|||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
async def show_xpub(ctx: GenericContext, xpub: str, title: str, cancel: str) -> None:
|
async def show_xpub(ctx: GenericContext, xpub: str, title: str) -> None:
|
||||||
await raise_if_cancelled(
|
await raise_if_cancelled(
|
||||||
interact(
|
interact(
|
||||||
ctx,
|
ctx,
|
||||||
_show_xpub(xpub, title, cancel),
|
_show_xpub(xpub, title, None),
|
||||||
"show_xpub",
|
"show_xpub",
|
||||||
ButtonRequestType.PublicKey,
|
ButtonRequestType.PublicKey,
|
||||||
)
|
)
|
||||||
@ -699,7 +739,7 @@ async def _show_modal(
|
|||||||
content: str,
|
content: str,
|
||||||
button_confirm: str | None,
|
button_confirm: str | None,
|
||||||
button_cancel: str | None,
|
button_cancel: str | None,
|
||||||
exc: ExceptionType = wire.ActionCancelled,
|
exc: ExceptionType = ActionCancelled,
|
||||||
) -> None:
|
) -> None:
|
||||||
await confirm_action(
|
await confirm_action(
|
||||||
ctx=ctx,
|
ctx=ctx,
|
||||||
@ -722,7 +762,7 @@ async def show_error_and_raise(
|
|||||||
subheader: str | None = None,
|
subheader: str | None = None,
|
||||||
button: str = "Close",
|
button: str = "Close",
|
||||||
red: bool = False,
|
red: bool = False,
|
||||||
exc: ExceptionType = wire.ActionCancelled,
|
exc: ExceptionType = ActionCancelled,
|
||||||
) -> NoReturn:
|
) -> NoReturn:
|
||||||
await _show_modal(
|
await _show_modal(
|
||||||
ctx=ctx,
|
ctx=ctx,
|
||||||
@ -949,7 +989,13 @@ async def confirm_properties(
|
|||||||
hold: bool = False,
|
hold: bool = False,
|
||||||
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
|
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
|
||||||
) -> None:
|
) -> None:
|
||||||
items = [(prop[0], prop[1], isinstance(prop[1], bytes)) for prop in props]
|
from ubinascii import hexlify
|
||||||
|
|
||||||
|
def handle_bytes(prop: PropertyType):
|
||||||
|
if isinstance(prop[1], bytes):
|
||||||
|
return (prop[0], hexlify(prop[1]).decode(), True)
|
||||||
|
else:
|
||||||
|
return (prop[0], prop[1], False)
|
||||||
|
|
||||||
await raise_if_cancelled(
|
await raise_if_cancelled(
|
||||||
interact(
|
interact(
|
||||||
@ -957,7 +1003,7 @@ async def confirm_properties(
|
|||||||
RustLayout(
|
RustLayout(
|
||||||
trezorui2.confirm_properties(
|
trezorui2.confirm_properties(
|
||||||
title=title.upper(),
|
title=title.upper(),
|
||||||
items=items,
|
items=map(handle_bytes, props), # type: ignore [cannot be assigned to parameter "items"]
|
||||||
hold=hold,
|
hold=hold,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -967,6 +1013,32 @@ async def confirm_properties(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_value(
|
||||||
|
ctx: GenericContext,
|
||||||
|
title: str,
|
||||||
|
value: str,
|
||||||
|
description: str,
|
||||||
|
br_type: str,
|
||||||
|
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||||
|
*,
|
||||||
|
verb: str | None = None,
|
||||||
|
hold: bool = False,
|
||||||
|
) -> Awaitable[None]:
|
||||||
|
"""General confirmation dialog, used by many other confirm_* functions."""
|
||||||
|
|
||||||
|
if not verb and not hold:
|
||||||
|
raise ValueError("Either verb or hold=True must be set")
|
||||||
|
|
||||||
|
return _placeholder_confirm(
|
||||||
|
ctx=ctx,
|
||||||
|
br_type=br_type,
|
||||||
|
title=title.upper(),
|
||||||
|
data=value,
|
||||||
|
description=description,
|
||||||
|
br_code=br_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def confirm_total(
|
async def confirm_total(
|
||||||
ctx: GenericContext,
|
ctx: GenericContext,
|
||||||
total_amount: str,
|
total_amount: str,
|
||||||
@ -975,7 +1047,6 @@ async def confirm_total(
|
|||||||
title: str = "Send transaction?",
|
title: str = "Send transaction?",
|
||||||
total_label: str = "Total amount:",
|
total_label: str = "Total amount:",
|
||||||
fee_label: str = "Including fee:",
|
fee_label: str = "Including fee:",
|
||||||
icon_color: int = ui.GREEN,
|
|
||||||
br_type: str = "confirm_total",
|
br_type: str = "confirm_total",
|
||||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -1018,19 +1089,14 @@ async def confirm_metadata(
|
|||||||
content: str,
|
content: str,
|
||||||
param: str | None = None,
|
param: str | None = None,
|
||||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||||
hide_continue: bool = False,
|
|
||||||
hold: bool = False,
|
hold: bool = False,
|
||||||
param_font: int = ui.BOLD,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
text = content.format(param)
|
# TODO: implement `hold`
|
||||||
if not hide_continue:
|
|
||||||
text += "\n\nContinue?"
|
|
||||||
|
|
||||||
await _placeholder_confirm(
|
await _placeholder_confirm(
|
||||||
ctx=ctx,
|
ctx=ctx,
|
||||||
br_type=br_type,
|
br_type=br_type,
|
||||||
title=title.upper(),
|
title=title.upper(),
|
||||||
data=text,
|
data=content.format(param),
|
||||||
description="",
|
description="",
|
||||||
br_code=br_code,
|
br_code=br_code,
|
||||||
)
|
)
|
||||||
@ -1173,7 +1239,23 @@ async def show_popup(
|
|||||||
description_param: str = "",
|
description_param: str = "",
|
||||||
timeout_ms: int = 3000,
|
timeout_ms: int = 3000,
|
||||||
) -> None:
|
) -> None:
|
||||||
raise NotImplementedError
|
if subtitle:
|
||||||
|
title += f"\n{subtitle}"
|
||||||
|
await RustLayout(
|
||||||
|
trezorui2.show_info(
|
||||||
|
title=title,
|
||||||
|
description=description.format(description_param),
|
||||||
|
time_ms=timeout_ms,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def request_passphrase_on_host() -> None:
|
||||||
|
draw_simple(
|
||||||
|
trezorui2.show_info(
|
||||||
|
title="Please type your passphrase on the connected host.",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def request_passphrase_on_device(ctx: GenericContext, max_len: int) -> str:
|
async def request_passphrase_on_device(ctx: GenericContext, max_len: int) -> str:
|
||||||
@ -1190,7 +1272,7 @@ async def request_passphrase_on_device(ctx: GenericContext, max_len: int) -> str
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
if result is trezorui2.CANCELLED:
|
if result is trezorui2.CANCELLED:
|
||||||
raise wire.ActionCancelled("Passphrase entry cancelled")
|
raise ActionCancelled("Passphrase entry cancelled")
|
||||||
|
|
||||||
assert isinstance(result, str)
|
assert isinstance(result, str)
|
||||||
return result
|
return result
|
||||||
@ -1202,6 +1284,8 @@ async def request_pin_on_device(
|
|||||||
attempts_remaining: int | None,
|
attempts_remaining: int | None,
|
||||||
allow_cancel: bool,
|
allow_cancel: bool,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
from trezor import wire
|
||||||
|
|
||||||
if attempts_remaining is None:
|
if attempts_remaining is None:
|
||||||
subprompt = ""
|
subprompt = ""
|
||||||
elif attempts_remaining == 1:
|
elif attempts_remaining == 1:
|
||||||
@ -1311,3 +1395,55 @@ async def confirm_set_new_pin(
|
|||||||
hold=True,
|
hold=True,
|
||||||
br_code=br_code,
|
br_code=br_code,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RustProgress:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
title: str,
|
||||||
|
description: str | None = None,
|
||||||
|
indeterminate: bool = False,
|
||||||
|
):
|
||||||
|
self.layout: Any = trezorui2.show_progress(
|
||||||
|
title=title.upper(),
|
||||||
|
indeterminate=indeterminate,
|
||||||
|
description=description or "",
|
||||||
|
)
|
||||||
|
ui.backlight_fade(ui.style.BACKLIGHT_DIM)
|
||||||
|
ui.display.clear()
|
||||||
|
self.layout.attach_timer_fn(self.set_timer)
|
||||||
|
self.layout.paint()
|
||||||
|
ui.backlight_fade(ui.style.BACKLIGHT_NORMAL)
|
||||||
|
|
||||||
|
def set_timer(self, token: int, deadline: int) -> None:
|
||||||
|
raise RuntimeError # progress layouts should not set timers
|
||||||
|
|
||||||
|
def report(self, value: int, description: str | None = None):
|
||||||
|
msg = self.layout.progress_event(value, description or "")
|
||||||
|
assert msg is None
|
||||||
|
self.layout.paint()
|
||||||
|
ui.refresh()
|
||||||
|
|
||||||
|
|
||||||
|
def progress(message: str = "PLEASE WAIT") -> ProgressLayout:
|
||||||
|
return RustProgress(message.upper())
|
||||||
|
|
||||||
|
|
||||||
|
def bitcoin_progress(message: str) -> ProgressLayout:
|
||||||
|
return RustProgress(message.upper())
|
||||||
|
|
||||||
|
|
||||||
|
def pin_progress(message: str, description: str) -> ProgressLayout:
|
||||||
|
return RustProgress(message.upper(), description=description)
|
||||||
|
|
||||||
|
|
||||||
|
def monero_keyimage_sync_progress() -> ProgressLayout:
|
||||||
|
return RustProgress("SYNCING")
|
||||||
|
|
||||||
|
|
||||||
|
def monero_live_refresh_progress() -> ProgressLayout:
|
||||||
|
return RustProgress("REFRESHING", description="", indeterminate=True)
|
||||||
|
|
||||||
|
|
||||||
|
def monero_transaction_progress_inner() -> ProgressLayout:
|
||||||
|
return RustProgress("SIGNING TRANSACTION", description="")
|
||||||
|
151
core/src/trezor/ui/layouts/tr/homescreen.py
Normal file
151
core/src/trezor/ui/layouts/tr/homescreen.py
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import storage.cache as storage_cache
|
||||||
|
from trezor import ui
|
||||||
|
|
||||||
|
import trezorui2
|
||||||
|
|
||||||
|
from . import RustLayout
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from trezor import loop
|
||||||
|
from typing import Any, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
class HomescreenBase(RustLayout):
|
||||||
|
RENDER_INDICATOR: object | None = None
|
||||||
|
|
||||||
|
def __init__(self, layout: Any) -> None:
|
||||||
|
super().__init__(layout=layout)
|
||||||
|
self.is_connected = True
|
||||||
|
|
||||||
|
async def __iter__(self) -> Any:
|
||||||
|
# We need to catch the ui.Cancelled exception that kills us, because that means
|
||||||
|
# that we will need to draw on screen again after restart.
|
||||||
|
try:
|
||||||
|
return await super().__iter__()
|
||||||
|
except ui.Cancelled:
|
||||||
|
storage_cache.homescreen_shown = None
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _first_paint(self) -> None:
|
||||||
|
from trezor import utils
|
||||||
|
|
||||||
|
if storage_cache.homescreen_shown is not self.RENDER_INDICATOR:
|
||||||
|
super()._first_paint()
|
||||||
|
storage_cache.homescreen_shown = self.RENDER_INDICATOR
|
||||||
|
|
||||||
|
# - RENDER_INDICATOR is set -> USB warning is not displayed
|
||||||
|
# - RENDER_INDICATOR is not set -> initially homescreen does not display warning
|
||||||
|
# - usb_checker_task only handles state changes
|
||||||
|
# Here we need to handle the case when homescreen is started with USB disconnected.
|
||||||
|
if not utils.usb_data_connected():
|
||||||
|
msg = self.layout.usb_event(False)
|
||||||
|
self._paint()
|
||||||
|
if msg is not None:
|
||||||
|
raise ui.Result(msg)
|
||||||
|
|
||||||
|
# In __debug__ mode, ignore {confirm,swipe,input}_signal.
|
||||||
|
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
|
||||||
|
return self.handle_timers(), self.handle_input_and_rendering()
|
||||||
|
|
||||||
|
|
||||||
|
class Homescreen(HomescreenBase):
|
||||||
|
RENDER_INDICATOR = storage_cache.HOMESCREEN_ON
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
label: str | None,
|
||||||
|
notification: str | None,
|
||||||
|
notification_is_error: bool,
|
||||||
|
hold_to_lock: bool,
|
||||||
|
) -> None:
|
||||||
|
level = 1
|
||||||
|
if notification is not None:
|
||||||
|
notification = notification.rstrip("!")
|
||||||
|
if "EXPERIMENTAL" in notification:
|
||||||
|
level = 2
|
||||||
|
elif notification_is_error:
|
||||||
|
level = 0
|
||||||
|
|
||||||
|
skip = storage_cache.homescreen_shown is self.RENDER_INDICATOR
|
||||||
|
super().__init__(
|
||||||
|
layout=trezorui2.show_homescreen(
|
||||||
|
label=label or "My Trezor",
|
||||||
|
notification=notification,
|
||||||
|
notification_level=level,
|
||||||
|
hold=hold_to_lock,
|
||||||
|
skip_first_paint=skip,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def usb_checker_task(self) -> None:
|
||||||
|
from trezor import io, loop
|
||||||
|
|
||||||
|
usbcheck = loop.wait(io.USB_CHECK)
|
||||||
|
while True:
|
||||||
|
is_connected = await usbcheck
|
||||||
|
if is_connected != self.is_connected:
|
||||||
|
self.is_connected = is_connected
|
||||||
|
self.layout.usb_event(is_connected)
|
||||||
|
self.layout.paint()
|
||||||
|
storage_cache.homescreen_shown = None
|
||||||
|
|
||||||
|
def create_tasks(self) -> Tuple[loop.AwaitableTask, ...]:
|
||||||
|
return super().create_tasks() + (self.usb_checker_task(),)
|
||||||
|
|
||||||
|
|
||||||
|
class Lockscreen(HomescreenBase):
|
||||||
|
RENDER_INDICATOR = storage_cache.LOCKSCREEN_ON
|
||||||
|
BACKLIGHT_LEVEL = ui.BACKLIGHT_LOW
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
label: str | None,
|
||||||
|
bootscreen: bool = False,
|
||||||
|
) -> None:
|
||||||
|
self.bootscreen = bootscreen
|
||||||
|
if bootscreen:
|
||||||
|
self.BACKLIGHT_LEVEL = ui.BACKLIGHT_NORMAL
|
||||||
|
|
||||||
|
skip = (
|
||||||
|
not bootscreen and storage_cache.homescreen_shown is self.RENDER_INDICATOR
|
||||||
|
)
|
||||||
|
super().__init__(
|
||||||
|
layout=trezorui2.show_lockscreen(
|
||||||
|
label=label or "My Trezor",
|
||||||
|
bootscreen=bootscreen,
|
||||||
|
skip_first_paint=skip,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def __iter__(self) -> Any:
|
||||||
|
result = await super().__iter__()
|
||||||
|
if self.bootscreen:
|
||||||
|
self.request_complete_repaint()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class Busyscreen(HomescreenBase):
|
||||||
|
RENDER_INDICATOR = storage_cache.BUSYSCREEN_ON
|
||||||
|
|
||||||
|
def __init__(self, delay_ms: int) -> None:
|
||||||
|
skip = storage_cache.homescreen_shown is self.RENDER_INDICATOR
|
||||||
|
super().__init__(
|
||||||
|
layout=trezorui2.show_busyscreen(
|
||||||
|
title="PLEASE WAIT",
|
||||||
|
description="CoinJoin in progress.\n\nDo not disconnect your\nTrezor.",
|
||||||
|
time_ms=delay_ms,
|
||||||
|
skip_first_paint=skip,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def __iter__(self) -> Any:
|
||||||
|
from apps.base import set_homescreen
|
||||||
|
|
||||||
|
# Handle timeout.
|
||||||
|
result = await super().__iter__()
|
||||||
|
assert result == trezorui2.CANCELLED
|
||||||
|
storage_cache.delete(storage_cache.APP_COMMON_BUSY_DEADLINE_MS)
|
||||||
|
set_homescreen()
|
||||||
|
return result
|
@ -750,7 +750,7 @@ def confirm_value(
|
|||||||
value: str,
|
value: str,
|
||||||
description: str,
|
description: str,
|
||||||
br_type: str,
|
br_type: str,
|
||||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||||
*,
|
*,
|
||||||
verb: str | None = None,
|
verb: str | None = None,
|
||||||
hold: bool = False,
|
hold: bool = False,
|
||||||
@ -975,7 +975,7 @@ async def confirm_coinjoin(
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
"coinjoin_final",
|
"coinjoin_final",
|
||||||
ButtonRequestType.Other,
|
BR_TYPE_OTHER,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1000,7 +1000,7 @@ async def confirm_sign_identity(
|
|||||||
data=identity,
|
data=identity,
|
||||||
description=challenge_visual + "\n" if challenge_visual else "",
|
description=challenge_visual + "\n" if challenge_visual else "",
|
||||||
br_type="sign_identity",
|
br_type="sign_identity",
|
||||||
br_code=ButtonRequestType.Other,
|
br_code=BR_TYPE_OTHER,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1020,7 +1020,7 @@ async def confirm_signverify(
|
|||||||
title,
|
title,
|
||||||
address,
|
address,
|
||||||
"Confirm address:",
|
"Confirm address:",
|
||||||
br_code=ButtonRequestType.Other,
|
br_code=BR_TYPE_OTHER,
|
||||||
)
|
)
|
||||||
|
|
||||||
await confirm_blob(
|
await confirm_blob(
|
||||||
@ -1029,7 +1029,7 @@ async def confirm_signverify(
|
|||||||
title,
|
title,
|
||||||
message,
|
message,
|
||||||
"Confirm message:",
|
"Confirm message:",
|
||||||
br_code=ButtonRequestType.Other,
|
br_code=BR_TYPE_OTHER,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1117,7 +1117,7 @@ async def confirm_pin_action(
|
|||||||
title: str,
|
title: str,
|
||||||
action: str | None,
|
action: str | None,
|
||||||
description: str | None = "Do you really want to",
|
description: str | None = "Do you really want to",
|
||||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||||
) -> None:
|
) -> None:
|
||||||
return await confirm_action(
|
return await confirm_action(
|
||||||
ctx,
|
ctx,
|
||||||
@ -1133,7 +1133,7 @@ async def confirm_pin_action(
|
|||||||
async def confirm_reenter_pin(
|
async def confirm_reenter_pin(
|
||||||
ctx: GenericContext,
|
ctx: GenericContext,
|
||||||
br_type: str = "set_pin",
|
br_type: str = "set_pin",
|
||||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||||
) -> None:
|
) -> None:
|
||||||
return await confirm_action(
|
return await confirm_action(
|
||||||
ctx,
|
ctx,
|
||||||
@ -1148,7 +1148,7 @@ async def confirm_reenter_pin(
|
|||||||
async def pin_mismatch(
|
async def pin_mismatch(
|
||||||
ctx: GenericContext,
|
ctx: GenericContext,
|
||||||
br_type: str = "set_pin",
|
br_type: str = "set_pin",
|
||||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||||
) -> None:
|
) -> None:
|
||||||
return await confirm_action(
|
return await confirm_action(
|
||||||
ctx,
|
ctx,
|
||||||
@ -1168,7 +1168,7 @@ async def confirm_set_new_pin(
|
|||||||
action: str,
|
action: str,
|
||||||
information: list[str],
|
information: list[str],
|
||||||
description: str = "Do you want to",
|
description: str = "Do you want to",
|
||||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||||
) -> None:
|
) -> None:
|
||||||
await confirm_action(
|
await confirm_action(
|
||||||
ctx,
|
ctx,
|
||||||
|
112
docs/ci/jobs.md
112
docs/ci/jobs.md
@ -54,7 +54,7 @@ or contain `[no changelog]` in the commit message.
|
|||||||
## BUILD stage - [build.yml](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml)
|
## BUILD stage - [build.yml](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml)
|
||||||
All builds are published as artifacts so they can be downloaded and used.
|
All builds are published as artifacts so they can be downloaded and used.
|
||||||
|
|
||||||
Consists of **33 jobs** below:
|
Consists of **32 jobs** below:
|
||||||
|
|
||||||
### [core fw regular build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L20)
|
### [core fw regular build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L20)
|
||||||
Build of Core into firmware. Regular version.
|
Build of Core into firmware. Regular version.
|
||||||
@ -106,53 +106,51 @@ Frozen version. That means you do not need any other files to run it,
|
|||||||
it is just a single binary file that you can execute directly.
|
it is just a single binary file that you can execute directly.
|
||||||
**Are you looking for a Trezor T emulator? This is most likely it.**
|
**Are you looking for a Trezor T emulator? This is most likely it.**
|
||||||
|
|
||||||
### [core unix frozen ui2 debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L288)
|
### [core unix frozen R debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L287)
|
||||||
|
|
||||||
### [core unix frozen R debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L302)
|
### [core unix frozen R debug build arm](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L301)
|
||||||
|
|
||||||
### [core unix frozen R debug build arm](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L316)
|
### [core unix R debugger build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L320)
|
||||||
|
|
||||||
### [core unix R debugger build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L335)
|
|
||||||
Debugger build for gdb/lldb.
|
Debugger build for gdb/lldb.
|
||||||
|
|
||||||
### [core unix frozen debug asan build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L350)
|
### [core unix frozen debug asan build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L335)
|
||||||
|
|
||||||
### [core unix frozen debug build arm](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L366)
|
### [core unix frozen debug build arm](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L351)
|
||||||
|
|
||||||
### [core unix frozen btconly debug t1 build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L388)
|
### [core unix frozen btconly debug t1 build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L373)
|
||||||
|
|
||||||
### [core macos frozen regular build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L404)
|
### [core macos frozen regular build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L389)
|
||||||
|
|
||||||
### [crypto build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L429)
|
### [crypto build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L414)
|
||||||
Build of our cryptographic library, which is then incorporated into the other builds.
|
Build of our cryptographic library, which is then incorporated into the other builds.
|
||||||
|
|
||||||
### [legacy fw regular build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L458)
|
### [legacy fw regular build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L443)
|
||||||
|
|
||||||
### [legacy fw regular debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L474)
|
### [legacy fw regular debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L459)
|
||||||
|
|
||||||
### [legacy fw btconly build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L491)
|
### [legacy fw btconly build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L476)
|
||||||
|
|
||||||
### [legacy fw btconly debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L510)
|
### [legacy fw btconly debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L495)
|
||||||
|
|
||||||
### [legacy emu regular debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L531)
|
### [legacy emu regular debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L516)
|
||||||
Regular version (not only Bitcoin) of above.
|
Regular version (not only Bitcoin) of above.
|
||||||
**Are you looking for a Trezor One emulator? This is most likely it.**
|
**Are you looking for a Trezor One emulator? This is most likely it.**
|
||||||
|
|
||||||
### [legacy emu regular debug asan build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L546)
|
### [legacy emu regular debug asan build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L531)
|
||||||
|
|
||||||
### [legacy emu regular debug build arm](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L564)
|
### [legacy emu regular debug build arm](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L549)
|
||||||
|
|
||||||
### [legacy emu btconly debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L590)
|
### [legacy emu btconly debug build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L575)
|
||||||
Build of Legacy into UNIX emulator. Use keyboard arrows to emulate button presses.
|
Build of Legacy into UNIX emulator. Use keyboard arrows to emulate button presses.
|
||||||
Bitcoin-only version.
|
Bitcoin-only version.
|
||||||
|
|
||||||
### [legacy emu btconly debug asan build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L607)
|
### [legacy emu btconly debug asan build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L592)
|
||||||
|
|
||||||
---
|
---
|
||||||
## TEST stage - [test.yml](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml)
|
## TEST stage - [test.yml](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml)
|
||||||
All the tests run test cases on the freshly built emulators from the previous `BUILD` stage.
|
All the tests run test cases on the freshly built emulators from the previous `BUILD` stage.
|
||||||
|
|
||||||
Consists of **36 jobs** below:
|
Consists of **35 jobs** below:
|
||||||
|
|
||||||
### [core unit python test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L15)
|
### [core unit python test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L15)
|
||||||
Python unit tests, checking core functionality.
|
Python unit tests, checking core functionality.
|
||||||
@ -170,76 +168,74 @@ with the expected UI result.
|
|||||||
See artifacts for a comprehensive report of UI.
|
See artifacts for a comprehensive report of UI.
|
||||||
See [docs/tests/ui-tests](../tests/ui-tests.md) for more info.
|
See [docs/tests/ui-tests](../tests/ui-tests.md) for more info.
|
||||||
|
|
||||||
### [core device asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L94)
|
### [core device R test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L94)
|
||||||
|
|
||||||
### [core device R test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L127)
|
### [core device asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L126)
|
||||||
|
|
||||||
### [core device asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L159)
|
### [core btconly device test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L145)
|
||||||
|
|
||||||
### [core btconly device test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L178)
|
|
||||||
Device tests excluding altcoins, only for BTC.
|
Device tests excluding altcoins, only for BTC.
|
||||||
|
|
||||||
### [core btconly device asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L198)
|
### [core btconly device asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L165)
|
||||||
|
|
||||||
### [core monero test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L219)
|
### [core monero test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L186)
|
||||||
Monero tests.
|
Monero tests.
|
||||||
|
|
||||||
### [core monero asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L238)
|
### [core monero asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L205)
|
||||||
|
|
||||||
### [core u2f test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L260)
|
### [core u2f test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L227)
|
||||||
Tests for U2F and HID.
|
Tests for U2F and HID.
|
||||||
|
|
||||||
### [core u2f asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L279)
|
### [core u2f asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L246)
|
||||||
|
|
||||||
### [core fido2 test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L297)
|
### [core fido2 test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L264)
|
||||||
FIDO2 device tests.
|
FIDO2 device tests.
|
||||||
|
|
||||||
### [core fido2 asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L320)
|
### [core fido2 asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L287)
|
||||||
|
|
||||||
### [core click test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L340)
|
### [core click test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L307)
|
||||||
Click tests.
|
Click tests.
|
||||||
See [docs/tests/click-tests](../tests/click-tests.md) for more info.
|
See [docs/tests/click-tests](../tests/click-tests.md) for more info.
|
||||||
|
|
||||||
### [core click asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L357)
|
### [core click asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L324)
|
||||||
|
|
||||||
### [core upgrade test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L378)
|
### [core upgrade test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L345)
|
||||||
Upgrade tests.
|
Upgrade tests.
|
||||||
See [docs/tests/upgrade-tests](../tests/upgrade-tests.md) for more info.
|
See [docs/tests/upgrade-tests](../tests/upgrade-tests.md) for more info.
|
||||||
|
|
||||||
### [core upgrade asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L397)
|
### [core upgrade asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L364)
|
||||||
|
|
||||||
### [core persistence test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L419)
|
### [core persistence test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L386)
|
||||||
Persistence tests.
|
Persistence tests.
|
||||||
|
|
||||||
### [core persistence asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L435)
|
### [core persistence asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L402)
|
||||||
|
|
||||||
### [core hwi test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L453)
|
### [core hwi test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L420)
|
||||||
|
|
||||||
### [crypto test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L471)
|
### [crypto test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L438)
|
||||||
|
|
||||||
### [legacy device test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L502)
|
### [legacy device test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L469)
|
||||||
|
|
||||||
### [legacy asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L529)
|
### [legacy asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L496)
|
||||||
|
|
||||||
### [legacy btconly test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L541)
|
### [legacy btconly test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L508)
|
||||||
|
|
||||||
### [legacy btconly asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L561)
|
### [legacy btconly asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L528)
|
||||||
|
|
||||||
### [legacy upgrade test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L576)
|
### [legacy upgrade test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L543)
|
||||||
|
|
||||||
### [legacy upgrade asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L595)
|
### [legacy upgrade asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L562)
|
||||||
|
|
||||||
### [legacy hwi test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L616)
|
### [legacy hwi test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L583)
|
||||||
|
|
||||||
### [python test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L635)
|
### [python test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L602)
|
||||||
|
|
||||||
### [python support test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L654)
|
### [python support test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L621)
|
||||||
|
|
||||||
### [storage test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L664)
|
### [storage test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L631)
|
||||||
|
|
||||||
### [core unix memory profiler](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L688)
|
### [core unix memory profiler](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L655)
|
||||||
|
|
||||||
### [connect test core](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L712)
|
### [connect test core](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L679)
|
||||||
|
|
||||||
---
|
---
|
||||||
## TEST-HW stage - [test-hw.yml](https://github.com/trezor/trezor-firmware/blob/master/ci/test-hw.yml)
|
## TEST-HW stage - [test-hw.yml](https://github.com/trezor/trezor-firmware/blob/master/ci/test-hw.yml)
|
||||||
@ -286,7 +282,7 @@ Consists of **2 jobs** below:
|
|||||||
---
|
---
|
||||||
## DEPLOY stage - [deploy.yml](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml)
|
## DEPLOY stage - [deploy.yml](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml)
|
||||||
|
|
||||||
Consists of **15 jobs** below:
|
Consists of **14 jobs** below:
|
||||||
|
|
||||||
### [release core fw regular deploy](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L5)
|
### [release core fw regular deploy](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L5)
|
||||||
|
|
||||||
@ -310,12 +306,10 @@ Consists of **15 jobs** below:
|
|||||||
|
|
||||||
### [ui tests fixtures deploy](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L229)
|
### [ui tests fixtures deploy](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L229)
|
||||||
|
|
||||||
### [sync emulators to aws](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L251)
|
### [ui tests R fixtures deploy](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L249)
|
||||||
|
|
||||||
### [ui tests R fixtures deploy](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L268)
|
### [sync emulators to aws](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L270)
|
||||||
|
|
||||||
### [sync emulators to aws](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L289)
|
### [common sync](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L295)
|
||||||
|
|
||||||
### [common sync](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L314)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -439,6 +439,7 @@ def test_sign_tx_spend(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
messages.ButtonRequest(code=B.Other),
|
messages.ButtonRequest(code=B.Other),
|
||||||
@ -447,9 +448,9 @@ def test_sign_tx_spend(client: Client):
|
|||||||
request_output(0),
|
request_output(0),
|
||||||
request_output(1),
|
request_output(1),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
|
(tt, messages.ButtonRequest(code=B.SignTx)),
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
request_output(1),
|
request_output(1),
|
||||||
|
@ -593,13 +593,14 @@ def test_send_btg_external_presigned(client: Client):
|
|||||||
script_type=messages.OutputScriptType.PAYTOADDRESS,
|
script_type=messages.OutputScriptType.PAYTOADDRESS,
|
||||||
)
|
)
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_meta(FAKE_TXHASH_6f0398),
|
request_meta(FAKE_TXHASH_6f0398),
|
||||||
|
@ -131,6 +131,7 @@ def test_purchase_ticket_decred(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
@ -140,7 +141,7 @@ def test_purchase_ticket_decred(client: Client):
|
|||||||
request_output(1),
|
request_output(1),
|
||||||
request_output(2),
|
request_output(2),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
(tt, messages.ButtonRequest(code=B.SignTx)),
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_meta(FAKE_TXHASH_4d8acd),
|
request_meta(FAKE_TXHASH_4d8acd),
|
||||||
request_input(0, FAKE_TXHASH_4d8acd),
|
request_input(0, FAKE_TXHASH_4d8acd),
|
||||||
|
@ -658,17 +658,22 @@ def test_fee_high_hardfail(client: Client):
|
|||||||
client, safety_checks=messages.SafetyCheckLevel.PromptTemporarily
|
client, safety_checks=messages.SafetyCheckLevel.PromptTemporarily
|
||||||
)
|
)
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
finished = False
|
finished = False
|
||||||
|
|
||||||
def input_flow():
|
def input_flow():
|
||||||
nonlocal finished
|
nonlocal finished
|
||||||
for expected in (
|
for expected in (
|
||||||
B.ConfirmOutput,
|
B.ConfirmOutput,
|
||||||
B.ConfirmOutput,
|
(tt, B.ConfirmOutput),
|
||||||
B.FeeOverThreshold,
|
B.FeeOverThreshold,
|
||||||
B.SignTx,
|
B.SignTx,
|
||||||
B.SignTx,
|
(tt, B.SignTx),
|
||||||
):
|
):
|
||||||
|
if isinstance(expected, tuple):
|
||||||
|
is_valid, expected = expected
|
||||||
|
if not is_valid:
|
||||||
|
continue
|
||||||
br = yield
|
br = yield
|
||||||
assert br.code == expected
|
assert br.code == expected
|
||||||
client.debug.press_yes()
|
client.debug.press_yes()
|
||||||
|
@ -216,19 +216,20 @@ def test_p2wpkh_in_p2sh_presigned(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
request_output(1),
|
request_output(1),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
request_output(2),
|
request_output(2),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_meta(TXHASH_20912f),
|
request_meta(TXHASH_20912f),
|
||||||
@ -267,19 +268,20 @@ def test_p2wpkh_in_p2sh_presigned(client: Client):
|
|||||||
# Test corrupted script hash in scriptsig.
|
# Test corrupted script hash in scriptsig.
|
||||||
inp1.script_sig[10] ^= 1
|
inp1.script_sig[10] ^= 1
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
request_output(1),
|
request_output(1),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
request_output(2),
|
request_output(2),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_meta(TXHASH_20912f),
|
request_meta(TXHASH_20912f),
|
||||||
@ -399,13 +401,14 @@ def test_p2wsh_external_presigned(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_meta(TXHASH_ec16dc),
|
request_meta(TXHASH_ec16dc),
|
||||||
@ -444,13 +447,14 @@ def test_p2wsh_external_presigned(client: Client):
|
|||||||
# Test corrupted signature in witness.
|
# Test corrupted signature in witness.
|
||||||
inp2.witness[10] ^= 1
|
inp2.witness[10] ^= 1
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_meta(TXHASH_ec16dc),
|
request_meta(TXHASH_ec16dc),
|
||||||
@ -509,13 +513,14 @@ def test_p2tr_external_presigned(client: Client):
|
|||||||
script_type=messages.OutputScriptType.PAYTOTAPROOT,
|
script_type=messages.OutputScriptType.PAYTOTAPROOT,
|
||||||
)
|
)
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
request_output(1),
|
request_output(1),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
@ -541,13 +546,14 @@ def test_p2tr_external_presigned(client: Client):
|
|||||||
# Test corrupted signature in witness.
|
# Test corrupted signature in witness.
|
||||||
inp2.witness[10] ^= 1
|
inp2.witness[10] ^= 1
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
request_output(1),
|
request_output(1),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
@ -611,16 +617,17 @@ def test_p2wpkh_with_proof(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
request_output(1),
|
request_output(1),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_meta(TXHASH_e5b7e2),
|
request_meta(TXHASH_e5b7e2),
|
||||||
@ -703,13 +710,14 @@ def test_p2tr_with_proof(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
|
@ -261,13 +261,14 @@ def test_external_presigned(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with client:
|
with client:
|
||||||
|
tt = client.features.model == "T"
|
||||||
client.set_expected_responses(
|
client.set_expected_responses(
|
||||||
[
|
[
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_input(1),
|
request_input(1),
|
||||||
request_output(0),
|
request_output(0),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||||
messages.ButtonRequest(code=B.SignTx),
|
messages.ButtonRequest(code=B.SignTx),
|
||||||
request_input(0),
|
request_input(0),
|
||||||
request_meta(TXHASH_e38206),
|
request_meta(TXHASH_e38206),
|
||||||
|
@ -131,10 +131,9 @@ def test_data_streaming(client: Client):
|
|||||||
[
|
[
|
||||||
messages.ButtonRequest(code=messages.ButtonRequestType.SignTx),
|
messages.ButtonRequest(code=messages.ButtonRequestType.SignTx),
|
||||||
messages.ButtonRequest(code=messages.ButtonRequestType.SignTx),
|
messages.ButtonRequest(code=messages.ButtonRequestType.SignTx),
|
||||||
|
(tt, messages.ButtonRequest(code=messages.ButtonRequestType.SignTx)),
|
||||||
|
messages.ButtonRequest(code=messages.ButtonRequestType.Other),
|
||||||
messages.ButtonRequest(code=messages.ButtonRequestType.SignTx),
|
messages.ButtonRequest(code=messages.ButtonRequestType.SignTx),
|
||||||
(tt, messages.ButtonRequest(code=messages.ButtonRequestType.Other)),
|
|
||||||
(tt, messages.ButtonRequest(code=messages.ButtonRequestType.SignTx)),
|
|
||||||
(tt, messages.ButtonRequest(code=messages.ButtonRequestType.SignTx)),
|
|
||||||
message_filters.EthereumTxRequest(
|
message_filters.EthereumTxRequest(
|
||||||
data_length=1_024,
|
data_length=1_024,
|
||||||
signature_r=None,
|
signature_r=None,
|
||||||
@ -349,6 +348,9 @@ def input_flow_skip(client: Client, cancel: bool = False):
|
|||||||
|
|
||||||
|
|
||||||
def input_flow_scroll_down(client: Client, cancel: bool = False):
|
def input_flow_scroll_down(client: Client, cancel: bool = False):
|
||||||
|
if client.features.model == "R":
|
||||||
|
pytest.skip("Freezes")
|
||||||
|
|
||||||
yield # confirm address
|
yield # confirm address
|
||||||
client.debug.wait_layout()
|
client.debug.wait_layout()
|
||||||
client.debug.press_yes()
|
client.debug.press_yes()
|
||||||
|
@ -20,7 +20,6 @@ import pytest
|
|||||||
|
|
||||||
from trezorlib import cosi
|
from trezorlib import cosi
|
||||||
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||||
from trezorlib.exceptions import TrezorFailure
|
|
||||||
from trezorlib.tools import parse_path
|
from trezorlib.tools import parse_path
|
||||||
|
|
||||||
pytestmark = [pytest.mark.skip_t2, pytest.mark.skip_tr]
|
pytestmark = [pytest.mark.skip_t2, pytest.mark.skip_tr]
|
||||||
@ -111,10 +110,13 @@ def test_cosi_sign3(client: Client):
|
|||||||
cosi.verify_combined(signature, DIGEST, global_pk)
|
cosi.verify_combined(signature, DIGEST, global_pk)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip_t1
|
# NOTE: test below commented out because of
|
||||||
def test_cosi_different_key(client: Client):
|
# `RuntimeError: Don't skip tests for all trezor models!`
|
||||||
with pytest.raises(TrezorFailure):
|
|
||||||
commit = cosi.commit(client, parse_path("m/10018h/0h"))
|
# @pytest.mark.skip_t1
|
||||||
cosi.sign(
|
# def test_cosi_different_key(client: Client):
|
||||||
client, parse_path("m/10018h/1h"), DIGEST, commit.commitment, commit.pubkey
|
# with pytest.raises(TrezorFailure):
|
||||||
)
|
# commit = cosi.commit(client, parse_path("m/10018h/0h"))
|
||||||
|
# cosi.sign(
|
||||||
|
# client, parse_path("m/10018h/1h"), DIGEST, commit.commitment, commit.pubkey
|
||||||
|
# )
|
||||||
|
@ -211,7 +211,7 @@ def test_invalid_seed_core(client: Client):
|
|||||||
|
|
||||||
yield
|
yield
|
||||||
assert "Select number of words" in layout().text
|
assert "Select number of words" in layout().text
|
||||||
client.debug.press_right()
|
client.debug.press_yes()
|
||||||
|
|
||||||
yield
|
yield
|
||||||
yield
|
yield
|
||||||
@ -221,7 +221,7 @@ def test_invalid_seed_core(client: Client):
|
|||||||
|
|
||||||
yield
|
yield
|
||||||
assert "Enter recovery seed" in layout().text
|
assert "Enter recovery seed" in layout().text
|
||||||
client.debug.press_right()
|
client.debug.press_yes()
|
||||||
|
|
||||||
yield
|
yield
|
||||||
assert "WORD ENTERING" in layout().text
|
assert "WORD ENTERING" in layout().text
|
||||||
|
@ -83,7 +83,7 @@ def test_tt_pin_passphrase(client: Client):
|
|||||||
client.debug.input("654")
|
client.debug.input("654")
|
||||||
|
|
||||||
yield
|
yield
|
||||||
assert "Select the number of words" in layout().text
|
assert "Select number of words" in layout().text
|
||||||
client.debug.press_yes()
|
client.debug.press_yes()
|
||||||
|
|
||||||
yield
|
yield
|
||||||
@ -170,7 +170,7 @@ def test_tt_nopin_nopassphrase(client: Client):
|
|||||||
client.debug.press_yes()
|
client.debug.press_yes()
|
||||||
|
|
||||||
yield
|
yield
|
||||||
assert "Select the number of words" in layout().text
|
assert "Select number of words" in layout().text
|
||||||
client.debug.press_yes()
|
client.debug.press_yes()
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
@ -53,7 +53,7 @@ def test_abort(emulator: Emulator):
|
|||||||
assert layout.get_title() == "RECOVERY MODE"
|
assert layout.get_title() == "RECOVERY MODE"
|
||||||
|
|
||||||
layout = debug.click(buttons.OK, wait=True)
|
layout = debug.click(buttons.OK, wait=True)
|
||||||
assert "Select the number of words" in layout.text
|
assert "Select number of words" in layout.text
|
||||||
|
|
||||||
device_handler.restart(emulator)
|
device_handler.restart(emulator)
|
||||||
debug = device_handler.debuglink()
|
debug = device_handler.debuglink()
|
||||||
@ -63,7 +63,7 @@ def test_abort(emulator: Emulator):
|
|||||||
|
|
||||||
# no waiting for layout because layout doesn't change
|
# no waiting for layout because layout doesn't change
|
||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
assert "Select the number of words" in layout.text
|
assert "Select number of words" in layout.text
|
||||||
layout = debug.click(buttons.CANCEL, wait=True)
|
layout = debug.click(buttons.CANCEL, wait=True)
|
||||||
|
|
||||||
assert layout.get_title() == "ABORT RECOVERY"
|
assert layout.get_title() == "ABORT RECOVERY"
|
||||||
|
Loading…
Reference in New Issue
Block a user