mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-23 07:58:09 +00:00
add touch event rotation, msg dispatcher, wallet app
This commit is contained in:
parent
b34b8e6959
commit
3677b8142b
@ -1,15 +1,11 @@
|
||||
# Every application is supposed to have two entry points:
|
||||
#
|
||||
# boot() is called during device boot time and it should prepare
|
||||
# all global things necessary to run.
|
||||
#
|
||||
# dispatch() is called once event subscribed in boot() is received.
|
||||
from trezor.dispatcher import register
|
||||
from trezor.messages.Initialize import Initialize
|
||||
|
||||
|
||||
def dispatch(message):
|
||||
from .layout_homescreen import layout_homescreen
|
||||
return layout_homescreen(message)
|
||||
|
||||
def dispatch():
|
||||
# Callback for HID messages
|
||||
print("Dispatch homescreen")
|
||||
|
||||
def boot():
|
||||
# Initilize app on boot time.
|
||||
# This should hookup HID message types dispatcher() wants to receive.
|
||||
print("Boot homescreen")
|
||||
register(Initialize, dispatch)
|
||||
|
@ -1,30 +1,45 @@
|
||||
from trezor import ui
|
||||
from trezor.ui.swipe import Swipe
|
||||
from trezor import loop
|
||||
from trezor.utils import unimport_gen
|
||||
from trezor.res import loadres
|
||||
from trezor import dispatcher
|
||||
from trezor import loop
|
||||
from trezor import msg
|
||||
|
||||
|
||||
def swipe_to_change_orientation():
|
||||
def swipe_to_rotate():
|
||||
while True:
|
||||
degrees = yield from Swipe().wait()
|
||||
degrees = yield from Swipe(absolute=True).wait()
|
||||
ui.display.orientation(degrees)
|
||||
|
||||
|
||||
def layout_homescreen():
|
||||
print("Homescreen layout!")
|
||||
|
||||
def animate_logo():
|
||||
def func(foreground):
|
||||
ui.display.icon(0, 0, loadres('apps/homescreen/res/trezor.toig'), foreground, ui.BLACK)
|
||||
yield from ui.animate_pulse(func, ui.WHITE, ui.GREY, speed=400000)
|
||||
|
||||
orientation = swipe_to_change_orientation()
|
||||
animation = ui.animate_pulse(func, ui.WHITE, ui.GREY, speed=400000)
|
||||
timeout = loop.Sleep(5000 * 1000)
|
||||
|
||||
yield loop.Wait([
|
||||
orientation,
|
||||
animation,
|
||||
timeout
|
||||
])
|
||||
|
||||
from apps import playground
|
||||
return playground.layout_tap_to_confirm('1BitkeyP2nDd5oa64x7AjvBbbwST54W5Zmx2', 110.126967, 'BTC')
|
||||
@unimport_gen
|
||||
def layout_homescreen(initialize_msg=None):
|
||||
if initialize_msg is not None:
|
||||
from trezor.messages.Features import Features
|
||||
features = Features()
|
||||
features.revision = 'deadbeef'
|
||||
features.bootloader_hash = 'deadbeef'
|
||||
features.device_id = 'DEADBEEF'
|
||||
features.coins = []
|
||||
features.imported = False
|
||||
features.initialized = True
|
||||
features.label = 'My TREZOR'
|
||||
features.major_version = 2
|
||||
features.minor_version = 0
|
||||
features.patch_version = 0
|
||||
features.pin_cached = False
|
||||
features.pin_protection = True
|
||||
features.passphrase_cached = False
|
||||
features.passphrase_protection = False
|
||||
features.vendor = 'bitcointrezor.com'
|
||||
msg.write_msg(features)
|
||||
yield loop.Wait([dispatcher.dispatch(),
|
||||
swipe_to_rotate(),
|
||||
animate_logo()])
|
||||
|
12
src/apps/wallet/__init__.py
Normal file
12
src/apps/wallet/__init__.py
Normal file
@ -0,0 +1,12 @@
|
||||
from trezor.dispatcher import register
|
||||
from trezor.messages.GetPublicKey import GetPublicKey
|
||||
|
||||
|
||||
def dispatch(message):
|
||||
if message.message_type is GetPublicKey:
|
||||
from .layout_get_public_key import layout_get_public_key
|
||||
return layout_get_public_key(message)
|
||||
|
||||
|
||||
def boot():
|
||||
register(GetPublicKey, dispatch)
|
26
src/apps/wallet/layout_get_public_key.py
Normal file
26
src/apps/wallet/layout_get_public_key.py
Normal file
@ -0,0 +1,26 @@
|
||||
from trezor import msg
|
||||
from trezor import ui
|
||||
from trezor.ui.button import Button, CONFIRM_BUTTON, CONFIRM_BUTTON_ACTIVE
|
||||
from trezor.ui.pin import PinDialog
|
||||
from trezor.utils import unimport_gen
|
||||
|
||||
|
||||
@unimport_gen
|
||||
def layout_get_public_key(message):
|
||||
|
||||
confirm = Button((0, 0, 240, 240), 'Export public key?',
|
||||
normal_style=CONFIRM_BUTTON,
|
||||
active_style=CONFIRM_BUTTON_ACTIVE)
|
||||
yield from confirm.wait()
|
||||
|
||||
from trezor.messages.PublicKey import PublicKey
|
||||
from trezor.messages.HDNodeType import HDNodeType
|
||||
|
||||
pubkey = PublicKey()
|
||||
pubkey.node = HDNodeType()
|
||||
pubkey.node.depth = 0
|
||||
pubkey.node.child_num = 0
|
||||
pubkey.node.fingerprint = 0
|
||||
pubkey.node.chain_code = 'deadbeef'
|
||||
pubkey.node.public_key = 'deadbeef'
|
||||
msg.write_msg(pubkey)
|
@ -2,14 +2,14 @@ import trezor.main
|
||||
from trezor import msg
|
||||
|
||||
# Load all applications
|
||||
from apps import homescreen
|
||||
from apps import playground
|
||||
# from apps import wallet
|
||||
from apps import homescreen
|
||||
from apps import wallet
|
||||
|
||||
# Initialize all applications
|
||||
homescreen.boot()
|
||||
playground.boot()
|
||||
# wallet.boot()
|
||||
homescreen.boot()
|
||||
wallet.boot()
|
||||
|
||||
# just a demo to show how to register USB ifaces
|
||||
msg.setup( [ (1, 0xF53C), (2, 0xF1D0) ] )
|
||||
|
20
src/trezor/dispatcher.py
Normal file
20
src/trezor/dispatcher.py
Normal file
@ -0,0 +1,20 @@
|
||||
from . import msg
|
||||
from . import layout
|
||||
|
||||
|
||||
message_handlers = {}
|
||||
|
||||
|
||||
def register(message_type, handler):
|
||||
message_handlers[message_type] = handler
|
||||
|
||||
|
||||
def unregister(message_type):
|
||||
del message_handlers[message_type]
|
||||
|
||||
|
||||
def dispatch():
|
||||
mtypes = message_handlers.keys()
|
||||
message = yield from msg.read_msg(*mtypes)
|
||||
handler = message_handlers[message.message_type]
|
||||
layout.change(handler(message))
|
@ -1,44 +1,33 @@
|
||||
import sys
|
||||
import utime
|
||||
|
||||
from . import log
|
||||
from . import utils
|
||||
|
||||
_new_layout = None
|
||||
_current_layout = None
|
||||
|
||||
class ChangeLayoutException(Exception):
|
||||
|
||||
def __init__(self, layout):
|
||||
self.layout = layout
|
||||
|
||||
|
||||
def change(layout):
|
||||
global _new_layout
|
||||
raise ChangeLayoutException(layout)
|
||||
|
||||
log.debug(__name__, "Changing layout to %s", layout)
|
||||
_new_layout = layout
|
||||
|
||||
yield _current_layout.throw(StopIteration())
|
||||
|
||||
def set_main(main_layout):
|
||||
global _new_layout
|
||||
global _current_layout
|
||||
layout = main_layout()
|
||||
|
||||
_current_layout = main_layout()
|
||||
while True:
|
||||
try:
|
||||
_current_layout = yield from _current_layout
|
||||
layout = yield from layout
|
||||
except ChangeLayoutException as e:
|
||||
layout = e.layout
|
||||
except Exception as e:
|
||||
sys.print_exception(e)
|
||||
log.exception(__name__, e)
|
||||
utime.sleep(1) # Don't produce wall of exceptions
|
||||
# if _current_layout == main_layout:
|
||||
# # Main layout thrown exception, what to do?
|
||||
# sys.exit()
|
||||
_current_layout = main_layout()
|
||||
continue
|
||||
|
||||
if _new_layout != None:
|
||||
log.info(__name__, "Switching to new layout %s", _new_layout)
|
||||
_current_layout = _new_layout
|
||||
_new_layout = None
|
||||
|
||||
elif type(_current_layout) != utils.type_gen:
|
||||
log.info(__name__, "Switching to main layout %s", main_layout)
|
||||
_current_layout = main_layout()
|
||||
if not isinstance(layout, utils.type_gen):
|
||||
log.info(__name__, 'Switching to main layout %s', main_layout)
|
||||
layout = main_layout()
|
||||
else:
|
||||
log.info(__name__, "Switching to proposed layout %s", _current_layout)
|
||||
log.info(__name__, 'Switching to proposed layout %s', layout)
|
||||
|
@ -73,31 +73,39 @@ class Wait():
|
||||
self.gens = gens
|
||||
self.wait_for = wait_for
|
||||
self.exit_others = exit_others
|
||||
self.received = 0
|
||||
self.scheduled = None
|
||||
self.scheduled = []
|
||||
self.finished = []
|
||||
self.callback = None
|
||||
|
||||
def handle(self, gen):
|
||||
self.scheduled = [schedule(self._wait(g)) for g in self.gens]
|
||||
self.scheduled = [schedule(self._wait(gen)) for gen in self.gens]
|
||||
self.callback = gen
|
||||
|
||||
def exit(self):
|
||||
for gen in self.scheduled:
|
||||
if gen not in self.finished and isinstance(gen, type_gen):
|
||||
unschedule(gen)
|
||||
unblock(gen)
|
||||
gen.close()
|
||||
|
||||
def _wait(self, gen):
|
||||
try:
|
||||
if isinstance(gen, type_gen):
|
||||
result = yield from gen
|
||||
else:
|
||||
result = yield gen
|
||||
except Exception as exc:
|
||||
self._finish(gen, exc)
|
||||
else:
|
||||
self._finish(gen, result)
|
||||
|
||||
def _finish(self, gen, result):
|
||||
self.received += 1
|
||||
if self.received == self.wait_for:
|
||||
schedule(self.callback, (gen, result))
|
||||
self.finished.append(gen)
|
||||
if self.wait_for == len(self.finished) or isinstance(result, Exception):
|
||||
if self.exit_others:
|
||||
for g in self.scheduled:
|
||||
if g is not gen and isinstance(g, type_gen):
|
||||
unschedule(g)
|
||||
unblock(g)
|
||||
g.close()
|
||||
self.exit()
|
||||
schedule(self.callback, result)
|
||||
self.callback = None
|
||||
|
||||
|
||||
def run_forever():
|
||||
@ -140,6 +148,9 @@ def run_forever():
|
||||
continue
|
||||
|
||||
try:
|
||||
if isinstance(data, Exception):
|
||||
result = gen.throw(data)
|
||||
else:
|
||||
result = gen.send(data)
|
||||
except StopIteration as e:
|
||||
log.debug(__name__, '%s finished', gen)
|
||||
|
@ -62,3 +62,16 @@ def animate_pulse(func, ca, cb, speed=200000, delay=30000):
|
||||
c = blend(ca, cb, y)
|
||||
func(c)
|
||||
yield loop.Sleep(delay)
|
||||
|
||||
|
||||
def rotate_coords(pos: tuple) -> tuple:
|
||||
r = display.orientation()
|
||||
if r == 0:
|
||||
return pos
|
||||
x, y = pos
|
||||
if r == 90:
|
||||
return (240 - y, x)
|
||||
if r == 180:
|
||||
return (240 - x, 240 - y)
|
||||
if r == 270:
|
||||
return (y, 240 - x)
|
||||
|
@ -1,4 +1,4 @@
|
||||
from . import display, in_area
|
||||
from . import display, in_area, rotate_coords
|
||||
from trezor import ui
|
||||
from trezor import loop
|
||||
|
||||
@ -51,13 +51,12 @@ BTN_DIRTY = const(4)
|
||||
|
||||
class Button():
|
||||
|
||||
|
||||
|
||||
def __init__(self, area, text, normal_style=None, active_style=None):
|
||||
def __init__(self, area, text, normal_style=None, active_style=None, absolute=False):
|
||||
self.area = area
|
||||
self.text = text
|
||||
self.normal_style = normal_style or DEFAULT_BUTTON
|
||||
self.active_style = active_style or DEFAULT_BUTTON_ACTIVE
|
||||
self.absolute = absolute
|
||||
self.state = BTN_DIRTY
|
||||
|
||||
def render(self):
|
||||
@ -77,6 +76,8 @@ class Button():
|
||||
self.state = state
|
||||
|
||||
def send(self, event, pos):
|
||||
if not self.absolute:
|
||||
pos = rotate_coords(pos)
|
||||
if event is loop.TOUCH_START:
|
||||
if in_area(pos, self.area):
|
||||
self.state = BTN_STARTED | BTN_DIRTY | BTN_ACTIVE
|
||||
@ -91,3 +92,13 @@ class Button():
|
||||
self.state = BTN_DIRTY
|
||||
if in_area(pos, self.area):
|
||||
return BTN_CLICKED
|
||||
|
||||
def wait(self):
|
||||
while True:
|
||||
self.render()
|
||||
event, *pos = yield loop.Select(loop.TOUCH_START,
|
||||
loop.TOUCH_MOVE,
|
||||
loop.TOUCH_END)
|
||||
result = self.send(event, pos)
|
||||
if result is not None:
|
||||
return result
|
||||
|
@ -1,6 +1,6 @@
|
||||
import utime
|
||||
|
||||
from . import in_area
|
||||
from . import in_area, rotate_coords
|
||||
from trezor import loop
|
||||
|
||||
|
||||
@ -16,8 +16,9 @@ SWIPE_RIGHT = const(270)
|
||||
|
||||
class Swipe():
|
||||
|
||||
def __init__(self, area=None):
|
||||
def __init__(self, area=None, absolute=False):
|
||||
self.area = area or (0, 0, 240, 240)
|
||||
self.absolute = absolute
|
||||
self.start_pos = None
|
||||
self.start_time = 0
|
||||
self.end_pos = None
|
||||
@ -25,6 +26,9 @@ class Swipe():
|
||||
|
||||
def send(self, event, pos):
|
||||
|
||||
if not self.absolute:
|
||||
pos = rotate_coords(pos)
|
||||
|
||||
if event is loop.TOUCH_START and in_area(pos, self.area):
|
||||
self.start_time = utime.time()
|
||||
self.start_pos = pos
|
||||
|
Loading…
Reference in New Issue
Block a user