From d41c6c8ff132d3412affd04a5635aba36e77492f Mon Sep 17 00:00:00 2001 From: Chaitanya-Keyal <66475772+Chaitanya-Keyal@users.noreply.github.com> Date: Thu, 22 May 2025 16:08:43 +0000 Subject: [PATCH 01/10] fix: restart on manual build --- src/seedsigner/views/view.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index 66c3a229..8d3d6d39 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -253,6 +253,8 @@ class RestartView(View): class DoResetThread(BaseThread): def run(self): + import os + import sys import time from subprocess import call @@ -260,12 +262,17 @@ class RestartView(View): # exiting. time.sleep(0.25) - # Kill the SeedSigner process; Running the process again. # `.*` is a wildcard to detect either `python`` or `python3`. + kill_cmd = "kill $(pidof python*)" if Settings.HOSTNAME == Settings.SEEDSIGNER_OS: - call("kill $(pidof python*) & python /opt/src/main.py", shell=True) + python_exec = "python" # Use system python on SeedSigner OS + script_path = "/opt/src/main.py" # Path to SeedSigner main script else: - call("kill $(ps aux | grep '[p]ython.*main.py' | awk '{print $2}')", shell=True) + python_exec = sys.executable # Current Python interpreter path + script_path = os.path.abspath(sys.argv[0]) # Absolute path to current script + + # Kill all running Python processes & restart the SeedSigner main script + call(f"{kill_cmd} & {python_exec} {script_path}", shell=True) From 3d721dbe58bb2bb79c2d4fa1869dcca8dcf0fa35 Mon Sep 17 00:00:00 2001 From: Chaitanya-Keyal <66475772+Chaitanya-Keyal@users.noreply.github.com> Date: Thu, 22 May 2025 16:23:28 +0000 Subject: [PATCH 02/10] docs: ensure `Restart=no` in manual build --- docs/raspberry_pi_os_build_instructions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/raspberry_pi_os_build_instructions.md b/docs/raspberry_pi_os_build_instructions.md index b1102a38..83e34369 100644 --- a/docs/raspberry_pi_os_build_instructions.md +++ b/docs/raspberry_pi_os_build_instructions.md @@ -214,19 +214,19 @@ WorkingDirectory=/home/pi/seedsigner/src/ ExecStart=/usr/bin/python3 main.py StandardOutput=null ErrorOutput=null -Restart=always +Restart=no [Install] WantedBy=multi-user.target ``` -_Note: For local dev you'll want to edit the `Restart=always` line to `Restart=no`. This way when your dev code crashes it won't keep trying to restart itself. Note that the UI "Reset" will no longer work when auto-restarts are disabled._ +_Note: The line `Restart=no` ensures that when your dev code crashes it won't keep trying to restart itself._ _Note: Debugging output is completely wiped via routing the stdout and stderr to `/dev/null`. When working in local dev, you'll `kill` the `systemd` SeedSigner service and just directly run the code on demand so you can see all the debugging output live._ Use `CTRL-X` and `y` to exit and save changes. -Configure the service to start running (this will restart the seedsigner code automatically at startup and if it crashes): +Configure the service to start running (this will restart the seedsigner code automatically at startup): ```bash sudo systemctl enable seedsigner.service ``` From 643b36722ddbbe2c24eee108581e2e197154046a Mon Sep 17 00:00:00 2001 From: Chaitanya-Keyal <66475772+Chaitanya-Keyal@users.noreply.github.com> Date: Fri, 23 May 2025 21:14:35 +0000 Subject: [PATCH 03/10] fix: use `os.execv` for restart --- src/seedsigner/views/view.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index 8d3d6d39..b599767c 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -256,23 +256,17 @@ class RestartView(View): import os import sys import time - from subprocess import call # Give the screen just enough time to display the reset message before # exiting. time.sleep(0.25) - # `.*` is a wildcard to detect either `python`` or `python3`. - kill_cmd = "kill $(pidof python*)" - if Settings.HOSTNAME == Settings.SEEDSIGNER_OS: - python_exec = "python" # Use system python on SeedSigner OS - script_path = "/opt/src/main.py" # Path to SeedSigner main script - else: - python_exec = sys.executable # Current Python interpreter path - script_path = os.path.abspath(sys.argv[0]) # Absolute path to current script + # Flush any buffered data. + sys.stdout.flush() + sys.stderr.flush() - # Kill all running Python processes & restart the SeedSigner main script - call(f"{kill_cmd} & {python_exec} {script_path}", shell=True) + # Replace the current process with a new one. + os.execv(sys.executable, [sys.executable] + sys.argv) From 459fb1493eccadfc892958dc382e0db69660a73c Mon Sep 17 00:00:00 2001 From: Chaitanya-Keyal <66475772+Chaitanya-Keyal@users.noreply.github.com> Date: Mon, 26 May 2025 15:16:13 +0000 Subject: [PATCH 04/10] fix(`RestartView`): stop restarting for screenshot renderer --- src/seedsigner/views/view.py | 14 ++++++++++++-- tests/screenshot_generator/generator.py | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index b599767c..e0782fac 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -242,16 +242,23 @@ class PowerOptionsView(View): return Destination(PowerOffView) - +@dataclass class RestartView(View): + is_screenshot_renderer: bool = False + def run(self): from seedsigner.gui.screens.screen import ResetScreen - thread = RestartView.DoResetThread() + thread = RestartView.DoResetThread(is_screenshot_renderer=self.is_screenshot_renderer) thread.start() self.run_screen(ResetScreen) class DoResetThread(BaseThread): + def __init__(self, is_screenshot_renderer: bool = False): + self.is_screenshot_renderer = is_screenshot_renderer + super().__init__() + + def run(self): import os import sys @@ -260,6 +267,9 @@ class RestartView(View): # Give the screen just enough time to display the reset message before # exiting. time.sleep(0.25) + if self.is_screenshot_renderer: + # For the screenshot generator, we don't actually want to restart + return # Flush any buffered data. sys.stdout.flush() diff --git a/tests/screenshot_generator/generator.py b/tests/screenshot_generator/generator.py index bd877174..71b5cab1 100644 --- a/tests/screenshot_generator/generator.py +++ b/tests/screenshot_generator/generator.py @@ -274,7 +274,7 @@ def generate_screenshots(locale): ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_DireWarningToast', toast_thread=DireWarningToast("This is a dire warning toast!", activation_delay=0, duration=0)), ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_ErrorToast', toast_thread=ErrorToast("This is an error toast!", activation_delay=0, duration=0)), ScreenshotConfig(PowerOptionsView), - ScreenshotConfig(RestartView), + ScreenshotConfig(RestartView, dict(is_screenshot_renderer=True)), ScreenshotConfig(PowerOffView), ], "Seed Views": [ From 7b0b4a4809805fc7322e697fd5f2591c808895d2 Mon Sep 17 00:00:00 2001 From: notTanveer Date: Thu, 17 Jul 2025 21:47:55 +0530 Subject: [PATCH 05/10] fix(BIP-39 passphrase): fallback to ExitDialog, if empty passphrase --- src/seedsigner/views/seed_views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index cd7d9cc9..e36b3196 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -367,7 +367,7 @@ class SeedAddPassphraseView(View): self.seed.set_passphrase(ret_dict["passphrase"]) if "is_back_button" in ret_dict: - if len(self.seed.passphrase) > 0: + if len(self.seed.passphrase) >= 0: return Destination(SeedAddPassphraseExitDialogView) else: return Destination(BackStackView) @@ -396,7 +396,7 @@ class SeedAddPassphraseExitDialogView(View): WarningScreen, title=_("Discard passphrase?"), status_headline=None, - text=_("Your current passphrase entry will be erased"), + text=_("Your current passphrase entry will be erased") if self.seed.passphrase else _("You have not entered a passphrase yet"), show_back_button=False, button_data=button_data, ) From 1bab6242cbc1cafe4f00283d0dbd9b4af626b5d8 Mon Sep 17 00:00:00 2001 From: notTanveer Date: Fri, 18 Jul 2025 15:02:37 +0530 Subject: [PATCH 06/10] always route to exit dialog, if empty and < button is pressed --- src/seedsigner/views/seed_views.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index e36b3196..41c38358 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -367,16 +367,13 @@ class SeedAddPassphraseView(View): self.seed.set_passphrase(ret_dict["passphrase"]) if "is_back_button" in ret_dict: - if len(self.seed.passphrase) >= 0: - return Destination(SeedAddPassphraseExitDialogView) - else: - return Destination(BackStackView) + return Destination(SeedAddPassphraseExitDialogView) elif len(self.seed.passphrase) > 0: return Destination(SeedReviewPassphraseView) else: - return Destination(SeedFinalizeView) + return Destination(SeedAddPassphraseExitDialogView) From c610c442211721364ee8ad417f369a05aa6a1873 Mon Sep 17 00:00:00 2001 From: notTanveer Date: Sun, 27 Jul 2025 09:57:29 +0530 Subject: [PATCH 07/10] imporove exit dialog UX --- src/seedsigner/views/seed_views.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index 41c38358..2dcc502b 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -380,6 +380,7 @@ class SeedAddPassphraseView(View): class SeedAddPassphraseExitDialogView(View): EDIT = ButtonOption("Edit passphrase") DISCARD = ButtonOption("Discard passphrase", button_label_color="red") + SKIP = ButtonOption("Skip passphrase") # NOT red since we're not throwing anything away def __init__(self): super().__init__() @@ -387,13 +388,20 @@ class SeedAddPassphraseExitDialogView(View): def run(self): - button_data = [self.EDIT, self.DISCARD] + if self.seed.passphrase: + title = _("Discard passphrase?") + message = _("Your current passphrase entry will be erased.") + button_data = [self.EDIT, self.DISCARD] + else: + title = _("Skip Passphrase?") + message = _("You have not entered a passphrase yet.") + button_data = [self.EDIT, self.SKIP] selected_menu_num = self.run_screen( WarningScreen, - title=_("Discard passphrase?"), + title=title, status_headline=None, - text=_("Your current passphrase entry will be erased") if self.seed.passphrase else _("You have not entered a passphrase yet"), + text=message, show_back_button=False, button_data=button_data, ) @@ -401,7 +409,7 @@ class SeedAddPassphraseExitDialogView(View): if button_data[selected_menu_num] == self.EDIT: return Destination(SeedAddPassphraseView) - elif button_data[selected_menu_num] == self.DISCARD: + elif button_data[selected_menu_num] in [self.DISCARD, self.SKIP]: self.seed.set_passphrase("") return Destination(SeedFinalizeView) From 2e6e410172b744fb01740e55b84c68ac3e7f6fd2 Mon Sep 17 00:00:00 2001 From: notTanveer Date: Sun, 27 Jul 2025 20:11:45 +0530 Subject: [PATCH 08/10] fix if-else conditionals --- src/seedsigner/views/seed_views.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index 2dcc502b..938dc372 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -366,14 +366,11 @@ class SeedAddPassphraseView(View): # The new passphrase will be the return value; it might be empty. self.seed.set_passphrase(ret_dict["passphrase"]) - if "is_back_button" in ret_dict: + if "is_back_button" in ret_dict or len(self.seed.passphrase) == 0: return Destination(SeedAddPassphraseExitDialogView) - - elif len(self.seed.passphrase) > 0: - return Destination(SeedReviewPassphraseView) - + else: - return Destination(SeedAddPassphraseExitDialogView) + return Destination(SeedReviewPassphraseView) From 348f26e58791e139a8e1a9b1f29b079c8ab6a4a6 Mon Sep 17 00:00:00 2001 From: okaybro <66475772+Chaitanya-Keyal@users.noreply.github.com> Date: Sun, 27 Jul 2025 14:37:13 +0000 Subject: [PATCH 09/10] fix(restart): dont start thread for screenshot renderer --- src/seedsigner/views/view.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index e0782fac..462196d3 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -1,3 +1,4 @@ +import logging from dataclasses import dataclass from gettext import gettext as _ from typing import Type @@ -10,6 +11,9 @@ from seedsigner.models.settings import Settings, SettingsConstants from seedsigner.models.settings_definition import SettingsDefinition from seedsigner.models.threads import BaseThread +logger = logging.getLogger(__name__) + + class BackStackView: """ @@ -246,19 +250,24 @@ class PowerOptionsView(View): class RestartView(View): is_screenshot_renderer: bool = False + def __post_init__(self): + super().__post_init__() + self.thread = self.DoResetThread() + def run(self): + if self.is_screenshot_renderer: + # For the screenshot generator, we don't actually want to restart + return + + logger.info("Restarting SeedSigner") + from seedsigner.gui.screens.screen import ResetScreen - thread = RestartView.DoResetThread(is_screenshot_renderer=self.is_screenshot_renderer) - thread.start() + + self.thread.start() self.run_screen(ResetScreen) class DoResetThread(BaseThread): - def __init__(self, is_screenshot_renderer: bool = False): - self.is_screenshot_renderer = is_screenshot_renderer - super().__init__() - - def run(self): import os import sys @@ -267,9 +276,6 @@ class RestartView(View): # Give the screen just enough time to display the reset message before # exiting. time.sleep(0.25) - if self.is_screenshot_renderer: - # For the screenshot generator, we don't actually want to restart - return # Flush any buffered data. sys.stdout.flush() From 533ba8433edbfd9a789ab0f43eccdfa23047bb68 Mon Sep 17 00:00:00 2001 From: notTanveer Date: Tue, 29 Jul 2025 01:39:23 +0530 Subject: [PATCH 10/10] fix: typo --- src/seedsigner/views/seed_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index 938dc372..9f36a4f6 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -390,7 +390,7 @@ class SeedAddPassphraseExitDialogView(View): message = _("Your current passphrase entry will be erased.") button_data = [self.EDIT, self.DISCARD] else: - title = _("Skip Passphrase?") + title = _("Skip passphrase?") message = _("You have not entered a passphrase yet.") button_data = [self.EDIT, self.SKIP]