1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-22 15:38:11 +00:00

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

[no changelog]
This commit is contained in:
Martin Milata 2021-08-02 22:23:19 +02:00
parent 3c49ef2f62
commit 4d60c10330
36 changed files with 220 additions and 86 deletions

View File

@ -701,7 +701,10 @@ def cargo_build():
profile = '--release'
else:
profile = ''
features = []
if TREZOR_MODEL == "1":
features = ["model_t1"]
else:
features = ["model_tt"]
if BITCOIN_ONLY == "1":
features.append("bitcoin_only")
if NEW_UI:
@ -709,7 +712,7 @@ def cargo_build():
if PYOPT == "0":
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(
target=RUST_LIBPATH,

View File

@ -653,7 +653,10 @@ def cargo_build():
profile = '--release'
else:
profile = ''
features = []
if TREZOR_MODEL == "1":
features = ["model_t1"]
else:
features = ["model_tt"]
if BITCOIN_ONLY == "1":
features.append("bitcoin_only")
if PYOPT == "0":
@ -662,7 +665,7 @@ def cargo_build():
elif NEW_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(
target=RUST_LIBPATH,

View File

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

View File

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

View File

@ -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) }
}
}
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Buffer {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
self.as_ref().trace(t)
}
}

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ use core::{mem, time::Duration};
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.
///
@ -81,9 +81,7 @@ where
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Event {
TouchStart(Point),
TouchMove(Point),
TouchEnd(Point),
HumanInput(HidEvent),
Timer(TimerToken),
}

View File

@ -1,5 +1,9 @@
mod base;
pub mod model_t1;
pub mod model_tt;
pub mod empty;
pub mod label;
pub mod text;
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};

View File

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

View File

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

View File

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

View File

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

View File

@ -13,10 +13,7 @@ use crate::{
qstr::Qstr,
typ::Type,
},
ui::{
component::{Child, Component, Event, EventCtx, Never, TimerToken},
geometry::Point,
},
ui::component::{model::HidEvent, Child, Component, Event, EventCtx, Never, TimerToken},
util,
};
@ -207,9 +204,7 @@ impl LayoutObj {
name: Qstr::MP_QSTR_Layout,
locals: &obj_dict!(obj_map! {
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_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_hid_event => obj_fn_var!(4, 4, ui_layout_hid_event).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_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) }
}
extern "C" fn ui_layout_touch_start(this: Obj, x: Obj, y: Obj) -> Obj {
let block = || {
let this: Gc<LayoutObj> = this.try_into()?;
let event = Event::TouchStart(Point::new(x.try_into()?, y.try_into()?));
let msg = this.obj_event(event)?;
extern "C" fn ui_layout_hid_event(n_args: usize, args: *const Obj) -> Obj {
let block = |args: &[Obj], _kwargs: &Map| {
if args.len() != 4 {
return Err(Error::TypeError);
}
let this: Gc<LayoutObj> = args[0].try_into()?;
let event = HidEvent::new(
args[1].try_into()?,
args[2].try_into()?,
args[3].try_into()?,
)?;
let msg = this.obj_event(Event::HumanInput(event))?;
Ok(msg)
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn ui_layout_touch_move(this: Obj, x: Obj, y: Obj) -> Obj {
let block = || {
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)
};
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 {

View File

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

View File

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

View File

@ -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;

View File

@ -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)
}
}

View File

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

View File

@ -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,
},
}
}

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ use crate::ui::{
geometry::{Point, Rect},
};
use super::theme;
use super::{theme, event::TouchEvent};
pub enum SwipeDirection {
Up,
@ -86,11 +86,11 @@ impl Component for Swipe {
fn event(&mut self, _ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
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.
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
// backlight accordingly.
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.
self.origin.take();

View File

@ -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;

View File

@ -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)
}
}

View File

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

View File

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

View File

@ -1,8 +1,10 @@
use crate::ui::{
component::model_tt::{ButtonStyle, ButtonStyleSheet, LabelStyle},
component::{label::LabelStyle, text::DefaultTextTheme},
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);

View File

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