add busy-wait queuing for alerts in case multiple alerts pop up

this case happens when e.g. you're asking for a password but
then the connection manager throws an error.
This commit is contained in:
bunnie 2022-02-05 02:03:56 +08:00
parent 08421999ad
commit b4a34bd9ec
10 changed files with 105 additions and 84 deletions

View file

@ -67,6 +67,12 @@ impl Canvas {
}
})
}
pub fn intersects(&self, other: &Canvas) -> bool {
self.clip_rect.intersects(other.clip_rect())
}
pub fn less_trusted_than(&self, other: &Canvas) -> bool {
self.trust_level() < other.trust_level()
}
pub fn pan_offset(&self) -> Point { self.pan_offset }
pub fn clip_rect(&self) -> Rectangle { self.clip_rect }
pub fn set_clip(&mut self, cr: Rectangle) { self.clip_rect = cr; self.state = CanvasState::OffScreenNotDrawable }
@ -322,36 +328,6 @@ pub fn recompute_canvases(canvases: &HashMap<Gid, Canvas>, screen: Rectangle) ->
}
higher_clipregions.push(canvas);
}
/*
loop {
if let Some(c) = sorted_clipregions.pop() {
if debug { info!(" CANVAS: considering {:?}", c);}
let mut canvas = c.clone();
let mut drawable: bool = true;
if canvas.trust_level() < trust_level {
trust_level = canvas.trust_level();
}
//if !clip_region.intersects(screen) {
// drawable = false;
// if debug { info!(" * CANVAS: not drawable, does not intersect");}
//} else { // short circuit this computation if it's not drawable because it's off screen
// note that this .iter() is *not* sorted by trust level, but all elements will be of greater than or equal to the current trust level
for &region in higher_clipregions.iter() {
// regions of the same trust level can draw over each other. Draw order is arbitrary.
if region.clip_rect().intersects(clip_region) && (region.trust_level() < trust_level) {
log::info!("{:?} {:?}->false", canvas.canvas_type, canvas.state);
drawable = false;
if debug { info!(" * CANVAS: not drawable, lower trust intersecting with higher trust region");}
}
}
//}
canvas.set_drawable(drawable);
higher_clipregions.push(canvas);
} else {
break;
}
}*/
// create a new index map out of the recomputed higher_clipregions
let mut map: HashMap<Gid, Canvas> = HashMap::new();

View file

