What changed, and why it matters
This commit is a routine user-interface cleanup. It centralizes how 'fast-forward' and 'fast-backward' button-hold navigation works across keypads, menus, QR transcribe, and seed editors. It also adds swipe gestures in more places and tweaks how the Yahboom device's extra page button behaves. There is no indication it fixes a security vulnerability or introduces a clear one.
No security action required. Treat as normal UI refactor review; verify Yahboom button remapping and swipe gesture behavior match intended user experience.
Security signals we found
No security-relevant signals in commit message or diff
Behavior change: Yahboom PAGE_PREV now aliases PAGE in several input paths
Behavior change: QR capture loop exits on TOUCH or non-Yahboom PAGE_PREV; on Yahboom PAGE_PREV no longer exits
Behavior change: Datum show navigation no longer wraps and hides total pages
Refactor only: no cryptographic, wallet, or secret-handling code changed
Evidence from the diff
The change refactors repeated inline ‘button turbo’ logic into a single Input.wait_for_fastnav_button() helper. It updates page_event/page_prev_event handling so that on the Yahboom board the PAGE_PREV button is treated as an alias for PAGE. It extends swipe navigation (SWIPE_UP/SWIPE_DOWN/SWIPE_LEFT/SWIPE_RIGHT) to keypads, menus, and QR transcribe mode. Datum show no longer wraps pages and hides total page count. Tests are updated to match the new helper and gesture mappings.
Changed components
src/krux/input.pysrc/krux/pages/__init__.pysrc/krux/pages/keypads.pysrc/krux/pages/qr_capture.pysrc/krux/pages/qr_view.pysrc/krux/pages/datum_tool.pysrc/krux/pages/mnemonic_editor.pysrc/krux/pages/stack_1248.pysrc/krux/pages/tiny_seed.pyInspect captured patch +214 / −175
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2812392..9834443 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,10 @@
# Changelog 25.XX.X - XXX 2025
### New Device Support: TZT
-The TZT CanMV is similar to the WonderMV but includes five buttons and a premium milled aluminum housing.
+The TZT CanMV is similar to the WonderMV but includes five buttons and a premium milled aluminum housing. Computer simulator for the TZT device is also included.
### New Device Support: WonderK PRO
-From the wonderful land of Korea, a new creation arrives: the WonderK PRO. Created by an entrepreneur who loves the Krux project, the WonderK follows in the footsteps of the WonderMV, but boasts a larger 2.8" display!
+From the wonderful land of Korea, a new creation arrives: the WonderK PRO. Created by an entrepreneur who loves the Krux project, the WonderK follows in the footsteps of the WonderMV, but boasts a larger 2.8" display! Computer simulator for the WonderK device is also included.
### Code optimization
Reduced firmware size by 25% and lowered RAM usage through code cleanup and optimizations.
@@ -19,6 +19,8 @@ The Maix Bit device has long been discouraged due to its poor-quality camera. St
- Touchscreen test added in Tools for detection check
- wbits for deflate-decompress window set to 10 bits to match KEF spec.
- Remove "Reboot" option and status bar, when empty, from Login menu
+- Optimized Datum show: total pages are no longer visible, and navigation no longer wraps from the first page to the last or vice versa
+- Enabled swipe up/down gestures on keypads, menus, and QR transcribe
# Changelog 25.09.0 - September 2025
diff --git a/src/krux/context.py b/src/krux/context.py
index 470e173..62d2400 100644
--- a/src/krux/context.py
+++ b/src/krux/context.py
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import gc
-from .display import display
+from .display import display, Display
from .input import Input
from .camera import Camera
from .light import Light
@@ -33,9 +33,9 @@ class Context:
"""
def __init__(self):
- self.display = display
- self.input = Input()
- self.camera = Camera()
+ self.display = display # type: Display
+ self.input = Input() # type: Input
+ self.camera = Camera() # type: Camera
self.light = Light() if kboard.has_light else None
self.power_manager = None
self.wallet = None
diff --git a/src/krux/input.py b/src/krux/input.py
index 77b344c..0eb0762 100644
--- a/src/krux/input.py
+++ b/src/krux/input.py
@@ -159,18 +159,14 @@ class Input:
def page_event(self):
"""Intermediary method to pull button PAGE event"""
- event_val = False
if self.page is not None:
- event_val = self.page.event()
- if kboard.is_yahboom:
- event_val = event_val or self.page_prev_event(check_yahboom=True)
- return event_val
+ return self.page.event()
+ return False
- def page_prev_event(self, check_yahboom=False):
+ def page_prev_event(self):
"""Intermediary method to pull button PAGE_PREV event"""
- if not kboard.is_yahboom or check_yahboom:
- if self.page_prev is not None:
- return self.page_prev.event()
+ if self.page_prev is not None:
+ return self.page_prev.event()
return False
def touch_event(self, validate_position=True):
@@ -236,6 +232,8 @@ class Input:
if self.page_event():
return BUTTON_PAGE
if self.page_prev_event():
+ if kboard.is_yahboom:
+ return BUTTON_PAGE
return BUTTON_PAGE_PREV
if self.touch_event():
return BUTTON_TOUCH
@@ -293,7 +291,10 @@ class Input:
if btn == BUTTON_ENTER:
return self.enter_value() == PRESSED
if btn == BUTTON_PAGE:
- return self.page_value() == PRESSED
+ val = self.page_value() == PRESSED
+ if kboard.is_yahboom and self.page_prev_value() == PRESSED:
+ return True
+ return val
# if btn == BUTTON_PAGE_PREV:
return self.page_prev_value() == PRESSED
@@ -338,6 +339,18 @@ class Input:
self.debounce_time = time.ticks_ms()
return btn
+ def wait_for_fastnav_button(self, block=True, wait_duration=QR_ANIM_PERIOD):
+ """Wait for a button press, with support for fast navigation."""
+ if self.page_value() == PRESSED:
+ time.sleep_ms(KEY_REPEAT_DELAY_MS)
+ return FAST_FORWARD
+ if self.page_prev_value() == PRESSED:
+ time.sleep_ms(KEY_REPEAT_DELAY_MS)
+ if kboard.is_yahboom:
+ return FAST_FORWARD
+ return FAST_BACKWARD
+ return self.wait_for_button(block, wait_duration)
+
def flush_events(self):
"""Clean eventual event flags unintentionally collected"""
self.enter_event()
diff --git a/src/krux/pages/__init__.py b/src/krux/pages/__init__.py
index 1094987..3d26bd4 100644
--- a/src/krux/pages/__init__.py
+++ b/src/krux/pages/__init__.py
@@ -23,6 +23,7 @@ import gc
import time
import lcd
import _thread
+from ..context import Context
from .keypads import Keypad
from ..themes import theme, WHITE, GREEN, DARKGREY
from ..input import (
@@ -37,9 +38,7 @@ from ..input import (
SWIPE_LEFT,
SWIPE_RIGHT,
ONE_MINUTE,
- KEY_REPEAT_DELAY_MS,
)
-from ..buttons import PRESSED
from ..display import (
DEFAULT_PADDING,
MINIMAL_PADDING,
@@ -90,7 +89,7 @@ class Page:
Must be subclassed.
"""
- def __init__(self, ctx, menu=None):
+ def __init__(self, ctx: Context, menu=None):
self.ctx = ctx
self.menu = menu
# context has its own keypad mapping in case touch is not used
@@ -165,9 +164,10 @@ class Page:
pad.get_valid_index()
pad.draw_keys()
pad.draw_keyset_index()
- btn = self.ctx.input.wait_for_button()
show_swipe_hint = False # unless overridden by a particular key,
# don't show the swipe hint after a key press
+
+ btn = self.ctx.input.wait_for_fastnav_button()
if btn == BUTTON_TOUCH:
btn = pad.touch_to_physical()
if btn == BUTTON_ENTER:
@@ -207,7 +207,7 @@ class Page:
if changed and go_on_change:
break
else:
- if btn in (SWIPE_RIGHT, SWIPE_LEFT):
+ if btn in (SWIPE_UP, SWIPE_LEFT, SWIPE_DOWN, SWIPE_RIGHT):
swipe_has_not_been_used = False
pad.navigate(btn)
if kboard.has_touchscreen:
@@ -619,7 +619,7 @@ class Menu:
def __init__(
self,
- ctx,
+ ctx: Context,
menu,
offset=None,
disable_statusbar=False,
@@ -658,19 +658,6 @@ class Menu:
ScreenSaver(self.ctx).start()
- def _get_btn_input(self):
- if self.ctx.input.page_value() == PRESSED:
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- return FAST_FORWARD
- if self.ctx.input.page_prev_value() == PRESSED:
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- return FAST_BACKWARD
- return self.ctx.input.wait_for_button(
- # Block if screen saver not active
- block=Settings().appearance.screensaver_time == 0,
- wait_duration=Settings().appearance.screensaver_time * ONE_MINUTE,
- )
-
def _process_page(self, selected_item_index):
selected_item_index = (selected_item_index + 1) % len(self.menu_view)
if selected_item_index == 0:
@@ -742,7 +729,12 @@ class Menu:
return (self.menu_view.index(selected_item_index), status)
start_from_submenu = False
else:
- btn = self._get_btn_input()
+ screensaver_time = Settings().appearance.screensaver_time
+ btn = self.ctx.input.wait_for_fastnav_button(
+ # Block if screen saver not active
+ screensaver_time == 0,
+ screensaver_time * ONE_MINUTE,
+ )
if kboard.has_touchscreen:
if btn == BUTTON_TOUCH:
selected_item_index = self.ctx.input.touch.current_index()
@@ -756,8 +748,8 @@ class Menu:
selected_item_index = self._process_page(selected_item_index)
elif btn in (BUTTON_PAGE_PREV, FAST_BACKWARD):
selected_item_index = self._process_page_prev(selected_item_index)
- elif btn in (SWIPE_UP, SWIPE_DOWN):
- if btn == SWIPE_UP:
+ elif btn in (SWIPE_UP, SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT):
+ if btn in (SWIPE_UP, SWIPE_LEFT):
selected_item_index = self._process_swipe_up(
selected_item_index, swipe_up_fnc
)
diff --git a/src/krux/pages/datum_tool.py b/src/krux/pages/datum_tool.py
index 2cac04d..97a1d27 100644
--- a/src/krux/pages/datum_tool.py
+++ b/src/krux/pages/datum_tool.py
@@ -49,9 +49,7 @@ from ..input import (
BUTTON_PAGE_PREV,
SWIPE_RIGHT,
SWIPE_DOWN,
- KEY_REPEAT_DELAY_MS,
)
-from ..buttons import PRESSED
DATUM_DESCRIPTOR = "DESC"
DATUM_PSBT = "PSBT"
@@ -577,7 +575,6 @@ class DatumTool(Page):
"""Displays infobox and contents"""
from binascii import hexlify
from ..kboard import kboard
- import time
page_indicator = "p.%d"
max_lines = 0
@@ -618,14 +615,7 @@ class DatumTool(Page):
self.ctx.display.draw_string(offset_x, offset_y, line)
offset_y += FONT_HEIGHT
- if self.ctx.input.page_value() == PRESSED:
- btn = FAST_FORWARD
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- elif self.ctx.input.page_prev_value() == PRESSED:
- btn = FAST_BACKWARD
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- else:
- btn = self.ctx.input.wait_for_button()
+ btn = self.ctx.input.wait_for_fastnav_button()
if btn in (BUTTON_PAGE, FAST_FORWARD, SWIPE_UP, SWIPE_LEFT):
if curr_page + 1 < len(pages):
curr_page += 1
diff --git a/src/krux/pages/keypads.py b/src/krux/pages/keypads.py
index 6dba0bb..8f50f2e 100644
--- a/src/krux/pages/keypads.py
+++ b/src/krux/pages/keypads.py
@@ -21,7 +21,6 @@
# THE SOFTWARE.
import math
-import time
import lcd
from ..krux_settings import t
from ..themes import theme
@@ -31,11 +30,11 @@ from ..input import (
BUTTON_PAGE_PREV,
SWIPE_RIGHT,
SWIPE_LEFT,
+ SWIPE_UP,
+ SWIPE_DOWN,
FAST_FORWARD,
FAST_BACKWARD,
- KEY_REPEAT_DELAY_MS,
)
-from ..buttons import PRESSED
from ..display import DEFAULT_PADDING, MINIMAL_PADDING, FONT_HEIGHT, FONT_WIDTH
from ..kboard import kboard
@@ -271,28 +270,14 @@ class Keypad:
def navigate(self, btn):
"""Groups navigation methods in one place"""
- if btn == BUTTON_PAGE:
+ if btn in (BUTTON_PAGE, FAST_FORWARD):
self._next_key()
- elif btn == BUTTON_PAGE_PREV:
+ elif btn in (BUTTON_PAGE_PREV, FAST_BACKWARD):
self._previous_key()
- elif btn == SWIPE_LEFT:
+ elif btn in (SWIPE_UP, SWIPE_LEFT):
self.next_keyset()
- elif btn == SWIPE_RIGHT:
+ elif btn in (SWIPE_DOWN, SWIPE_RIGHT):
self.previous_keyset()
- elif btn == FAST_FORWARD:
- while self.ctx.input.page_value() == PRESSED:
- self._next_key()
- self.get_valid_index()
- self._clean_keypad_area()
- self.draw_keys()
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- elif btn == FAST_BACKWARD:
- while self.ctx.input.page_prev_value() == PRESSED:
- self._previous_key()
- self.get_valid_index()
- self._clean_keypad_area()
- self.draw_keys()
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
def _clean_keypad_area(self):
self.ctx.display.fill_rectangle(
diff --git a/src/krux/pages/mnemonic_editor.py b/src/krux/pages/mnemonic_editor.py
index b54a8b4..916ead3 100644
--- a/src/krux/pages/mnemonic_editor.py
+++ b/src/krux/pages/mnemonic_editor.py
@@ -33,12 +33,9 @@ from ..input import (
BUTTON_PAGE_PREV,
FAST_FORWARD,
FAST_BACKWARD,
- KEY_REPEAT_DELAY_MS,
)
-from ..buttons import PRESSED
from ..key import Key
from ..kboard import kboard
-import time
GO_INDEX = 25
ESC_INDEX = 24
@@ -300,14 +297,7 @@ class MnemonicEditor(Page):
self.ctx.display.clear()
self._draw_header()
self._map_words(button_index, page)
- if self.ctx.input.page_value() == PRESSED:
- btn = FAST_FORWARD
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- elif self.ctx.input.page_prev_value() == PRESSED:
- btn = FAST_BACKWARD
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- else:
- btn = self.ctx.input.wait_for_button()
+ btn = self.ctx.input.wait_for_fastnav_button()
if btn == BUTTON_TOUCH:
button_index = self.ctx.input.touch.current_index()
if button_index < ESC_INDEX:
diff --git a/src/krux/pages/qr_capture.py b/src/krux/pages/qr_capture.py
index b3f1431..1fedf0c 100644
--- a/src/krux/pages/qr_capture.py
+++ b/src/krux/pages/qr_capture.py
@@ -157,14 +157,17 @@ class QRCodeCapture(Page):
break
# Anti-glare / zoom / normal mode
- if self.ctx.input.page_event():
+ page_prev_event = self.ctx.input.page_prev_event()
+ if self.ctx.input.page_event() or (kboard.is_yahboom and page_prev_event):
if self.ctx.camera.has_antiglare():
self.anti_glare_control()
else:
break
- # Exit the capture loop with PAGE_PREV or TOUCH
- if self.ctx.input.page_prev_event() or self.ctx.input.touch_event():
+ # Exit the capture loop with TOUCH or PAGE_PREV (except yahboom)
+ if self.ctx.input.touch_event() or (
+ not kboard.is_yahboom and page_prev_event
+ ):
break
if new_part is not None and new_part != previous_part:
diff --git a/src/krux/pages/qr_view.py b/src/krux/pages/qr_view.py
index 564aba6..6c224b3 100644
--- a/src/krux/pages/qr_view.py
+++ b/src/krux/pages/qr_view.py
@@ -518,7 +518,7 @@ class SeedQRView(Page):
mode = 0
while True:
button = None
- while button not in (SWIPE_DOWN, SWIPE_UP):
+ while True:
def toggle_brightness():
if self.qr_foreground == WHITE:
@@ -539,11 +539,15 @@ class SeedQRView(Page):
highlight_function(label, y_offset)
button = self.ctx.input.wait_for_button()
if transcript_tools:
- if button in (BUTTON_PAGE, SWIPE_LEFT): # page, swipe
+ if button in (BUTTON_PAGE, SWIPE_UP, SWIPE_LEFT): # page, swipe
mode += 1
mode %= 5
self.lr_index = 0
- elif button in (BUTTON_PAGE_PREV, SWIPE_RIGHT): # page, swipe
+ elif button in (
+ BUTTON_PAGE_PREV,
+ SWIPE_DOWN,
+ SWIPE_RIGHT,
+ ): # page, swipe
mode -= 1
mode %= 5
self.lr_index = 0
@@ -554,7 +558,7 @@ class SeedQRView(Page):
self.lr_index += 1
else:
if not (button == BUTTON_TOUCH and mode == TRANSCRIBE_MODE):
- button = SWIPE_DOWN # leave
+ break # leave
if mode == LINE_MODE:
self.lr_index %= self.qr_size
elif mode in (REGION_MODE, ZOOMED_R_MODE):
diff --git a/src/krux/pages/stack_1248.py b/src/krux/pages/stack_1248.py
index c1dd253..0eaa700 100644
--- a/src/krux/pages/stack_1248.py
+++ b/src/krux/pages/stack_1248.py
@@ -32,11 +32,8 @@ from ..input import (
BUTTON_TOUCH,
FAST_FORWARD,
FAST_BACKWARD,
- KEY_REPEAT_DELAY_MS,
)
-from ..buttons import PRESSED
from ..kboard import kboard
-import time
STACKBIT_GO_INDEX = 38
STACKBIT_ESC_INDEX = 35
@@ -426,14 +423,7 @@ class Stackbit(Page):
self._draw_index(index)
self.preview_word(digits)
self._draw_punched(digits, y_offset)
- if self.ctx.input.page_value() == PRESSED:
- btn = FAST_FORWARD
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- elif self.ctx.input.page_prev_value() == PRESSED:
- btn = FAST_BACKWARD
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- else:
- btn = self.ctx.input.wait_for_button()
+ btn = self.ctx.input.wait_for_fastnav_button()
if btn == BUTTON_TOUCH:
btn = BUTTON_ENTER
index = self.ctx.input.touch.current_index()
diff --git a/src/krux/pages/tiny_seed.py b/src/krux/pages/tiny_seed.py
index 0c9cee4..8cbf29e 100644
--- a/src/krux/pages/tiny_seed.py
+++ b/src/krux/pages/tiny_seed.py
@@ -43,9 +43,7 @@ from ..input import (
BUTTON_TOUCH,
FAST_FORWARD,
FAST_BACKWARD,
- KEY_REPEAT_DELAY_MS,
)
-from ..buttons import PRESSED
from ..bip39 import entropy_checksum
from ..kboard import kboard
@@ -377,14 +375,7 @@ class TinySeed(Page):
if self.ctx.input.buttons_active:
self._draw_index(index)
- if self.ctx.input.page_value() == PRESSED:
- btn = FAST_FORWARD
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- elif self.ctx.input.page_prev_value() == PRESSED:
- btn = FAST_BACKWARD
- time.sleep_ms(KEY_REPEAT_DELAY_MS)
- else:
- btn = self.ctx.input.wait_for_button()
+ btn = self.ctx.input.wait_for_fastnav_button()
if btn == BUTTON_TOUCH:
btn = BUTTON_ENTER
index = self.ctx.input.touch.current_index()
diff --git a/tests/pages/__init__.py b/tests/pages/__init__.py
index b1e81ea..f9543cf 100644
--- a/tests/pages/__init__.py
+++ b/tests/pages/__init__.py
@@ -4,12 +4,14 @@ from ..shared_mocks import mock_context, MockPrinter
def create_ctx(mocker, btn_seq, wallet=None, printer=None, touch_seq=None):
"""Helper to create mocked context obj"""
from krux.krux_settings import Settings, THERMAL_ADAFRUIT_TXT
+ from krux.context import Context
HASHED_IMAGE_BYTES = b"3\x0fr\x7fKY\x15\t\x83\xaab\x92\x0f&\x820\xb4\x14\x87\x19\xee\x95F\x9c\x8f\x0c\xbdo\xbc\x1d\xcbT"
- ctx = mock_context(mocker)
+ ctx: Context = mock_context(mocker)
ctx.power_manager.battery_charge_remaining.return_value = 1
ctx.input.wait_for_button = mocker.MagicMock(side_effect=btn_seq)
+ ctx.input.wait_for_fastnav_button = ctx.input.wait_for_button
ctx.camera.capture_entropy = mocker.MagicMock(return_value=HASHED_IMAGE_BYTES)
ctx.display.qr_offset = mocker.MagicMock(return_value=250)
ctx.is_logged_in.return_value = False
diff --git a/tests/pages/test_datum_tool.py b/tests/pages/test_datum_tool.py
index b862e15..298972d 100644
--- a/tests/pages/test_datum_tool.py
+++ b/tests/pages/test_datum_tool.py
@@ -1095,25 +1095,28 @@ def test_datumtool_view_contents_multi_page(m5stickv, mocker):
def test_datumtool_show_contents_button_turbo(mocker, m5stickv):
from krux.pages.datum_tool import DatumTool
- from krux.input import PRESSED, BUTTON_ENTER, KEY_REPEAT_DELAY_MS
+ from krux.input import Input, PRESSED, BUTTON_ENTER, KEY_REPEAT_DELAY_MS
import time
ctx = create_ctx(mocker, [BUTTON_ENTER, BUTTON_ENTER])
+ input = Input()
+ input.wait_for_button = ctx.input.wait_for_button
+ ctx.input.wait_for_fastnav_button = input.wait_for_fastnav_button
datum = DatumTool(ctx)
datum.contents = "testing 123 " * 250
mocker.patch("time.sleep_ms", new=mocker.MagicMock())
# fast forward
- ctx.input.page_value = mocker.MagicMock(side_effect=[PRESSED, None])
+ input.page_value = mocker.MagicMock(side_effect=[PRESSED, None])
datum._show_contents()
time.sleep_ms.assert_called_with(KEY_REPEAT_DELAY_MS)
# fast backward
- ctx.input.page_value = mocker.MagicMock(return_value=None)
- ctx.input.page_prev_value = mocker.MagicMock(side_effect=[PRESSED, None])
+ input.page_value = mocker.MagicMock(return_value=None)
+ input.page_prev_value = mocker.MagicMock(side_effect=[PRESSED, None])
datum._show_contents()
diff --git a/tests/pages/test_menu.py b/tests/pages/test_menu.py
index 203b4e2..cf6d821 100644
--- a/tests/pages/test_menu.py
+++ b/tests/pages/test_menu.py
@@ -34,23 +34,23 @@ def test_run_loop(mocker, m5stickv):
BTN_SEQUENCE = [BUTTON_ENTER, BUTTON_PAGE, BUTTON_ENTER]
call_count = len(BTN_SEQUENCE)
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
ctx.power_manager.battery_charge_remaining.return_value = 1
index, status = menu.run_loop()
assert index == 1
assert status == MENU_EXIT
- assert ctx.input.wait_for_button.call_count == call_count
+ assert ctx.input.wait_for_fastnav_button.call_count == call_count
BTN_SEQUENCE = [BUTTON_PAGE, BUTTON_PAGE, BUTTON_ENTER]
call_count += len(BTN_SEQUENCE)
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
ctx.power_manager.battery_charge_remaining.return_value = 1
index, status = menu.run_loop()
assert index == 2
assert status == MENU_SHUTDOWN
- assert ctx.input.wait_for_button.call_count == call_count
+ assert ctx.input.wait_for_fastnav_button.call_count == call_count
BTN_SEQUENCE = [
BUTTON_PAGE,
@@ -63,13 +63,13 @@ def test_run_loop(mocker, m5stickv):
BUTTON_ENTER,
]
call_count += len(BTN_SEQUENCE)
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
ctx.power_manager.battery_charge_remaining.return_value = 1
index, status = menu.run_loop()
assert index == 1
assert status == MENU_EXIT
- assert ctx.input.wait_for_button.call_count == call_count
+ assert ctx.input.wait_for_fastnav_button.call_count == call_count
def test_run_loop_on_amigo_tft(mocker, amigo):
@@ -100,13 +100,13 @@ def test_run_loop_on_amigo_tft(mocker, amigo):
BUTTON_ENTER,
]
call_count = len(BTN_SEQUENCE)
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
ctx.power_manager.battery_charge_remaining.return_value = 1
index, status = menu.run_loop()
assert index == 1
assert status == MENU_EXIT
- assert ctx.input.wait_for_button.call_count == call_count
+ assert ctx.input.wait_for_fastnav_button.call_count == call_count
BTN_SEQUENCE = [
BUTTON_PAGE_PREV,
@@ -114,24 +114,24 @@ def test_run_loop_on_amigo_tft(mocker, amigo):
BUTTON_ENTER,
]
call_count += len(BTN_SEQUENCE)
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
ctx.power_manager.battery_charge_remaining.return_value = 1
index, status = menu.run_loop()
assert index == 2
assert status == MENU_SHUTDOWN
- assert ctx.input.wait_for_button.call_count == call_count
+ assert ctx.input.wait_for_fastnav_button.call_count == call_count
mocker.patch.object(ctx.input.touch, "current_index", new=lambda: 1)
mocker.patch.object(ctx.input, "buttons_active", False)
BTN_SEQUENCE = [BUTTON_TOUCH]
call_count += len(BTN_SEQUENCE)
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
index, status = menu.run_loop()
assert index == 1
assert status == MENU_EXIT
- assert ctx.input.wait_for_button.call_count == call_count
+ assert ctx.input.wait_for_fastnav_button.call_count == call_count
def test_two_screens_menu(mocker, amigo):
@@ -159,7 +159,7 @@ def test_two_screens_menu(mocker, amigo):
+ [BUTTON_ENTER] # click back
)
ctx = mock_context(mocker)
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
# each menu page has 3 items
ctx.display.max_menu_lines.return_value = 3
print(ctx.display.max_menu_lines())
@@ -185,14 +185,14 @@ def test_two_screens_menu(mocker, amigo):
assert seq_clicked == first_seq_clicked
assert index == menu.back_index
assert status == MENU_EXIT
- assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE)
+ assert ctx.input.wait_for_fastnav_button.call_count == len(BTN_SEQUENCE)
BTN_SEQUENCE = (
[BUTTON_ENTER] # click index 0
+ [BUTTON_PAGE]
+ [BUTTON_ENTER] # click index 1
+ [BUTTON_ENTER] # click index 1
- + [SWIPE_DOWN] # go to last index on new menu page
+ + [SWIPE_DOWN] # go to last index on new menu page #5
+ [BUTTON_PAGE_PREV] # go to one before back
+ [BUTTON_ENTER] # click index 4
+ [SWIPE_UP] # go to first index on new menu page
@@ -203,8 +203,11 @@ def test_two_screens_menu(mocker, amigo):
+ [BUTTON_ENTER] # click back
)
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
- ctx.input.wait_for_button.call_count = 0
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.call_count = 0
+ ctx.input.wait_for_button = (
+ ctx.input.wait_for_fastnav_button
+ ) # screensaver will call for wait_for_button
seq_clicked = []
menu_items = []
@@ -214,28 +217,12 @@ def test_two_screens_menu(mocker, amigo):
ctx,
menu_items,
)
+ print(menu_items)
index, status = menu.run_loop()
assert seq_clicked == [0, 1, 1, 4, 0]
assert index == menu.back_index
assert status == MENU_EXIT
- assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE)
-
-
-def test_fast_forward(mocker, m5stickv):
- from krux.input import PRESSED, FAST_FORWARD, FAST_BACKWARD
- from krux.pages import Menu
-
- ctx = mock_context(mocker)
- ctx.input.page_value = mocker.MagicMock(return_value=PRESSED)
- menu = Menu(ctx, [])
-
- assert menu._get_btn_input() == FAST_FORWARD
-
- ctx.input.page_value = mocker.MagicMock(return_value=None)
-
- ctx.input.page_prev_value = mocker.MagicMock(return_value=PRESSED)
-
- assert menu._get_btn_input() == FAST_BACKWARD
+ assert ctx.input.wait_for_fastnav_button.call_count == len(BTN_SEQUENCE)
def test_swipe_up(mocker, amigo):
@@ -254,7 +241,7 @@ def test_swipe_up(mocker, amigo):
BTN_SEQUENCE = [
SWIPE_UP,
]
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
index, status = menu.run_loop(swipe_up_fnc=swipe_fnc)
assert index == 1
assert status == MENU_EXIT
@@ -285,12 +272,12 @@ def test_start_from(mocker, m5stickv):
# test start menu clicking on index 0 that will NOT exit
BTN_SEQUENCE = [BUTTON_PAGE_PREV] + [BUTTON_ENTER] # go to back # click back
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
index, status = menu.run_loop(start_from_index=0)
assert index == menu.back_index
assert status == MENU_EXIT
mock_fnc.assert_called()
- assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE)
+ assert ctx.input.wait_for_fastnav_button.call_count == len(BTN_SEQUENCE)
def test_disabled_entry(mocker, m5stickv):
@@ -302,9 +289,9 @@ def test_disabled_entry(mocker, m5stickv):
BTN_SEQUENCE = (
[BUTTON_ENTER] + [BUTTON_PAGE] + [BUTTON_ENTER] # click disabled # click back
)
- ctx.input.wait_for_button.side_effect = BTN_SEQUENCE
+ ctx.input.wait_for_fastnav_button.side_effect = BTN_SEQUENCE
menu = Menu(ctx, menu_items)
index, status = menu.run_loop()
assert index == menu.back_index
assert status == MENU_EXIT
- assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE)
+ assert ctx.input.wait_for_fastnav_button.call_count == len(BTN_SEQUENCE)
diff --git a/tests/pages/test_mnemonic_editor.py b/tests/pages/test_mnemonic_editor.py
index 63a82ae..19c0a29 100644
--- a/tests/pages/test_mnemonic_editor.py
+++ b/tests/pages/test_mnemonic_editor.py
@@ -43,17 +43,20 @@ def test_loop_through_words(mocker, cube):
def test_button_turbo(mocker, m5stickv):
from krux.pages.mnemonic_editor import MnemonicEditor
- from krux.input import PRESSED
+ from krux.input import PRESSED, Input
import pytest
ctx = create_ctx(mocker, [])
+ input = Input()
+ input.wait_for_button = ctx.input.wait_for_button
+ ctx.input.wait_for_fastnav_button = input.wait_for_fastnav_button
mnemonic_editor = MnemonicEditor(ctx, TEST_12_WORD_MNEMONIC)
mnemonic_editor._map_words = mocker.MagicMock(
side_effect=[True, ValueError, True, ValueError]
)
# fast forward
- ctx.input.page_value = mocker.MagicMock(return_value=PRESSED)
+ input.page_value = mocker.MagicMock(return_value=PRESSED)
with pytest.raises(ValueError):
edited_mnemonic = mnemonic_editor.edit()
@@ -65,8 +68,8 @@ def test_button_turbo(mocker, m5stickv):
)
# fast backward
- ctx.input.page_value = mocker.MagicMock(return_value=None)
- ctx.input.page_prev_value = mocker.MagicMock(return_value=PRESSED)
+ input.page_value = mocker.MagicMock(return_value=None)
+ input.page_prev_value = mocker.MagicMock(return_value=PRESSED)
with pytest.raises(ValueError):
edited_mnemonic = mnemonic_editor.edit()
diff --git a/tests/pages/test_qr_capture.py b/tests/pages/test_qr_capture.py
index 2d2e30f..49ec844 100644
--- a/tests/pages/test_qr_capture.py
+++ b/tests/pages/test_qr_capture.py
@@ -151,8 +151,8 @@ def test_light_control(mocker, multiple_devices):
]
ctx.input.enter_value = mocker.MagicMock(side_effect=ENTER_SEQ)
# Leaves the loop at 5th iteration
- PAGE_PREV_SEQ = [False, False, False, False, True]
- ctx.input.page_prev_event = mocker.MagicMock(side_effect=PAGE_PREV_SEQ)
+ TOUCH_PREV_SEQ = [False, False, False, False, True]
+ ctx.input.touch_event = mocker.MagicMock(side_effect=TOUCH_PREV_SEQ)
qr_capturer = QRCodeCapture(ctx)
qr_code, qr_format = qr_capturer.qr_capture_loop()
assert qr_code == None
diff --git a/tests/pages/test_qr_view.py b/tests/pages/test_qr_view.py
index 06c169c..64f5869 100644
--- a/tests/pages/test_qr_view.py
+++ b/tests/pages/test_qr_view.py
@@ -101,7 +101,7 @@ def test_init_qr_view_background_white(amigo, mocker, mocker_theme_background_wh
def test_load_qr_no_title(mocker, amigo):
- from krux.input import SWIPE_DOWN
+ from krux.input import BUTTON_TOUCH
from krux.pages import MENU_CONTINUE
from krux.pages.qr_view import SeedQRView
@@ -116,7 +116,7 @@ def test_load_qr_no_title(mocker, amigo):
# UI methods like wait_for_button,
# draw_hcentered_text, draw_grided_qr, etc..
mocker.patch.object(qr_view, "draw_grided_qr")
- mocker.patch.object(qr_view.ctx.input, "wait_for_button", return_value=SWIPE_DOWN)
+ mocker.patch.object(qr_view.ctx.input, "wait_for_button", return_value=BUTTON_TOUCH)
mocker.patch.object(qr_view.ctx.display, "height", return_value=240)
mocker.patch.object(qr_view.ctx.display, "width", return_value=240)
mocker.patch.object(qr_view.ctx.display, "qr_offset", return_value=10)
@@ -130,7 +130,7 @@ def test_load_qr_no_title(mocker, amigo):
def test_display_qr_toggle_brightness(amigo, mocker):
- from krux.input import BUTTON_PAGE, SWIPE_DOWN
+ from krux.input import BUTTON_PAGE, BUTTON_TOUCH
from krux.pages import MENU_CONTINUE
from krux.pages.qr_view import SeedQRView
from krux.themes import DARKGREY, WHITE
@@ -143,7 +143,7 @@ def test_display_qr_toggle_brightness(amigo, mocker):
ctx.input = mocker.Mock()
ctx.input.wait_for_button.side_effect = [
BUTTON_PAGE,
- SWIPE_DOWN,
+ BUTTON_TOUCH,
]
ctx.display = mocker.Mock()
diff --git a/tests/pages/test_settings_page.py b/tests/pages/test_settings_page.py
index 4b2bfef..1222640 100644
--- a/tests/pages/test_settings_page.py
+++ b/tests/pages/test_settings_page.py
@@ -343,12 +343,13 @@ def test_settings_on_amigo_tft(amigo, mocker, mocker_printer):
]
case_num = 0
for case in cases:
- print("test_settings_on_amigo_tft cases[" + str(case_num) + "]")
+ print("test_settings_on_amigo_tft cases[" + str(case_num) + "]", case[0])
case_num = case_num + 1
ctx = mock_context(mocker)
ctx.power_manager.battery_charge_remaining.return_value = 1
ctx.input.wait_for_button = mocker.MagicMock(return_value=BUTTON_TOUCH)
+ ctx.input.wait_for_fastnav_button = ctx.input.wait_for_button
ctx.input.touch = mocker.MagicMock(
current_index=mocker.MagicMock(side_effect=case[0])
)
diff --git a/tests/pages/test_stackbit.py b/tests/pages/test_stackbit.py
index 87657a3..e695db7 100644
--- a/tests/pages/test_stackbit.py
+++ b/tests/pages/test_stackbit.py
@@ -155,15 +155,18 @@ def test_esc_entering_stackbit(amigo, mocker):
def test_entering_stackbit_buttons_turbo(mocker, m5stickv):
from krux.pages.stack_1248 import Stackbit
- from krux.input import PRESSED, FAST_FORWARD, FAST_BACKWARD
+ from krux.input import PRESSED, FAST_FORWARD, FAST_BACKWARD, Input
import pytest
ctx = create_ctx(mocker, [])
+ input = Input()
+ input.wait_for_button = ctx.input.wait_for_button
+ ctx.input.wait_for_fastnav_button = input.wait_for_fastnav_button
stackbit = Stackbit(ctx)
stackbit.index = mocker.MagicMock(side_effect=ValueError)
# fast forward
- ctx.input.page_value = mocker.MagicMock(return_value=PRESSED)
+ input.page_value = mocker.MagicMock(return_value=PRESSED)
with pytest.raises(ValueError):
stackbit.enter_1248()
@@ -171,8 +174,8 @@ def test_entering_stackbit_buttons_turbo(mocker, m5stickv):
stackbit.index.assert_called_with(0, FAST_FORWARD)
# fast backward
- ctx.input.page_value = mocker.MagicMock(return_value=None)
- ctx.input.page_prev_value = mocker.MagicMock(return_value=PRESSED)
+ input.page_value = mocker.MagicMock(return_value=None)
+ input.page_prev_value = mocker.MagicMock(return_value=PRESSED)
with pytest.raises(ValueError):
stackbit.enter_1248()
diff --git a/tests/pages/test_tiny_seed.py b/tests/pages/test_tiny_seed.py
index 1582023..42fe572 100644
--- a/tests/pages/test_tiny_seed.py
+++ b/tests/pages/test_tiny_seed.py
@@ -96,23 +96,26 @@ def test_enter_tiny_seed_12w_m5stickv(m5stickv, mocker):
def test_enter_tiny_seed_button_turbo(mocker, m5stickv):
from krux.pages.tiny_seed import TinySeed
- from krux.input import PRESSED, FAST_FORWARD, FAST_BACKWARD
+ from krux.input import PRESSED, FAST_FORWARD, FAST_BACKWARD, Input
import pytest
ctx = create_ctx(mocker, [])
+ input = Input()
+ input.wait_for_button = ctx.input.wait_for_button
+ ctx.input.wait_for_fastnav_button = input.wait_for_fastnav_button
tiny_seed = TinySeed(ctx)
tiny_seed._new_index = mocker.MagicMock(side_effect=ValueError)
# fast forward
- ctx.input.page_value = mocker.MagicMock(return_value=PRESSED)
+ input.page_value = mocker.MagicMock(return_value=PRESSED)
with pytest.raises(ValueError):
tiny_seed.enter_tiny_seed()
tiny_seed._new_index.assert_called_with(0, FAST_FORWARD, False, 0)
# fast backward
- ctx.input.page_value = mocker.MagicMock(return_value=None)
- ctx.input.page_prev_value = mocker.MagicMock(return_value=PRESSED)
+ input.page_value = mocker.MagicMock(return_value=None)
+ input.page_prev_value = mocker.MagicMock(return_value=PRESSED)
with pytest.raises(ValueError):
tiny_seed.enter_tiny_seed()
diff --git a/tests/test_input.py b/tests/test_input.py
index 0a2ee25..38e67cb 100644
--- a/tests/test_input.py
+++ b/tests/test_input.py
@@ -375,7 +375,7 @@ def test_swipe_down_value_released_when_none(mocker, m5stickv):
def test_wait_for_release(mocker, m5stickv):
import krux
- from krux.input import Input, RELEASED, PRESSED
+ from krux.input import Input, RELEASED, PRESSED, BUTTON_ENTER
input = Input()
input = reset_input_states(mocker, input)
@@ -386,7 +386,45 @@ def test_wait_for_release(mocker, m5stickv):
)
assert input.entropy == 0
- input.wait_for_button()
+ assert input.wait_for_button() == BUTTON_ENTER
+ assert input.entropy > 0
+ krux.input.wdt.feed.assert_called()
+
+
+def test_wait_for_release_page_yahboom(mocker, yahboom):
+ import krux
+ from krux.input import Input, RELEASED, PRESSED, BUTTON_PAGE, BUTTON_PAGE_PREV
+
+ input = Input()
+ input = reset_input_states(mocker, input)
+ mocker.patch.object(time, "ticks_ms", side_effect=mock_timer_ticks())
+ mocker.patch.object(input, "page_event", side_effect=[False, True, False])
+ mocker.patch.object(
+ input, "page_value", side_effect=[RELEASED, PRESSED, PRESSED, RELEASED]
+ )
+
+ assert input.entropy == 0
+ input.wait_for_button() == BUTTON_PAGE
+ assert input.entropy > 0
+ krux.input.wdt.feed.assert_called()
+
+
+def test_wait_for_release_page_prev_yahboom(mocker, yahboom):
+ import krux
+ from krux.input import Input, RELEASED, PRESSED, BUTTON_PAGE
+
+ input = Input()
+ input = reset_input_states(mocker, input)
+ mocker.patch.object(time, "ticks_ms", side_effect=mock_timer_ticks())
+ mocker.patch.object(input, "page_prev_event", side_effect=[False, True, False])
+ mocker.patch.object(
+ input, "page_prev_value", side_effect=[RELEASED, PRESSED, PRESSED, RELEASED]
+ )
+
+ assert input.entropy == 0
+
+ # Test with BUTTON_PAGE_PREV, expect it to be PAGE on yahboom
+ input.wait_for_button() == BUTTON_PAGE
assert input.entropy > 0
krux.input.wdt.feed.assert_called()
@@ -943,3 +981,42 @@ def test_amigo_fast_forward_from_start(mocker, amigo):
# input.page_value = lambda: PRESSED
value = input._detect_press_type(BUTTON_PAGE)
assert value == ACTIVATING_BUTTONS
+
+
+def test_fast_forward(mocker, m5stickv):
+ from krux.input import Input, PRESSED, FAST_FORWARD, FAST_BACKWARD
+
+ input = Input()
+ input.page_value = mocker.MagicMock(return_value=PRESSED)
+
+ assert input.wait_for_fastnav_button() == FAST_FORWARD
+
+ input.page_value = mocker.MagicMock(return_value=None)
+
+ input.page_prev_value = mocker.MagicMock(return_value=PRESSED)
+
+ assert input.wait_for_fastnav_button() == FAST_BACKWARD
+
+
+def test_fast_forward_yahboom(mocker, yahboom):
+ from krux.input import Input, PRESSED, FAST_FORWARD
+
+ input = Input()
+ input.page_value = mocker.MagicMock(return_value=PRESSED)
+
+ assert input.wait_for_fastnav_button() == FAST_FORWARD
+
+ input.page_value = mocker.MagicMock(return_value=None)
+ input.page_prev_value = mocker.MagicMock(return_value=PRESSED)
+
+ # Test with BUTTON_PAGE_PREV PRESSED expect it to be FAST_FORWARD on yahboom
+ assert input.wait_for_fastnav_button() == FAST_FORWARD
+
+
+def test_fast_forward_no_pressed(mocker, m5stickv):
+ from krux.input import Input, BUTTON_ENTER
+
+ input = Input()
+ input.wait_for_button = mocker.MagicMock(return_value=BUTTON_ENTER)
+
+ assert input.wait_for_fastnav_button() == BUTTON_ENTER
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.