mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-30 03:18:20 +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:
|
from trezor.dispatcher import register
|
||||||
#
|
from trezor.messages.Initialize import Initialize
|
||||||
# boot() is called during device boot time and it should prepare
|
|
||||||
# all global things necessary to run.
|
|
||||||
#
|
def dispatch(message):
|
||||||
# dispatch() is called once event subscribed in boot() is received.
|
from .layout_homescreen import layout_homescreen
|
||||||
|
return layout_homescreen(message)
|
||||||
|
|
||||||
def dispatch():
|
|
||||||
# Callback for HID messages
|
|
||||||
print("Dispatch homescreen")
|
|
||||||
|
|
||||||
def boot():
|
def boot():
|
||||||
# Initilize app on boot time.
|
register(Initialize, dispatch)
|
||||||
# This should hookup HID message types dispatcher() wants to receive.
|
|
||||||
print("Boot homescreen")
|
|
||||||
|
@ -1,30 +1,45 @@
|
|||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.ui.swipe import Swipe
|
from trezor.ui.swipe import Swipe
|
||||||
from trezor import loop
|
from trezor.utils import unimport_gen
|
||||||
from trezor.res import loadres
|
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:
|
while True:
|
||||||
degrees = yield from Swipe().wait()
|
degrees = yield from Swipe(absolute=True).wait()
|
||||||
ui.display.orientation(degrees)
|
ui.display.orientation(degrees)
|
||||||
|
|
||||||
|
|
||||||
def layout_homescreen():
|
def animate_logo():
|
||||||
print("Homescreen layout!")
|
|
||||||
|
|
||||||
def func(foreground):
|
def func(foreground):
|
||||||
ui.display.icon(0, 0, loadres('apps/homescreen/res/trezor.toig'), foreground, ui.BLACK)
|
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([
|
@unimport_gen
|
||||||
orientation,
|
def layout_homescreen(initialize_msg=None):
|
||||||
animation,
|
if initialize_msg is not None:
|
||||||
timeout
|
from trezor.messages.Features import Features
|
||||||
])
|
features = Features()
|
||||||
|
features.revision = 'deadbeef'
|
||||||
from apps import playground
|
features.bootloader_hash = 'deadbeef'
|
||||||
return playground.layout_tap_to_confirm('1BitkeyP2nDd5oa64x7AjvBbbwST54W5Zmx2', 110.126967, 'BTC')
|
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
|
from trezor import msg
|
||||||
|
|
||||||
# Load all applications
|
# Load all applications
|
||||||
from apps import homescreen
|
|
||||||
from apps import playground
|
from apps import playground
|
||||||
# from apps import wallet
|
from apps import homescreen
|
||||||
|
from apps import wallet
|
||||||
|
|
||||||
# Initialize all applications
|
# Initialize all applications
|
||||||
homescreen.boot()
|
|
||||||
playground.boot()
|
playground.boot()
|
||||||
# wallet.boot()
|
homescreen.boot()
|
||||||
|
wallet.boot()
|
||||||
|
|
||||||
# just a demo to show how to register USB ifaces
|
# just a demo to show how to register USB ifaces
|
||||||
msg.setup( [ (1, 0xF53C), (2, 0xF1D0) ] )
|
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
|
import utime
|
||||||
|
|
||||||
from . import log
|
from . import log
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
_new_layout = None
|
|
||||||
_current_layout = None
|
class ChangeLayoutException(Exception):
|
||||||
|
|
||||||
|
def __init__(self, layout):
|
||||||
|
self.layout = layout
|
||||||
|
|
||||||
|
|
||||||
def change(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):
|
def set_main(main_layout):
|
||||||
global _new_layout
|
layout = main_layout()
|
||||||
global _current_layout
|
|
||||||
|
|
||||||
_current_layout = main_layout()
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
_current_layout = yield from _current_layout
|
layout = yield from layout
|
||||||
|
except ChangeLayoutException as e:
|
||||||
|
layout = e.layout
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sys.print_exception(e)
|
log.exception(__name__, e)
|
||||||
utime.sleep(1) # Don't produce wall of exceptions
|
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:
|
if not isinstance(layout, utils.type_gen):
|
||||||
log.info(__name__, "Switching to new layout %s", _new_layout)
|
log.info(__name__, 'Switching to main layout %s', main_layout)
|
||||||
_current_layout = _new_layout
|
layout = main_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()
|
|
||||||
else:
|
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.gens = gens
|
||||||
self.wait_for = wait_for
|
self.wait_for = wait_for
|
||||||
self.exit_others = exit_others
|
self.exit_others = exit_others
|
||||||
self.received = 0
|
self.scheduled = []
|
||||||
self.scheduled = None
|
self.finished = []
|
||||||
self.callback = None
|
self.callback = None
|
||||||
|
|
||||||
def handle(self, gen):
|
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
|
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):
|
def _wait(self, gen):
|
||||||
|
try:
|
||||||
if isinstance(gen, type_gen):
|
if isinstance(gen, type_gen):
|
||||||
result = yield from gen
|
result = yield from gen
|
||||||
else:
|
else:
|
||||||
result = yield gen
|
result = yield gen
|
||||||
|
except Exception as exc:
|
||||||
|
self._finish(gen, exc)
|
||||||
|
else:
|
||||||
self._finish(gen, result)
|
self._finish(gen, result)
|
||||||
|
|
||||||
def _finish(self, gen, result):
|
def _finish(self, gen, result):
|
||||||
self.received += 1
|
self.finished.append(gen)
|
||||||
if self.received == self.wait_for:
|
if self.wait_for == len(self.finished) or isinstance(result, Exception):
|
||||||
schedule(self.callback, (gen, result))
|
|
||||||
if self.exit_others:
|
if self.exit_others:
|
||||||
for g in self.scheduled:
|
self.exit()
|
||||||
if g is not gen and isinstance(g, type_gen):
|
schedule(self.callback, result)
|
||||||
unschedule(g)
|
self.callback = None
|
||||||
unblock(g)
|
|
||||||
g.close()
|
|
||||||
|
|
||||||
|
|
||||||
def run_forever():
|
def run_forever():
|
||||||
@ -140,6 +148,9 @@ def run_forever():
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if isinstance(data, Exception):
|
||||||
|
result = gen.throw(data)
|
||||||
|
else:
|
||||||
result = gen.send(data)
|
result = gen.send(data)
|
||||||
except StopIteration as e:
|
except StopIteration as e:
|
||||||
log.debug(__name__, '%s finished', gen)
|
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)
|
c = blend(ca, cb, y)
|
||||||
func(c)
|
func(c)
|
||||||
yield loop.Sleep(delay)
|
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 ui
|
||||||
from trezor import loop
|
from trezor import loop
|
||||||
|
|
||||||
@ -51,13 +51,12 @@ BTN_DIRTY = const(4)
|
|||||||
|
|
||||||
class Button():
|
class Button():
|
||||||
|
|
||||||
|
def __init__(self, area, text, normal_style=None, active_style=None, absolute=False):
|
||||||
|
|
||||||
def __init__(self, area, text, normal_style=None, active_style=None):
|
|
||||||
self.area = area
|
self.area = area
|
||||||
self.text = text
|
self.text = text
|
||||||
self.normal_style = normal_style or DEFAULT_BUTTON
|
self.normal_style = normal_style or DEFAULT_BUTTON
|
||||||
self.active_style = active_style or DEFAULT_BUTTON_ACTIVE
|
self.active_style = active_style or DEFAULT_BUTTON_ACTIVE
|
||||||
|
self.absolute = absolute
|
||||||
self.state = BTN_DIRTY
|
self.state = BTN_DIRTY
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
@ -77,6 +76,8 @@ class Button():
|
|||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
def send(self, event, pos):
|
def send(self, event, pos):
|
||||||
|
if not self.absolute:
|
||||||
|
pos = rotate_coords(pos)
|
||||||
if event is loop.TOUCH_START:
|
if event is loop.TOUCH_START:
|
||||||
if in_area(pos, self.area):
|
if in_area(pos, self.area):
|
||||||
self.state = BTN_STARTED | BTN_DIRTY | BTN_ACTIVE
|
self.state = BTN_STARTED | BTN_DIRTY | BTN_ACTIVE
|
||||||
@ -91,3 +92,13 @@ class Button():
|
|||||||
self.state = BTN_DIRTY
|
self.state = BTN_DIRTY
|
||||||
if in_area(pos, self.area):
|
if in_area(pos, self.area):
|
||||||
return BTN_CLICKED
|
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
|
import utime
|
||||||
|
|
||||||
from . import in_area
|
from . import in_area, rotate_coords
|
||||||
from trezor import loop
|
from trezor import loop
|
||||||
|
|
||||||
|
|
||||||
@ -16,8 +16,9 @@ SWIPE_RIGHT = const(270)
|
|||||||
|
|
||||||
class Swipe():
|
class Swipe():
|
||||||
|
|
||||||
def __init__(self, area=None):
|
def __init__(self, area=None, absolute=False):
|
||||||
self.area = area or (0, 0, 240, 240)
|
self.area = area or (0, 0, 240, 240)
|
||||||
|
self.absolute = absolute
|
||||||
self.start_pos = None
|
self.start_pos = None
|
||||||
self.start_time = 0
|
self.start_time = 0
|
||||||
self.end_pos = None
|
self.end_pos = None
|
||||||
@ -25,6 +26,9 @@ class Swipe():
|
|||||||
|
|
||||||
def send(self, event, pos):
|
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):
|
if event is loop.TOUCH_START and in_area(pos, self.area):
|
||||||
self.start_time = utime.time()
|
self.start_time = utime.time()
|
||||||
self.start_pos = pos
|
self.start_pos = pos
|
||||||
|
Loading…
Reference in New Issue
Block a user