Skip to content

Commit

Permalink
add frame progress bar to BBQr and PMofN
Browse files Browse the repository at this point in the history
refactor capture_qr_loop as a Page
  • Loading branch information
odudex committed Jul 24, 2024
1 parent 9218820 commit 89a74bb
Show file tree
Hide file tree
Showing 13 changed files with 203 additions and 150 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

[tool.poetry]
name = "krux"
version = "24.09.beta0"
version = "24.09.beta2"
description = "Open-source signing device firmware for Bitcoin"
authors = ["Jeff S <[email protected]>"]

Expand Down
64 changes: 8 additions & 56 deletions src/krux/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ def disable_antiglare(self):
sensor.skip_frames()
self.antiglare_enabled = False

def toggle_antiglare(self):
"""Toggles anti-glare mode and returns the new state"""
if self.antiglare_enabled:
self.disable_antiglare()
return False
self.enable_antiglare()
return True

def snapshot(self):
"""Helper to take a customized snapshot from sensor"""
img = sensor.snapshot()
Expand All @@ -193,59 +201,3 @@ def stop_sensor(self):
"""Stops capturing from sensor"""
gc.collect()
sensor.run(0)

def capture_qr_code_loop(self, callback, flipped_x_coordinates=False):
"""Captures either singular or animated QRs and parses their contents until
all parts of the message have been captured. The part data are then ordered
and assembled into one message and returned.
"""
self.initialize_run()

parser = QRPartParser()

prev_parsed_count = 0
new_part = False
while True:
wdt.feed()
command = callback(parser.total_count(), parser.parsed_count(), new_part)
if not self.initialized:
# Ignores first callback as it may contain unintentional events
self.initialized = True
command = 0
if command == 1:
break
new_part = False

img = self.snapshot()
res = img.find_qrcodes()

# different cases of lcd.display to show a progress bar on different devices!
if board.config["type"] == "m5stickv":
img.lens_corr(strength=1.0, zoom=0.56)
lcd.display(img, oft=(0, 0), roi=(68, 52, 185, 135))
elif board.config["type"] == "amigo":
if flipped_x_coordinates:
lcd.display(img, oft=(40, 40))
else:
lcd.display(img, oft=(120, 40)) # X and Y are swapped
elif board.config["type"] == "cube":
lcd.display(img, oft=(0, 0), roi=(0, 0, 224, 240))
else:
lcd.display(img, oft=(0, 0), roi=(0, 0, 304, 240))

if len(res) > 0:
data = res[0].payload()

parser.parse(data)

if parser.processed_parts_count() > prev_parsed_count:
prev_parsed_count = parser.processed_parts_count()
new_part = True

if parser.is_complete():
break
self.stop_sensor()

if parser.is_complete():
return (parser.result(), parser.format)
return (None, None)
2 changes: 1 addition & 1 deletion src/krux/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
# 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.
VERSION = "24.09.beta0"
VERSION = "24.09.beta2"
SIGNER_PUBKEY = "03339e883157e45891e61ca9df4cd3bb895ef32d475b8e793559ea10a36766689b"
86 changes: 1 addition & 85 deletions src/krux/pages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
STATUS_BAR_HEIGHT,
)
from ..qr import to_qr_codes
from ..krux_settings import t, Settings, DefaultWallet
from ..krux_settings import t, Settings
from ..sd_card import SDHandler

MENU_CONTINUE = 0
Expand All @@ -55,7 +55,6 @@
ESC_KEY = 1
FIXED_KEYS = 3 # 'More' key only appears when there are multiple keysets

ANTI_GLARE_WAIT_TIME = 500
SHUTDOWN_WAIT_TIME = 300

