core/debug: add "show arbitrary screen" capability, for easier prototyping

pull/828/head
matejcik 4 years ago
parent 8c75c94125
commit 05a1d6f771

@ -57,6 +57,33 @@ message DebugLinkRecordScreen {
optional string target_directory = 1; // empty or missing to stop recording
}
/**
* Request: Show text on the screen
* @start
* @next Success
*/
message DebugLinkShowText {
optional string header_text = 1; // screen header text
repeated DebugLinkShowTextItem body_text = 2; // body text segments
optional string header_icon = 3; // icon name in ui.style
optional string icon_color = 4; // color name in ui.style
message DebugLinkShowTextItem {
optional DebugLinkShowTextStyle style = 1;
optional string content = 2;
}
enum DebugLinkShowTextStyle {
NORMAL = 0;
BOLD = 1;
MONO = 2;
MONO_BOLD = 3;
BR = 4;
BR_HALF = 5;
SET_COLOR = 6;
}
}
/**
* Request: Computer asks for device state
* @start

@ -109,6 +109,7 @@ enum MessageType {
MessageType_DebugLinkLayout = 9001 [(wire_debug_out) = true];
MessageType_DebugLinkReseedRandom = 9002 [(wire_debug_in) = true];
MessageType_DebugLinkRecordScreen = 9003 [(wire_debug_in) = true];
MessageType_DebugLinkShowText = 9004 [(wire_debug_in) = true];
// Ethereum
MessageType_EthereumGetPublicKey = 450 [(wire_in) = true];

@ -145,6 +145,7 @@ if __debug__:
config.wipe()
wire.add(MessageType.LoadDevice, __name__, "load_device")
wire.add(MessageType.DebugLinkShowText, __name__, "show_text")
wire.register(MessageType.DebugLinkDecision, dispatch_DebugLinkDecision)
wire.register(MessageType.DebugLinkGetState, dispatch_DebugLinkGetState)
wire.register(MessageType.DebugLinkReseedRandom, dispatch_DebugLinkReseedRandom)

@ -0,0 +1,50 @@
import trezor.messages.DebugLinkShowTextStyle as S
from trezor import ui, wire
from trezor.messages.DebugLinkShowText import DebugLinkShowText
from trezor.messages.Success import Success
from trezor.ui import style, text
from trezor.ui.text import Text
from apps.common.confirm import confirm
STYLES = {
S.NORMAL: ui.NORMAL,
S.BOLD: ui.BOLD,
S.MONO: ui.MONO,
S.MONO_BOLD: ui.MONO_BOLD,
S.BR: text.BR,
S.BR_HALF: text.BR_HALF,
}
async def show_text(ctx: wire.Context, msg: DebugLinkShowText) -> Success:
if msg.header_icon is not None:
icon_name = "ICON_" + msg.header_icon
icon = getattr(style, icon_name)
if not isinstance(icon, str):
raise wire.DataError("Invalid icon name: {}".format(msg.header_icon))
else:
icon = style.ICON_DEFAULT
if msg.icon_color is not None:
color = getattr(style, msg.icon_color)
if not isinstance(color, int):
raise wire.DataError("Invalid color name: {}".format(msg.icon_color))
else:
color = style.ORANGE_ICON
dlg = Text(msg.header_text, icon, color, new_lines=False)
for item in msg.body_text:
if item.style in STYLES:
dlg.content.append(STYLES[item.style])
elif item.style == S.SET_COLOR:
color = getattr(style, item.content)
if not isinstance(color, int):
raise wire.DataError("Invalid color name: {}".format(item.content))
dlg.content.append(color)
elif item.content is not None:
dlg.content.append(item.content)
await confirm(ctx, dlg)
return Success("text shown")

@ -0,0 +1,37 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .DebugLinkShowTextItem import DebugLinkShowTextItem
if __debug__:
try:
from typing import Dict, List # noqa: F401
from typing_extensions import Literal # noqa: F401
except ImportError:
pass
class DebugLinkShowText(p.MessageType):
MESSAGE_WIRE_TYPE = 9004
def __init__(
self,
header_text: str = None,
body_text: List[DebugLinkShowTextItem] = None,
header_icon: str = None,
icon_color: str = None,
) -> None:
self.header_text = header_text
self.body_text = body_text if body_text is not None else []
self.header_icon = header_icon
self.icon_color = icon_color
@classmethod
def get_fields(cls) -> Dict:
return {
1: ('header_text', p.UnicodeType, 0),
2: ('body_text', DebugLinkShowTextItem, p.FLAG_REPEATED),
3: ('header_icon', p.UnicodeType, 0),
4: ('icon_color', p.UnicodeType, 0),
}

@ -0,0 +1,29 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
if __debug__:
try:
from typing import Dict, List # noqa: F401
from typing_extensions import Literal # noqa: F401
EnumTypeDebugLinkShowTextStyle = Literal[0, 1, 2, 3, 4, 5, 6]
except ImportError:
pass
class DebugLinkShowTextItem(p.MessageType):
def __init__(
self,
style: EnumTypeDebugLinkShowTextStyle = None,
content: str = None,
) -> None:
self.style = style
self.content = content
@classmethod
def get_fields(cls) -> Dict:
return {
1: ('style', p.EnumType("DebugLinkShowTextStyle", (0, 1, 2, 3, 4, 5, 6)), 0),
2: ('content', p.UnicodeType, 0),
}

@ -0,0 +1,12 @@
# Automatically generated by pb2py
# fmt: off
if False:
from typing_extensions import Literal
NORMAL = 0 # type: Literal[0]
BOLD = 1 # type: Literal[1]
MONO = 2 # type: Literal[2]
MONO_BOLD = 3 # type: Literal[3]
BR = 4 # type: Literal[4]
BR_HALF = 5 # type: Literal[5]
SET_COLOR = 6 # type: Literal[6]

@ -76,6 +76,7 @@ DebugLinkFlashErase = 113 # type: Literal[113]
DebugLinkLayout = 9001 # type: Literal[9001]
DebugLinkReseedRandom = 9002 # type: Literal[9002]
DebugLinkRecordScreen = 9003 # type: Literal[9003]
DebugLinkShowText = 9004 # type: Literal[9004]
if not utils.BITCOIN_ONLY:
EthereumGetPublicKey = 450 # type: Literal[450]
EthereumPublicKey = 451 # type: Literal[451]

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

@ -0,0 +1,138 @@
# Displaying text screens on Trezor T
## Requirements
For this feature, you will need a debug-enabled firmware for Trezor T. Usually that
will be the emulator.
You will also need `trezorctl` 0.12 or later. Best bet is to run the version from github
master.
## Trezor T text capabilities
The Trezor T screen has a _header_, with an icon and text. Below it is _body_, which
can fit up to 5 lines of text.
Text can be in one of several colors, and in one of four styles: NORMAL, BOLD, MONO
and MONO_BOLD.
It is possible to have multiple styles on the same line, but different styles must be
separated by a space.
I.e., this works fine:
> This word is **bold** and this is `monospaced`.
This will not work:
> Em**bold**ened middle, mono`space`d middle.
A line that is too long to fit on screen will automatically break, using a hyphen (`-`)
character. This is usually undesirable. Instead, a manual line-break should be inserted
in the appropriate place.
## Command line syntax
The most basic way to put words on screen is this:
```sh
trezorctl debug show-text "My hovercraft is full of eels, call the porter, there is a frog in my bidet."
```
The above command will show:
![Screenshot01](show-text-01.png)
Notice the "quotes" around the text. The whole body text must be enquoted.
To use quotes inside the text, prefix them with backslashes `\`:
```sh
trezorctl debug show-text "My \"hovercraft\" is full of eels."
```
![Screenshot02](show-text-02.png)
### Line breaks
Let's insert some line breaks. Do that by placing `@@BR` in the appropriate place
in the text:
```sh
trezorctl debug show-text "My hovercraft is full of @@BR eels, call the porter, @@BR there is a frog in my @@BR bidet."
```
![Screenshot03](show-text-03.png)
Better!
### Text styles
Now let's add some style. Use `@@BOLD` to start printing in bold. Use `@@NORMAL`
to go back to normal text. `@@MONO` and `@@MONO_BOLD` works similarly.
```sh
trezorctl debug show-text "My hovercraft is @@BOLD full of @@BR eels. @@NORMAL Call the porter, @@BR there is a @@MONO frog @@NORMAL in my @@BR bidet."
```
![Screenshot04](show-text-04.png)
### Line spacing
Adding another `@@BR` after a `@@BR` will leave one line empty -- just like pressing
\<Enter\> twice.
If you don't want a full empty line, you can make a half-break with `@@BR_HALF`.
```sh
trezorctl debug show-text "Line one. @@BR @@BR Line two. @@BR @@BR_HALF Line three."
```
![Screenshot05](show-text-05.png)
### Text colors
To switch to one of the [available colors](../../core/src/trezor/ui/style.py#L14-L47),
use the color name prefixed with `%%`: e.g., `%%RED`, `%%LIGHT_BLUE`...
To switch back to the default color, you can use `%%FG`:
```sh
trezorctl debug show-text "My %%RED hovercraft is @@BOLD full %%GREEN of @@BR eels. @@NORMAL Call %%ORANGE the %%FG porter."
```
![Screenshot06](show-text-06.png)
### Headers
The default header says "Showing text" with an orange gear icon. It is possible to
change all of that.
To change the text, use `-h` option:
```sh
trezorctl debug show-text -h "Hello world" "My hovercraft is full."
```
To change the icon, you can pick [an icon name from here](../../core/src/trezor/ui/style.py#L50-L70) and specify it with the `-i` option:
```sh
trezorctl debug show-text -i RECEIVE "My hovercraft is full."
```
The icons are defined as shapes, and you can specify a custom color [from the list](../../core/src/trezor/ui/style.py#L14-L47) with the `-c` option:
```sh
trezorctl debug show-text -c RED "My hovercraft is full."
```
### Putting it all together
Here is how to reproduce the confirmation screen after the wallet is created:
```sh
trezorctl debug show-text -h "Success" -i CONFIRM -c GREEN "@@BOLD New wallet created @@BR successfully! @@BR @@BR_HALF @@NORMAL You should back up your @@BR new wallet right now."
```
![Screenshot07](show-text-07.png)

@ -2,7 +2,8 @@ ifneq ($(V),1)
Q := @
endif
SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdProtect Tezos WebAuthn DebugLinkRecordScreen DebugLinkReseedRandom
SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdProtect Tezos WebAuthn \
DebugLinkRecordScreen DebugLinkReseedRandom DebugLinkShowText
ifeq ($(BITCOIN_ONLY), 1)
SKIPPED_MESSAGES += Ethereum Lisk NEM Stellar

@ -17,4 +17,3 @@ DebugLinkMemoryWrite.memory max_size:1024
# unused fields
DebugLinkState.layout_lines max_count:10 max_size:30
DebugLinkLayout.lines max_count:10 max_size:30
DebugLinkRecordScreen.target_directory max_size:16

@ -0,0 +1,73 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2012-2019 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import click
from .. import debuglink
from ..messages import DebugLinkShowTextStyle as S
@click.group(name="debug")
def cli():
"""Miscellaneous debug features."""
STYLES = {
"@@NORMAL": S.NORMAL,
"@@BOLD": S.BOLD,
"@@MONO": S.MONO,
"@@MONO_BOLD": S.MONO_BOLD,
"@@BR": S.BR,
"@@BR_HALF": S.BR_HALF,
}
@cli.command()
@click.option("-i", "--icon", help="Header icon name")
@click.option("-c", "--color", help="Header icon color")
@click.option("-h", "--header", help="Header text", default="Showing text")
@click.argument("body")
@click.pass_obj
def show_text(connect, icon, color, header, body):
"""Show text on Trezor display.
For usage instructions, see:
https://github.com/trezor/trezor-firmware/blob/master/docs/python/show-text.md
"""
body = body.split()
body_text = []
words = []
def _flush():
if words:
body_text.append((None, " ".join(words)))
words.clear()
for word in body:
if word in STYLES:
_flush()
body_text.append((STYLES[word], None))
elif word.startswith("%%"):
_flush()
body_text.append((S.SET_COLOR, word[2:]))
else:
words.append(word)
_flush()
return debuglink.show_text(
connect(), header, body_text, icon=icon, icon_color=color
)

@ -33,6 +33,7 @@ from . import (
cardano,
cosi,
crypto,
debug,
device,
eos,
ethereum,
@ -59,6 +60,7 @@ COMMAND_ALIASES = {
"sd-protect": device.sd_protect,
"load-device": device.load,
"self-test": device.self_test,
"show-text": debug.show_text,
"get-entropy": crypto.get_entropy,
"encrypt-keyvalue": crypto.encrypt_keyvalue,
"decrypt-keyvalue": crypto.decrypt_keyvalue,
@ -296,7 +298,7 @@ cli.add_command(stellar.cli)
cli.add_command(tezos.cli)
cli.add_command(firmware.firmware_update)
cli.add_command(debug.cli)
#
# Main

@ -510,3 +510,18 @@ def self_test(client):
payload=b"\x00\xFF\x55\xAA\x66\x99\x33\xCCABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\x00\xFF\x55\xAA\x66\x99\x33\xCC"
)
)
@expect(proto.Success, field="message")
def show_text(client, header_text, body_text, icon=None, icon_color=None):
body_text = [
proto.DebugLinkShowTextItem(style=style, content=content)
for style, content in body_text
]
msg = proto.DebugLinkShowText(
header_text=header_text,
body_text=body_text,
header_icon=icon,
icon_color=icon_color,
)
return client.call(msg)

@ -0,0 +1,37 @@
# Automatically generated by pb2py
# fmt: off
from .. import protobuf as p
from .DebugLinkShowTextItem import DebugLinkShowTextItem
if __debug__:
try:
from typing import Dict, List # noqa: F401
from typing_extensions import Literal # noqa: F401
except ImportError:
pass
class DebugLinkShowText(p.MessageType):
MESSAGE_WIRE_TYPE = 9004
def __init__(
self,
header_text: str = None,
body_text: List[DebugLinkShowTextItem] = None,
header_icon: str = None,
icon_color: str = None,
) -> None:
self.header_text = header_text
self.body_text = body_text if body_text is not None else []
self.header_icon = header_icon
self.icon_color = icon_color
@classmethod
def get_fields(cls) -> Dict:
return {
1: ('header_text', p.UnicodeType, 0),
2: ('body_text', DebugLinkShowTextItem, p.FLAG_REPEATED),
3: ('header_icon', p.UnicodeType, 0),
4: ('icon_color', p.UnicodeType, 0),
}

@ -0,0 +1,29 @@
# Automatically generated by pb2py
# fmt: off
from .. import protobuf as p
if __debug__:
try:
from typing import Dict, List # noqa: F401
from typing_extensions import Literal # noqa: F401
EnumTypeDebugLinkShowTextStyle = Literal[0, 1, 2, 3, 4, 5, 6]
except ImportError:
pass
class DebugLinkShowTextItem(p.MessageType):
def __init__(
self,
style: EnumTypeDebugLinkShowTextStyle = None,
content: str = None,
) -> None:
self.style = style
self.content = content
@classmethod
def get_fields(cls) -> Dict:
return {
1: ('style', p.EnumType("DebugLinkShowTextStyle", (0, 1, 2, 3, 4, 5, 6)), 0),
2: ('content', p.UnicodeType, 0),
}

@ -0,0 +1,12 @@
# Automatically generated by pb2py
# fmt: off
if False:
from typing_extensions import Literal
NORMAL = 0 # type: Literal[0]
BOLD = 1 # type: Literal[1]
MONO = 2 # type: Literal[2]
MONO_BOLD = 3 # type: Literal[3]
BR = 4 # type: Literal[4]
BR_HALF = 5 # type: Literal[5]
SET_COLOR = 6 # type: Literal[6]

@ -74,6 +74,7 @@ DebugLinkFlashErase = 113 # type: Literal[113]
DebugLinkLayout = 9001 # type: Literal[9001]
DebugLinkReseedRandom = 9002 # type: Literal[9002]
DebugLinkRecordScreen = 9003 # type: Literal[9003]
DebugLinkShowText = 9004 # type: Literal[9004]
EthereumGetPublicKey = 450 # type: Literal[450]
EthereumPublicKey = 451 # type: Literal[451]
EthereumGetAddress = 56 # type: Literal[56]

@ -49,6 +49,8 @@ from .DebugLinkMemoryRead import DebugLinkMemoryRead
from .DebugLinkMemoryWrite import DebugLinkMemoryWrite
from .DebugLinkRecordScreen import DebugLinkRecordScreen
from .DebugLinkReseedRandom import DebugLinkReseedRandom
from .DebugLinkShowText import DebugLinkShowText
from .DebugLinkShowTextItem import DebugLinkShowTextItem
from .DebugLinkState import DebugLinkState
from .DebugLinkStop import DebugLinkStop
from .DebugMoneroDiagAck import DebugMoneroDiagAck
@ -270,6 +272,7 @@ from . import BinanceOrderType
from . import BinanceTimeInForce
from . import ButtonRequestType
from . import Capability
from . import DebugLinkShowTextStyle
from . import DebugSwipeDirection
from . import FailureType
from . import InputScriptType

Loading…
Cancel
Save