diff --git a/src/default_settings.ini b/src/default_settings.ini index de7b047b..2aeffa0d 100644 --- a/src/default_settings.ini +++ b/src/default_settings.ini @@ -6,6 +6,7 @@ persistent_settings = False [display] text_color = ORANGE qr_background_color = FFFFFF +camera_rotation = 0 [wallet] network = main diff --git a/src/seedsigner/camera.py b/src/seedsigner/camera.py index e2af97be..edac1702 100644 --- a/src/seedsigner/camera.py +++ b/src/seedsigner/camera.py @@ -10,6 +10,16 @@ from seedsigner.helpers import PiVideoStream, Singleton class Camera(Singleton): _video_stream = None _picamera = None + _camera_rotation = None + + @classmethod + def get_instance(cls): + # This is the only way to access the one and only Controller + from seedsigner.models import Settings + if cls._instance is None: + cls._instance = cls.__new__(cls) + cls._instance._camera_rotation = Settings.get_instance().camera_rotation + return cls._instance def start_video_stream_mode(self, resolution=(512, 384), framerate=12, format="bgr"): @@ -28,7 +38,7 @@ class Camera(Singleton): return frame else: if frame is not None: - return Image.fromarray(frame.astype('uint8'), 'RGB').rotate(90) + return Image.fromarray(frame.astype('uint8'), 'RGB').rotate(90 + self._camera_rotation) return None @@ -59,37 +69,12 @@ class Camera(Singleton): self._picamera.awb_mode = 'off' self._picamera.awb_gains = g - """ - When outputting to unencoded formats, the camera rounds the requested resolution. - The horizontal resolution is rounded up to the nearest multiple of 32 pixels, - while the vertical resolution is rounded up to the nearest multiple of 16 pixels. - """ - # resolution = self._picamera.resolution - # if resolution.width % 32 == 0: - # raw_width = resolution.width - # else: - # raw_width = resolution.width - (resolution.width % 32) + 32 - - # if resolution.height % 16 == 0: - # raw_height = resolution.height - # else: - # raw_height = resolution.height + 16 - - # print(resolution) - # print(raw_width, raw_height) - # np_array = numpy.empty((raw_width, raw_height, 3), dtype=numpy.uint8) - # self._picamera.capture(np_array, format='rgb') - - # np_array.reshape((raw_width, raw_height, 3)) - - # return Image.fromarray(np_array[:resolution.width, :resolution.height, :].astype('uint8'), 'RGB') - stream = io.BytesIO() self._picamera.capture(stream, format='jpeg') # "Rewind" the stream to the beginning so we can read its content stream.seek(0) - return Image.open(stream) + return Image.open(stream).rotate(90 + self._camera_rotation) def stop_single_frame_mode(self): diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py index 8b806485..80debdba 100644 --- a/src/seedsigner/controller.py +++ b/src/seedsigner/controller.py @@ -76,7 +76,8 @@ class Controller(Singleton): controller.settings_tools_view = SettingsToolsView() controller.screensaver = ScreensaverView(controller.buttons) - controller.screensaver_activation_ms = 60 * 1000 + controller.screensaver_activation_ms = 120 * 1000 + @property def camera(self): @@ -156,6 +157,8 @@ class Controller(Singleton): ret_val = self.show_qr_density_tool() elif ret_val == Path.PERSISTENT_SETTINGS: ret_val = self.show_persistent_settings_tool() + elif ret_val == Path.CAMERA_ROTATION: + ret_val = self.show_camera_rotation_tool() elif ret_val == Path.DONATE: ret_val = self.show_donate_tool() elif ret_val == Path.RESET: @@ -679,10 +682,13 @@ class Controller(Singleton): self.buttons.wait_for([B.KEY_RIGHT]) validate_network = NETWORKS[self.settings.network] + validate_network_text = self.settings.network if "main" in address_type: validate_network = NETWORKS["main"] + validate_network_text = "main" elif "test" in address_type: validate_network = NETWORKS["test"] + validate_network_text = "test" r = 0 if address_type in ("Bech32-main", "Bech32-test") and len(address) == 62: @@ -779,7 +785,7 @@ class Controller(Singleton): derivation = self.settings_tools_view.draw_derivation_keyboard_entry(existing_derivation=self.settings.custom_derivation) self.settings.custom_derivation = derivation # save for next time else: - derivation = Settings.calc_derivation(validate_network, "single sig", script_type) + derivation = Settings.calc_derivation(validate_network_text, "single sig", script_type) if derivation == "" or derivation == None: self.menu_view.draw_modal(["Invalid Derivation", "try again"], "", "Right to Continue") @@ -1074,6 +1080,13 @@ class Controller(Singleton): ### Show Donate Screen and QR + def show_camera_rotation_tool(self): + r = self.settings_tools_view.display_camera_rotation() + if r is not None: + self.settings.camera_rotation = r + + return Path.SETTINGS_SUB_MENU ### Show Donate Screen and QR + def show_donate_tool(self): self.settings_tools_view.display_donate_info_screen() diff --git a/src/seedsigner/helpers/mnemonic_generation.py b/src/seedsigner/helpers/mnemonic_generation.py index 5b3f8a44..d7baf472 100644 --- a/src/seedsigner/helpers/mnemonic_generation.py +++ b/src/seedsigner/helpers/mnemonic_generation.py @@ -1,7 +1,7 @@ from embit import bip39 from embit.bip39 import mnemonic_to_bytes, mnemonic_from_bytes import unicodedata - +import hashlib def calculate_checksum(partial_mnemonic: list, wordlist): @@ -28,8 +28,7 @@ def generate_mnemonic_from_bytes(entropy_bytes): def generate_mnemonic_from_dice(roll_data: str): - entropyinteger = int(roll_data, 6) - entropy_bytes = entropyinteger.to_bytes(32, byteorder="little") + entropy_bytes = hashlib.sha256(roll_data.encode()).digest() # Return as a list return bip39.mnemonic_from_bytes(entropy_bytes).split() diff --git a/src/seedsigner/helpers/path.py b/src/seedsigner/helpers/path.py index 2873aed4..f641c459 100644 --- a/src/seedsigner/helpers/path.py +++ b/src/seedsigner/helpers/path.py @@ -28,6 +28,7 @@ class Path: WALLET = 64 QR_DENSITY_SETTING = 65 PERSISTENT_SETTINGS = 67 + CAMERA_ROTATION = 69 RESET = 68 # Seed Slots diff --git a/src/seedsigner/models/settings.py b/src/seedsigner/models/settings.py index 54799080..586f3040 100644 --- a/src/seedsigner/models/settings.py +++ b/src/seedsigner/models/settings.py @@ -13,7 +13,7 @@ class Settings(Singleton): if cls._instance: raise Exception("Instance already configured") - # Instantiate the one and only Controller instance + # Instantiate the one and only instance settings = cls.__new__(cls) cls._instance = settings @@ -27,7 +27,8 @@ class Settings(Singleton): }, 'display': { 'text_color': "ORANGE", - 'qr_background_color': "FFFFFF" + 'qr_background_color': "FFFFFF", + 'camera_rotation': 0 }, 'wallet': { 'network': "main", @@ -50,6 +51,7 @@ class Settings(Singleton): self._data["system"]["debug"] = config.getboolean("system", "debug") self._data["system"]["default_language"] = config["system"]["default_language"] self._data["display"]["text_color"] = config["display"]["text_color"] + self._data["display"]["camera_rotation"] = int(config["display"]["camera_rotation"]) self.network = config["wallet"]["network"] self.software = config["wallet"]["software"] self.qr_density = int(config["wallet"]["qr_density"]) @@ -136,6 +138,18 @@ class Settings(Singleton): self._data["display"]["qr_background_color"] = value self.__writeConfig() + @property + def camera_rotation(self): + return self._data["display"]["camera_rotation"] + + @camera_rotation.setter + def camera_rotation(self, value): + if value in [0, 90, 180, 270]: + self._data["display"]["camera_rotation"] = value + self.__writeConfig() + else: + raise Exception("Unexpected display.camera_rotation settings.ini value") + ### wallet @property diff --git a/src/seedsigner/views/menu_view.py b/src/seedsigner/views/menu_view.py index 68a60603..6fcbd1a9 100644 --- a/src/seedsigner/views/menu_view.py +++ b/src/seedsigner/views/menu_view.py @@ -124,7 +124,7 @@ class MenuView(View): ### Settings Menu def display_settings_menu(self) -> int: - lines = ["... [ Return to Main ]", "Wallet: ", "Network: ", "QR Density: ", "Input / Output Tests", "Persistent Settings: ", "Version Info", "Donate to SeedSigner", "Reset SeedSigner"] + lines = ["... [ Return to Main ]", "Wallet: ", "Network: ", "QR Density: ", "Input / Output Tests", "Persistent Settings: ", "Camera Rotation", "Version Info", "Donate to SeedSigner", "Reset SeedSigner"] input = 0 lines[1] = lines[1].replace("", Settings.get_instance().software) @@ -157,10 +157,12 @@ class MenuView(View): elif self.selected_menu_num == 6: return Path.PERSISTENT_SETTINGS elif self.selected_menu_num == 7: - return Path.VERSION_INFO + return Path.CAMERA_ROTATION elif self.selected_menu_num == 8: - return Path.DONATE + return Path.VERSION_INFO elif self.selected_menu_num == 9: + return Path.DONATE + elif self.selected_menu_num == 10: return Path.RESET raise Exception("Unhandled case") diff --git a/src/seedsigner/views/seed_tools_view.py b/src/seedsigner/views/seed_tools_view.py index 137caa74..a3ab85e3 100644 --- a/src/seedsigner/views/seed_tools_view.py +++ b/src/seedsigner/views/seed_tools_view.py @@ -1064,10 +1064,12 @@ class SeedToolsView(View): draw.rectangle((mask_width, 0, View.canvas_width - mask_width, pixels_per_block), fill=View.color) draw.rectangle((0, mask_height, pixels_per_block, View.canvas_height - mask_height), fill=View.color) - label_font = View.ROBOTOMONO24 + label_font = View.ASSISTANT26 x_label = block_labels_x[cur_block_x] tw, th = draw.textsize(x_label, font=label_font) - draw.text(((View.canvas_width - tw) / 2, (pixels_per_block - th) / 2), x_label, fill="BLACK", font=label_font) + + # note: have to nudge the y-coord up (the extra "- 4") for some reason + draw.text(((View.canvas_width - tw) / 2, ((pixels_per_block - th) / 2) - 4), x_label, fill="BLACK", font=label_font) y_label = block_labels_y[cur_block_y] tw, th = draw.textsize(y_label, font=label_font) @@ -1209,8 +1211,6 @@ class SeedToolsView(View): display_version = autocontrast( seed_entropy_image, cutoff=2 - ).rotate( - 90 ).crop( (120, 0, 600, 480) ).resize( diff --git a/src/seedsigner/views/settings_tools_view.py b/src/seedsigner/views/settings_tools_view.py index 1b533716..36cef358 100644 --- a/src/seedsigner/views/settings_tools_view.py +++ b/src/seedsigner/views/settings_tools_view.py @@ -79,6 +79,28 @@ class SettingsToolsView(View): else: return None + + def display_camera_rotation(self) -> int: + lines = ["... [ Return to Settings ]"] + lines.append("0 (default)") + lines.append("90") + lines.append("180") + lines.append("270") + + r = self.controller.menu_view.display_generic_selection_menu(lines, "Camera Rotation") + if r == 1: + return None + elif r == 2: + return 0 + elif r == 3: + return 90 + elif r == 4: + return 180 + elif r == 5: + return 270 + else: + return None + ### ### Version Info ### diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index d1770c28..dda71f3a 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -158,7 +158,7 @@ class View: text_overlay = Image.new("RGBA", (View.canvas_width, View.canvas_height), (255,255,255,0)) text_overlay_draw = ImageDraw.Draw(text_overlay) if not font: - font = View.COURIERNEW14 + font = View.ASSISTANT18 tw, th = text_overlay_draw.textsize(text, font=font) if text_background: text_overlay_draw.rectangle(((240 - tw) / 2 - 3, 240 - th, (240 - tw) / 2 + tw + 3, 240), fill=text_background) diff --git a/src/settings.ini b/src/settings.ini index a5876498..2a37f377 100644 --- a/src/settings.ini +++ b/src/settings.ini @@ -6,9 +6,10 @@ persistent_settings = False [display] text_color = ORANGE qr_background_color = FFFFFF +camera_rotation = 180 [wallet] network = main software = Prompt qr_density = 2 -custom_derivation = m/0 \ No newline at end of file +custom_derivation = m/0/0 \ No newline at end of file