TOGGLE_BRIGHTNESS = (BUTTON_PAGE, BUTTON_PAGE_PREV)
Expand Down Expand Up @@ -83,7 +82,6 @@ class Page:
def __init__(self, ctx, menu=None):
self.ctx = ctx
self.menu = menu
self._time_frame = 0
# context has its own keypad mapping in case touch is not used
self.y_keypad_map = []
self.x_keypad_map = []
Expand Down Expand Up @@ -203,88 +201,6 @@ def capture_from_keypad(
self.ctx.input.touch.clear_regions()
return buffer

def capture_qr_code(self):
"""Captures a singular or animated series of QR codes and displays progress to the user.
Returns the contents of the QR code(s).
"""
self._time_frame = time.ticks_ms()

def callback(part_total, num_parts_captured, new_part):
# Turn on the light as long as the enter button is held down (M5stickV and Amigo)
if self.ctx.light:
if self.ctx.input.enter_value() == PRESSED:
self.ctx.light.turn_on()
else:
self.ctx.light.turn_off()
# If board don't have light, ENTER stops the capture
elif self.ctx.input.enter_event():
return 1

# Anti-glare mode
if self.ctx.input.page_event() or (
# Yahboom may have page or page_prev mapped to its single button
board.config["type"] == "yahboom"
and self.ctx.input.page_prev_event()
):
if self.ctx.camera.has_antiglare():
self._time_frame = time.ticks_ms()
self.ctx.display.to_portrait()
if not self.ctx.camera.antiglare_enabled:
self.ctx.camera.enable_antiglare()
self.ctx.display.draw_centered_text(t("Anti-glare enabled"))
else:
self.ctx.camera.disable_antiglare()
self.ctx.display.draw_centered_text(t("Anti-glare disabled"))
time.sleep_ms(ANTI_GLARE_WAIT_TIME)
self.ctx.display.to_landscape()
self.ctx.input.reset_ios_state()
return 0
return 1

# Exit the capture loop with PAGE_PREV or TOUCH
if self.ctx.input.page_prev_event() or self.ctx.input.touch_event():
return 1

# Indicate progress to the user that a new part was captured
if new_part:
self.ctx.display.to_portrait()
filled = self.ctx.display.width() * num_parts_captured
filled //= part_total
if board.config["type"] == "cube":
height = 225
elif self.ctx.display.height() < 320: # M5StickV
height = 210
elif self.ctx.display.height() > 320: # Amigo
height = 380
else:
height = 305
self.ctx.display.fill_rectangle(
0,
height,
filled,
15,
theme.fg_color,
)
self.ctx.display.to_landscape()

return 0

self.ctx.display.clear()
self.ctx.display.draw_centered_text(t("Loading Camera.."))
self.ctx.display.to_landscape()
code = None
qr_format = None
try:
code, qr_format = self.ctx.camera.capture_qr_code_loop(
callback, self.ctx.display.flipped_x_coordinates
)
except:
print("Camera error")
if self.ctx.light:
self.ctx.light.turn_off()
self.ctx.display.to_portrait()
return (code, qr_format)

def display_qr_codes(self, data, qr_format, title=""):
"""Displays a QR code or an animated series of QR codes to the user, encoding them
in the specified format
Expand Down
6 changes: 5 additions & 1 deletion src/krux/pages/encryption_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ def load_key(self):

def load_qr_encryption_key(self):
"""Loads and returns a key from a QR code"""
data, _ = self.capture_qr_code()

from .qr_capture import QRCodeCapture

qr_capture = QRCodeCapture(self.ctx)
data, _ = qr_capture.qr_capture_loop()
if data is None:
self.flash_error(t("Failed to load key"))
return None
Expand Down
5 changes: 4 additions & 1 deletion src/krux/pages/home_pages/addresses.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ def pre_scan_address(self):

def scan_address(self, addr_type=0):
"""Handler for the 'receive' or 'change' menu item"""
data, qr_format = self.capture_qr_code()
from ..qr_capture import QRCodeCapture

qr_capture = QRCodeCapture(self.ctx)
data, qr_format = qr_capture.qr_capture_loop()
if data is None or qr_format != FORMAT_NONE:
self.flash_error(t("Failed to load address"))
return MENU_CONTINUE
Expand Down
5 changes: 4 additions & 1 deletion src/krux/pages/home_pages/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,10 @@ def load_psbt(self):
return (None, None, "")

if load_method == LOAD_FROM_CAMERA:
data, qr_format = self.capture_qr_code()
from ..qr_capture import QRCodeCapture

qr_capture = QRCodeCapture(self.ctx)
data, qr_format = qr_capture.qr_capture_loop()
return (data, qr_format, "")

# If load_method == LOAD_FROM_SD
Expand Down
5 changes: 4 additions & 1 deletion src/krux/pages/home_pages/sign_message_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ def load_message(self):
return (None, None, "")

if load_method == LOAD_FROM_CAMERA:
data, qr_format = self.capture_qr_code()
from ..qr_capture import QRCodeCapture

qr_capture = QRCodeCapture(self.ctx)
data, qr_format = qr_capture.qr_capture_loop()
return (data, qr_format, "")

# If load_method == LOAD_FROM_SD
Expand Down
5 changes: 4 additions & 1 deletion src/krux/pages/home_pages/wallet_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ def _load_wallet(self):
persisted = False
load_method = self.load_method()
if load_method == LOAD_FROM_CAMERA:
wallet_data, qr_format = self.capture_qr_code()
from ..qr_capture import QRCodeCapture

qr_capture = QRCodeCapture(self.ctx)
wallet_data, qr_format = qr_capture.qr_capture_loop()
elif load_method == LOAD_FROM_SD:
# Try to read the wallet output descriptor from a file on the SD card
qr_format = FORMAT_NONE
Expand Down
5 changes: 4 additions & 1 deletion src/krux/pages/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,10 @@ def _encrypted_qr_code(self, data):

def load_key_from_qr_code(self):
"""Handler for the 'via qr code' menu item"""
data, qr_format = self.capture_qr_code()
from .qr_capture import QRCodeCapture

qr_capture = QRCodeCapture(self.ctx)
data, qr_format = qr_capture.qr_capture_loop()
if data is None:
self.flash_error(t("Failed to load mnemonic"))
return MENU_CONTINUE
Expand Down
Loading

0 comments on commit 89a74bb

Please sign in to comment.