async def __iter__(self) -> ResultValue: """ Run the layout and wait until it completes. Returns the result value. Usually not overridden. """ value = None try: # If any other layout is running (waiting on the layout channel), # we close it with the Cancelled exception, and wait until it is # closed, just to be sure. if layout_chan.takers: await layout_chan.put(Cancelled()) # Now, no other layout should be running. In a loop, we create new # layout tasks and execute them in parallel, while waiting on the # layout channel. This allows other layouts to cancel us, and the # layout tasks to trigger restart by exiting (new tasks are created # and we continue, because we are in a loop). if __debug__: notify_layout_change(self) while True: await loop.race(layout_chan.take(), *self.create_tasks()) except Result as result: # Result exception was raised, this means this layout is complete. value = result.value return value
async def handle_paging(self) -> None: if self.page == 0: directions = SWIPE_UP elif self.page == len(self.pages) - 1: directions = SWIPE_DOWN else: directions = SWIPE_VERTICAL if __debug__: swipe = await loop.race(Swipe(directions), swipe_signal()) else: swipe = await Swipe(directions) if swipe is SWIPE_UP: self.page = min(self.page + 1, len(self.pages) - 1) elif swipe is SWIPE_DOWN: self.page = max(self.page - 1, 0) self.pages[self.page].dispatch(ui.REPAINT, 0, 0) self.repaint = True if __debug__: notify_layout_change(self) self.on_change()
def handle_rendering(self) -> loop.Task: # type: ignore """Task that is rendering the layout in a busy loop.""" # Before the first render, we dim the display. backlight_fade(style.BACKLIGHT_DIM) # Clear the screen of any leftovers, make sure everything is marked for # repaint (we can be running the same layout instance multiple times) # and paint it. display.clear() self.dispatch(REPAINT, 0, 0) self.dispatch(RENDER, 0, 0) if __debug__ and self.should_notify_layout_change: # notify about change and do not notify again until next await. # (handle_rendering might be called multiple times in a single await, # because of the endless loop in __iter__) self.should_notify_layout_change = False notify_layout_change(self) # Display is usually refreshed after every loop step, but here we are # rendering everything synchronously, so refresh it manually and turn # the brightness on again. refresh() backlight_fade(self.BACKLIGHT_LEVEL) sleep = self.RENDER_SLEEP while True: # Wait for a couple of ms and render the layout again. Because # components use re-paint marking, they do not really draw on the # display needlessly. Using `yield` instead of `await` to avoid allocations. # TODO: remove the busy loop yield sleep self.dispatch(RENDER, 0, 0)
async def handle_swipe(self): from apps.debug import notify_layout_change, swipe_signal from trezor.ui.components.common import ( SWIPE_UP, SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT, ) while True: direction = await swipe_signal() orig_x = orig_y = 120 off_x, off_y = { SWIPE_UP: (0, -30), SWIPE_DOWN: (0, 30), SWIPE_LEFT: (-30, 0), SWIPE_RIGHT: (30, 0), }[direction] for event, x, y in ( (io.TOUCH_START, orig_x, orig_y), (io.TOUCH_MOVE, orig_x + 1 * off_x, orig_y + 1 * off_y), (io.TOUCH_END, orig_x + 2 * off_x, orig_y + 2 * off_y), ): msg = self.layout.touch_event(event, x, y) self.layout.paint() if msg is not None: raise ui.Result(msg) notify_layout_change(self)
def _before_render(self) -> None: # Clear the screen of any leftovers. ui.backlight_fade(ui.style.BACKLIGHT_DIM) ui.display.clear() if __debug__ and self.should_notify_layout_change: from apps.debug import notify_layout_change # notify about change and do not notify again until next await. # (handle_rendering might be called multiple times in a single await, # because of the endless loop in __iter__) self.should_notify_layout_change = False notify_layout_change(self) # Turn the brightness on again. ui.backlight_fade(self.BACKLIGHT_LEVEL)
def _before_render(self) -> None: # Before the first render, we dim the display. backlight_fade(style.BACKLIGHT_DIM) # Clear the screen of any leftovers, make sure everything is marked for # repaint (we can be running the same layout instance multiple times) # and paint it. display.clear() self.dispatch(REPAINT, 0, 0) self.dispatch(RENDER, 0, 0) if __debug__ and self.should_notify_layout_change: from apps.debug import notify_layout_change # notify about change and do not notify again until next await. # (handle_rendering might be called multiple times in a single await, # because of the endless loop in __iter__) self.should_notify_layout_change = False notify_layout_change(self) # Display is usually refreshed after every loop step, but here we are # rendering everything synchronously, so refresh it manually and turn # the brightness on again. refresh() backlight_fade(self.BACKLIGHT_LEVEL)