From cfba499677ab1cbc6e18db128e9f36bd726c6edf Mon Sep 17 00:00:00 2001 From: Samuel Dionne-Riel Date: Sun, 22 Nov 2020 15:12:35 -0500 Subject: [PATCH 1/4] boot/splash: Add recovery notification support To be shown, the publisher has to publish the `recovery` value. --- boot/splash/lib/ui.rb | 33 +++++++++++++++++++++++++++++++++ boot/splash/main.rb | 1 + 2 files changed, 34 insertions(+) diff --git a/boot/splash/lib/ui.rb b/boot/splash/lib/ui.rb index ec11c886..db40915e 100644 --- a/boot/splash/lib/ui.rb +++ b/boot/splash/lib/ui.rb @@ -26,6 +26,7 @@ class UI add_logo add_progress_bar add_label + add_recovery add_textarea add_keyboard @@ -99,6 +100,34 @@ class UI end end + def add_recovery() + @recovery_container = LVGL::LVContainer.new(@page) + @recovery_container.set_hidden(true) + @recovery_container.set_width(@page.get_width) + @recovery_container.get_style(LVGL::CONT_STYLE::MAIN).dup.tap do |style| + @recovery_container.set_style(LVGL::CONT_STYLE::MAIN, style) + style.body_main_color = 0xFF000000 + style.body_grad_color = 0xFF000000 + style.body_border_width = 0 + end + + recovery_label = LVGL::LVLabel.new(@recovery_container) + recovery_label.get_style(LVGL::LABEL_STYLE::MAIN).dup.tap do |style| + recovery_label.set_style(LVGL::LABEL_STYLE::MAIN, style) + style.text_color = 0xFFFFFFFF + end + recovery_label.set_long_mode(LVGL::LABEL_LONG::BREAK) + recovery_label.set_align(LVGL::LABEL_ALIGN::CENTER) + + recovery_label.set_width(@recovery_container.get_width() * 0.9) + recovery_label.set_text("Booting to recovery menu") + recovery_label.set_x(@recovery_container.get_width()/2 - recovery_label.get_width()/2) + recovery_label.set_y(@unit) + + @recovery_container.set_height(recovery_label.get_height() + 2*@unit) + @recovery_container.set_pos(0, @page.get_height() - @recovery_container.get_height()) + end + # Used to handle fade-in/fade-out # This is because opacity handles multiple overlaid objects wrong. def add_cover() @@ -167,6 +196,10 @@ class UI end end + def show_recovery_notice(val = true) + @recovery_container.set_hidden(!val) + end + def offset_page(delta) LVGL::LVAnim.new().tap do |anim| anim.set_exec_cb(@page, :lv_obj_set_y) diff --git a/boot/splash/main.rb b/boot/splash/main.rb index 5923a255..ecb7c125 100644 --- a/boot/splash/main.rb +++ b/boot/splash/main.rb @@ -73,6 +73,7 @@ LVGUI.main_loop do # First updating the current progress ui.set_progress(msg["progress"]) + ui.show_recovery_notice(msg["recovery"]) # And updating the label as needed. if msg["label"] From ff9d66713e169a25fbfe36fc70c69f0c4596a22b Mon Sep 17 00:00:00 2001 From: Samuel Dionne-Riel Date: Sun, 22 Nov 2020 15:40:37 -0500 Subject: [PATCH 2/4] boot/splash: Update UI before executing commands Otherwise we might miss the last message, which may include things to present to the user, e.g. the label "Continuing to ..." --- boot/splash/main.rb | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/boot/splash/main.rb b/boot/splash/main.rb index ecb7c125..c89664e7 100644 --- a/boot/splash/main.rb +++ b/boot/splash/main.rb @@ -42,6 +42,19 @@ LVGUI.main_loop do p msg end + # Update the UI... + + # First updating the current progress + ui.set_progress(msg["progress"]) + ui.show_recovery_notice(msg["recovery"]) + + # Update the label as needed. + if msg["label"] + ui.set_label(msg["label"]) + else + ui.set_label("") + end + # We might have a special command; handle it. if msg["command"] then command = msg["command"] @@ -68,19 +81,6 @@ LVGUI.main_loop do $stderr.puts "[splash] Unexpected command #{command.to_json}..." end end - - # Update the UI... - - # First updating the current progress - ui.set_progress(msg["progress"]) - ui.show_recovery_notice(msg["recovery"]) - - # And updating the label as needed. - if msg["label"] - ui.set_label(msg["label"]) - else - ui.set_label("") - end end end From 67957f4972dff3e9f23d03cb4238137d35d10a8c Mon Sep 17 00:00:00 2001 From: Samuel Dionne-Riel Date: Sun, 22 Nov 2020 15:45:02 -0500 Subject: [PATCH 3/4] boot/init: Move recovery detection to an abstraction layer --- boot/init/tasks/switch_root.rb | 46 ++---------------------------- boot/lib/hal/recovery.rb | 51 ++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 44 deletions(-) create mode 100644 boot/lib/hal/recovery.rb diff --git a/boot/init/tasks/switch_root.rb b/boot/init/tasks/switch_root.rb index 39e17a9f..3b6158e8 100644 --- a/boot/init/tasks/switch_root.rb +++ b/boot/init/tasks/switch_root.rb @@ -82,7 +82,7 @@ class Tasks::SwitchRoot < SingletonTask # May pause the boot to allow the user to select a generation. def selected_generation() - if user_wants_selection() + if Hal::Recovery.wants_recovery? generate_selection() # FIXME: In the future, boot GUIs will be launched async, before this # task is ran. @@ -102,50 +102,8 @@ class Tasks::SwitchRoot < SingletonTask end end - def boot_as_recovery_wants_recovery() - # "Boot as recovery" systems do not have a discrete recovery partition. - # For those systems, when `[s_]kip_initramfs` is in the kernel cmdline, we - # know the intent is to boot the normal system. - # Is a "boot as recovery" device, and Is `[s_]kip_initramfs` missing? - Configuration["device"]["boot_as_recovery"] and - !File.read("/proc/cmdline").split(/\s+/).grep(/[s_]kip_initramfs/).any? - end - - def is_recovery() - # Check in /etc/boot/config for `is_recovery`, it's assumed to be set, and - # true, for recovery.img. - Configuration["is_recovery"] or - boot_as_recovery_wants_recovery - end - - def is_boot_interrupted() - keys = [ - # Keys used for "mobile" use-cases - :KEY_VOLUMEUP, - :KEY_VOLUMEDOWN, - # Keys used for "computer" use-cases - :KEY_LEFTCTRL, - :KEY_RIGHTCTRL, - :KEY_LEFTSHIFT, - :KEY_RIGHTSHIFT, - :KEY_ESC, - ] - - Evdev.keys_held(keys) - end - - # Checks if the user wants to select a generation. - def user_wants_selection() - [ - # Booted a recovery partition. - is_recovery, - # Or signaling the boot selection menu should be shown. - is_boot_interrupted, - ].any? - end - def run() - if user_wants_selection + if Hal::Recovery.wants_recovery? Tasks::Splash.instance.quit("Continuing to recovery menu") else Tasks::Splash.instance.quit("Continuing to stage-2") diff --git a/boot/lib/hal/recovery.rb b/boot/lib/hal/recovery.rb new file mode 100644 index 00000000..adcc18d9 --- /dev/null +++ b/boot/lib/hal/recovery.rb @@ -0,0 +1,51 @@ +module Hal + module Recovery + extend self + + KEYS = [ + # Keys used for "mobile" use-cases + :KEY_VOLUMEUP, + :KEY_VOLUMEDOWN, + # Keys used for "computer" use-cases + :KEY_LEFTCTRL, + :KEY_RIGHTCTRL, + :KEY_LEFTSHIFT, + :KEY_RIGHTSHIFT, + :KEY_ESC, + ] + + def firmware_wants_recovery?() + # "Boot as recovery" systems do not have a discrete recovery partition. + # For those systems, when `[s_]kip_initramfs` is in the kernel cmdline, we + # know the intent is to boot the normal system. + # Is a "boot as recovery" device, and Is `[s_]kip_initramfs` missing? + if Configuration["device"]["boot_as_recovery"] then + if File.exists?("/proc/cmdline") then + !File.read("/proc/cmdline").split(/\s+/).grep(/[s_]kip_initramfs/).any? + end + end + end + + # Is this boot image a recovery image, or booted into recovery mode by the + # device firmware? + def is_recovery?() + # Check in /etc/boot/config for `is_recovery`, it's assumed to be set, and + # true, for recovery builds. + Configuration["is_recovery"] or firmware_wants_recovery? + end + + # Is the normal boot flow interrupted by a key input? + def boot_interrupted?() + Evdev.keys_held(KEYS) + end + + def wants_recovery? + [ + # Booted a recovery partition. + is_recovery?, + # Or signaling the boot selection menu should be shown. + boot_interrupted?, + ].any? + end + end +end From fd0a9856bdd0bc098171708fd94ed3b8f7c064d0 Mon Sep 17 00:00:00 2001 From: Samuel Dionne-Riel Date: Sun, 22 Nov 2020 15:45:16 -0500 Subject: [PATCH 4/4] boot/init: publish desire for recovery mode --- boot/init/lib/task.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/boot/init/lib/task.rb b/boot/init/lib/task.rb index 9a810b73..36d14973 100644 --- a/boot/init/lib/task.rb +++ b/boot/init/lib/task.rb @@ -49,6 +49,7 @@ module Tasks # Update the current progress count = @tasks.length.to_f Progress.update({progress: (100 * (1 - (todo.length / count))).ceil}) + Progress.update({recovery: Hal::Recovery.wants_recovery?}) todo.each do |task| if task._try_run_task then