diff --git a/src/apps/homescreen/__init__.py b/src/apps/homescreen/__init__.py index 678a799dc7..9c6519d6f5 100644 --- a/src/apps/homescreen/__init__.py +++ b/src/apps/homescreen/__init__.py @@ -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) diff --git a/src/apps/homescreen/layout_homescreen.py b/src/apps/homescreen/layout_homescreen.py index f2add3cf1c..773353b325 100644 --- a/src/apps/homescreen/layout_homescreen.py +++ b/src/apps/homescreen/layout_homescreen.py @@ -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()]) diff --git a/src/apps/wallet/__init__.py b/src/apps/wallet/__init__.py new file mode 100644 index 0000000000..7947ab585f --- /dev/null +++ b/src/apps/wallet/__init__.py @@ -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) diff --git a/src/apps/wallet/layout_get_public_key.py b/src/apps/wallet/layout_get_public_key.py new file mode 100644 index 0000000000..ef799a26c6 --- /dev/null +++ b/src/apps/wallet/layout_get_public_key.py @@ -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) diff --git a/src/main.py b/src/main.py index 0bd3b8d3f4..64b8cbb3cb 100644 --- a/src/main.py +++ b/src/main.py @@ -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) ] ) diff --git a/src/trezor/dispatcher.py b/src/trezor/dispatcher.py new file mode 100644 index 0000000000..e7ede09a6f --- /dev/null +++ b/src/trezor/dispatcher.py @@ -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)) diff --git a/src/trezor/layout.py b/src/trezor/layout.py index 0d5c6c1226..e549225048 100644 --- a/src/trezor/layout.py +++ b/src/trezor/layout.py @@ -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) diff --git a/src/trezor/loop.py b/src/trezor/loop.py index cc6f2c6317..daed72441a 100644 --- a/src/trezor/loop.py +++ b/src/trezor/loop.py @@ -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): - if isinstance(gen, type_gen): - result = yield from gen + try: + if isinstance(gen, type_gen): + result = yield from gen + else: + result = yield gen + except Exception as exc: + self._finish(gen, exc) else: - result = yield gen - self._finish(gen, result) + 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,7 +148,10 @@ def run_forever(): continue try: - result = gen.send(data) + if isinstance(data, Exception): + result = gen.throw(data) + else: + result = gen.send(data) except StopIteration as e: log.debug(__name__, '%s finished', gen) continue diff --git a/src/trezor/ui/__init__.py b/src/trezor/ui/__init__.py index cddf161d21..79f6a609b5 100644 --- a/src/trezor/ui/__init__.py +++ b/src/trezor/ui/__init__.py @@ -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) diff --git a/src/trezor/ui/button.py b/src/trezor/ui/button.py index 937b67ebfd..ab2c0968ce 100644 --- a/src/trezor/ui/button.py +++ b/src/trezor/ui/button.py @@ -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 diff --git a/src/trezor/ui/swipe.py b/src/trezor/ui/swipe.py index e362fe3ba7..d8eac86dd5 100644 --- a/src/trezor/ui/swipe.py +++ b/src/trezor/ui/swipe.py @@ -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