From a56b8c802fc804b464036823b8f3a979b8ab1894 Mon Sep 17 00:00:00 2001 From: bunnie Date: Sun, 6 Nov 2022 03:11:23 +0800 Subject: [PATCH] add automounter to shellchat, and initial wait message The initial wait message is drawn directly under where the modals would appear, so the message "goes away" when modals pop up. There's still a bit of trickiness around the timing of some of the calls. Attempting to mount the PDDB too early before all the contexts have been registered leads to a lot of needless defacing. Also, the final redraw needs a short delay to allow the system to foreground shellchat again after closing the modals. --- services/shellchat/locales/i18n.json | 7 +++ services/shellchat/src/main.rs | 82 +++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/services/shellchat/locales/i18n.json b/services/shellchat/locales/i18n.json index 7560abb66..8854bab5d 100644 --- a/services/shellchat/locales/i18n.json +++ b/services/shellchat/locales/i18n.json @@ -12,5 +12,12 @@ "fr": "", "ja": "", "zh": "" + }, + "shellchat.bootwait": { + "en": "Please wait...", + "en-tts": "Please wait...", + "fr": "S'il vous plaît, attendez*MT*", + "ja": "お待ちください...", + "zh": "请稍等..." } } diff --git a/services/shellchat/src/main.rs b/services/shellchat/src/main.rs index 85e5d5bc6..11ab93350 100644 --- a/services/shellchat/src/main.rs +++ b/services/shellchat/src/main.rs @@ -32,6 +32,7 @@ Check for more detailed docs under Modules/cmds "Shell Chat" below use log::info; use core::fmt::Write; +use core::sync::atomic::{AtomicBool, Ordering}; use gam::UxRegistration; use graphics_server::{Gid, Point, Rectangle, TextBounds, TextView, DrawStyle, PixelColor}; @@ -39,6 +40,10 @@ use graphics_server::api::GlyphStyle; use xous::MessageEnvelope; use xous_ipc::Buffer; +use locales::t; +use std::thread; +use std::sync::Arc; + #[doc = include_str!("../README.md")] mod cmds; use cmds::*; @@ -202,7 +207,7 @@ impl Repl{ } /// update the loop, in response to various inputs - fn update(&mut self, was_callback: bool) -> Result<(), xous::Error> { + fn update(&mut self, was_callback: bool, init_done: bool) -> Result<(), xous::Error> { let debug1 = false; // if we had an input string, do something if let Some(local) = &self.input { @@ -219,7 +224,7 @@ impl Repl{ // redraw UI once upon accepting all input if !was_callback { // don't need to redraw on a callback, save some cycles - self.redraw().expect("can't redraw"); + self.redraw(init_done).expect("can't redraw"); } let mut dirty = true; @@ -265,7 +270,7 @@ impl Repl{ self.msg = None; // redraw UI now that we've responded if dirty { - self.redraw().expect("can't redraw"); + self.redraw(init_done).expect("can't redraw"); } if debug1 { @@ -286,10 +291,28 @@ impl Repl{ } )).expect("can't clear content area"); } - fn redraw(&mut self) -> Result<(), xous::Error> { + fn redraw(&mut self, init_done: bool) -> Result<(), xous::Error> { log::trace!("going into redraw"); self.clear_area(); + if !init_done { + let mut init_tv = TextView::new( + self.content, + TextBounds::CenteredTop( + Rectangle::new( + Point::new(0, self.screensize.y / 3 - 64), + Point::new(self.screensize.x, self.screensize.y / 3) + ) + ) + ); + init_tv.style = GlyphStyle::Bold; + init_tv.draw_border = false; + write!(init_tv.text, "{}", t!("shellchat.bootwait", xous::LANG)).ok(); + self.gam.post_textview(&mut init_tv).expect("couldn't render wait text"); + self.gam.redraw().expect("couldn't redraw screen"); + return Ok(()) + } + // this defines the bottom border of the text bubbles as they stack up wards let mut bubble_baseline = self.screensize.y - self.margin.y; @@ -393,8 +416,53 @@ fn wrapped_main() -> ! { let mut was_callback = false; let mut allow_redraw = true; - log::trace!("starting main loop"); + let pddb_init_done = Arc::new(AtomicBool::new(false)); + + // spawn a thread to auto-mount the PDDB. It's important that this spawn happens after + // our GAM context has been registered (which happened in the `Repl::new()` call above) + repl.redraw(false).ok(); + let _ = thread::spawn({ + let pddb_init_done = pddb_init_done.clone(); + let main_conn = xous::connect(shch_sid).unwrap(); + move || { + let tt = ticktimer_server::Ticktimer::new().unwrap(); + tt.sleep_ms(500).ok(); // give some time for the system to finish booting + loop { + let (no_retry_failure, count) = pddb::Pddb::new().try_mount(); + pddb_init_done.store(true, Ordering::SeqCst); + if no_retry_failure { + // this includes both successfully mounted, and user abort of mount attempt + break; + } else { + // this indicates system was guttered due to a retry failure + let xns = xous_names::XousNames::new().unwrap(); + let susres = susres::Susres::new_without_hook(&xns).unwrap(); + let llio = llio::Llio::new(&xns); + if ((llio.adc_vbus().unwrap() as u32) * 503) < 150_000 { + // try to force suspend if possible, so that users who are just playing around with + // the device don't run the battery down accidentally. + susres.initiate_suspend().ok(); + tt.sleep_ms(1000).unwrap(); + let modals = modals::Modals::new(&xns).unwrap(); + modals.show_notification( + &t!("login.fail", xous::LANG).replace("{fails}", &count.to_string()), + None + ).ok(); + } else { + // otherwise force a reboot cycle to slow down guessers + susres.reboot(true).expect("Couldn't reboot after too many failed password attempts"); + tt.sleep_ms(5000).unwrap(); + } + } + } + tt.sleep_ms(100).ok(); // this allows the shellchat context to foreground before calling the redraw + xous::send_message(main_conn, + xous::Message::new_scalar(ShellOpcode::Redraw.to_usize().unwrap(), 0, 0, 0, 0) + ).ok(); + } + }); + log::trace!("starting main loop"); #[cfg(feature = "autobasis-ci")] { log::info!("starting autobasis CI launcher"); @@ -420,7 +488,7 @@ fn wrapped_main() -> ! { } Some(ShellOpcode::Redraw) => { if allow_redraw { - repl.redraw().expect("REPL couldn't redraw"); + repl.redraw(pddb_init_done.load(Ordering::SeqCst)).expect("REPL couldn't redraw"); } } Some(ShellOpcode::ChangeFocus) => xous::msg_scalar_unpack!(msg, new_state_code, _, _, _, { @@ -446,7 +514,7 @@ fn wrapped_main() -> ! { } } if update_repl { - repl.update(was_callback).expect("REPL had problems updating"); + repl.update(was_callback, pddb_init_done.load(Ordering::SeqCst)).expect("REPL had problems updating"); update_repl = false; } log::trace!("reached bottom of main loop");