mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-25 00:48:19 +00:00
feat(core): allow different screen sizes for TT UI
[no changelog]
This commit is contained in:
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…
Reference in New Issue
Block a user