Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ Exported Uniform Resource (UR) QR codes, a widely adopted standard for exchangin
### Other Bug Fixes and Improvements
- Settings: Reduced default _Buttons Debounce_ value (with an even lower default on _M5StickV_)
- Settings: Expanded value ranges for _Touch Threshold_ and _Buttons Debounce_
- Swipe handling: Detection threshold has been slightly reduced
- Swipe handling: Diagonal and long-hold swipes are now discarded, and the swipe detection threshold has been slightly reduced
- Touch handling: Discards touches near edges of adjacent regions
- Keypad: Added backtick **`**
- Bugfix: Screensaver not activating in menu pages without statusbar
- Embit: Improved BIP39 mnemonic validation
Expand Down
34 changes: 22 additions & 12 deletions src/krux/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# pylint: disable=unnecessary-lambda

import time
import board
from .wdt import wdt
Expand All @@ -35,6 +37,7 @@
SWIPE_LEFT = 5
SWIPE_UP = 6
SWIPE_DOWN = 7
SWIPE_FAIL = 99
FAST_FORWARD = 8
FAST_BACKWARD = 9

Expand Down Expand Up @@ -175,29 +178,30 @@ def touch_event(self, validate_position=True):
return self.touch.event(validate_position)
return False

def swipe_right_value(self):
"""Intermediary method to pull touch gesture, if touch available"""
def _swipe_check_value(self, swipe_fnc):
if kboard.has_touchscreen:
return self.touch.swipe_right_value()
return swipe_fnc()
return RELEASED

def swipe_none_value(self):
"""Intermediary method to pull touch gesture, if touch available"""
return self._swipe_check_value(lambda: self.touch.swipe_none_value())

def swipe_right_value(self):
"""Intermediary method to pull touch gesture, if touch available"""
return self._swipe_check_value(lambda: self.touch.swipe_right_value())

def swipe_left_value(self):
"""Intermediary method to pull touch gesture, if touch available"""
if kboard.has_touchscreen:
return self.touch.swipe_left_value()
return RELEASED
return self._swipe_check_value(lambda: self.touch.swipe_left_value())

def swipe_up_value(self):
"""Intermediary method to pull touch gesture, if touch available"""
if kboard.has_touchscreen:
return self.touch.swipe_up_value()
return RELEASED
return self._swipe_check_value(lambda: self.touch.swipe_up_value())

def swipe_down_value(self):
"""Intermediary method to pull touch gesture, if touch available"""
if kboard.has_touchscreen:
return self.touch.swipe_down_value()
return RELEASED
return self._swipe_check_value(lambda: self.touch.swipe_down_value())

def wdt_feed_inc_entropy(self):
"""Feeds the watchdog and increments the input's entropy"""
Expand Down Expand Up @@ -310,6 +314,10 @@ def _handle_touch_input():
while self.touch_value() == PRESSED:
self.wdt_feed_inc_entropy()
self.buttons_active = False

# Check if was a swipe
if self.swipe_none_value() == PRESSED:
return SWIPE_FAIL
if self.swipe_right_value() == PRESSED:
return SWIPE_RIGHT
if self.swipe_left_value() == PRESSED:
Expand All @@ -318,6 +326,8 @@ def _handle_touch_input():
return SWIPE_UP
if self.swipe_down_value() == PRESSED:
return SWIPE_DOWN

# was a simple touch
return BUTTON_TOUCH

if btn in [BUTTON_ENTER, BUTTON_PAGE, BUTTON_PAGE_PREV]:
Expand Down
137 changes: 58 additions & 79 deletions src/krux/pages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
BUTTON_TOUCH,
SWIPE_DOWN,
SWIPE_UP,
SWIPE_FAIL,
FAST_FORWARD,
FAST_BACKWARD,
SWIPE_LEFT,
Expand Down Expand Up @@ -157,7 +158,7 @@ def capture_from_keypad(
"""
buffer = starting_buffer
pad = Keypad(self.ctx, keysets, possible_keys_fn)
swipe_has_not_been_used = True
swipe_used = False
show_swipe_hint = False
while True:
self.ctx.display.clear()
Expand All @@ -171,9 +172,12 @@ def capture_from_keypad(
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()
# wait until valid input is captured
btn = BUTTON_TOUCH
while btn in (BUTTON_TOUCH, SWIPE_FAIL):
btn = self.ctx.input.wait_for_fastnav_button()
if btn == BUTTON_TOUCH:
btn = pad.touch_to_physical()
if btn == BUTTON_ENTER:
pad.moving_forward = True
changed = False
Expand All @@ -193,8 +197,7 @@ def capture_from_keypad(
elif pad.cur_key_index == pad.go_index:
break
elif pad.cur_key_index == pad.more_index:
swipeable = kboard.has_touchscreen
if swipeable and swipe_has_not_been_used:
if kboard.has_touchscreen and not swipe_used:
show_swipe_hint = True
pad.next_keyset()
elif pad.cur_key_index < len(pad.keys):
Expand All @@ -212,7 +215,7 @@ def capture_from_keypad(
break
else:
if btn in (SWIPE_UP, SWIPE_LEFT, SWIPE_DOWN, SWIPE_RIGHT):
swipe_has_not_been_used = False
swipe_used = True
pad.navigate(btn)
if kboard.has_touchscreen:
self.ctx.input.touch.clear_regions()
Expand Down Expand Up @@ -401,90 +404,58 @@ def print_prompt(self, text, check_printer=True):

def prompt(self, text, offset_y=0, highlight_prefix=""):
"""Prompts user to answer Yes or No"""
lines = self.ctx.display.to_lines(text)
offset_y -= (len(lines) - 1) * FONT_HEIGHT
self.ctx.display.draw_hcentered_text(
lines = self.ctx.display.draw_hcentered_text(
text,
offset_y,
theme.fg_color,
theme.bg_color,
highlight_prefix=highlight_prefix,
)
self.y_keypad_map = []
self.x_keypad_map = []
offset_y = min(
offset_y + lines * FONT_HEIGHT, self.ctx.display.height() - FONT_HEIGHT
)
if kboard.has_minimal_display:
return self.ctx.input.wait_for_button() == BUTTON_ENTER
offset_y += (len(lines) + 1) * FONT_HEIGHT
self.x_keypad_map.extend(
[0, self.ctx.display.width() // 2, self.ctx.display.width()]
)
y_key_map = offset_y - (3 * FONT_HEIGHT // 2)
self.y_keypad_map.append(y_key_map)
y_key_map += 4 * FONT_HEIGHT
self.y_keypad_map.append(min(y_key_map, self.ctx.display.height()))
self.x_keypad_map = [
DEFAULT_PADDING,
self.ctx.display.width() // 2,
self.ctx.display.width() - DEFAULT_PADDING,
]
touch_offset_y = self.proceed_menu_text_y_offset(offset_y) - FONT_HEIGHT
self.y_keypad_map = [touch_offset_y, touch_offset_y + 3 * FONT_HEIGHT]
if kboard.has_touchscreen:
self.ctx.input.touch.set_regions(self.x_keypad_map, self.y_keypad_map)

go_str = t("Yes")
no_str = t("No")
btn = None
answer = True
index = 1
while btn != BUTTON_ENTER:
go_str = t("Yes")
no_str = t("No")
offset_x = (self.ctx.display.width() * 3) // 4 - (
lcd.string_width_px(go_str) // 2
)
self.ctx.display.draw_string(
offset_x, offset_y, go_str, theme.go_color, theme.bg_color
)
offset_x = self.ctx.display.width() // 4 - (
lcd.string_width_px(no_str) // 2
)
self.ctx.display.draw_string(
offset_x, offset_y, no_str, theme.no_esc_color, theme.bg_color
)
if self.ctx.input.buttons_active:
if answer:
self.ctx.display.outline(
self.ctx.display.width() // 2,
offset_y - FONT_HEIGHT // 2,
self.ctx.display.usable_width() // 2,
2 * FONT_HEIGHT - 2,
theme.go_color,
)
else:
self.ctx.display.outline(
DEFAULT_PADDING,
offset_y - FONT_HEIGHT // 2,
self.ctx.display.usable_width() // 2,
2 * FONT_HEIGHT - 2,
theme.no_esc_color,
)
elif kboard.has_touchscreen:
self.ctx.display.draw_vline(
self.ctx.display.width() // 2,
self.y_keypad_map[0] + FONT_HEIGHT,
2 * FONT_HEIGHT,
theme.frame_color,
)
btn = self.ctx.input.wait_for_button()
self.draw_proceed_menu(go_str, no_str, offset_y, index)
# wait until valid input is captured
btn = BUTTON_TOUCH
while btn in (BUTTON_TOUCH, SWIPE_FAIL):
btn = self.ctx.input.wait_for_button()
if btn == BUTTON_TOUCH:
self.ctx.input.touch.clear_regions()
# index 0 is No / 1 is Yes / -1 is Edge touch (ignore)
new_index = self.ctx.input.touch.current_index()
if new_index in (0, 1):
if new_index == 1:
return True
return False
if btn in (BUTTON_PAGE, BUTTON_PAGE_PREV):
answer = not answer
# erase yes/no area for next loop
self.ctx.display.fill_rectangle(
0,
offset_y - FONT_HEIGHT,
self.proceed_menu_text_y_offset(offset_y) - FONT_HEIGHT // 2,
self.ctx.display.width(),
3 * FONT_HEIGHT,
theme.bg_color,
)
elif btn == BUTTON_TOUCH:
self.ctx.input.touch.clear_regions()
# index 0 = No
# index 1 = Yes
if self.ctx.input.touch.current_index():
return True
return False
index = (index + 1) % 2
# BUTTON_ENTER
return answer
return index == 1

def fit_to_line(self, text, prefix="", fixed_chars=0, crop_middle=True):
"""Fits text with prefix plus fixed_chars at the beginning into one line,
Expand Down Expand Up @@ -534,6 +505,12 @@ def run(self, start_from_index=None):
_, status = self.menu.run_loop(start_from_index)
return status != MENU_SHUTDOWN

def proceed_menu_text_y_offset(self, y_offset):
"""Y offset for the text on proceed menu"""
return (
self.ctx.display.height() - (y_offset + FONT_HEIGHT + MINIMAL_PADDING)
) // 2 + y_offset

def draw_proceed_menu(
self, go_txt, esc_txt, y_offset=0, menu_index=None, go_enabled=True
):
Expand All @@ -544,9 +521,7 @@ def draw_proceed_menu(
esc_x_offset = (
self.ctx.display.width() // 2 - lcd.string_width_px(esc_txt)
) // 2
go_esc_y_offset = (
self.ctx.display.height() - (y_offset + FONT_HEIGHT + MINIMAL_PADDING)
) // 2 + y_offset
go_esc_y_offset = self.proceed_menu_text_y_offset(y_offset)
if menu_index == 0 and self.ctx.input.buttons_active:
self.ctx.display.outline(
DEFAULT_PADDING,
Expand Down Expand Up @@ -753,15 +728,19 @@ def run_loop(self, start_from_index=None, swipe_up_fnc=None, swipe_down_fnc=None
start_from_submenu = False
else:
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:
btn = BUTTON_TOUCH
while btn in (BUTTON_TOUCH, SWIPE_FAIL):
btn = self.ctx.input.wait_for_fastnav_button(
# Block if screen saver not active
screensaver_time == 0,
screensaver_time * ONE_MINUTE,
)
if btn == BUTTON_TOUCH:
selected_item_index = self.ctx.input.touch.current_index()
if selected_item_index < 0:
continue
btn = BUTTON_ENTER
if kboard.has_touchscreen:
self.ctx.input.touch.clear_regions()
if btn == BUTTON_ENTER:
status = self._clicked_item(selected_item_index)
Expand Down
13 changes: 11 additions & 2 deletions src/krux/pages/keypads.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
BUTTON_ENTER,
BUTTON_PAGE,
BUTTON_PAGE_PREV,
BUTTON_TOUCH,
SWIPE_RIGHT,
SWIPE_LEFT,
SWIPE_UP,
Expand Down Expand Up @@ -256,11 +257,19 @@ def get_valid_index(self):
def touch_to_physical(self):
"""Convert a touch press in button press"""
self.cur_key_index = self.ctx.input.touch.current_index()
actual_button = None
if self.cur_key_index < 0:
self.cur_key_index = 0
return BUTTON_TOUCH

special_keys = [self.del_index, self.esc_index, self.go_index]
if self.has_more_key():
special_keys.append(self.more_index)

actual_button = BUTTON_TOUCH
if self.cur_key_index < len(self.keys):
if self.keys[self.cur_key_index] in self.possible_keys:
actual_button = BUTTON_ENTER
elif self.cur_key_index < self.layout.max_index:
elif self.cur_key_index in special_keys:
actual_button = BUTTON_ENTER
else:
self.cur_key_index = 0
Expand Down
28 changes: 16 additions & 12 deletions src/krux/pages/mnemonic_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
BUTTON_ENTER,
BUTTON_PAGE,
BUTTON_PAGE_PREV,
SWIPE_FAIL,
FAST_FORWARD,
FAST_BACKWARD,
)
Expand Down Expand Up @@ -294,16 +295,19 @@ def edit(self):
self.ctx.display.clear()
self._draw_header()
self._map_words(button_index, page)
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:
if self.mnemonic_length == 24 and button_index % 2 == 1:
button_index //= 2
button_index += 12
else:
button_index //= 2
btn = BUTTON_ENTER
btn = BUTTON_TOUCH
while btn in (BUTTON_TOUCH, SWIPE_FAIL):
btn = self.ctx.input.wait_for_fastnav_button()
if btn == BUTTON_TOUCH:
button_index = self.ctx.input.touch.current_index()
if button_index < 0:
continue
if button_index < ESC_INDEX:
if self.mnemonic_length == 24 and button_index % 2 == 1:
button_index = (button_index >> 1) + 12
else:
button_index >>= 1
btn = BUTTON_ENTER
if btn == BUTTON_ENTER:
if button_index == GO_INDEX:
if self.mnemonic_length == 24 and kboard.is_m5stickv and page == 0:
Expand All @@ -316,15 +320,15 @@ def edit(self):
if button_index == ESC_INDEX:
# Cancel
self.ctx.display.clear()
if self.prompt(t("Are you sure?"), self.ctx.display.height() // 2):
if self.prompt(t("Are you sure?"), self.ctx.display.height() >> 1):
return None
continue
new_word = self.edit_word(button_index + page * 12)
if new_word is not None:
self.ctx.display.clear()
if self.prompt(
str(button_index + page * 12 + 1) + ".\n\n" + new_word + "\n\n",
self.ctx.display.height() // 2,
self.ctx.display.height() >> 1,
):
self.current_mnemonic[button_index + page * 12] = new_word
self.calculate_checksum()
Expand Down
Loading