From 49cc4651c27e1c6323d4dab5359286bc4b174d8b Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Sat, 16 Sep 2017 15:00:31 +0200 Subject: [PATCH] bunch of api cleanups --- src/apps/common/confirm.py | 17 ++- src/apps/common/storage.py | 1 + src/apps/fido_u2f/__init__.py | 4 +- src/apps/homescreen/homescreen.py | 6 +- src/main.py | 2 - src/protobuf.py | 20 +-- src/trezor/crypto/hmac.py | 21 ++-- src/trezor/loop.py | 191 ++++++++++++++--------------- src/trezor/ui/__init__.py | 54 ++++---- src/trezor/ui/button.py | 24 ++-- src/trezor/ui/confirm.py | 4 +- src/trezor/ui/keyboard.py | 14 +-- src/trezor/ui/scroll.py | 25 ++-- src/trezor/ui/swipe.py | 12 +- src/trezor/wire/__init__.py | 2 +- src/trezor/wire/codec_v1.py | 9 +- src/trezor/wire/codec_v2.py | 15 +-- src/trezor/workflow.py | 2 +- tests/test_trezor.wire.codec_v1.py | 15 +-- tests/test_trezor.wire.codec_v2.py | 15 +-- 20 files changed, 235 insertions(+), 218 deletions(-) diff --git a/src/apps/common/confirm.py b/src/apps/common/confirm.py index aeeed28b6e..46538ecbc0 100644 --- a/src/apps/common/confirm.py +++ b/src/apps/common/confirm.py @@ -3,7 +3,8 @@ from trezor.utils import unimport # used to confirm/cancel the dialogs from outside of this module (i.e. # through debug link) -signal = loop.Signal() +if __debug__: + signal = loop.signal() @unimport @@ -20,7 +21,12 @@ async def confirm(ctx, content, code=None, *args, **kwargs): if code is None: code = Other await ctx.call(ButtonRequest(code=code), ButtonAck) - return await loop.Wait((signal, dialog)) == CONFIRMED + + if __debug__: + waiter = loop.wait(signal, dialog) + else: + waiter = dialog + return await waiter == CONFIRMED @unimport @@ -37,7 +43,12 @@ async def hold_to_confirm(ctx, content, code=None, *args, **kwargs): if code is None: code = Other await ctx.call(ButtonRequest(code=code), ButtonAck) - return await loop.Wait((signal, dialog)) == CONFIRMED + + if __debug__: + waiter = loop.wait(signal, dialog) + else: + waiter = dialog + return await waiter == CONFIRMED @unimport diff --git a/src/apps/common/storage.py b/src/apps/common/storage.py index 00909be2ff..cc911306e6 100644 --- a/src/apps/common/storage.py +++ b/src/apps/common/storage.py @@ -1,4 +1,5 @@ from micropython import const + import ustruct import utime diff --git a/src/apps/fido_u2f/__init__.py b/src/apps/fido_u2f/__init__.py index 614bcc7fc5..9bb27c5c33 100644 --- a/src/apps/fido_u2f/__init__.py +++ b/src/apps/fido_u2f/__init__.py @@ -312,7 +312,7 @@ def send_cmd(cmd: Cmd, iface: io.HID) -> None: def boot(iface: io.HID): - loop.schedule_task(handle_reports(iface)) + loop.schedule(handle_reports(iface)) async def handle_reports(iface: io.HID): @@ -446,7 +446,7 @@ class ConfirmState: self.deadline_ms = utime.ticks_ms() + _CONFIRM_STATE_TIMEOUT_MS self.task = self.confirm() workflow.onstart(self.task) - loop.schedule_task(self.task) + loop.schedule(self.task) def kill(self) -> None: if self.task is not None: diff --git a/src/apps/homescreen/homescreen.py b/src/apps/homescreen/homescreen.py index 0f4360ec42..7c99242e39 100644 --- a/src/apps/homescreen/homescreen.py +++ b/src/apps/homescreen/homescreen.py @@ -10,10 +10,10 @@ async def swipe_to_rotate(): async def dim_screen(): - await loop.Sleep(5 * 1000000) + await loop.sleep(5 * 1000000) await ui.backlight_slide(ui.BACKLIGHT_DIM) while True: - await loop.Sleep(1000000) + await loop.sleep(1000000) def display_homescreen(): @@ -35,4 +35,4 @@ def display_homescreen(): async def layout_homescreen(): while True: display_homescreen() - await loop.Wait([swipe_to_rotate(), dim_screen()]) + await loop.wait(swipe_to_rotate(), dim_screen()) diff --git a/src/main.py b/src/main.py index de7e51b579..55a3f03d30 100644 --- a/src/main.py +++ b/src/main.py @@ -1,5 +1,3 @@ -from micropython import const - from trezor import config from trezor import io from trezor import log diff --git a/src/protobuf.py b/src/protobuf.py index 8174874954..84ddf35c86 100644 --- a/src/protobuf.py +++ b/src/protobuf.py @@ -5,20 +5,20 @@ bytes, string, embedded message and repeated fields. For de-sererializing (loading) protobuf types, object with `AsyncReader` interface is required: - class AsyncReader: - async def areadinto(self, buffer): - """ - Reads `len(buffer)` bytes into `buffer`, or raises `EOFError`. - """ +>>> class AsyncReader: +>>> async def areadinto(self, buffer): +>>> """ +>>> Reads `len(buffer)` bytes into `buffer`, or raises `EOFError`. +>>> """ For serializing (dumping) protobuf types, object with `AsyncWriter` interface is required: - class AsyncWriter: - async def awrite(self, buffer): - """ - Writes all bytes from `buffer`, or raises `EOFError`. - """ +>>> class AsyncWriter: +>>> async def awrite(self, buffer): +>>> """ +>>> Writes all bytes from `buffer`, or raises `EOFError`. +>>> """ ''' from micropython import const diff --git a/src/trezor/crypto/hmac.py b/src/trezor/crypto/hmac.py index 7722e00f43..005013f3fa 100644 --- a/src/trezor/crypto/hmac.py +++ b/src/trezor/crypto/hmac.py @@ -2,14 +2,15 @@ class Hmac: def __init__(self, key, msg, digestmod): - self.__digestmod = digestmod - self.__inner = digestmod() - self.digest_size = self.__inner.digest_size - self.block_size = self.__inner.block_size + self.digestmod = digestmod + self.inner = digestmod() + self.digest_size = self.inner.digest_size + self.block_size = self.inner.block_size + if len(key) > self.block_size: key = digestmod(key).digest() - self.__key = key + bytes(self.block_size - len(key)) - self.__inner.update(bytes((x ^ 0x36) for x in self.__key)) + self.key = key + bytes(self.block_size - len(key)) + self.inner.update(bytes((x ^ 0x36) for x in self.key)) if msg is not None: self.update(msg) @@ -17,15 +18,15 @@ class Hmac: ''' Update the context with data. ''' - self.__inner.update(msg) + self.inner.update(msg) def digest(self) -> bytes: ''' Returns the digest of processed data. ''' - outer = self.__digestmod() - outer.update(bytes((x ^ 0x5C) for x in self.__key)) - outer.update(self.__inner.digest()) + outer = self.digestmod() + outer.update(bytes((x ^ 0x5C) for x in self.key)) + outer.update(self.inner.digest()) return outer.digest() diff --git a/src/trezor/loop.py b/src/trezor/loop.py index 75ee4357b7..69ea4784b7 100644 --- a/src/trezor/loop.py +++ b/src/trezor/loop.py @@ -4,8 +4,7 @@ the form of python coroutines (either plain generators or `async` functions) are stepped through until completion, and can get asynchronously blocked by `yield`ing or `await`ing a syscall. -See `schedule_task`, `run`, and syscalls `Sleep`, `Select`, `Signal` -and `Wait`. +See `schedule`, `run`, and syscalls `sleep`, `select`, `signal` and `wait`. ''' import utime @@ -14,21 +13,11 @@ from micropython import const from trezor import log from trezor import io -TOUCH = io.TOUCH -TOUCH_START = io.TOUCH_START -TOUCH_MOVE = io.TOUCH_MOVE -TOUCH_END = io.TOUCH_END - -READ = io.POLL_READ -WRITE = io.POLL_WRITE - after_step_hook = None # function, called after each task step -_MAX_SELECT_DELAY = const(1000000) # usec delay if queue is empty -_MAX_QUEUE_SIZE = const(64) # maximum number of scheduled tasks - -_paused_tasks = {} # {message interface: [task]} -_scheduled_tasks = utimeq.utimeq(_MAX_QUEUE_SIZE) +_QUEUE_SIZE = const(64) # maximum number of scheduled tasks +_queue = utimeq.utimeq(_QUEUE_SIZE) +_paused = {} if __debug__: # for performance stats @@ -38,47 +27,48 @@ if __debug__: log_delay_rb = array.array('i', [0] * log_delay_rb_len) -def schedule_task(task, value=None, deadline=None): +def schedule(task, value=None, deadline=None): ''' Schedule task to be executed with `value` on given `deadline` (in microseconds). Does not start the event loop itself, see `run`. ''' if deadline is None: deadline = utime.ticks_us() - _scheduled_tasks.push(deadline, task, value) + _queue.push(deadline, task, value) -def unschedule_task(task): +def unschedule(task): ''' - Remove task from the time queue. Cancels previous `schedule_task`. + Remove task from the time queue. Cancels previous `schedule`. ''' - global _scheduled_tasks + global _queue task_entry = [0, 0, 0] # deadline, task, value - queue_copy = utimeq.utimeq(_MAX_QUEUE_SIZE) - while _scheduled_tasks: - _scheduled_tasks.pop(task_entry) - if task_entry[1] is not task: - queue_copy.push(task_entry[0], task_entry[1], task_entry[2]) - _scheduled_tasks = queue_copy + queue_copy = utimeq.utimeq(_QUEUE_SIZE) + while _queue: + _queue.pop(task_entry) + t, v, d = task_entry + if t is not task: + queue_copy.push(t, v, d) + _queue = queue_copy -def _pause_task(task, iface): - tasks = _paused_tasks.get(iface, None) +def pause(task, iface): + tasks = _paused.get(iface, None) if tasks is None: - tasks = _paused_tasks[iface] = [] + tasks = _paused[iface] = [] tasks.append(task) -def _unpause_task(task): - for iface in _paused_tasks: - if task in _paused_tasks[iface]: - _paused_tasks[iface].remove(task) +def unpause(task): + for iface in _paused: + if task in _paused[iface]: + _paused[iface].remove(task) def run(): ''' Loop forever, stepping through scheduled tasks and awaiting I/O events - inbetween. Use `schedule_task` first to add a coroutine to the task queue. + inbetween. Use `schedule` first to add a coroutine to the task queue. Tasks yield back to the scheduler on any I/O, usually by calling `await` on a `Syscall`. ''' @@ -86,34 +76,35 @@ def run(): if __debug__: global log_delay_pos + max_delay = const(1000000) # usec delay if queue is empty + task_entry = [0, 0, 0] # deadline, task, value msg_entry = [0, 0] # iface | flags, value while True: # compute the maximum amount of time we can wait for a message - if _scheduled_tasks: - delay = utime.ticks_diff( - _scheduled_tasks.peektime(), utime.ticks_us()) + if _queue: + delay = utime.ticks_diff(_queue.peektime(), utime.ticks_us()) else: - delay = _MAX_SELECT_DELAY + delay = max_delay if __debug__: # add current delay to ring buffer for performance stats log_delay_rb[log_delay_pos] = delay log_delay_pos = (log_delay_pos + 1) % log_delay_rb_len - if io.poll(_paused_tasks, msg_entry, delay): + if io.poll(_paused, msg_entry, delay): # message received, run tasks paused on the interface - msg_tasks = _paused_tasks.pop(msg_entry[0], ()) + msg_tasks = _paused.pop(msg_entry[0], ()) for task in msg_tasks: - _step_task(task, msg_entry[1]) + _step(task, msg_entry[1]) else: # timeout occurred, run the first scheduled task - if _scheduled_tasks: - _scheduled_tasks.pop(task_entry) - _step_task(task_entry[1], task_entry[2]) + if _queue: + _queue.pop(task_entry) + _step(task_entry[1], task_entry[2]) -def _step_task(task, value): +def _step(task, value): try: if isinstance(value, Exception): result = task.throw(value) @@ -129,7 +120,7 @@ def _step_task(task, value): if isinstance(result, Syscall): result.handle(task) elif result is None: - schedule_task(task) + schedule(task) else: if __debug__: log.error(__name__, 'unknown syscall: %s', result) @@ -148,15 +139,16 @@ class Syscall: return (yield self) -class Sleep(Syscall): +class sleep(Syscall): ''' Pause current task and resume it after given delay. Although the delay is given in microseconds, sub-millisecond precision is not guaranteed. Result value is the calculated deadline. Example: - planned = await loop.Sleep(1000 * 1000) # sleep for 1ms - print('missed by %d us', utime.ticks_diff(utime.ticks_us(), planned)) + + >>> planned = await loop.sleep(1000 * 1000) # sleep for 1ms + >>> print('missed by %d us', utime.ticks_diff(utime.ticks_us(), planned)) ''' def __init__(self, delay_us): @@ -164,43 +156,45 @@ class Sleep(Syscall): def handle(self, task): deadline = utime.ticks_add(utime.ticks_us(), self.delay_us) - schedule_task(task, deadline, deadline) + schedule(task, deadline, deadline) -class Select(Syscall): +class select(Syscall): ''' Pause current task, and resume only after a message on `msg_iface` is received. Messages are received either from an USB interface, or the touch display. Result value a tuple of message values. Example: - hid_report, = await loop.Select(0xABCD) # await USB HID report - event, x, y = await loop.Select(loop.TOUCH) # await touch event + + >>> hid_report, = await loop.select(0xABCD) # await USB HID report + >>> event, x, y = await loop.select(io.TOUCH) # await touch event ''' def __init__(self, msg_iface): self.msg_iface = msg_iface def handle(self, task): - _pause_task(task, self.msg_iface) + pause(task, self.msg_iface) _NO_VALUE = () -class Signal(Syscall): +class signal(Syscall): ''' Pause current task, and let other running task to resume it later with a result value or an exception. Example: - # in task #1: - signal = loop.Signal() - result = await signal - print('awaited result:', result) - # in task #2: - signal.send('hello from task #2') - # prints in the next iteration of the event loop + + >>> # in task #1: + >>> signal = loop.signal() + >>> result = await signal + >>> print('awaited result:', result) + >>> # in task #2: + >>> signal.send('hello from task #2') + >>> # prints in the next iteration of the event loop ''' def __init__(self): @@ -217,36 +211,37 @@ class Signal(Syscall): def _deliver(self): if self.task is not None and self.value is not _NO_VALUE: - schedule_task(self.task, self.value) + schedule(self.task, self.value) self.task = None self.value = _NO_VALUE -class Wait(Syscall): +class wait(Syscall): ''' Execute one or more children tasks and wait until one or more of them exit. - Return value of `Wait` is the return value of task that triggered the - completion. By default, `Wait` returns after the first child completes, and + Return value of `wait` is the return value of task that triggered the + completion. By default, `wait` returns after the first child completes, and other running children are killed (by cancelling any pending schedules and calling `close()`). Example: - # async def wait_for_touch(): ... - # async def animate_logo(): ... - touch_task = wait_for_touch() - animation_task = animate_logo() - waiter = loop.Wait((touch_task, animation_task)) - result = await waiter - if animation_task in waiter.finished: - print('animation task returned', result) - else: - print('touch task returned', result) - Note: You should not directly `yield` a `Wait` instance, see logic in - `Wait.__iter__` for explanation. Always use `await`. + >>> # async def wait_for_touch(): ... + >>> # async def animate_logo(): ... + >>> touch_task = wait_for_touch() + >>> animation_task = animate_logo() + >>> waiter = loop.wait(touch_task, animation_task) + >>> result = await waiter + >>> if animation_task in waiter.finished: + >>> print('animation task returned', result) + >>> else: + >>> print('touch task returned', result) + + Note: You should not directly `yield` a `wait` instance, see logic in + `wait.__iter__` for explanation. Always use `await`. ''' - def __init__(self, children, wait_for=1, exit_others=True): + def __init__(self, *children, wait_for=1, exit_others=True): self.children = children self.wait_for = wait_for self.exit_others = exit_others @@ -259,13 +254,13 @@ class Wait(Syscall): self.finished = [] self.scheduled = [self._wait(c) for c in self.children] for ct in self.scheduled: - schedule_task(ct) + schedule(ct) def exit(self): for task in self.scheduled: if task not in self.finished: - _unpause_task(task) - unschedule_task(task) + unpause(task) + unschedule(task) task.close() async def _wait(self, child): @@ -281,7 +276,7 @@ class Wait(Syscall): if self.wait_for == len(self.finished) or isinstance(result, Exception): if self.exit_others: self.exit() - schedule_task(self.callback, result) + schedule(self.callback, result) def __iter__(self): try: @@ -293,10 +288,10 @@ class Wait(Syscall): raise -class Put(Syscall): +class put(Syscall): - def __init__(self, chan, value=None): - self.chan = chan + def __init__(self, ch, value=None): + self.ch = ch self.value = value def __call__(self, value): @@ -304,30 +299,30 @@ class Put(Syscall): return self def handle(self, task): - self.chan.schedule_put(schedule_task, task, self.value) + self.ch.schedule_put(schedule, task, self.value) -class Take(Syscall): +class take(Syscall): - def __init__(self, chan): - self.chan = chan + def __init__(self, ch): + self.ch = ch def __call__(self): return self def handle(self, task): - if self.chan.schedule_take(schedule_task, task) and self.chan.id is not None: - _pause_task(self.chan, self.chan.id) + if self.ch.schedule_take(schedule, task) and self.ch.id is not None: + pause(self.ch, self.ch.id) -class Chan: +class chan: def __init__(self, id=None): self.id = id self.putters = [] self.takers = [] - self.put = Put(self) - self.take = Take(self) + self.put = put(self) + self.take = take(self) def schedule_publish(self, schedule, value): if self.takers: @@ -357,9 +352,3 @@ class Chan: else: self.takers.append(taker) return False - - -select = Select -sleep = Sleep -wait = Wait -signal = Signal diff --git a/src/trezor/ui/__init__.py b/src/trezor/ui/__init__.py index e3fffa5605..dfa7384d95 100644 --- a/src/trezor/ui/__init__.py +++ b/src/trezor/ui/__init__.py @@ -5,11 +5,14 @@ import math import utime from trezorui import Display -from trezor import loop, res + +from trezor import io +from trezor import loop +from trezor import res display = Display() -if sys.platform not in ('trezor', 'pyboard'): # stmhal +if sys.platform != 'trezor': loop.after_step_hook = display.refresh @@ -72,7 +75,7 @@ ICON_WIPE = 'trezor/res/header_icons/wipe.toig' ICON_RECOVERY = 'trezor/res/header_icons/recovery.toig' -def in_area(pos: tuple, area: tuple) -> bool: +def contains(pos: tuple, area: tuple) -> bool: x, y = pos ax, ay, aw, ah = area return ax <= x <= ax + aw and ay <= y <= ay + ah @@ -88,42 +91,35 @@ def blend(ca: int, cb: int, t: float) -> int: lerpi((ca << 3) & 0xF8, (cb << 3) & 0xF8, t)) +def pulse(delay): + while True: + # normalize sin from interval -1:1 to 0:1 + yield 0.5 + 0.5 * math.sin(utime.ticks_us() / delay) + + async def alert(count=3): + short_sleep = loop.sleep(20000) + long_sleep = loop.sleep(80000) current = display.backlight() for i in range(count * 2): if i % 2 == 0: display.backlight(BACKLIGHT_MAX) - yield loop.Sleep(20000) + yield short_sleep else: display.backlight(BACKLIGHT_NORMAL) - yield loop.Sleep(80000) + yield long_sleep display.backlight(current) -async def backlight_slide(val, speed=20000): +async def backlight_slide(val, delay=20000): + sleep = loop.sleep(delay) current = display.backlight() for i in range(current, val, -1 if current > val else 1): display.backlight(i) - await loop.Sleep(speed) + await sleep -def animate_pulse(func, ca, cb, speed=200000, delay=30000): - while True: - # normalize sin from interval -1:1 to 0:1 - y = 0.5 + 0.5 * math.sin(utime.ticks_us() / speed) - c = blend(ca, cb, y) - func(c) - yield loop.Sleep(delay) - - -def header(title, icon=ICON_RESET, fg=BLACK, bg=BLACK): - display.bar(0, 0, 240, 32, bg) - if icon is not None: - display.icon(8, 4, res.load(icon), fg, bg) - display.text(8 + 24 + 2, 24, title, BOLD, fg, bg) - - -def rotate_coords(pos: tuple) -> tuple: +def rotate(pos: tuple) -> tuple: r = display.orientation() if r == 0: return pos @@ -136,6 +132,13 @@ def rotate_coords(pos: tuple) -> tuple: return (240 - y, x) +def header(title, icon=ICON_RESET, fg=BLACK, bg=BLACK): + display.bar(0, 0, 240, 32, bg) + if icon is not None: + display.icon(8, 4, res.load(icon), fg, bg) + display.text(8 + 24 + 2, 24, title, BOLD, fg, bg) + + class Widget: def render(self): @@ -145,9 +148,10 @@ class Widget: pass def __iter__(self): + touch = loop.select(io.TOUCH) while True: self.render() - event, *pos = yield loop.Select(loop.TOUCH) + event, *pos = yield touch result = self.touch(event, pos) if result is not None: return result diff --git a/src/trezor/ui/button.py b/src/trezor/ui/button.py index e909e39c39..0635627ac8 100644 --- a/src/trezor/ui/button.py +++ b/src/trezor/ui/button.py @@ -1,6 +1,12 @@ from micropython import const -from trezor import ui, loop -from . import display, in_area, rotate_coords, Widget + +from trezor import io +from trezor import ui + +from trezor.ui import display +from trezor.ui import contains +from trezor.ui import rotate +from trezor.ui import Widget DEFAULT_BUTTON = { @@ -142,18 +148,18 @@ class Button(Widget): if self.state & BTN_DISABLED: return if not self.absolute: - pos = rotate_coords(pos) - if event == loop.TOUCH_START: - if in_area(pos, self.area): + pos = rotate(pos) + if event == io.TOUCH_START: + if contains(pos, self.area): self.state = BTN_STARTED | BTN_DIRTY | BTN_ACTIVE - elif event == loop.TOUCH_MOVE and self.state & BTN_STARTED: - if in_area(pos, self.area): + elif event == io.TOUCH_MOVE and self.state & BTN_STARTED: + if contains(pos, self.area): if not self.state & BTN_ACTIVE: self.state = BTN_STARTED | BTN_DIRTY | BTN_ACTIVE else: if self.state & BTN_ACTIVE: self.state = BTN_STARTED | BTN_DIRTY - elif event == loop.TOUCH_END and self.state & BTN_STARTED: + elif event == io.TOUCH_END and self.state & BTN_STARTED: self.state = BTN_DIRTY - if in_area(pos, self.area): + if contains(pos, self.area): return BTN_CLICKED diff --git a/src/trezor/ui/confirm.py b/src/trezor/ui/confirm.py index 0827dcfc3f..56cf5ae090 100644 --- a/src/trezor/ui/confirm.py +++ b/src/trezor/ui/confirm.py @@ -41,7 +41,7 @@ class ConfirmDialog(Widget): return CANCELLED async def __iter__(self): - return await loop.Wait((super().__iter__(), self.content)) + return await loop.wait(super().__iter__(), self.content) _STARTED = const(-1) @@ -82,5 +82,5 @@ class HoldToConfirmDialog(Widget): else: content_loop = self.content confirm_loop = super().__iter__() # default loop (render on touch) - result = await loop.wait((content_loop, confirm_loop)) + result = await loop.wait(content_loop, confirm_loop) return result diff --git a/src/trezor/ui/keyboard.py b/src/trezor/ui/keyboard.py index aa131030ca..9acbd14acc 100644 --- a/src/trezor/ui/keyboard.py +++ b/src/trezor/ui/keyboard.py @@ -1,4 +1,4 @@ -from trezor import ui, res, loop +from trezor import ui, res, loop, io from trezor.crypto import bip39 from trezor.ui import display from trezor.ui.button import Button, BTN_CLICKED, CLEAR_BUTTON, CLEAR_BUTTON_ACTIVE @@ -136,9 +136,9 @@ class KeyboardMultiTap(ui.Widget): btn.disable() def __iter__(self): - timeout = loop.Sleep(1000 * 1000 * 1) - touch = loop.Select(loop.TOUCH) - wait = loop.Wait((touch, timeout)) + timeout = loop.sleep(1000 * 1000 * 1) + touch = loop.select(io.TOUCH) + wait = loop.wait(touch, timeout) while True: self.render() result = yield wait @@ -223,9 +223,9 @@ class KeyboardZooming(ui.Widget): break def __iter__(self): - timeout = loop.Sleep(1000 * 1000 * 1) - touch = loop.Select(loop.TOUCH) - wait = loop.Wait((touch, timeout)) + timeout = loop.sleep(1000 * 1000 * 1) + touch = loop.select(io.TOUCH) + wait = loop.wait(touch, timeout) while True: self.render() result = yield wait diff --git a/src/trezor/ui/scroll.py b/src/trezor/ui/scroll.py index 8f408c7cca..4d0f657ae2 100644 --- a/src/trezor/ui/scroll.py +++ b/src/trezor/ui/scroll.py @@ -16,7 +16,7 @@ async def paginate(render_page, page_count, page=0, *args): while True: changer = change_page(page, page_count) renderer = render_page(page, page_count, *args) - waiter = loop.Wait([changer, renderer]) + waiter = loop.wait(changer, renderer) result = await waiter if changer in waiter.finished: page = result @@ -25,25 +25,28 @@ async def paginate(render_page, page_count, page=0, *args): async def animate_swipe(): - await ui.animate_pulse(render_swipe_icon, ui.GREY, ui.DARK_GREY, speed=300000, delay=200000) + time_delay = const(30000) + draw_delay = const(200000) - -def render_swipe_icon(fg): - ui.display.bar_radius(102, 214, 36, 4, fg, ui.BLACK, 2) - ui.display.bar_radius(106, 222, 28, 4, fg, ui.BLACK, 2) - ui.display.bar_radius(110, 230, 20, 4, fg, ui.BLACK, 2) + sleep = loop.sleep(time_delay) + for t in ui.pulse(draw_delay): + fg = ui.blend(ui.GREY, ui.DARK_GREY, t) + ui.display.bar_radius(102, 214, 36, 4, fg, ui.BLACK, 2) + ui.display.bar_radius(106, 222, 28, 4, fg, ui.BLACK, 2) + ui.display.bar_radius(110, 230, 20, 4, fg, ui.BLACK, 2) + await sleep def render_scrollbar(page, page_count): - screen_height = const(220) + bbox = const(220) size = const(10) padding = 18 - if page_count * padding > screen_height: - padding = screen_height // page_count + if page_count * padding > bbox: + padding = bbox // page_count x = const(225) - y = (screen_height // 2) - (page_count // 2) * padding + y = (bbox // 2) - (page_count // 2) * padding for i in range(0, page_count): if i != page: diff --git a/src/trezor/ui/swipe.py b/src/trezor/ui/swipe.py index 64a476905d..753bb2a7d0 100644 --- a/src/trezor/ui/swipe.py +++ b/src/trezor/ui/swipe.py @@ -1,7 +1,7 @@ import utime from micropython import const -from trezor import loop, ui -from . import in_area, rotate_coords +from trezor import io, ui +from . import contains, rotate _SWIPE_DISTANCE_THRESHOLD = const(20) # Min pixels in the primary direction _SWIPE_VELOCITY_THRESHOLD = const(200) # Min pixels per second @@ -26,12 +26,12 @@ class Swipe(ui.Widget): def touch(self, event, pos): if not self.absolute: - pos = rotate_coords(pos) + pos = rotate(pos) temp_time = utime.ticks_ms() / 1000 # primary now for fading purposes - if event == loop.TOUCH_MOVE and self.start_pos is not None: + if event == io.TOUCH_MOVE and self.start_pos is not None: pdx = pos[0] - self.start_pos[0] pdy = pos[1] - self.start_pos[1] td = temp_time - self.start_time @@ -52,13 +52,13 @@ class Swipe(ui.Widget): else: ui.display.backlight(self.light_target) - elif event == loop.TOUCH_START and in_area(pos, self.area): + elif event == io.TOUCH_START and contains(pos, self.area): self.start_time = temp_time self.start_pos = pos self.light_origin = ui.BACKLIGHT_NORMAL ui.display.backlight(self.light_origin) - elif event == loop.TOUCH_END and self.start_pos is not None: + elif event == io.TOUCH_END and self.start_pos is not None: td = temp_time - self.start_time pdx = pos[0] - self.start_pos[0] pdy = pos[1] - self.start_pos[1] diff --git a/src/trezor/wire/__init__.py b/src/trezor/wire/__init__.py index d39ce22177..8be3159ddf 100644 --- a/src/trezor/wire/__init__.py +++ b/src/trezor/wire/__init__.py @@ -22,7 +22,7 @@ def setup(iface): '''Initialize the wire stack on passed USB interface.''' session_supervisor = codec_v2.SesssionSupervisor(iface, session_handler) session_supervisor.open(codec_v1.SESSION_ID) - loop.schedule_task(session_supervisor.listen()) + loop.schedule(session_supervisor.listen()) class Context: diff --git a/src/trezor/wire/codec_v1.py b/src/trezor/wire/codec_v1.py index 01f1553197..25c9bb9940 100644 --- a/src/trezor/wire/codec_v1.py +++ b/src/trezor/wire/codec_v1.py @@ -1,6 +1,7 @@ from micropython import const import ustruct +from trezor import io from trezor import loop from trezor import utils @@ -37,7 +38,7 @@ class Reader: on this session. `self.type` and `self.size` are initialized and available after `aopen()` returns. ''' - read = loop.select(self.iface.iface_num() | loop.READ) + read = loop.select(self.iface.iface_num() | io.POLL_READ) while True: # wait for initial report report = await read @@ -63,7 +64,7 @@ class Reader: if self.size < len(buf): raise EOFError - read = loop.select(self.iface.iface_num() | loop.READ) + read = loop.select(self.iface.iface_num() | io.POLL_READ) nread = 0 while nread < len(buf): if self.ofs == len(self.data): @@ -122,7 +123,7 @@ class Writer: if self.size < len(buf): raise EOFError - write = loop.select(self.iface.iface_num() | loop.WRITE) + write = loop.select(self.iface.iface_num() | io.POLL_WRITE) nwritten = 0 while nwritten < len(buf): # copy as much as possible to report buffer @@ -148,5 +149,5 @@ class Writer: self.data[self.ofs] = 0x00 self.ofs += 1 - await loop.select(self.iface.iface_num() | loop.WRITE) + await loop.select(self.iface.iface_num() | io.POLL_WRITE) self.iface.write(self.data) diff --git a/src/trezor/wire/codec_v2.py b/src/trezor/wire/codec_v2.py index afe88032c0..b4dacf2f7e 100644 --- a/src/trezor/wire/codec_v2.py +++ b/src/trezor/wire/codec_v2.py @@ -1,6 +1,7 @@ from micropython import const import ustruct +from trezor import io from trezor import loop from trezor import utils from trezor.crypto import random @@ -59,7 +60,7 @@ class Reader: on this session. `self.type` and `self.size` are initialized and available after `aopen()` returns. ''' - read = loop.select(self.iface.iface_num() | loop.READ) + read = loop.select(self.iface.iface_num() | io.POLL_READ) while True: # wait for initial report report = await read @@ -83,7 +84,7 @@ class Reader: if self.size < len(buf): raise EOFError - read = loop.select(self.iface.iface_num() | loop.READ) + read = loop.select(self.iface.iface_num() | io.POLL_READ) nread = 0 while nread < len(buf): if self.ofs == len(self.data): @@ -148,7 +149,7 @@ class Writer: if self.size < len(buf): raise EOFError - write = loop.select(self.iface.iface_num() | loop.WRITE) + write = loop.select(self.iface.iface_num() | io.POLL_WRITE) nwritten = 0 while nwritten < len(buf): # copy as much as possible to report buffer @@ -177,7 +178,7 @@ class Writer: self.data[self.ofs] = 0x00 self.ofs += 1 - await loop.select(self.iface.iface_num() | loop.WRITE) + await loop.select(self.iface.iface_num() | io.POLL_WRITE) self.iface.write(self.data) @@ -197,8 +198,8 @@ class SesssionSupervisor: After close request, the handling task is closed and session terminated. Both requests receive responses confirming the operation. ''' - read = loop.select(self.iface.iface_num() | loop.READ) - write = loop.select(self.iface.iface_num() | loop.WRITE) + read = loop.select(self.iface.iface_num() | io.POLL_READ) + write = loop.select(self.iface.iface_num() | io.POLL_WRITE) while True: report = await read repmarker, repsid = ustruct.unpack(_REP, report) @@ -221,7 +222,7 @@ class SesssionSupervisor: def open(self, sid): if sid not in self.handling_tasks: task = self.handling_tasks[sid] = self.handler(self.iface, sid) - loop.schedule_task(task) + loop.schedule(task) def close(self, sid): if sid in self.handling_tasks: diff --git a/src/trezor/workflow.py b/src/trezor/workflow.py index aa2a6d83e3..8de25da3a6 100644 --- a/src/trezor/workflow.py +++ b/src/trezor/workflow.py @@ -38,6 +38,6 @@ def startdefault(handler): if not default: default_handler = handler default = handler() - loop.schedule_task(default) + loop.schedule(default) ui.display.backlight(ui.BACKLIGHT_NORMAL) log.debug(__name__, 'startdefault') diff --git a/tests/test_trezor.wire.codec_v1.py b/tests/test_trezor.wire.codec_v1.py index 456beb93d9..78ac96599a 100644 --- a/tests/test_trezor.wire.codec_v1.py +++ b/tests/test_trezor.wire.codec_v1.py @@ -6,7 +6,8 @@ from utest import * from ustruct import pack, unpack from ubinascii import hexlify, unhexlify -from trezor.loop import Select, Syscall, READ, WRITE +from trezor import io +from trezor.loop import select, Syscall from trezor.crypto import random from trezor.utils import chunks from trezor.wire import codec_v1 @@ -39,7 +40,7 @@ def test_reader(): # open, expected one read first_report = report_header + message[:rep_len - len(report_header)] - assert_async(reader.aopen(), [(None, Select(READ | interface_num)), (first_report, StopIteration()),]) + assert_async(reader.aopen(), [(None, select(io.POLL_READ | interface_num)), (first_report, StopIteration()),]) assert_eq(reader.type, message_type) assert_eq(reader.size, message_len) @@ -66,7 +67,7 @@ def test_reader(): next_report_header = bytearray(unhexlify('3f')) next_report = next_report_header + message[rep_len - len(report_header):][:rep_len - len(next_report_header)] onebyte_buffer = bytearray(1) - assert_async(reader.areadinto(onebyte_buffer), [(None, Select(READ | interface_num)), (next_report, StopIteration()),]) + assert_async(reader.areadinto(onebyte_buffer), [(None, select(io.POLL_READ | interface_num)), (next_report, StopIteration()),]) assert_eq(onebyte_buffer, message[len(short_buffer):][len(aligned_buffer):][:len(onebyte_buffer)]) assert_eq(reader.size, message_len - len(short_buffer) - len(aligned_buffer) - len(onebyte_buffer)) @@ -85,7 +86,7 @@ def test_reader(): expected_syscalls = [] for i, _ in enumerate(next_reports): prev_report = next_reports[i - 1] if i > 0 else None - expected_syscalls.append((prev_report, Select(READ | interface_num))) + expected_syscalls.append((prev_report, select(io.POLL_READ | interface_num))) expected_syscalls.append((next_reports[-1], StopIteration())) assert_async(reader.areadinto(long_buffer), expected_syscalls) assert_eq(long_buffer, message[-start_size:]) @@ -128,7 +129,7 @@ def test_writer(): # aligned write, expected one report start_size = writer.size aligned_payload = bytearray(range(rep_len - len(report_header) - len(short_payload))) - assert_async(writer.awrite(aligned_payload), [(None, Select(WRITE | interface_num)), (None, StopIteration()),]) + assert_async(writer.awrite(aligned_payload), [(None, select(io.POLL_WRITE | interface_num)), (None, StopIteration()),]) assert_eq(interface.data, [report_header + short_payload + aligned_payload @@ -154,7 +155,7 @@ def test_writer(): expected_reports[-1] += bytearray(bytes(1) * (rep_len - len(expected_reports[-1]))) # test write expected_write_reports = expected_reports[:-1] - assert_async(writer.awrite(long_payload), len(expected_write_reports) * [(None, Select(WRITE | interface_num))] + [(None, StopIteration())]) + assert_async(writer.awrite(long_payload), len(expected_write_reports) * [(None, select(io.POLL_WRITE | interface_num))] + [(None, StopIteration())]) assert_eq(interface.data, expected_write_reports) assert_eq(writer.size, start_size - len(long_payload)) interface.data.clear() @@ -163,7 +164,7 @@ def test_writer(): assert_eq(interface.data, []) # test close expected_close_reports = expected_reports[-1:] - assert_async(writer.aclose(), len(expected_close_reports) * [(None, Select(WRITE | interface_num))] + [(None, StopIteration())]) + assert_async(writer.aclose(), len(expected_close_reports) * [(None, select(io.POLL_WRITE | interface_num))] + [(None, StopIteration())]) assert_eq(interface.data, expected_close_reports) assert_eq(writer.size, 0) diff --git a/tests/test_trezor.wire.codec_v2.py b/tests/test_trezor.wire.codec_v2.py index 256fa6deb9..ce10f582ee 100644 --- a/tests/test_trezor.wire.codec_v2.py +++ b/tests/test_trezor.wire.codec_v2.py @@ -6,7 +6,8 @@ from utest import * from ustruct import pack, unpack from ubinascii import hexlify, unhexlify -from trezor.loop import Select, Syscall, READ, WRITE +from trezor import io +from trezor.loop import select, Syscall from trezor.utils import chunks from trezor.wire import codec_v2 @@ -39,7 +40,7 @@ def test_reader(): # open, expected one read first_report = report_header + message[:rep_len - len(report_header)] - assert_async(reader.aopen(), [(None, Select(READ | interface_num)), (first_report, StopIteration()),]) + assert_async(reader.aopen(), [(None, select(io.POLL_READ | interface_num)), (first_report, StopIteration()),]) assert_eq(reader.type, message_type) assert_eq(reader.size, message_len) @@ -66,7 +67,7 @@ def test_reader(): next_report_header = bytearray(unhexlify('021234567800000000')) next_report = next_report_header + message[rep_len - len(report_header):][:rep_len - len(next_report_header)] onebyte_buffer = bytearray(1) - assert_async(reader.areadinto(onebyte_buffer), [(None, Select(READ | interface_num)), (next_report, StopIteration()),]) + assert_async(reader.areadinto(onebyte_buffer), [(None, select(io.POLL_READ | interface_num)), (next_report, StopIteration()),]) assert_eq(onebyte_buffer, message[len(short_buffer):][len(aligned_buffer):][:len(onebyte_buffer)]) assert_eq(reader.size, message_len - len(short_buffer) - len(aligned_buffer) - len(onebyte_buffer)) @@ -85,7 +86,7 @@ def test_reader(): expected_syscalls = [] for i, _ in enumerate(next_reports): prev_report = next_reports[i - 1] if i > 0 else None - expected_syscalls.append((prev_report, Select(READ | interface_num))) + expected_syscalls.append((prev_report, select(io.POLL_READ | interface_num))) expected_syscalls.append((next_reports[-1], StopIteration())) assert_async(reader.areadinto(long_buffer), expected_syscalls) assert_eq(long_buffer, message[-start_size:]) @@ -129,7 +130,7 @@ def test_writer(): # aligned write, expected one report start_size = writer.size aligned_payload = bytearray(range(rep_len - len(report_header) - len(short_payload))) - assert_async(writer.awrite(aligned_payload), [(None, Select(WRITE | interface_num)), (None, StopIteration()),]) + assert_async(writer.awrite(aligned_payload), [(None, select(io.POLL_WRITE | interface_num)), (None, StopIteration()),]) assert_eq(interface.data, [report_header + short_payload + aligned_payload @@ -157,7 +158,7 @@ def test_writer(): expected_reports[-1] += bytearray(bytes(1) * (rep_len - len(expected_reports[-1]))) # test write expected_write_reports = expected_reports[:-1] - assert_async(writer.awrite(long_payload), len(expected_write_reports) * [(None, Select(WRITE | interface_num))] + [(None, StopIteration())]) + assert_async(writer.awrite(long_payload), len(expected_write_reports) * [(None, select(io.POLL_WRITE | interface_num))] + [(None, StopIteration())]) assert_eq(interface.data, expected_write_reports) assert_eq(writer.size, start_size - len(long_payload)) interface.data.clear() @@ -166,7 +167,7 @@ def test_writer(): assert_eq(interface.data, []) # test close expected_close_reports = expected_reports[-1:] - assert_async(writer.aclose(), len(expected_close_reports) * [(None, Select(WRITE | interface_num))] + [(None, StopIteration())]) + assert_async(writer.aclose(), len(expected_close_reports) * [(None, select(io.POLL_WRITE | interface_num))] + [(None, StopIteration())]) assert_eq(interface.data, expected_close_reports) assert_eq(writer.size, 0) interface.data.clear()