1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-26 08:29:26 +00:00

refactor(core): unify touch and button handling, enable usage of both in one model

[no changelog]
This commit is contained in:
tychovrahe 2023-04-25 22:44:59 +02:00
parent cfb674cf01
commit b757bff185
11 changed files with 116 additions and 117 deletions

View File

@ -25,8 +25,9 @@
#include "embed/extmod/trezorobj.h" #include "embed/extmod/trezorobj.h"
#define USB_DATA_IFACE (253) #define USB_DATA_IFACE (253)
#define BUTTON_IFACE (254) #define INPUT_IFACE (255)
#define TOUCH_IFACE (255) #define TOUCH_INPUT_FLAG (0x40000000)
#define BUTTON_INPUT_FLAG (0x80000000)
#define POLL_READ (0x0000) #define POLL_READ (0x0000)
#define POLL_WRITE (0x0100) #define POLL_WRITE (0x0100)
@ -81,17 +82,36 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
#endif #endif
if (false) { if (false) {
} } else if (iface == INPUT_IFACE) {
#if defined USE_TOUCH uint32_t evt;
else if (iface == TOUCH_IFACE) { #ifdef USE_BUTTON
const uint32_t evt = touch_read(); evt = button_read();
if (evt & (BTN_EVT_DOWN | BTN_EVT_UP)) {
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
uint32_t etype =
((evt >> 24) & 0x3U) | BUTTON_INPUT_FLAG; // button down/up
uint32_t en = evt & 0xFFFF; // button number
if (display_orientation(-1) == 180) {
en = (en == BTN_LEFT) ? BTN_RIGHT : BTN_LEFT;
}
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(etype);
tuple->items[1] = MP_OBJ_NEW_SMALL_INT(en);
tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0);
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = MP_OBJ_FROM_PTR(tuple);
return mp_const_true;
}
#endif
#ifdef USE_TOUCH
evt = touch_read();
if (evt) { if (evt) {
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
const uint32_t etype = (evt >> 24) & 0xFFU; // event type const uint32_t etype =
const uint32_t ex = (evt >> 12) & 0xFFFU; // x position ((evt >> 24) & 0xFFU) | TOUCH_INPUT_FLAG; // event type
const uint32_t ey = evt & 0xFFFU; // y position const uint32_t ex = (evt >> 12) & 0xFFFU; // x position
uint32_t exr; // rotated x position const uint32_t ey = evt & 0xFFFU; // y position
uint32_t eyr; // rotated y position uint32_t exr; // rotated x position
uint32_t eyr; // rotated y position
switch (display_orientation(-1)) { switch (display_orientation(-1)) {
case 90: case 90:
exr = ey; exr = ey;
@ -117,6 +137,7 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
ret->items[1] = MP_OBJ_FROM_PTR(tuple); ret->items[1] = MP_OBJ_FROM_PTR(tuple);
return mp_const_true; return mp_const_true;
} }
#endif
} else if (iface == USB_DATA_IFACE) { } else if (iface == USB_DATA_IFACE) {
bool usb_connected = usb_configured() == sectrue ? true : false; bool usb_connected = usb_configured() == sectrue ? true : false;
if (usb_connected != usb_connected_previously) { if (usb_connected != usb_connected_previously) {
@ -125,27 +146,7 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
ret->items[1] = usb_connected ? mp_const_true : mp_const_false; ret->items[1] = usb_connected ? mp_const_true : mp_const_false;
return mp_const_true; return mp_const_true;
} }
} } else if (mode == POLL_READ) {
#endif
#if USE_BUTTON
else if (iface == BUTTON_IFACE) {
const uint32_t evt = button_read();
if (evt & (BTN_EVT_DOWN | BTN_EVT_UP)) {
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
uint32_t etype = (evt >> 24) & 0x3U; // button down/up
uint32_t en = evt & 0xFFFF; // button number
if (display_orientation(-1) == 180) {
en = (en == BTN_LEFT) ? BTN_RIGHT : BTN_LEFT;
}
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(etype);
tuple->items[1] = MP_OBJ_NEW_SMALL_INT(en);
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = MP_OBJ_FROM_PTR(tuple);
return mp_const_true;
}
}
#endif
else if (mode == POLL_READ) {
if (sectrue == usb_hid_can_read(iface)) { if (sectrue == usb_hid_can_read(iface)) {
uint8_t buf[64] = {0}; uint8_t buf[64] = {0};
int len = usb_hid_read(iface, buf, sizeof(buf)); int len = usb_hid_read(iface, buf, sizeof(buf));

View File

@ -62,12 +62,12 @@ bool usb_connected_previously = true;
/// POLL_READ: int # wait until interface is readable and return read data /// POLL_READ: int # wait until interface is readable and return read data
/// POLL_WRITE: int # wait until interface is writable /// POLL_WRITE: int # wait until interface is writable
/// ///
/// TOUCH: int # interface id of the touch events /// INPUT: int # interface id of unified input events
///
/// TOUCH_START: int # event id of touch start event /// TOUCH_START: int # event id of touch start event
/// TOUCH_MOVE: int # event id of touch move event /// TOUCH_MOVE: int # event id of touch move event
/// TOUCH_END: int # event id of touch end event /// TOUCH_END: int # event id of touch end event
/// BUTTON: int # interface id of button events
/// BUTTON_PRESSED: int # button down event /// BUTTON_PRESSED: int # button down event
/// BUTTON_RELEASED: int # button up event /// BUTTON_RELEASED: int # button up event
/// BUTTON_LEFT: int # button number of left button /// BUTTON_LEFT: int # button number of left button
@ -88,19 +88,20 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_fatfs), MP_ROM_PTR(&mod_trezorio_fatfs_module)}, {MP_ROM_QSTR(MP_QSTR_fatfs), MP_ROM_PTR(&mod_trezorio_fatfs_module)},
{MP_ROM_QSTR(MP_QSTR_sdcard), MP_ROM_PTR(&mod_trezorio_sdcard_module)}, {MP_ROM_QSTR(MP_QSTR_sdcard), MP_ROM_PTR(&mod_trezorio_sdcard_module)},
#endif #endif
{MP_ROM_QSTR(MP_QSTR_INPUT), MP_ROM_INT(INPUT_IFACE)},
#ifdef USE_TOUCH #ifdef USE_TOUCH
{MP_ROM_QSTR(MP_QSTR_TOUCH), MP_ROM_INT(TOUCH_IFACE)}, {MP_ROM_QSTR(MP_QSTR_TOUCH_START),
{MP_ROM_QSTR(MP_QSTR_TOUCH_START), MP_ROM_INT((TOUCH_START >> 24) & 0xFFU)}, MP_ROM_INT(((TOUCH_START >> 24) & 0xFFU) | TOUCH_INPUT_FLAG)},
{MP_ROM_QSTR(MP_QSTR_TOUCH_MOVE), MP_ROM_INT((TOUCH_MOVE >> 24) & 0xFFU)}, {MP_ROM_QSTR(MP_QSTR_TOUCH_MOVE),
{MP_ROM_QSTR(MP_QSTR_TOUCH_END), MP_ROM_INT((TOUCH_END >> 24) & 0xFFU)}, MP_ROM_INT(((TOUCH_MOVE >> 24) & 0xFFU) | TOUCH_INPUT_FLAG)},
{MP_ROM_QSTR(MP_QSTR_TOUCH_END),
MP_ROM_INT(((TOUCH_END >> 24) & 0xFFU) | TOUCH_INPUT_FLAG)},
#endif #endif
#ifdef USE_BUTTON #ifdef USE_BUTTON
{MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_INT(BUTTON_IFACE)},
{MP_ROM_QSTR(MP_QSTR_BUTTON_PRESSED), {MP_ROM_QSTR(MP_QSTR_BUTTON_PRESSED),
MP_ROM_INT((BTN_EVT_DOWN >> 24) & 0x3U)}, MP_ROM_INT(((BTN_EVT_DOWN >> 24) & 0x3U) | BUTTON_INPUT_FLAG)},
{MP_ROM_QSTR(MP_QSTR_BUTTON_RELEASED), {MP_ROM_QSTR(MP_QSTR_BUTTON_RELEASED),
MP_ROM_INT((BTN_EVT_UP >> 24) & 0x3U)}, MP_ROM_INT(((BTN_EVT_UP >> 24) & 0x3U) | BUTTON_INPUT_FLAG)},
{MP_ROM_QSTR(MP_QSTR_BUTTON_LEFT), MP_ROM_INT(BTN_LEFT)}, {MP_ROM_QSTR(MP_QSTR_BUTTON_LEFT), MP_ROM_INT(BTN_LEFT)},
{MP_ROM_QSTR(MP_QSTR_BUTTON_RIGHT), MP_ROM_INT(BTN_RIGHT)}, {MP_ROM_QSTR(MP_QSTR_BUTTON_RIGHT), MP_ROM_INT(BTN_RIGHT)},
#endif #endif

View File

@ -259,6 +259,8 @@ STATIC mp_obj_str_t mod_trezorutils_revision_obj = {
/// VERSION_MINOR: int /// VERSION_MINOR: int
/// VERSION_PATCH: int /// VERSION_PATCH: int
/// USE_SD_CARD: bool /// USE_SD_CARD: bool
/// USE_TOUCH: bool
/// USE_BUTTON: bool
/// MODEL: str /// MODEL: str
/// INTERNAL_MODEL: str /// INTERNAL_MODEL: str
/// EMULATOR: bool /// EMULATOR: bool
@ -289,6 +291,16 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_USE_SD_CARD), mp_const_true}, {MP_ROM_QSTR(MP_QSTR_USE_SD_CARD), mp_const_true},
#else #else
{MP_ROM_QSTR(MP_QSTR_USE_SD_CARD), mp_const_false}, {MP_ROM_QSTR(MP_QSTR_USE_SD_CARD), mp_const_false},
#endif
#ifdef USE_TOUCH
{MP_ROM_QSTR(MP_QSTR_USE_TOUCH), mp_const_true},
#else
{MP_ROM_QSTR(MP_QSTR_USE_TOUCH), mp_const_false},
#endif
#ifdef USE_BUTTON
{MP_ROM_QSTR(MP_QSTR_USE_BUTTON), mp_const_true},
#else
{MP_ROM_QSTR(MP_QSTR_USE_BUTTON), mp_const_false},
#endif #endif
{MP_ROM_QSTR(MP_QSTR_MODEL), MP_ROM_QSTR(MODEL_NAME_QSTR)}, {MP_ROM_QSTR(MP_QSTR_MODEL), MP_ROM_QSTR(MODEL_NAME_QSTR)},
{MP_ROM_QSTR(MP_QSTR_INTERNAL_MODEL), {MP_ROM_QSTR(MP_QSTR_INTERNAL_MODEL),

View File

@ -193,11 +193,10 @@ class WebUSB:
from . import fatfs, sdcard from . import fatfs, sdcard
POLL_READ: int # wait until interface is readable and return read data POLL_READ: int # wait until interface is readable and return read data
POLL_WRITE: int # wait until interface is writable POLL_WRITE: int # wait until interface is writable
TOUCH: int # interface id of the touch events INPUT: int # interface id of unified input events
TOUCH_START: int # event id of touch start event TOUCH_START: int # event id of touch start event
TOUCH_MOVE: int # event id of touch move event TOUCH_MOVE: int # event id of touch move event
TOUCH_END: int # event id of touch end event TOUCH_END: int # event id of touch end event
BUTTON: int # interface id of button events
BUTTON_PRESSED: int # button down event BUTTON_PRESSED: int # button down event
BUTTON_RELEASED: int # button up event BUTTON_RELEASED: int # button up event
BUTTON_LEFT: int # button number of left button BUTTON_LEFT: int # button number of left button

View File

@ -84,6 +84,8 @@ VERSION_MAJOR: int
VERSION_MINOR: int VERSION_MINOR: int
VERSION_PATCH: int VERSION_PATCH: int
USE_SD_CARD: bool USE_SD_CARD: bool
USE_TOUCH: bool
USE_BUTTON: bool
MODEL: str MODEL: str
INTERNAL_MODEL: str INTERNAL_MODEL: str
EMULATOR: bool EMULATOR: bool

View File

@ -233,7 +233,7 @@ class wait(Syscall):
Example: Example:
>>> hid_report, = await loop.wait(0xABCD) # await USB HID report >>> hid_report, = await loop.wait(0xABCD) # await USB HID report
>>> event, x, y = await loop.wait(io.TOUCH) # await touch event >>> event, x, y = await loop.wait(io.INPUT) # await input event
""" """
def __init__(self, msg_iface: int) -> None: def __init__(self, msg_iface: int) -> None:

View File

@ -118,46 +118,38 @@ class Component:
def __init__(self) -> None: def __init__(self) -> None:
self.repaint = True self.repaint = True
if utils.MODEL in ("T", "DISC1"): def dispatch(self, event: int, x: int, y: int) -> None:
from trezor.utils import USE_TOUCH, USE_BUTTON
def dispatch(self, event: int, x: int, y: int) -> None: if event is RENDER:
if event is RENDER: self.on_render()
self.on_render() elif USE_BUTTON and event is io.BUTTON_PRESSED:
elif event is io.TOUCH_START: self.on_button_pressed(x)
self.on_touch_start(x, y) elif USE_BUTTON and event is io.BUTTON_RELEASED:
elif event is io.TOUCH_MOVE: self.on_button_released(x)
self.on_touch_move(x, y) elif USE_TOUCH and event is io.TOUCH_START:
elif event is io.TOUCH_END: self.on_touch_start(x, y)
self.on_touch_end(x, y) elif USE_TOUCH and event is io.TOUCH_MOVE:
elif event is REPAINT: self.on_touch_move(x, y)
self.repaint = True elif USE_TOUCH and event is io.TOUCH_END:
self.on_touch_end(x, y)
elif event is REPAINT:
self.repaint = True
def on_touch_start(self, x: int, y: int) -> None: def on_touch_start(self, x: int, y: int) -> None:
pass pass
def on_touch_move(self, x: int, y: int) -> None: def on_touch_move(self, x: int, y: int) -> None:
pass pass
def on_touch_end(self, x: int, y: int) -> None: def on_touch_end(self, x: int, y: int) -> None:
pass pass
elif utils.MODEL in ("1", "R"): def on_button_pressed(self, button_number: int) -> None:
pass
def dispatch(self, event: int, x: int, y: int) -> None: def on_button_released(self, button_number: int) -> None:
if event is RENDER: pass
self.on_render()
elif event is io.BUTTON_PRESSED:
self.on_button_pressed(x)
elif event is io.BUTTON_RELEASED:
self.on_button_released(x)
elif event is REPAINT:
self.repaint = True
def on_button_pressed(self, button_number: int) -> None:
pass
def on_button_released(self, button_number: int) -> None:
pass
def on_render(self) -> None: def on_render(self) -> None:
pass pass
@ -253,33 +245,17 @@ class Layout(Component):
Usually overridden to add another tasks to the list.""" Usually overridden to add another tasks to the list."""
return self.handle_input(), self.handle_rendering() return self.handle_input(), self.handle_rendering()
if utils.MODEL in ("T", "DISC1"): def handle_input(self) -> Generator:
"""Task that is waiting for the user input."""
def handle_input(self) -> Generator: input_ = loop.wait(io.INPUT)
"""Task that is waiting for the user input.""" while True:
touch = loop.wait(io.TOUCH) # Using `yield` instead of `await` to avoid allocations.
while True: event, x, y = yield input_
# Using `yield` instead of `await` to avoid allocations. workflow.idle_timer.touch()
event, x, y = yield touch self.dispatch(event, x, y)
workflow.idle_timer.touch() # We dispatch a render event right after the input. Quick and dirty
self.dispatch(event, x, y) # way to get the lowest input-to-render latency.
# We dispatch a render event right after the touch. Quick and dirty self.dispatch(RENDER, 0, 0)
# way to get the lowest input-to-render latency.
self.dispatch(RENDER, 0, 0)
elif utils.MODEL in ("1", "R"):
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)
else:
raise ValueError("Unknown Trezor model")
def _before_render(self) -> None: def _before_render(self) -> None:
# Before the first render, we dim the display. # Before the first render, we dim the display.

View File

@ -218,7 +218,7 @@ class RustLayout(ui.Layout):
def handle_input_and_rendering(self) -> loop.Task: # type: ignore [awaitable-is-generator] def handle_input_and_rendering(self) -> loop.Task: # type: ignore [awaitable-is-generator]
from trezor import workflow from trezor import workflow
button = loop.wait(io.BUTTON) button = loop.wait(io.INPUT)
self._first_paint() self._first_paint()
while True: while True:
# Using `yield` instead of `await` to avoid allocations. # Using `yield` instead of `await` to avoid allocations.
@ -226,7 +226,7 @@ class RustLayout(ui.Layout):
workflow.idle_timer.touch() workflow.idle_timer.touch()
msg = None msg = None
if event in (io.BUTTON_PRESSED, io.BUTTON_RELEASED): if event in (io.BUTTON_PRESSED, io.BUTTON_RELEASED):
msg = self.layout.button_event(event, button_num) msg = self.layout.button_event(event & 0xFF, button_num)
if msg is not None: if msg is not None:
raise ui.Result(msg) raise ui.Result(msg)
self._paint() self._paint()

View File

@ -1,6 +1,6 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from trezor import io, loop, ui from trezor import io, loop, ui, utils
from trezor.enums import ButtonRequestType from trezor.enums import ButtonRequestType
from trezor.wire import ActionCancelled from trezor.wire import ActionCancelled
@ -187,15 +187,21 @@ class RustLayout(ui.Layout):
def handle_input_and_rendering(self) -> loop.Task: # type: ignore [awaitable-is-generator] def handle_input_and_rendering(self) -> loop.Task: # type: ignore [awaitable-is-generator]
from trezor import workflow from trezor import workflow
touch = loop.wait(io.TOUCH) input = loop.wait(io.INPUT)
self._first_paint() self._first_paint()
while True: while True:
# Using `yield` instead of `await` to avoid allocations. # Using `yield` instead of `await` to avoid allocations.
event, x, y = yield touch event, p0, p1 = yield input
workflow.idle_timer.touch() workflow.idle_timer.touch()
msg = None msg = None
if event in (io.TOUCH_START, io.TOUCH_MOVE, io.TOUCH_END): if utils.USE_TOUCH and event in (
msg = self.layout.touch_event(event, x, y) io.TOUCH_START,
io.TOUCH_MOVE,
io.TOUCH_END,
):
msg = self.layout.touch_event(event & 0xFF, p0, p1)
if utils.USE_BUTTON and event in (io.BUTTON_PRESSED, io.BUTTON_RELEASED):
msg = self.layout.button_event(event & 0xFF, p0)
if msg is not None: if msg is not None:
raise ui.Result(msg) raise ui.Result(msg)
self._paint() self._paint()

View File

@ -6,7 +6,9 @@ from trezorutils import ( # noqa: F401
INTERNAL_MODEL, INTERNAL_MODEL,
MODEL, MODEL,
SCM_REVISION, SCM_REVISION,
USE_BUTTON,
USE_SD_CARD, USE_SD_CARD,
USE_TOUCH,
VERSION_MAJOR, VERSION_MAJOR,
VERSION_MINOR, VERSION_MINOR,
VERSION_PATCH, VERSION_PATCH,

View File

@ -62,23 +62,23 @@ def test_touch(v):
d.refresh() d.refresh()
r = [0, 0] r = [0, 0]
# flush all events # flush all events
while io.poll([io.TOUCH], r, 10000): while io.poll([io.INPUT], r, 10000):
pass pass
# wait for event # wait for event
touch = False touch = False
while True: while True:
if not touch: if not touch:
if io.poll([io.TOUCH], r, 10000) and r[0] == io.TOUCH and r[1][0] == io.TOUCH_START: if io.poll([io.INPUT], r, 10000) and r[0] == io.INPUT and r[1][0] == io.TOUCH_START:
touch = True touch = True
else: else:
if io.poll([io.TOUCH], r, 10000) and r[0] == io.TOUCH and r[1][0] == io.TOUCH_END: if io.poll([io.INPUT], r, 10000) and r[0] == io.INPUT and r[1][0] == io.TOUCH_END:
print(f'OK {r[1][1]} {r[1][2]}') print(f'OK {r[1][1]} {r[1][2]}')
break break
if utime.ticks_us() > deadline: if utime.ticks_us() > deadline:
print('ERROR TIMEOUT') print('ERROR TIMEOUT')
break break
# flush all events # flush all events
while io.poll([io.TOUCH], r, 10000): while io.poll([io.INPUT], r, 10000):
pass pass
d.clear() d.clear()
d.refresh() d.refresh()