@ -309,7 +309,7 @@ impl ContextManager {
canvases: &mut HashMap<Gid, Canvas>,
token: [u32; 4],
clear: bool,
) -> ActivationResult {
) -> Result<(), xous::Error> {
let mut leaving_visibility: bool = false;
{
// using a temp copy of the old focus, check if we need to update any visibility state
@ -327,28 +327,29 @@ impl ContextManager {
if let Some(context) = maybe_new_focus {
if let Some(leaving_focused_context) = maybe_leaving_focused_context {
if token != leaving_focused_context.app_token {
if (context.layout.behavior() == LayoutBehavior::Alert) &&
(leaving_focused_context.layout.behavior() == LayoutBehavior::Alert) {
// left off: determine if the currently visible context is a password field. if it is, deny the activation.
context.layout.set_visibility_state(true, canvases);
//leaving_focused_context.layout.set_visibility_state(false, canvases);
leaving_visibility = false;
} else if (context.layout.behavior() == LayoutBehavior::App) &&
(leaving_focused_context.layout.behavior() == LayoutBehavior::App) {
context.layout.set_visibility_state(true, canvases);
//leaving_focused_context.layout.set_visibility_state(false, canvases);
leaving_visibility = false;
} else if (context.layout.behavior() == LayoutBehavior::Alert) &&
(leaving_focused_context.layout.behavior() == LayoutBehavior::App) {
context.layout.set_visibility_state(true, canvases);
//leaving_focused_context.layout.set_visibility_state(true, canvases);
leaving_visibility = true;
} else if (context.layout.behavior() == LayoutBehavior::App) &&
(leaving_focused_context.layout.behavior() == LayoutBehavior::Alert) {
context.layout.set_visibility_state(true, canvases);
//leaving_focused_context.layout.set_visibility_state(false, canvases);
leaving_visibility = false;
if // alert covering an alert
(context.layout.behavior() == LayoutBehavior::Alert) &&
(leaving_focused_context.layout.behavior() == LayoutBehavior::Alert) {
// just disallow alerts covering alerts for now...it's first come, first-serve.
log::warn!("Disallowing raise of alert over alert");
return Err(xous::Error::ShareViolation)
// context.layout.set_visibility_state(true, canvases);
// leaving_visibility = false;
} else if // app covering an app
(context.layout.behavior() == LayoutBehavior::App) &&
(leaving_focused_context.layout.behavior() == LayoutBehavior::App) {
context.layout.set_visibility_state(true, canvases);
leaving_visibility = false;
} else if // alert covering an app
(context.layout.behavior() == LayoutBehavior::Alert) &&
(leaving_focused_context.layout.behavior() == LayoutBehavior::App) {
context.layout.set_visibility_state(true, canvases);
leaving_visibility = true;
} else if // app covering an alert
(context.layout.behavior() == LayoutBehavior::App) &&
(leaving_focused_context.layout.behavior() == LayoutBehavior::Alert) {
context.layout.set_visibility_state(true, canvases);
leaving_visibility = false;
}
}
} else {
@ -440,14 +441,16 @@ impl ContextManager {
log::trace!("activate redraw");
self.redraw().expect("couldn't redraw the currently focused app");
}
ActivationResult::Success
Ok(())
}
pub(crate) fn revert_focus(&mut self,
gfx: &graphics_server::Gfx,
canvases: &mut HashMap<Gid, Canvas>,
) {
) -> Result<(), xous::Error> {
if let Some(last) = self.last_context {
self.activate(gfx, canvases, last, false);
self.activate(gfx, canvases, last, false)
} else {
Err(xous::Error::UseBeforeInit)
}
}
fn notify_focus_change_to(&self, new_state: gam::FocusState, context: &UxContext) -> Result<(), xous::Error> {
@ -511,7 +514,9 @@ impl ContextManager {
if context.layout.behavior() == LayoutBehavior::App {
if let Some(menu_token) = self.find_app_token_by_name(MAIN_MENU_NAME) {
// set the menu to the active context
self.activate(gfx, canvases, menu_token, false);
match self.activate(gfx, canvases, menu_token, false) {
_ => log::warn!("Couldn't raise menu, user will have to try again."),
}
// don't pass the initial key hit back to the menu app, just eat it and return
return;
}
@ -568,7 +573,7 @@ impl ContextManager {
name: &str,
gfx: &graphics_server::Gfx,
canvases: &mut HashMap<Gid, Canvas>,
) -> ActivationResult {
) -> Result<(), xous::Error> {
log::debug!("looking for menu {}", name);
if let Some(token) = self.find_app_token_by_name(name) {
log::debug!("found menu token: {:?}", token);
@ -579,10 +584,11 @@ impl ContextManager {
if context.layout.behavior() == LayoutBehavior::Alert {
log::debug!("activating context");
return self.activate(gfx, canvases, token, false)
} else {
return Err(xous::Error::AccessDenied)
}
}
}
ActivationResult::Failure
Err(xous::Error::ProcessNotFound)
}
}

View file

@ -19,7 +19,7 @@ pub(crate) struct ChatLayout {
// my internal bookkeeping records. Allow input area to grow into content area
min_content_height: i16,
min_input_height: i16,
screensize: Point,
_screensize: Point,
_small_height: i16,
_regular_height: i16,
}
@ -61,7 +61,7 @@ impl ChatLayout {
input: input_canvas.gid(),
min_content_height: 64,
min_input_height,
screensize,
_screensize: screensize,
_small_height: small_height,
_regular_height: regular_height,
})

View file

@ -9,7 +9,7 @@ const TRUST_OFFSET: u8 = 4;
#[derive(Debug, Copy, Clone)]
pub(crate) struct Framebuffer {
pub gid: Gid,
screensize: Point,
_screensize: Point,
}
impl Framebuffer {
pub fn init(
@ -28,7 +28,7 @@ impl Framebuffer {
Ok(Framebuffer {
gid: fb_canvas.gid(),
screensize,
_screensize: screensize,
})
}
}

View file

@ -335,6 +335,8 @@ impl Gam {
}
/// requests a context switch to a new app. Doesn't have to be obeyed, depending
/// upon the GAM's policy. Only the certain sources can switch apps, hence the token.
/// Failure to switch *is silent* because waiting for an activation to go through and
/// confirm can cause a deadlock condition.
pub fn switch_to_app(&self, app_name: &str, token: [u32; 4]) -> Result<(), xous::Error> {
let switchapp = SwitchToApp {
token,
@ -343,21 +345,27 @@ impl Gam {
let buf = Buffer::into_buf(switchapp).or(Err(xous::Error::InternalError))?;
buf.send(self.conn, Opcode::SwitchToApp.to_u32().unwrap()).or(Err(xous::Error::InternalError)).map(|_|())
}
pub fn raise_menu(&self, menu_name: &str) -> Result<ActivationResult, xous::Error> {
pub fn raise_menu(&self, menu_name_str: &str) -> Result<(), xous::Error> {
let menu_name = GamActivation {
name: String::<128>::from_str(menu_name),
name: String::<128>::from_str(menu_name_str),
result: None,
};
let mut buf = Buffer::into_buf(menu_name).or(Err(xous::Error::InternalError))?;
buf.lend_mut(self.conn, Opcode::RaiseMenu.to_u32().unwrap()).or(Err(xous::Error::InternalError)).expect("couldn't send RaiseMenu opcode");
let result = buf.to_original::<GamActivation, _>().unwrap();
if let Some(code) = result.result {
Ok(code)
match code {
ActivationResult::Success => Ok(()),
ActivationResult::Failure => {
log::warn!("Couldn't raise {}", menu_name_str);
Err(xous::Error::ShareViolation)
}
}
} else {
Err(xous::Error::InternalError)
}
}
pub fn raise_modal(&self, modal_name: &str) -> Result<ActivationResult, xous::Error> {
pub fn raise_modal(&self, modal_name: &str) -> Result<(), xous::Error> {
self.raise_menu(modal_name)
}
/// this is a one-way door, once you've set it, you can't unset it.

View file

@ -468,11 +468,15 @@ fn xmain() -> ! {
else { context_mgr.vibe(false) }
}),
Some(Opcode::RevertFocus) => {
context_mgr.revert_focus(&gfx, &mut canvases);
xous::return_scalar(msg.sender, 0).expect("couldn't unblock caller");
match context_mgr.revert_focus(&gfx, &mut canvases) {
Ok(_) => xous::return_scalar(msg.sender, 0).expect("couldn't unblock caller"),
_ => xous::return_scalar(msg.sender, 1).expect("couldn't unblock caller"),
}
},
Some(Opcode::RevertFocusNb) => {
context_mgr.revert_focus(&gfx, &mut canvases);
match context_mgr.revert_focus(&gfx, &mut canvases) {
_ => log::warn!("failed to revert focus, silent error!"),
}
},
Some(Opcode::QueryGlyphProps) => msg_blocking_scalar_unpack!(msg, style, _, _, _, {
let height = gfx.glyph_height_hint(GlyphStyle::from(style)).expect("couldn't query glyph height from gfx");
@ -489,25 +493,33 @@ fn xmain() -> ! {
if let Some(new_app_token) = context_mgr.find_app_token_by_name(switchapp.app_name.as_str().unwrap()) {
if let Some(menu_token) = context_mgr.find_app_token_by_name(MAIN_MENU_NAME) {
if menu_token == switchapp.token {
context_mgr.activate(&gfx, &mut canvases, new_app_token, false);
match context_mgr.activate(&gfx, &mut canvases, new_app_token, false) {
_ => log::warn!("failed to switch to {}, silent error!", switchapp.app_name.as_str().unwrap()),
}
continue;
}
}
if let Some(modal_token) = context_mgr.find_app_token_by_name(ROOTKEY_MODAL_NAME) {
if modal_token == switchapp.token {
context_mgr.activate(&gfx, &mut canvases, new_app_token, false);
match context_mgr.activate(&gfx, &mut canvases, new_app_token, false) {
_ => log::warn!("failed to switch to {}, silent error!", switchapp.app_name.as_str().unwrap()),
}
continue;
}
}
if let Some(token) = context_mgr.find_app_token_by_name(gam::STATUS_BAR_NAME) {
if token == switchapp.token {
context_mgr.activate(&gfx, &mut canvases, new_app_token, true);
match context_mgr.activate(&gfx, &mut canvases, new_app_token, true) {
_ => log::warn!("failed to switch to {}, silent error!", switchapp.app_name.as_str().unwrap()),
}
continue;
}
}
// this message came from ourselves
if gam_token == switchapp.token {
context_mgr.activate(&gfx, &mut canvases, new_app_token, true);
match context_mgr.activate(&gfx, &mut canvases, new_app_token, true) {
_ => log::warn!("failed to switch to {}, silent error!", switchapp.app_name.as_str().unwrap()),
}
}
}
},
@ -516,7 +528,11 @@ fn xmain() -> ! {
let mut activation = buffer.to_original::<GamActivation, _>().unwrap();
log::debug!("got request to raise context {}", activation.name);
let result = context_mgr.raise_menu(activation.name.as_str().unwrap(), &gfx, &mut canvases);
activation.result = Some(result);
activation.result = Some(
match result {
Ok(_) => ActivationResult::Success,
Err(_) => ActivationResult::Failure,
});
buffer.replace(activation).unwrap();
},
Some(Opcode::Devboot) => msg_scalar_unpack!(msg, ena, _, _, _, {

View file

@ -330,10 +330,25 @@ impl<'a> Modal<'a> {
modal
}
pub fn activate(&self) {
let tt = ticktimer_server::Ticktimer::new().unwrap();
while self.gam.raise_modal(self.name.to_str()).expect("couldn't activate modal") != ActivationResult::Success {
// wait a bit then try again
tt.sleep_ms(857).unwrap();
const POLL_DELAY_MS: usize = 857;
match self.gam.raise_modal(self.name.to_str()) {
Ok(_) => (),
Err(_) => {
std::thread::spawn({
let name = self.name.clone();
move || {
let xns = xous_names::XousNames::new().unwrap();
let ticktimer = ticktimer_server::Ticktimer::new().unwrap();
ticktimer.sleep_ms(POLL_DELAY_MS).unwrap();
let gam = crate::Gam::new(&xns).unwrap();
while gam.raise_modal(name.to_str()).is_err() {
log::info!("Couldn't raise {}; retrying...", name);
ticktimer.sleep_ms(POLL_DELAY_MS).unwrap();
}
log::info!("Success! exiting polling thread.");
}
});
}
}
}

View file

@ -24,4 +24,4 @@ utralib = { path = "../../utralib"}
[features]
ux_tests = []
default = ["ux_tests"]
default = []

View file

@ -467,9 +467,9 @@ fn xmain() -> ! {
}
});
// try to mount the PDDB automatically before starting the server
//#[cfg(not(any(windows, unix)))] // avoid errors in hosted mode
#[cfg(not(any(windows, unix)))] // avoid errors in hosted mode
let tt = ticktimer_server::Ticktimer::new().unwrap();
//#[cfg(not(any(windows, unix)))] // skip this step if running in hosted mode, for now...
#[cfg(not(any(windows, unix)))] // skip this step if running in hosted mode, for now...
if pddb_os.rootkeys_initialized() {
tt.sleep_ms(1000).unwrap(); // wait 1 second after boot before attempting to mount, to let the boot screen finish redrawing (cometic issue).
match ensure_password(&modals, &mut pddb_os) {

View file

@ -193,7 +193,7 @@ mod implementation {
false
}
pub fn is_pcache_boot_password_valid(&self) -> bool {
false
true
}
pub fn fpga_key_source(&self) -> FpgaKeySource {
FpgaKeySource::Efuse