mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-01 12:22:34 +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_payment_request;
|
||||
MP_QSTR_confirm_reset_device;
|
||||
MP_QSTR_confirm_recovery;
|
||||
MP_QSTR_confirm_text;
|
||||
MP_QSTR_confirm_total;
|
||||
MP_QSTR_confirm_total_r;
|
||||
@ -39,16 +38,10 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_confirm_with_info;
|
||||
MP_QSTR_confirm_more;
|
||||
MP_QSTR_confirm_recovery;
|
||||
MP_QSTR_show_checklist;
|
||||
MP_QSTR_show_error;
|
||||
MP_QSTR_show_qr;
|
||||
MP_QSTR_show_success;
|
||||
MP_QSTR_show_warning;
|
||||
MP_QSTR_show_info;
|
||||
MP_QSTR_show_simple;
|
||||
MP_QSTR_request_number;
|
||||
MP_QSTR_request_pin;
|
||||
MP_QSTR_request_passphrase;
|
||||
MP_QSTR_confirm_word;
|
||||
MP_QSTR_request_bip39;
|
||||
MP_QSTR_request_number;
|
||||
@ -58,31 +51,19 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_select_word;
|
||||
MP_QSTR_select_word_count;
|
||||
MP_QSTR_show_busyscreen;
|
||||
MP_QSTR_show_group_share_success;
|
||||
MP_QSTR_show_homescreen;
|
||||
MP_QSTR_show_lockscreen;
|
||||
MP_QSTR_share_words;
|
||||
MP_QSTR_show_checklist;
|
||||
MP_QSTR_show_error;
|
||||
MP_QSTR_show_group_share_success;
|
||||
MP_QSTR_show_info;
|
||||
MP_QSTR_show_qr;
|
||||
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_progress;
|
||||
|
||||
MP_QSTR_attach_timer_fn;
|
||||
MP_QSTR_touch_event;
|
||||
MP_QSTR_button_event;
|
||||
MP_QSTR_progress_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_bip39;
|
||||
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();
|
||||
}
|
||||
}
|
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 common;
|
||||
mod confirm;
|
||||
mod dialog;
|
||||
mod flow;
|
||||
mod flow_pages;
|
||||
mod flow_pages_poc_helpers;
|
||||
mod frame;
|
||||
mod homescreen;
|
||||
mod loader;
|
||||
mod no_btn_dialog;
|
||||
mod page;
|
||||
mod passphrase;
|
||||
mod pin;
|
||||
mod progress;
|
||||
mod qr_code;
|
||||
mod result_anim;
|
||||
mod result_popup;
|
||||
@ -35,15 +37,17 @@ pub use button_controller::{ButtonController, ButtonControllerMsg};
|
||||
pub use changing_text::ChangingTextLine;
|
||||
pub use choice::{Choice, ChoiceFactory, ChoicePage, ChoicePageMsg};
|
||||
pub use choice_item::ChoiceItem;
|
||||
pub use dialog::{Dialog, DialogMsg};
|
||||
pub use flow::{Flow, FlowMsg};
|
||||
pub use flow_pages::{FlowPages, Page};
|
||||
pub use flow_pages_poc_helpers::LineAlignment;
|
||||
pub use frame::Frame;
|
||||
pub use homescreen::{Homescreen, HomescreenMsg, Lockscreen};
|
||||
pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
|
||||
pub use no_btn_dialog::{NoBtnDialog, NoBtnDialogMsg};
|
||||
pub use page::ButtonPage;
|
||||
pub use passphrase::{PassphraseEntry, PassphraseEntryMsg};
|
||||
pub use pin::{PinEntry, PinEntryMsg};
|
||||
pub use progress::Progress;
|
||||
pub use qr_code::{QRCodePage, QRCodePageMessage};
|
||||
pub use result_anim::{ResultAnim, ResultAnimMsg};
|
||||
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,
|
||||
paginated::{PageMsg, Paginate},
|
||||
painter,
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecLong, Paragraphs, VecExt},
|
||||
FormattedText,
|
||||
text::paragraphs::{
|
||||
Paragraph, ParagraphSource, ParagraphStrType, ParagraphVecLong, Paragraphs, VecExt,
|
||||
},
|
||||
ComponentExt, Empty, FormattedText, Timeout, TimeoutMsg,
|
||||
},
|
||||
display::Font,
|
||||
layout::{
|
||||
@ -35,12 +37,42 @@ use crate::{
|
||||
use super::{
|
||||
component::{
|
||||
Bip39Entry, Bip39EntryMsg, ButtonActions, ButtonDetails, ButtonLayout, ButtonPage, Flow,
|
||||
FlowMsg, FlowPages, Frame, Page, PassphraseEntry, PassphraseEntryMsg, PinEntry,
|
||||
PinEntryMsg, QRCodePage, QRCodePageMessage, ShareWords, SimpleChoice, SimpleChoiceMsg,
|
||||
FlowMsg, FlowPages, Frame, Homescreen, HomescreenMsg, Lockscreen, NoBtnDialog,
|
||||
NoBtnDialogMsg, Page, PassphraseEntry, PassphraseEntryMsg, PinEntry, PinEntryMsg, Progress,
|
||||
QRCodePage, QRCodePageMessage, ShareWords, SimpleChoice, SimpleChoiceMsg,
|
||||
},
|
||||
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>
|
||||
where
|
||||
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 {
|
||||
let block = |_args: &[Obj], kwargs: &Map| {
|
||||
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 {
|
||||
// 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 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) }
|
||||
}
|
||||
|
||||
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 title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
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) }
|
||||
}
|
||||
|
||||
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 address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?;
|
||||
// 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) }
|
||||
}
|
||||
|
||||
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 title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.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) }
|
||||
}
|
||||
|
||||
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 title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.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) }
|
||||
}
|
||||
|
||||
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
|
||||
/// General pattern of most tutorial screens.
|
||||
/// (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) }
|
||||
}
|
||||
|
||||
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 prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.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) }
|
||||
}
|
||||
|
||||
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 share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?;
|
||||
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) }
|
||||
}
|
||||
|
||||
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 title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
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) }
|
||||
}
|
||||
|
||||
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 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) }
|
||||
}
|
||||
|
||||
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 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) }
|
||||
}
|
||||
|
||||
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 prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.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) }
|
||||
}
|
||||
|
||||
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]
|
||||
pub static mp_module_trezorui2: Module = obj_module! {
|
||||
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
|
||||
/// 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."""
|
||||
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(
|
||||
/// *,
|
||||
@ -633,7 +808,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// amount: str,
|
||||
/// ) -> object:
|
||||
/// """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(
|
||||
/// *,
|
||||
@ -645,7 +820,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// fee_label: str,
|
||||
/// ) -> object:
|
||||
/// """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(
|
||||
/// *,
|
||||
@ -655,7 +830,16 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// case_sensitive: bool,
|
||||
/// ) -> object:
|
||||
/// """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:
|
||||
/// """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,
|
||||
/// ) -> str | object:
|
||||
/// """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(
|
||||
/// *,
|
||||
@ -684,7 +868,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// share_words: Iterable[str],
|
||||
/// ) -> None:
|
||||
/// """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(
|
||||
/// *,
|
||||
@ -692,21 +876,21 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// words: Iterable[str],
|
||||
/// ) -> str:
|
||||
/// """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(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// ) -> str: # TODO: make it return int
|
||||
/// """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(
|
||||
/// *,
|
||||
/// prompt: str,
|
||||
/// ) -> str:
|
||||
/// """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(
|
||||
/// *,
|
||||
@ -714,7 +898,48 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// max_len: int,
|
||||
/// ) -> str:
|
||||
/// """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)]
|
||||
|
@ -21,6 +21,8 @@ pub const TEXT_BOLD: TextStyle =
|
||||
TextStyle::new(Font::BOLD, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0);
|
||||
pub const TEXT_MONO: TextStyle =
|
||||
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 {
|
||||
normal: Font::NORMAL,
|
||||
|
@ -1598,7 +1598,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// ) -> 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 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(),
|
||||
|
||||
/// def show_homescreen(
|
||||
|
@ -101,6 +101,16 @@ def show_qr(
|
||||
"""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
|
||||
def tutorial() -> object:
|
||||
"""Show user how to interact with the device."""
|
||||
@ -166,6 +176,40 @@ def request_passphrase(
|
||||
max_len: int,
|
||||
) -> str:
|
||||
"""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
|
||||
CANCELLED: object
|
||||
INFO: object
|
||||
@ -505,7 +549,7 @@ def show_progress(
|
||||
) -> 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 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
|
||||
|
@ -167,6 +167,8 @@ trezor.ui.layouts.tr.altcoin
|
||||
import trezor.ui.layouts.tr.altcoin
|
||||
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
|
||||
import trezor.ui.layouts.tr.recovery
|
||||
trezor.ui.layouts.tr.reset
|
||||
|
@ -108,7 +108,9 @@ async def _request_wipe_code_confirm(ctx: Context, pin: str) -> str:
|
||||
# _wipe_code_invalid
|
||||
await show_popup(
|
||||
"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
|
||||
|
||||
@ -118,7 +120,5 @@ async def _request_wipe_code_confirm(ctx: Context, pin: str) -> str:
|
||||
# _wipe_code_mismatch
|
||||
await show_popup(
|
||||
"Code mismatch",
|
||||
text_r(
|
||||
"The wipe code must be\ndifferent from your PIN.\n\nPlease try again."
|
||||
),
|
||||
text_r("The wipe codes you\nentered do not match.\n\nPlease try again."),
|
||||
)
|
||||
|
@ -65,7 +65,7 @@ async def _continue_recovery_process(ctx: GenericContext) -> Success:
|
||||
if is_first_step:
|
||||
# If we are starting recovery, ask for word count first...
|
||||
# _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
|
||||
word_count = await layout.request_word_count(ctx, dry_run)
|
||||
# ...and only then show the starting screen with word count.
|
||||
|
@ -1 +1,6 @@
|
||||
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 trezor import io, log, loop, ui, wire, workflow
|
||||
from trezor import io, log, loop, ui, workflow
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.utils import DISABLE_ANIMATION
|
||||
from trezor.wire import ActionCancelled
|
||||
|
||||
import trezorui2
|
||||
|
||||
from ..common import button_request, interact
|
||||
|
||||
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 ..common import PropertyType
|
||||
from ..common import PropertyType, ExceptionType, ProgressLayout
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
ExceptionType = BaseException | Type[BaseException]
|
||||
|
||||
|
||||
BR_TYPE_OTHER = ButtonRequestType.Other # global_import_cache
|
||||
|
||||
@ -56,11 +55,11 @@ class RustLayoutContent:
|
||||
|
||||
def active_page(self) -> int:
|
||||
"""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:
|
||||
"""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:
|
||||
"""Whether we are in flow."""
|
||||
@ -136,22 +135,20 @@ class RustLayoutContent:
|
||||
|
||||
def buttons_content(self) -> tuple[str, str, str]:
|
||||
"""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)
|
||||
assert len(btns) == 3
|
||||
return btns[0], btns[1], btns[2]
|
||||
|
||||
def button_actions(self) -> tuple[str, str, str]:
|
||||
"""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")
|
||||
assert len(action_ids) == 3
|
||||
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:
|
||||
"""Getting the value of a key-value pair as an integer. None if missing."""
|
||||
val = self.kw_pair(key)
|
||||
@ -196,6 +193,35 @@ class RustLayout(ui.Layout):
|
||||
def set_timer(self, token: int, deadline: int) -> None:
|
||||
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__:
|
||||
from trezor.enums import DebugPhysicalButton
|
||||
|
||||
@ -394,6 +420,19 @@ class RustLayout(ui.Layout):
|
||||
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
|
||||
# Should be gradually replaced by custom designs/layouts
|
||||
async def _placeholder_confirm(
|
||||
@ -448,7 +487,7 @@ async def get_bool(
|
||||
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
|
||||
if result is trezorui2.CANCELLED:
|
||||
raise exc
|
||||
@ -469,8 +508,9 @@ async def confirm_action(
|
||||
verb: str = "CONFIRM",
|
||||
verb_cancel: str | None = None,
|
||||
hold: bool = False,
|
||||
hold_danger: bool = False,
|
||||
reverse: bool = False,
|
||||
exc: ExceptionType = wire.ActionCancelled,
|
||||
exc: ExceptionType = ActionCancelled,
|
||||
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||
) -> 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(
|
||||
trezorui2.confirm_text(
|
||||
title=title.upper(),
|
||||
@ -596,11 +636,11 @@ def _show_xpub(xpub: str, title: str, cancel: str) -> ui.Layout:
|
||||
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(
|
||||
interact(
|
||||
ctx,
|
||||
_show_xpub(xpub, title, cancel),
|
||||
_show_xpub(xpub, title, None),
|
||||
"show_xpub",
|
||||
ButtonRequestType.PublicKey,
|
||||
)
|
||||
@ -699,7 +739,7 @@ async def _show_modal(
|
||||
content: str,
|
||||
button_confirm: str | None,
|
||||
button_cancel: str | None,
|
||||
exc: ExceptionType = wire.ActionCancelled,
|
||||
exc: ExceptionType = ActionCancelled,
|
||||
) -> None:
|
||||
await confirm_action(
|
||||
ctx=ctx,
|
||||
@ -722,7 +762,7 @@ async def show_error_and_raise(
|
||||
subheader: str | None = None,
|
||||
button: str = "Close",
|
||||
red: bool = False,
|
||||
exc: ExceptionType = wire.ActionCancelled,
|
||||
exc: ExceptionType = ActionCancelled,
|
||||
) -> NoReturn:
|
||||
await _show_modal(
|
||||
ctx=ctx,
|
||||
@ -949,7 +989,13 @@ async def confirm_properties(
|
||||
hold: bool = False,
|
||||
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
|
||||
) -> 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(
|
||||
interact(
|
||||
@ -957,7 +1003,7 @@ async def confirm_properties(
|
||||
RustLayout(
|
||||
trezorui2.confirm_properties(
|
||||
title=title.upper(),
|
||||
items=items,
|
||||
items=map(handle_bytes, props), # type: ignore [cannot be assigned to parameter "items"]
|
||||
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(
|
||||
ctx: GenericContext,
|
||||
total_amount: str,
|
||||
@ -975,7 +1047,6 @@ async def confirm_total(
|
||||
title: str = "Send transaction?",
|
||||
total_label: str = "Total amount:",
|
||||
fee_label: str = "Including fee:",
|
||||
icon_color: int = ui.GREEN,
|
||||
br_type: str = "confirm_total",
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
) -> None:
|
||||
@ -1018,19 +1089,14 @@ async def confirm_metadata(
|
||||
content: str,
|
||||
param: str | None = None,
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
hide_continue: bool = False,
|
||||
hold: bool = False,
|
||||
param_font: int = ui.BOLD,
|
||||
) -> None:
|
||||
text = content.format(param)
|
||||
if not hide_continue:
|
||||
text += "\n\nContinue?"
|
||||
|
||||
# TODO: implement `hold`
|
||||
await _placeholder_confirm(
|
||||
ctx=ctx,
|
||||
br_type=br_type,
|
||||
title=title.upper(),
|
||||
data=text,
|
||||
data=content.format(param),
|
||||
description="",
|
||||
br_code=br_code,
|
||||
)
|
||||
@ -1173,7 +1239,23 @@ async def show_popup(
|
||||
description_param: str = "",
|
||||
timeout_ms: int = 3000,
|
||||
) -> 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:
|
||||
@ -1190,7 +1272,7 @@ async def request_passphrase_on_device(ctx: GenericContext, max_len: int) -> str
|
||||
)
|
||||
)
|
||||
if result is trezorui2.CANCELLED:
|
||||
raise wire.ActionCancelled("Passphrase entry cancelled")
|
||||
raise ActionCancelled("Passphrase entry cancelled")
|
||||
|
||||
assert isinstance(result, str)
|
||||
return result
|
||||
@ -1202,6 +1284,8 @@ async def request_pin_on_device(
|
||||
attempts_remaining: int | None,
|
||||
allow_cancel: bool,
|
||||
) -> str:
|
||||
from trezor import wire
|
||||
|
||||
if attempts_remaining is None:
|
||||
subprompt = ""
|
||||
elif attempts_remaining == 1:
|
||||
@ -1311,3 +1395,55 @@ async def confirm_set_new_pin(
|
||||
hold=True,
|
||||
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,
|
||||
description: str,
|
||||
br_type: str,
|
||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
||||
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||
*,
|
||||
verb: str | None = None,
|
||||
hold: bool = False,
|
||||
@ -975,7 +975,7 @@ async def confirm_coinjoin(
|
||||
)
|
||||
),
|
||||
"coinjoin_final",
|
||||
ButtonRequestType.Other,
|
||||
BR_TYPE_OTHER,
|
||||
)
|
||||
)
|
||||
|
||||
@ -1000,7 +1000,7 @@ async def confirm_sign_identity(
|
||||
data=identity,
|
||||
description=challenge_visual + "\n" if challenge_visual else "",
|
||||
br_type="sign_identity",
|
||||
br_code=ButtonRequestType.Other,
|
||||
br_code=BR_TYPE_OTHER,
|
||||
)
|
||||
|
||||
|
||||
@ -1020,7 +1020,7 @@ async def confirm_signverify(
|
||||
title,
|
||||
address,
|
||||
"Confirm address:",
|
||||
br_code=ButtonRequestType.Other,
|
||||
br_code=BR_TYPE_OTHER,
|
||||
)
|
||||
|
||||
await confirm_blob(
|
||||
@ -1029,7 +1029,7 @@ async def confirm_signverify(
|
||||
title,
|
||||
message,
|
||||
"Confirm message:",
|
||||
br_code=ButtonRequestType.Other,
|
||||
br_code=BR_TYPE_OTHER,
|
||||
)
|
||||
|
||||
|
||||
@ -1117,7 +1117,7 @@ async def confirm_pin_action(
|
||||
title: str,
|
||||
action: str | None,
|
||||
description: str | None = "Do you really want to",
|
||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
||||
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||
) -> None:
|
||||
return await confirm_action(
|
||||
ctx,
|
||||
@ -1133,7 +1133,7 @@ async def confirm_pin_action(
|
||||
async def confirm_reenter_pin(
|
||||
ctx: GenericContext,
|
||||
br_type: str = "set_pin",
|
||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
||||
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||
) -> None:
|
||||
return await confirm_action(
|
||||
ctx,
|
||||
@ -1148,7 +1148,7 @@ async def confirm_reenter_pin(
|
||||
async def pin_mismatch(
|
||||
ctx: GenericContext,
|
||||
br_type: str = "set_pin",
|
||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
||||
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||
) -> None:
|
||||
return await confirm_action(
|
||||
ctx,
|
||||
@ -1168,7 +1168,7 @@ async def confirm_set_new_pin(
|
||||
action: str,
|
||||
information: list[str],
|
||||
description: str = "Do you want to",
|
||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
||||
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||
) -> None:
|
||||
await confirm_action(
|
||||
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)
|
||||
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)
|
||||
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.
|
||||
**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#L335)
|
||||
### [core unix R debugger build](https://github.com/trezor/trezor-firmware/blob/master/ci/build.yml#L320)
|
||||
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.
|
||||
|
||||
### [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.
|
||||
**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.
|
||||
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)
|
||||
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)
|
||||
Python unit tests, checking core functionality.
|
||||
@ -170,76 +168,74 @@ with the expected UI result.
|
||||
See artifacts for a comprehensive report of UI.
|
||||
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#L178)
|
||||
### [core btconly device test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L145)
|
||||
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.
|
||||
|
||||
### [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.
|
||||
|
||||
### [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.
|
||||
|
||||
### [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.
|
||||
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.
|
||||
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.
|
||||
|
||||
### [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)
|
||||
@ -286,7 +282,7 @@ Consists of **2 jobs** below:
|
||||
---
|
||||
## 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)
|
||||
|
||||
@ -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)
|
||||
|
||||
### [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#L314)
|
||||
### [common sync](https://github.com/trezor/trezor-firmware/blob/master/ci/deploy.yml#L295)
|
||||
|
||||
---
|
||||
|
@ -439,6 +439,7 @@ def test_sign_tx_spend(client: Client):
|
||||
)
|
||||
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
messages.ButtonRequest(code=B.Other),
|
||||
@ -447,9 +448,9 @@ def test_sign_tx_spend(client: Client):
|
||||
request_output(0),
|
||||
request_output(1),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
(tt, messages.ButtonRequest(code=B.SignTx)),
|
||||
request_input(0),
|
||||
request_output(0),
|
||||
request_output(1),
|
||||
|
@ -593,13 +593,14 @@ def test_send_btg_external_presigned(client: Client):
|
||||
script_type=messages.OutputScriptType.PAYTOADDRESS,
|
||||
)
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
request_output(0),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
request_input(0),
|
||||
request_meta(FAKE_TXHASH_6f0398),
|
||||
|
@ -131,6 +131,7 @@ def test_purchase_ticket_decred(client: Client):
|
||||
)
|
||||
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
@ -140,7 +141,7 @@ def test_purchase_ticket_decred(client: Client):
|
||||
request_output(1),
|
||||
request_output(2),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
(tt, messages.ButtonRequest(code=B.SignTx)),
|
||||
request_input(0),
|
||||
request_meta(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
|
||||
)
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
finished = False
|
||||
|
||||
def input_flow():
|
||||
nonlocal finished
|
||||
for expected in (
|
||||
B.ConfirmOutput,
|
||||
B.ConfirmOutput,
|
||||
(tt, B.ConfirmOutput),
|
||||
B.FeeOverThreshold,
|
||||
B.SignTx,
|
||||
B.SignTx,
|
||||
(tt, B.SignTx),
|
||||
):
|
||||
if isinstance(expected, tuple):
|
||||
is_valid, expected = expected
|
||||
if not is_valid:
|
||||
continue
|
||||
br = yield
|
||||
assert br.code == expected
|
||||
client.debug.press_yes()
|
||||
|
@ -216,19 +216,20 @@ def test_p2wpkh_in_p2sh_presigned(client: Client):
|
||||
)
|
||||
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
request_output(0),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
request_output(1),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
request_output(2),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
request_input(0),
|
||||
request_meta(TXHASH_20912f),
|
||||
@ -267,19 +268,20 @@ def test_p2wpkh_in_p2sh_presigned(client: Client):
|
||||
# Test corrupted script hash in scriptsig.
|
||||
inp1.script_sig[10] ^= 1
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
request_output(0),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
request_output(1),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
request_output(2),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
request_input(0),
|
||||
request_meta(TXHASH_20912f),
|
||||
@ -399,13 +401,14 @@ def test_p2wsh_external_presigned(client: Client):
|
||||
)
|
||||
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
request_output(0),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
request_input(0),
|
||||
request_meta(TXHASH_ec16dc),
|
||||
@ -444,13 +447,14 @@ def test_p2wsh_external_presigned(client: Client):
|
||||
# Test corrupted signature in witness.
|
||||
inp2.witness[10] ^= 1
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
request_output(0),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
request_input(0),
|
||||
request_meta(TXHASH_ec16dc),
|
||||
@ -509,13 +513,14 @@ def test_p2tr_external_presigned(client: Client):
|
||||
script_type=messages.OutputScriptType.PAYTOTAPROOT,
|
||||
)
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
request_output(0),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
request_output(1),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
request_input(1),
|
||||
@ -541,13 +546,14 @@ def test_p2tr_external_presigned(client: Client):
|
||||
# Test corrupted signature in witness.
|
||||
inp2.witness[10] ^= 1
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
request_output(0),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
request_output(1),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
request_input(1),
|
||||
@ -611,16 +617,17 @@ def test_p2wpkh_with_proof(client: Client):
|
||||
)
|
||||
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
request_output(0),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
request_output(1),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
request_input(0),
|
||||
request_meta(TXHASH_e5b7e2),
|
||||
@ -703,13 +710,14 @@ def test_p2tr_with_proof(client: Client):
|
||||
)
|
||||
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
request_output(0),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
|
@ -261,13 +261,14 @@ def test_external_presigned(client: Client):
|
||||
)
|
||||
|
||||
with client:
|
||||
tt = client.features.model == "T"
|
||||
client.set_expected_responses(
|
||||
[
|
||||
request_input(0),
|
||||
request_input(1),
|
||||
request_output(0),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
messages.ButtonRequest(code=B.ConfirmOutput),
|
||||
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
|
||||
messages.ButtonRequest(code=B.SignTx),
|
||||
request_input(0),
|
||||
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),
|
||||
(tt, messages.ButtonRequest(code=messages.ButtonRequestType.SignTx)),
|
||||
messages.ButtonRequest(code=messages.ButtonRequestType.Other),
|
||||
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(
|
||||
data_length=1_024,
|
||||
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):
|
||||
if client.features.model == "R":
|
||||
pytest.skip("Freezes")
|
||||
|
||||
yield # confirm address
|
||||
client.debug.wait_layout()
|
||||
client.debug.press_yes()
|
||||
|
@ -20,7 +20,6 @@ import pytest
|
||||
|
||||
from trezorlib import cosi
|
||||
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||
from trezorlib.exceptions import TrezorFailure
|
||||
from trezorlib.tools import parse_path
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@pytest.mark.skip_t1
|
||||
def test_cosi_different_key(client: Client):
|
||||
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
|
||||
)
|
||||
# NOTE: test below commented out because of
|
||||
# `RuntimeError: Don't skip tests for all trezor models!`
|
||||
|
||||
# @pytest.mark.skip_t1
|
||||
# def test_cosi_different_key(client: Client):
|
||||
# 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
|
||||
assert "Select number of words" in layout().text
|
||||
client.debug.press_right()
|
||||
client.debug.press_yes()
|
||||
|
||||
yield
|
||||
yield
|
||||
@ -221,7 +221,7 @@ def test_invalid_seed_core(client: Client):
|
||||
|
||||
yield
|
||||
assert "Enter recovery seed" in layout().text
|
||||
client.debug.press_right()
|
||||
client.debug.press_yes()
|
||||
|
||||
yield
|
||||
assert "WORD ENTERING" in layout().text
|
||||
|
@ -83,7 +83,7 @@ def test_tt_pin_passphrase(client: Client):
|
||||
client.debug.input("654")
|
||||
|
||||
yield
|
||||
assert "Select the number of words" in layout().text
|
||||
assert "Select number of words" in layout().text
|
||||
client.debug.press_yes()
|
||||
|
||||
yield
|
||||
@ -170,7 +170,7 @@ def test_tt_nopin_nopassphrase(client: Client):
|
||||
client.debug.press_yes()
|
||||
|
||||
yield
|
||||
assert "Select the number of words" in layout().text
|
||||
assert "Select number of words" in layout().text
|
||||
client.debug.press_yes()
|
||||
|
||||
yield
|
||||
|
@ -53,7 +53,7 @@ def test_abort(emulator: Emulator):
|
||||
assert layout.get_title() == "RECOVERY MODE"
|
||||
|
||||
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)
|
||||
debug = device_handler.debuglink()
|
||||
@ -63,7 +63,7 @@ def test_abort(emulator: Emulator):
|
||||
|
||||
# no waiting for layout because layout doesn't change
|
||||
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)
|
||||
|
||||
assert layout.get_title() == "ABORT RECOVERY"
|
||||
|
Loading…
Reference in New Issue
Block a user