feat(core/rust): add support for T1 UI

[no changelog]
pull/1948/head
Martin Milata 3 years ago
parent 3c49ef2f62
commit 4d60c10330

@ -701,7 +701,10 @@ def cargo_build():
profile = '--release' profile = '--release'
else: else:
profile = '' profile = ''
features = [] if TREZOR_MODEL == "1":
features = ["model_t1"]
else:
features = ["model_tt"]
if BITCOIN_ONLY == "1": if BITCOIN_ONLY == "1":
features.append("bitcoin_only") features.append("bitcoin_only")
if NEW_UI: if NEW_UI:
@ -709,7 +712,7 @@ def cargo_build():
if PYOPT == "0": if PYOPT == "0":
features.append("ui_debug") features.append("ui_debug")
return f'cd embed/rust; cargo build {profile} --target={RUST_TARGET} --target-dir=../../build/firmware/rust --features "{" ".join(features)}"' return f'cd embed/rust; cargo build {profile} --target={RUST_TARGET} --target-dir=../../build/firmware/rust --no-default-features --features "{" ".join(features)}"'
rust = env.Command( rust = env.Command(
target=RUST_LIBPATH, target=RUST_LIBPATH,

@ -653,7 +653,10 @@ def cargo_build():
profile = '--release' profile = '--release'
else: else:
profile = '' profile = ''
features = [] if TREZOR_MODEL == "1":
features = ["model_t1"]
else:
features = ["model_tt"]
if BITCOIN_ONLY == "1": if BITCOIN_ONLY == "1":
features.append("bitcoin_only") features.append("bitcoin_only")
if PYOPT == "0": if PYOPT == "0":
@ -662,7 +665,7 @@ def cargo_build():
elif NEW_UI: elif NEW_UI:
features.append("ui") features.append("ui")
return f'cd embed/rust; cargo build {profile} --target-dir=../../build/unix/rust --features "{" ".join(features)}"' return f'cd embed/rust; cargo build {profile} --target-dir=../../build/unix/rust --no-default-features --features "{" ".join(features)}"'
rust = env.Command( rust = env.Command(
target=RUST_LIBPATH, target=RUST_LIBPATH,

@ -7,7 +7,10 @@ resolver = "2"
build = "build.rs" build = "build.rs"
[features] [features]
default = ["model_tt"]
bitcoin_only = [] bitcoin_only = []
model_tt = []
model_t1 = []
ui = [] ui = []
ui_debug = [] ui_debug = []
test = ["cc", "glob"] test = ["cc", "glob"]

@ -12,9 +12,7 @@ static void _librust_qstrs(void) {
// layout // layout
MP_QSTR_Layout; MP_QSTR_Layout;
MP_QSTR_set_timer_fn; MP_QSTR_set_timer_fn;
MP_QSTR_touch_start; MP_QSTR_hid_event;
MP_QSTR_touch_move;
MP_QSTR_touch_end;
MP_QSTR_timer; MP_QSTR_timer;
MP_QSTR_paint; MP_QSTR_paint;
MP_QSTR_trace; MP_QSTR_trace;

@ -164,3 +164,10 @@ fn buffer_as_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut [u8] {
unsafe { slice::from_raw_parts_mut(ptr, len) } unsafe { slice::from_raw_parts_mut(ptr, len) }
} }
} }
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Buffer {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
self.as_ref().trace(t)
}
}

@ -37,6 +37,8 @@ impl Map {
value, value,
} }
} }
pub const EMPTY: Map = Self::from_fixed_static::<0>(&[]);
} }
impl Map { impl Map {

@ -44,8 +44,7 @@ extern "C" {
) -> bool; ) -> bool;
} }
const WIDTH: i32 = 240; use crate::ui::component::model::constants::{HEIGHT, WIDTH};
const HEIGHT: i32 = 240;
pub struct ToifInfo { pub struct ToifInfo {
pub width: u16, pub width: u16,

@ -1,3 +1,4 @@
pub mod common; pub mod common;
#[cfg(feature = "ui")]
pub mod display; pub mod display;
pub mod random; pub mod random;

@ -2,7 +2,7 @@ use core::{mem, time::Duration};
use heapless::Vec; use heapless::Vec;
use crate::ui::geometry::Point; use crate::ui::{model_t1::event::ButtonEvent, model_tt::event::TouchEvent};
/// Type used by components that do not return any messages. /// Type used by components that do not return any messages.
/// ///
@ -81,9 +81,7 @@ where
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum Event { pub enum Event {
TouchStart(Point), HumanInput(HidEvent),
TouchMove(Point),
TouchEnd(Point),
Timer(TimerToken), Timer(TimerToken),
} }

@ -1,5 +1,9 @@
mod base; mod base;
pub mod model_t1; pub mod empty;
pub mod model_tt; pub mod label;
pub mod text;
pub use base::{Child, Component, Event, EventCtx, Never, TimerToken}; pub use base::{Child, Component, Event, EventCtx, Never, TimerToken};
pub use empty::Empty;
pub use label::{Label, LabelStyle};
pub use text::{LineBreaking, PageBreaking, Text, TextLayout};

@ -1 +1,3 @@
pub mod constants;
pub mod theme;

@ -12,7 +12,7 @@ use crate::ui::{
geometry::{Offset, Point, Rect}, geometry::{Offset, Point, Rect},
}; };
use super::theme; use super::model::theme;
pub const MAX_ARGUMENTS: usize = 6; pub const MAX_ARGUMENTS: usize = 6;

@ -1,6 +1,9 @@
use crate::trezorhal::display; use crate::trezorhal::display;
use super::geometry::{Offset, Point, Rect}; use super::{
component::model::constants,
geometry::{Offset, Point, Rect},
};
pub fn width() -> i32 { pub fn width() -> i32 {
display::width() display::width()
@ -73,13 +76,11 @@ pub fn text_width(text: &[u8], font: Font) -> i32 {
} }
pub fn text_height() -> i32 { pub fn text_height() -> i32 {
const TEXT_HEIGHT: i32 = 16; constants::TEXT_HEIGHT
TEXT_HEIGHT
} }
pub fn line_height() -> i32 { pub fn line_height() -> i32 {
const LINE_HEIGHT: i32 = 26; constants::LINE_HEIGHT
LINE_HEIGHT
} }
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]

@ -1,2 +1 @@
mod example; pub mod obj;
mod obj;

@ -13,10 +13,7 @@ use crate::{
qstr::Qstr, qstr::Qstr,
typ::Type, typ::Type,
}, },
ui::{ ui::component::{model::HidEvent, Child, Component, Event, EventCtx, Never, TimerToken},
component::{Child, Component, Event, EventCtx, Never, TimerToken},
geometry::Point,
},
util, util,
}; };
@ -207,9 +204,7 @@ impl LayoutObj {
name: Qstr::MP_QSTR_Layout, name: Qstr::MP_QSTR_Layout,
locals: &obj_dict!(obj_map! { locals: &obj_dict!(obj_map! {
Qstr::MP_QSTR_set_timer_fn => obj_fn_2!(ui_layout_set_timer_fn).as_obj(), Qstr::MP_QSTR_set_timer_fn => obj_fn_2!(ui_layout_set_timer_fn).as_obj(),
Qstr::MP_QSTR_touch_start => obj_fn_3!(ui_layout_touch_start).as_obj(), Qstr::MP_QSTR_hid_event => obj_fn_var!(4, 4, ui_layout_hid_event).as_obj(),
Qstr::MP_QSTR_touch_move => obj_fn_3!(ui_layout_touch_move).as_obj(),
Qstr::MP_QSTR_touch_end => obj_fn_3!(ui_layout_touch_end).as_obj(),
Qstr::MP_QSTR_timer => obj_fn_2!(ui_layout_timer).as_obj(), Qstr::MP_QSTR_timer => obj_fn_2!(ui_layout_timer).as_obj(),
Qstr::MP_QSTR_paint => obj_fn_1!(ui_layout_paint).as_obj(), Qstr::MP_QSTR_paint => obj_fn_1!(ui_layout_paint).as_obj(),
Qstr::MP_QSTR_trace => obj_fn_2!(ui_layout_trace).as_obj(), Qstr::MP_QSTR_trace => obj_fn_2!(ui_layout_trace).as_obj(),
@ -286,34 +281,21 @@ extern "C" fn ui_layout_set_timer_fn(this: Obj, timer_fn: Obj) -> Obj {
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }
} }
extern "C" fn ui_layout_touch_start(this: Obj, x: Obj, y: Obj) -> Obj { extern "C" fn ui_layout_hid_event(n_args: usize, args: *const Obj) -> Obj {
let block = || { let block = |args: &[Obj], _kwargs: &Map| {
let this: Gc<LayoutObj> = this.try_into()?; if args.len() != 4 {
let event = Event::TouchStart(Point::new(x.try_into()?, y.try_into()?)); return Err(Error::TypeError);
let msg = this.obj_event(event)?; }
Ok(msg) let this: Gc<LayoutObj> = args[0].try_into()?;
}; let event = HidEvent::new(
unsafe { util::try_or_raise(block) } args[1].try_into()?,
} args[2].try_into()?,
args[3].try_into()?,
extern "C" fn ui_layout_touch_move(this: Obj, x: Obj, y: Obj) -> Obj { )?;
let block = || { let msg = this.obj_event(Event::HumanInput(event))?;
let this: Gc<LayoutObj> = this.try_into()?;
let event = Event::TouchMove(Point::new(x.try_into()?, y.try_into()?));
let msg = this.obj_event(event)?;
Ok(msg)
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn ui_layout_touch_end(this: Obj, x: Obj, y: Obj) -> Obj {
let block = || {
let this: Gc<LayoutObj> = this.try_into()?;
let event = Event::TouchEnd(Point::new(x.try_into()?, y.try_into()?));
let msg = this.obj_event(event)?;
Ok(msg) Ok(msg)
}; };
unsafe { util::try_or_raise(block) } unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) }
} }
extern "C" fn ui_layout_timer(this: Obj, token: Obj) -> Obj { extern "C" fn ui_layout_timer(this: Obj, token: Obj) -> Obj {

@ -1,3 +1,4 @@
#[allow(unused_macros)] // T1 doesn't use icons (yet)
macro_rules! include_res { macro_rules! include_res {
($filename:expr) => { ($filename:expr) => {
include_bytes!(concat!( include_bytes!(concat!(

@ -5,3 +5,6 @@ pub mod component;
pub mod display; pub mod display;
pub mod geometry; pub mod geometry;
pub mod layout; pub mod layout;
pub mod model_t1;
pub mod model_tt;

@ -0,0 +1,4 @@
pub const WIDTH: i32 = 128;
pub const HEIGHT: i32 = 64;
pub const TEXT_HEIGHT: i32 = 8;
pub const LINE_HEIGHT: i32 = 9;

@ -0,0 +1,29 @@
use crate::error;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum T1Button {
Left,
Right,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum ButtonEvent {
ButtonPressed(T1Button),
ButtonReleased(T1Button),
}
impl ButtonEvent {
pub fn new(event: u32, button: u32, _unused: u32) -> Result<Self, error::Error> {
let button = match button {
0 => T1Button::Left,
1 => T1Button::Right,
_ => return Err(error::Error::OutOfRange),
};
let result = match event {
1 => Self::ButtonPressed(button),
2 => Self::ButtonReleased(button),
_ => return Err(error::Error::OutOfRange),
};
Ok(result)
}
}

@ -0,0 +1,3 @@
pub mod constant;
pub mod event;
pub mod theme;

@ -0,0 +1,61 @@
use crate::ui::display::{Color, Font};
use super::component::{ButtonStyle, ButtonStyleSheet};
// Font constants.
pub const FONT_NORMAL: Font = Font::new(-1);
pub const FONT_BOLD: Font = Font::new(-2);
pub const FONT_MONO: Font = Font::new(-3);
// Color palette.
pub const WHITE: Color = Color::rgb(255, 255, 255);
pub const BLACK: Color = Color::rgb(0, 0, 0);
pub const GREY_LIGHT: Color = WHITE; // Word/page break characters.
pub const FG: Color = WHITE; // Default foreground (text & icon) color.
pub const BG: Color = BLACK; // Default background color.
pub fn button_default() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: FONT_BOLD,
text_color: BG,
background_color: FG,
border_horiz: true,
},
active: &ButtonStyle {
font: FONT_BOLD,
text_color: FG,
background_color: BG,
border_horiz: true,
},
disabled: &ButtonStyle {
font: FONT_BOLD,
text_color: FG,
background_color: BG,
border_horiz: true,
},
}
}
pub fn button_cancel() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: FONT_BOLD,
text_color: FG,
background_color: BG,
border_horiz: false,
},
active: &ButtonStyle {
font: FONT_BOLD,
text_color: BG,
background_color: FG,
border_horiz: false,
},
disabled: &ButtonStyle {
font: FONT_BOLD,
text_color: BG,
background_color: FG,
border_horiz: false,
},
}
}

@ -3,8 +3,7 @@ use crate::ui::{
display::{self, Color, Font}, display::{self, Color, Font},
geometry::{Offset, Rect}, geometry::{Offset, Rect},
}; };
use super::event::TouchEvent;
use super::theme;
pub enum ButtonMsg { pub enum ButtonMsg {
Clicked, Clicked,
@ -79,7 +78,7 @@ impl Component for Button {
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> { fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
match event { match event {
Event::TouchStart(pos) => { Event::Touch(TouchEvent::TouchStart(pos)) => {
match self.state { match self.state {
State::Disabled => { State::Disabled => {
// Do nothing. // Do nothing.
@ -92,7 +91,7 @@ impl Component for Button {
} }
} }
} }
Event::TouchMove(pos) => { Event::Touch(TouchEvent::TouchMove(pos)) => {
match self.state { match self.state {
State::Released if self.area.contains(pos) => { State::Released if self.area.contains(pos) => {
// Touch entered our area, transform to `Pressed` state. // Touch entered our area, transform to `Pressed` state.
@ -107,7 +106,7 @@ impl Component for Button {
} }
} }
} }
Event::TouchEnd(pos) => { Event::Touch(TouchEvent::TouchEnd(pos)) => {
match self.state { match self.state {
State::Initial | State::Disabled => { State::Initial | State::Disabled => {
// Do nothing. // Do nothing.

@ -1,17 +1,13 @@
mod button; mod button;
mod dialog; mod dialog;
mod empty;
mod label;
mod page; mod page;
mod passphrase; mod passphrase;
mod pin; mod pin;
mod swipe; mod swipe;
pub mod text;
pub mod theme;
pub use button::{Button, ButtonContent, ButtonMsg, ButtonStyle, ButtonStyleSheet}; pub use button::{Button, ButtonContent, ButtonMsg, ButtonStyle, ButtonStyleSheet};
pub use dialog::{Dialog, DialogMsg}; pub use dialog::{Dialog, DialogMsg};
pub use empty::Empty;
pub use label::{Label, LabelStyle};
pub use swipe::{Swipe, SwipeDirection}; pub use swipe::{Swipe, SwipeDirection};
pub use text::{LineBreaking, PageBreaking, Text, TextLayout};
use super::event;
use super::theme;

@ -3,7 +3,10 @@ use heapless::Vec;
use crate::{ use crate::{
trezorhal::random, trezorhal::random,
ui::{ ui::{
component::{Child, Component, Event, EventCtx, Never}, component::{
label::{Label, LabelStyle},
Child, Component, Event, EventCtx, Never,
},
display, display,
geometry::{Grid, Offset, Point, Rect}, geometry::{Grid, Offset, Point, Rect},
}, },
@ -11,7 +14,6 @@ use crate::{
use super::{ use super::{
button::{Button, ButtonContent, ButtonMsg::Clicked}, button::{Button, ButtonContent, ButtonMsg::Clicked},
label::{Label, LabelStyle},
theme, theme,
}; };

@ -4,7 +4,7 @@ use crate::ui::{
geometry::{Point, Rect}, geometry::{Point, Rect},
}; };
use super::theme; use super::{theme, event::TouchEvent};
pub enum SwipeDirection { pub enum SwipeDirection {
Up, Up,
@ -86,11 +86,11 @@ impl Component for Swipe {
fn event(&mut self, _ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> { fn event(&mut self, _ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
match (event, self.origin) { match (event, self.origin) {
(Event::TouchStart(pos), _) if self.area.contains(pos) => { (Event::Touch(TouchEvent::TouchStart(pos)), _) if self.area.contains(pos) => {
// Mark the starting position of this touch. // Mark the starting position of this touch.
self.origin.replace(pos); self.origin.replace(pos);
} }
(Event::TouchMove(pos), Some(origin)) => { (Event::Touch(TouchEvent::TouchMove(pos)), Some(origin)) => {
// Consider our allowed directions and the touch distance and modify the display // Consider our allowed directions and the touch distance and modify the display
// backlight accordingly. // backlight accordingly.
let ofs = pos - origin; let ofs = pos - origin;
@ -107,7 +107,7 @@ impl Component for Swipe {
} }
}; };
} }
(Event::TouchEnd(pos), Some(origin)) => { (Event::Touch(TouchEvent::TouchEnd(pos)), Some(origin)) => {
// Touch interaction is over, reset the position. // Touch interaction is over, reset the position.
self.origin.take(); self.origin.take();

@ -0,0 +1,4 @@
pub const WIDTH: i32 = 240;
pub const HEIGHT: i32 = 240;
pub const TEXT_HEIGHT: i32 = 16;
pub const LINE_HEIGHT: i32 = 26;

@ -0,0 +1,24 @@
use core::convert::TryInto;
use crate::{error, ui::geometry::Point};
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum TouchEvent {
TouchStart(Point),
TouchMove(Point),
TouchEnd(Point),
}
impl TouchEvent {
pub fn new(event: u32, x: u32, y: u32) -> Result<Self, error::Error> {
let point = Point::new(x.try_into()?, y.try_into()?);
let result = match event {
1 => Self::TouchStart(point),
2 => Self::TouchMove(point),
4 => Self::TouchEnd(point),
_ => return Err(error::Error::OutOfRange),
};
Ok(result)
}
}

@ -4,16 +4,17 @@ use crate::{
error::Error, error::Error,
micropython::{buffer::Buffer, obj::Obj}, micropython::{buffer::Buffer, obj::Obj},
ui::{ ui::{
component::{ component::{Child, Text},
model_tt::{theme, Button, Dialog, DialogMsg, Text},
Child,
},
display, display,
layout::obj::LayoutObj,
}, },
util, util,
}; };
use super::obj::LayoutObj; use super::{
component::{Button, Dialog, DialogMsg},
theme,
};
impl<T> TryFrom<DialogMsg<T>> for Obj impl<T> TryFrom<DialogMsg<T>> for Obj
where where

@ -0,0 +1,5 @@
pub mod component;
pub mod constant;
pub mod event;
pub mod layout;
pub mod theme;

@ -1,8 +1,10 @@
use crate::ui::{ use crate::ui::{
component::model_tt::{ButtonStyle, ButtonStyleSheet, LabelStyle}, component::{label::LabelStyle, text::DefaultTextTheme},
display::{Color, Font}, display::{Color, Font},
}; };
use super::component::{ButtonStyle, ButtonStyleSheet};
// Font constants. // Font constants.
pub const FONT_NORMAL: Font = Font::new(-1); pub const FONT_NORMAL: Font = Font::new(-1);
pub const FONT_BOLD: Font = Font::new(-2); pub const FONT_BOLD: Font = Font::new(-2);

@ -456,11 +456,9 @@ if utils.MODEL == "1":
msg = None msg = None
if event is RENDER: if event is RENDER:
self.layout.paint() self.layout.paint()
elif event is io.TOUCH_START: elif event in (io.BUTTON_PRESSED, io.BUTTON_RELEASED):
msg = self.layout.touch_start(x, y) msg = self.layout.hid_event(event, x, 0)
elif event is io.TOUCH_MOVE: # elif event in (io.TOUCH_START, io.TOUCH_MOVE, io.TOUCH_END):
msg = self.layout.touch_move(x, y) # self.layout.hid_event(event, x, y)
elif event is io.TOUCH_END:
msg = self.layout.touch_end(x, y)
if msg is not None: if msg is not None:
raise Result(msg) raise Result(msg)

Loading…
Cancel
Save