async def __iter__(self): timeout = loop.sleep(1000 * 1000 * 1) touch = loop.select(io.TOUCH) wait_timeout = loop.wait(touch, timeout) wait_touch = loop.wait(touch) content = None self.back.taint() self.input.taint() while content is None: self.render() if self.pbutton is not None: wait = wait_timeout else: wait = wait_touch result = await wait if touch in wait.finished: event, *pos = result content = self.touch(event, pos) else: if self.input.word: # just reset the pending state self.edit(self.input.content) else: # invalid character, backspace it self.edit(self.input.content[:-1]) return content
async def listen(self): ''' Listen for open/close requests on configured interface. After open request, session is started and a new task is scheduled to handle it. After close request, the handling task is closed and session terminated. Both requests receive responses confirming the operation. ''' read = loop.wait(self.iface.iface_num() | io.POLL_READ) write = loop.wait(self.iface.iface_num() | io.POLL_WRITE) while True: report = await read repmarker, repsid = ustruct.unpack(_REP, report) # because tasks paused on I/O have a priority over time-scheduled # tasks, we need to `yield` explicitly before sending a response to # open/close request. Otherwise the handler would have no chance to # run and schedule communication. if repmarker == _REP_MARKER_OPEN: newsid = self.newsid() self.open(newsid) yield await write self.writeopen(newsid) elif repmarker == _REP_MARKER_CLOSE: self.close(repsid) yield await write self.writeclose(repsid)
async def send_cmd(cmd: Cmd, iface: io.HID) -> None: init_desc = frame_init() cont_desc = frame_cont() offset = 0 seq = 0 datalen = len(cmd.data) buf, frm = make_struct(init_desc) frm.cid = cmd.cid frm.cmd = cmd.cmd frm.bcnt = datalen offset += utils.memcpy(frm.data, 0, cmd.data, offset, datalen) iface.write(buf) if offset < datalen: frm = overlay_struct(buf, cont_desc) write = loop.wait(iface.iface_num() | io.POLL_WRITE) while offset < datalen: frm.seq = seq offset += utils.memcpy(frm.data, 0, cmd.data, offset, datalen) while True: await write if iface.write(buf) > 0: break seq += 1
async def areadinto(self, buf): """ Read exactly `len(buf)` bytes into `buf`, waiting for additional reports, if needed. Raises `EOFError` if end-of-message is encountered before the full read can be completed. """ if self.size < len(buf): raise EOFError read = loop.wait(self.iface.iface_num() | io.POLL_READ) nread = 0 while nread < len(buf): if self.ofs == len(self.data): # we are at the end of received data # wait for continuation report while True: report = await read marker = report[0] if marker == _REP_MARKER: break self.data = report[_REP_CONT_DATA : _REP_CONT_DATA + self.size] self.ofs = 0 # copy as much as possible to target buffer nbytes = utils.memcpy(buf, nread, self.data, self.ofs, len(buf)) nread += nbytes self.ofs += nbytes self.size -= nbytes return nread
async def awrite(self, buf): """ Encode and write every byte from `buf`. Does not need to be called in case message has zero length. Raises `EOFError` if the length of `buf` exceeds the remaining message length. """ if self.size < len(buf): raise EOFError write = loop.wait(self.iface.iface_num() | io.POLL_WRITE) nwritten = 0 while nwritten < len(buf): # copy as much as possible to report buffer nbytes = utils.memcpy(self.data, self.ofs, buf, nwritten, len(buf)) nwritten += nbytes self.ofs += nbytes self.size -= nbytes if self.ofs == _REP_LEN: # we are at the end of the report, flush it while True: await write n = self.iface.write(self.data) if n == len(self.data): break self.ofs = _REP_CONT_DATA return nwritten
def wait(self, *tasks): ''' Wait until one of the passed tasks finishes, and return the result, while servicing the wire context. If a message comes until one of the tasks ends, `UnexpectedMessageError` is raised. ''' return loop.wait(self.read(()), *tasks)
async def awrite(self, buf): ''' Encode and write every byte from `buf`. Does not need to be called in case message has zero length. Raises `EOFError` if the length of `buf` exceeds the remaining message length. ''' if self.size < len(buf): raise EOFError write = loop.wait(self.iface.iface_num() | io.POLL_WRITE) nwritten = 0 while nwritten < len(buf): # copy as much as possible to report buffer nbytes = utils.memcpy(self.data, self.ofs, buf, nwritten, len(buf)) nwritten += nbytes self.ofs += nbytes self.size -= nbytes if self.ofs == _REP_LEN: # we are at the end of the report, flush it, and prepare header await write self.iface.write(self.data) ustruct.pack_into(_REP_CONT, self.data, 0, _REP_MARKER_CONT, self.sid, self.seq) self.ofs = _REP_CONT_DATA self.seq += 1 return nwritten
async def write_message(iface: WireInterface, mtype: int, mdata: bytes) -> None: write = loop.wait(iface.iface_num() | io.POLL_WRITE) # gather data from msg msize = len(mdata) # prepare the report buffer with header data report = bytearray(_REP_LEN) repofs = _REP_INIT_DATA ustruct.pack_into(_REP_INIT, report, 0, _REP_MARKER, _REP_MAGIC, _REP_MAGIC, mtype, msize) nwritten = 0 while True: # copy as much as possible to the report buffer nwritten += utils.memcpy(report, repofs, mdata, nwritten) # write the report while True: await write n = iface.write(report) if n == len(report): break # if we have more data to write, use continuation reports for it if nwritten < msize: repofs = _REP_CONT_DATA else: break
def __iter__(self): touch = loop.wait(io.TOUCH) result = None while result is None: self.render() event, *pos = yield touch result = self.touch(event, pos) return result
def __iter__(self): try: touch = loop.wait(io.TOUCH) while True: event, x, y = yield touch self.dispatch(event, x, y) except ui.Result as result: return result.value
def __iter__(self) -> loop.Task: # type: ignore [awaitable-is-generator] try: touch = loop.wait(io.TOUCH) while True: event, x, y = yield touch self.dispatch(event, x, y) except ui.Result as result: return result.value
def handle_input(self) -> Generator: """Task that is waiting for the user input.""" button = loop.wait(io.BUTTON) while True: event, button_num = yield button workflow.idle_timer.touch() self.dispatch(event, button_num, 0) self.dispatch(RENDER, 0, 0)
def handle_input(self) -> loop.Task: # type: ignore """Task that is waiting for the user input.""" touch = loop.wait(io.TOUCH) while True: event, x, y = yield touch self.dispatch(event, x, y) # We dispatch a render event right after the touch. Quick and dirty # way to get the lowest input-to-render latency. self.dispatch(RENDER, 0, 0)
async def __iter__(self): self.edit(self.input.content) # init button state while True: change = self.change_page() enter = self.enter_text() wait = loop.wait(change, enter) result = await wait if enter in wait.finished: return result
async def read_cmd(iface: io.HID) -> Cmd: desc_init = frame_init() desc_cont = frame_cont() read = loop.wait(iface.iface_num() | io.POLL_READ) buf = await read ifrm = overlay_struct(buf, desc_init) bcnt = ifrm.bcnt data = ifrm.data datalen = len(data) seq = 0 if ifrm.cmd & _TYPE_MASK == _TYPE_CONT: # unexpected cont packet, abort current msg if __debug__: log.warning(__name__, "_TYPE_CONT") return None if datalen < bcnt: databuf = bytearray(bcnt) utils.memcpy(databuf, 0, data, 0, bcnt) data = databuf else: data = data[:bcnt] while datalen < bcnt: buf = await read cfrm = overlay_struct(buf, desc_cont) if cfrm.seq == _CMD_INIT: # _CMD_INIT frame, cancels current channel ifrm = overlay_struct(buf, desc_init) data = ifrm.data[: ifrm.bcnt] break if cfrm.cid != ifrm.cid: # cont frame for a different channel, reply with BUSY and skip if __debug__: log.warning(__name__, "_ERR_CHANNEL_BUSY") await send_cmd(cmd_error(cfrm.cid, _ERR_CHANNEL_BUSY), iface) continue if cfrm.seq != seq: # cont frame for this channel, but incorrect seq number, abort # current msg if __debug__: log.warning(__name__, "_ERR_INVALID_SEQ") await send_cmd(cmd_error(cfrm.cid, _ERR_INVALID_SEQ), iface) return None datalen += utils.memcpy(data, datalen, cfrm.data, 0, bcnt - datalen) seq += 1 return Cmd(ifrm.cid, ifrm.cmd, data)
async def homescreen() -> None: # render homescreen in dimmed mode and fade back in ui.backlight_fade(ui.BACKLIGHT_DIM) display_homescreen() ui.backlight_fade(ui.BACKLIGHT_NORMAL) # loop forever, never return touch = loop.wait(io.TOUCH) while True: await touch
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) result = await waiter if changer in waiter.finished: page = result else: return result
def handle_input(self) -> loop.Task: # type: ignore """Task that is waiting for the user input.""" touch = loop.wait(io.TOUCH) while True: # Using `yield` instead of `await` to avoid allocations. event, x, y = yield touch workflow.idle_timer.touch() self.dispatch(event, x, y) # We dispatch a render event right after the touch. Quick and dirty # way to get the lowest input-to-render latency. self.dispatch(RENDER, 0, 0)
async def enter_text(self): timeout = loop.sleep(1000 * 1000 * 1) touch = loop.select(io.TOUCH) wait_timeout = loop.wait(touch, timeout) wait_touch = loop.wait(touch) content = None while content is None: self.render() if self.pbutton is not None: wait = wait_timeout else: wait = wait_touch result = await wait if touch in wait.finished: event, *pos = result content = self.touch(event, pos) else: # disable the pending buttons self.edit(self.input.content) return content
async def click() -> Pos: touch = loop.wait(io.TOUCH) while True: ev, *pos = await touch if ev == io.TOUCH_START: break while True: ev, *pos = await touch if ev == io.TOUCH_END: break return pos # type: ignore [Expression of type "list[Unknown]" cannot be assigned to return type "Pos"]
async def click() -> tuple: touch = loop.wait(io.TOUCH) while True: ev, *pos = yield touch if ev == io.TOUCH_START: break while True: ev, *pos = yield touch if ev == io.TOUCH_END: break return pos
async def click() -> Pos: touch = loop.wait(io.TOUCH) while True: ev, *pos = await touch if ev == io.TOUCH_START: break while True: ev, *pos = await touch if ev == io.TOUCH_END: break return pos # type: ignore
async def hold_to_confirm(ctx, content, code=None, *args, **kwargs): if code is None: code = ButtonRequestType.Other await ctx.call(ButtonRequest(code=code), wire_types.ButtonAck) dialog = HoldToConfirmDialog(content, 'Hold to confirm', *args, **kwargs) if __debug__: waiter = loop.wait(signal, dialog) else: waiter = dialog return await waiter == CONFIRMED
def __iter__(self): timeout = loop.sleep(1000 * 1000 * 1) touch = loop.select(io.TOUCH) wait = loop.wait(touch, timeout) while True: self.render() result = yield wait if touch in wait.finished: event, *pos = result self.touch(event, pos) elif self.zoom_buttons: self.zoom_buttons = None for btn in self.key_buttons: btn.taint()
def handle_input_and_rendering( self) -> loop.Task: # type: ignore [awaitable-is-generator] button = loop.wait(io.BUTTON) ui.display.clear() self.layout.paint() while True: # Using `yield` instead of `await` to avoid allocations. event, button_num = yield button workflow.idle_timer.touch() msg = None if event in (io.BUTTON_PRESSED, io.BUTTON_RELEASED): msg = self.layout.button_event(event, button_num) self.layout.paint() if msg is not None: raise ui.Result(msg)
def __iter__(self): timeout = loop.sleep(1000 * 1000 * 1) touch = loop.select(io.TOUCH) wait = loop.wait(touch, timeout) while True: self.render() result = yield wait if touch in wait.finished: event, *pos = result self.touch(event, pos) else: self.pending_button = None self.pending_index = 0 self._update_suggestion() self._update_buttons()
async def aclose(self): """Flush and close the message transmission.""" if self.ofs != _REP_CONT_DATA: # we didn't write anything or last write() wasn't report-aligned, # pad the final report and flush it while self.ofs < _REP_LEN: self.data[self.ofs] = 0x00 self.ofs += 1 write = loop.wait(self.iface.iface_num() | io.POLL_WRITE) while True: await write n = self.iface.write(self.data) if n == len(self.data): break
def handle_input_and_rendering( self) -> loop.Task: # type: ignore [awaitable-is-generator] touch = loop.wait(io.TOUCH) ui.display.clear() self.layout.paint() # self.layout.bounds() while True: # Using `yield` instead of `await` to avoid allocations. event, x, y = yield touch workflow.idle_timer.touch() msg = None if event in (io.TOUCH_START, io.TOUCH_MOVE, io.TOUCH_END): msg = self.layout.touch_event(event, x, y) self.layout.paint() # self.layout.bounds() if msg is not None: raise ui.Result(msg)
async def read_message(iface: WireInterface, buffer: utils.BufferType) -> Message: read = loop.wait(iface.iface_num() | io.POLL_READ) # wait for initial report report = await read if report[0] != _REP_MARKER: raise CodecError("Invalid magic") _, magic1, magic2, mtype, msize = ustruct.unpack(_REP_INIT, report) if magic1 != _REP_MAGIC or magic2 != _REP_MAGIC: raise CodecError("Invalid magic") read_and_throw_away = False with buffer_lock: if msize > len(buffer): # allocate a new buffer to fit the message try: mdata: utils.BufferType = bytearray(msize) except MemoryError: mdata = bytearray(_REP_LEN) read_and_throw_away = True else: # reuse a part of the supplied buffer mdata = memoryview(buffer)[:msize] # buffer the initial data nread = utils.memcpy(mdata, 0, report, _REP_INIT_DATA) while nread < msize: # wait for continuation report report = await read if report[0] != _REP_MARKER: raise CodecError("Invalid magic") # buffer the continuation data if read_and_throw_away: nread += len(report) - 1 else: nread += utils.memcpy(mdata, nread, report, _REP_CONT_DATA) if read_and_throw_away: raise CodecError("Message too large") return Message(mtype, mdata)
async def handle_input(self) -> None: touch = loop.wait(io.TOUCH) timeout = loop.sleep(1000) race_touch = loop.race(touch) race_timeout = loop.race(touch, timeout) while True: if self.pending_button is not None: race = race_timeout else: race = race_touch result = await race if touch in race.finished: event, x, y = result self.dispatch(event, x, y) else: self.on_timeout()