feat(core): allow different screen sizes for TT UI

[no changelog]
pull/3055/head
tychovrahe 1 year ago committed by TychoVrahe
parent d3284baf21
commit 32a2b371bd

@ -32,8 +32,9 @@ enum SafetyCheckLevel {
* Format of the homescreen image
*/
enum HomescreenFormat {
Toif144x144 = 1;
Jpeg240x240 = 2;
Toif = 1; // full-color toif
Jpeg = 2; // jpeg
ToiG = 3; // greyscale toif
}
/**
@ -121,11 +122,13 @@ message Features {
optional uint32 display_rotation = 39; // in degrees from North
optional bool experimental_features = 40; // are experimental message types enabled?
optional bool busy = 41; // is the device busy, showing "Do not disconnect"?
optional HomescreenFormat homescreen_format = 42; // format of the homescreen, 1 = TOIf 144x144, 2 = jpg 240x240
optional HomescreenFormat homescreen_format = 42; // format of the homescreen, 1 = TOIf, 2 = jpg, 3 = TOIG
optional bool hide_passphrase_from_host = 43; // should we hide the passphrase when it comes from host?
optional string internal_model = 44; // internal model name
optional uint32 unit_color = 45; // color of the unit/device
optional bool unit_btconly = 46; // unit/device is intended as bitcoin only
optional uint32 homescreen_width = 47; // homescreen width in pixels
optional uint32 homescreen_height = 48; // homescreen height in pixels
}
/**

@ -285,6 +285,8 @@ fn generate_trezorhal_bindings() {
.allowlist_function("display_set_window")
.allowlist_function("display_sync")
.allowlist_var("DISPLAY_DATA_ADDRESS")
.allowlist_var("DISPLAY_RESX")
.allowlist_var("DISPLAY_RESY")
.allowlist_type("toif_format_t")
// fonts
.allowlist_function("font_height")

@ -4,6 +4,8 @@ use cty::c_int;
use crate::trezorhal::buffers::BufferText;
pub use ffi::{DISPLAY_RESX, DISPLAY_RESY};
#[derive(PartialEq, Debug, Eq, FromPrimitive, Clone, Copy)]
pub enum ToifFormat {
FullColorBE = ffi::toif_format_t_TOIF_FULL_COLOR_BE as _,

@ -1,7 +1,9 @@
use crate::ui::geometry::{Offset, Point, Rect};
pub const WIDTH: i16 = 128;
pub const HEIGHT: i16 = 64;
use crate::trezorhal::display::{DISPLAY_RESX, DISPLAY_RESY};
pub const WIDTH: i16 = DISPLAY_RESX as _;
pub const HEIGHT: i16 = DISPLAY_RESY as _;
pub const LINE_SPACE: i16 = 1;
pub const FONT_BPP: i16 = 1;

@ -1,7 +1,9 @@
use crate::ui::geometry::{Offset, Point, Rect};
pub const WIDTH: i16 = 240;
pub const HEIGHT: i16 = 240;
use crate::trezorhal::display::{DISPLAY_RESX, DISPLAY_RESY};
pub const WIDTH: i16 = DISPLAY_RESX as _;
pub const HEIGHT: i16 = DISPLAY_RESY as _;
pub const LINE_SPACE: i16 = 4;
pub const FONT_BPP: i16 = 4;

@ -1,6 +1,11 @@
#ifndef _STM32F429I_DISC1_H
#define _STM32F429I_DISC1_H
#define MAX_DISPLAY_RESX 240
#define MAX_DISPLAY_RESY 320
#define DISPLAY_RESX 240
#define DISPLAY_RESY 320
#define USE_I2C 1
#define USE_TOUCH 1
#define USE_SDRAM 1

@ -1,6 +1,9 @@
#ifndef _TREZOR_T_H
#define _TREZOR_T_H
#define DISPLAY_RESX 240
#define DISPLAY_RESY 240
#define USE_SD_CARD 1
#define USE_I2C 1
#define USE_TOUCH 1

@ -4,10 +4,6 @@
#include STM32_HAL_H
#define MAX_DISPLAY_RESX 240
#define MAX_DISPLAY_RESY 320
#define DISPLAY_RESX 240
#define DISPLAY_RESY 240
#define TREZOR_FONT_BPP 4
extern uint8_t *const DISPLAY_DATA_ADDRESS;

@ -6,8 +6,6 @@
// ILI9341V, GC9307 and ST7789V drivers support 240px x 320px display resolution
#define MAX_DISPLAY_RESX 240
#define MAX_DISPLAY_RESY 320
#define DISPLAY_RESX 240
#define DISPLAY_RESY 240
#define TREZOR_FONT_BPP 4
#ifdef USE_DISP_I8080_16BIT_DW

@ -44,6 +44,7 @@ def get_features() -> Features:
from trezor.enums import Capability
from trezor.messages import Features
from trezor.ui import WIDTH, HEIGHT
from apps.common import mnemonic, safety_checks
@ -62,11 +63,17 @@ def get_features() -> Features:
pin_protection=config.has_pin(),
unlocked=config.is_unlocked(),
busy=busy_expiry_ms() > 0,
homescreen_format=HomescreenFormat.Jpeg240x240,
homescreen_width=WIDTH,
homescreen_height=HEIGHT,
unit_color=utils.unit_color(),
unit_btconly=utils.unit_btconly(),
)
if utils.MODEL in ("1", "R"):
f.homescreen_format = HomescreenFormat.ToiG
else:
f.homescreen_format = HomescreenFormat.Jpeg
if utils.BITCOIN_ONLY:
f.capabilities = [
Capability.Bitcoin,

@ -15,27 +15,31 @@ if TYPE_CHECKING:
BRT_PROTECT_CALL = ButtonRequestType.ProtectCall # CACHE
if utils.MODEL == "R":
if utils.MODEL in ("1", "R"):
def _validate_homescreen_model_specific(homescreen: bytes) -> None:
from trezor.ui import WIDTH, HEIGHT
try:
w, h, is_grayscale = trezorui2.toif_info(homescreen)
except ValueError:
raise DataError("Invalid homescreen")
if w != 128 or h != 64:
raise DataError("Homescreen must be 128x64 pixel large")
if w != WIDTH or h != HEIGHT:
raise DataError(f"Homescreen must be {WIDTH}x{HEIGHT} pixel large")
if not is_grayscale:
raise DataError("Homescreen must be grayscale")
else:
def _validate_homescreen_model_specific(homescreen: bytes) -> None:
from trezor.ui import WIDTH, HEIGHT
try:
w, h, mcu_height = trezorui2.jpeg_info(homescreen)
except ValueError:
raise DataError("Invalid homescreen")
if w != 240 or h != 240:
raise DataError("Homescreen must be 240x240 pixel large")
if w != WIDTH or h != HEIGHT:
raise DataError(f"Homescreen must be {WIDTH}x{HEIGHT} pixel large")
if mcu_height > 16:
raise DataError("Unsupported jpeg type")
try:

@ -2,5 +2,6 @@
# fmt: off
# isort:skip_file
Toif144x144 = 1
Jpeg240x240 = 2
Toif = 1
Jpeg = 2
ToiG = 3

@ -410,8 +410,9 @@ if TYPE_CHECKING:
PromptTemporarily = 2
class HomescreenFormat(IntEnum):
Toif144x144 = 1
Jpeg240x240 = 2
Toif = 1
Jpeg = 2
ToiG = 3
class Capability(IntEnum):
Bitcoin = 1

@ -2112,6 +2112,8 @@ if TYPE_CHECKING:
internal_model: "str | None"
unit_color: "int | None"
unit_btconly: "bool | None"
homescreen_width: "int | None"
homescreen_height: "int | None"
def __init__(
self,
@ -2159,6 +2161,8 @@ if TYPE_CHECKING:
internal_model: "str | None" = None,
unit_color: "int | None" = None,
unit_btconly: "bool | None" = None,
homescreen_width: "int | None" = None,
homescreen_height: "int | None" = None,
) -> None:
pass

@ -69,47 +69,11 @@ def image_to_t1(filename: Path) -> bytes:
return image.tobytes("raw", "1")
def image_to_tr(filename: Path) -> bytes:
if not PIL_AVAILABLE:
raise click.ClickException(
"Image library is missing. Please install via 'pip install Pillow'."
)
try:
image = Image.open(filename)
except Exception as e:
raise click.ClickException("Failed to load image") from e
if image.size != T1_TR_IMAGE_SIZE:
if click.confirm(
f"Image is not 128x64, but {image.size}. Do you want to resize it automatically?",
default=True,
):
image = image.resize(T1_TR_IMAGE_SIZE, Image.ANTIALIAS)
else:
raise click.ClickException("Wrong size of the image - should be 128x64")
image = image.convert("1") # black-and-white
toif_image = toif.from_image(image)
return toif_image.to_bytes()
def image_to_tt(client: "TrezorClient", path: Path) -> bytes:
if client.features.homescreen_format == messages.HomescreenFormat.Jpeg240x240:
return image_to_jpeg_240x240(path)
elif client.features.homescreen_format in (
messages.HomescreenFormat.Toif144x144,
None,
):
return image_to_toif_144x144(path)
else:
raise click.ClickException("Unknown image format requested by the device.")
def image_to_toif_144x144(filename: Path) -> bytes:
def image_to_toif(filename: Path, width: int, height: int, greyscale: bool) -> bytes:
if filename.suffix == ".toif":
try:
toif_image = toif.from_bytes(filename.read_bytes())
image = toif_image.to_image()
except Exception as e:
raise click.ClickException("TOIF file is corrupted") from e
@ -127,16 +91,30 @@ def image_to_toif_144x144(filename: Path) -> bytes:
"Failed to convert image to Trezor format"
) from e
if toif_image.size != (144, 144):
raise click.ClickException("Wrong size of image - should be 144x144")
if toif_image.size != (width, height):
if click.confirm(
f"Image is not {width}x{height}, but {image.size[0]}x{image.size[1]}. Do you want to resize it automatically?",
default=True,
):
image = image.resize((width, height), Image.ANTIALIAS)
else:
raise click.ClickException(
f"Wrong size of image - should be {width}x{height}"
)
if toif_image.mode != toif.ToifMode.full_color:
if greyscale:
image = image.convert("1")
toif_image = toif.from_image(image)
if not greyscale and toif_image.mode != toif.ToifMode.full_color:
raise click.ClickException("Wrong image mode - should be full_color")
if greyscale and toif_image.mode != toif.ToifMode.grayscale_eh:
raise click.ClickException("Wrong image mode - should be grayscale_eh")
return toif_image.to_bytes()
def image_to_jpeg_240x240(filename: Path) -> bytes:
def image_to_jpeg(filename: Path, width: int, height: int) -> bytes:
if filename.suffix in (".jpg", ".jpeg") and not PIL_AVAILABLE:
click.echo("Warning: Image library is missing, skipping image validation.")
return filename.read_bytes()
@ -151,8 +129,16 @@ def image_to_jpeg_240x240(filename: Path) -> bytes:
except Exception as e:
raise click.ClickException("Failed to open image") from e
if image.size != (240, 240):
raise click.ClickException("Wrong size of image - should be 240x240")
if image.size != (width, height):
if click.confirm(
f"Image is not {width}x{height}, but {image.size[0]}x{image.size[1]}. Do you want to resize it automatically?",
default=True,
):
image = image.resize((width, height), Image.ANTIALIAS)
else:
raise click.ClickException(
f"Wrong size of image - should be {width}x{height}"
)
if image.mode != "RGB":
image = image.convert("RGB")
@ -281,12 +267,45 @@ def homescreen(client: "TrezorClient", filename: str) -> str:
if client.features.model == "1":
img = image_to_t1(path)
elif client.features.model == "R":
img = image_to_tr(path)
elif client.features.model == "T":
img = image_to_tt(client, path)
else:
raise click.ClickException("Unknown device model")
if client.features.homescreen_format == messages.HomescreenFormat.Jpeg:
width = (
client.features.homescreen_width
if client.features.homescreen_width is not None
else 240
)
height = (
client.features.homescreen_height
if client.features.homescreen_height is not None
else 240
)
img = image_to_jpeg(path, width, height)
elif client.features.homescreen_format == messages.HomescreenFormat.ToiG:
width = client.features.homescreen_width
height = client.features.homescreen_height
if width is None or height is None:
raise click.ClickException("Device did not report homescreen size.")
img = image_to_toif(path, width, height, True)
elif (
client.features.homescreen_format == messages.HomescreenFormat.Toif
or client.features.homescreen_format is None
):
width = (
client.features.homescreen_width
if client.features.homescreen_width is not None
else 144
)
height = (
client.features.homescreen_height
if client.features.homescreen_height is not None
else 144
)
img = image_to_toif(path, width, height, False)
else:
raise click.ClickException(
"Unknown image format requested by the device."
)
return device.apply_settings(client, homescreen=img)

@ -440,8 +440,9 @@ class SafetyCheckLevel(IntEnum):
class HomescreenFormat(IntEnum):
Toif144x144 = 1
Jpeg240x240 = 2
Toif = 1
Jpeg = 2
ToiG = 3
class Capability(IntEnum):
@ -3163,6 +3164,8 @@ class Features(protobuf.MessageType):
44: protobuf.Field("internal_model", "string", repeated=False, required=False, default=None),
45: protobuf.Field("unit_color", "uint32", repeated=False, required=False, default=None),
46: protobuf.Field("unit_btconly", "bool", repeated=False, required=False, default=None),
47: protobuf.Field("homescreen_width", "uint32", repeated=False, required=False, default=None),
48: protobuf.Field("homescreen_height", "uint32", repeated=False, required=False, default=None),
}
def __init__(
@ -3212,6 +3215,8 @@ class Features(protobuf.MessageType):
internal_model: Optional["str"] = None,
unit_color: Optional["int"] = None,
unit_btconly: Optional["bool"] = None,
homescreen_width: Optional["int"] = None,
homescreen_height: Optional["int"] = None,
) -> None:
self.capabilities: Sequence["Capability"] = capabilities if capabilities is not None else []
self.major_version = major_version
@ -3257,6 +3262,8 @@ class Features(protobuf.MessageType):
self.internal_model = internal_model
self.unit_color = unit_color
self.unit_btconly = unit_btconly
self.homescreen_width = homescreen_width
self.homescreen_height = homescreen_height
class LockDevice(protobuf.MessageType):

Loading…
Cancel
Save