diff --git a/services/gam/src/api.rs b/services/gam/src/api.rs index e434162c8..7e3176037 100644 --- a/services/gam/src/api.rs +++ b/services/gam/src/api.rs @@ -161,9 +161,6 @@ pub(crate) enum Opcode { RevertFocus, RevertFocusNb, // non-blocking version - /// request an app to take the focus - RequestFocus, - /// pass-through to get glyph heights to assist with layout planning, without having to create a gfx connection QueryGlyphProps, diff --git a/services/gam/src/contexts.rs b/services/gam/src/contexts.rs index c15ed10d3..b417da3f6 100644 --- a/services/gam/src/contexts.rs +++ b/services/gam/src/contexts.rs @@ -408,7 +408,6 @@ impl ContextManager { // now re-check-out the new context and finalize things let maybe_new_focus = self.get_context_by_token(token); if let Some(context) = maybe_new_focus { - self.notify_focus_change_to(gam::FocusState::Foreground, context).unwrap(); if clear { context.layout.clear(gfx, canvases).expect("can't clear on context activation"); } @@ -422,6 +421,7 @@ impl ContextManager { // revert the keyboard vibe state self.kbd.set_vibe(context.vibe).expect("couldn't restore keyboard vibe"); + self.notify_focus_change_to(gam::FocusState::Foreground, context).unwrap(); log::debug!("raised focus to: {:?}", context); let last_token = context.app_token; self.last_context = self.focused_context; diff --git a/services/gam/src/lib.rs b/services/gam/src/lib.rs index 3c1ac0523..fd7b88e2d 100644 --- a/services/gam/src/lib.rs +++ b/services/gam/src/lib.rs @@ -315,12 +315,6 @@ impl Gam { 0, 0, 0, 0,) ).map(|_| ()) } - pub fn request_focus(&self, token: [u32; 4]) -> Result<(), xous::Error> { - send_message(self.conn, - Message::new_blocking_scalar(Opcode::RequestFocus.to_usize().unwrap(), - token[0] as usize, token[1] as usize, token[2] as usize, token[3] as usize,) - ).map(|_| ()) - } pub fn glyph_height_hint(&self, glyph: GlyphStyle) -> Result { let response = send_message(self.conn, diff --git a/services/gam/src/main.rs b/services/gam/src/main.rs index d2e6b8230..772dd1c43 100644 --- a/services/gam/src/main.rs +++ b/services/gam/src/main.rs @@ -21,7 +21,10 @@ use gam::{MAIN_MENU_NAME, ROOTKEY_MODAL_NAME}; use log::info; use std::collections::HashMap; use num_traits::*; -use core::{sync::atomic::{AtomicU32, Ordering}}; +use core::sync::atomic::{AtomicU32, Ordering}; + +/// This sets the initial app focus on boot +const INITIAL_APP_FOCUS: &'static str = gam::APP_NAME_SHELLCHAT; static CB_TO_MAIN_CONN: AtomicU32 = AtomicU32::new(0); fn imef_cb(s: String::<4000>) { @@ -35,7 +38,7 @@ fn imef_cb(s: String::<4000>) { #[xous::xous_main] fn xmain() -> ! { log_server::init_wait().unwrap(); - log::set_max_level(log::LevelFilter::Info); + log::set_max_level(log::LevelFilter::Trace); info!("my PID is {}", xous::process::id()); let xns = xous_names::XousNames::new().unwrap(); @@ -78,6 +81,9 @@ fn xmain() -> ! { ) ).expect("couldn't set status GID"); + // a random number we can use to identify ourselves between API calls + let gam_token = [trng.get_u32().unwrap(), trng.get_u32().unwrap(), trng.get_u32().unwrap(), trng.get_u32().unwrap()]; + let mut powerdown_requested = false; let mut last_time: u64 = ticktimer.elapsed_ms(); let mut did_test = false; // allow one go at the test pattern @@ -396,6 +402,11 @@ fn xmain() -> ! { let mut buffer = unsafe{ Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap()) }; let registration = buffer.to_original::().unwrap(); + let init_focus_found = if registration.app_name.as_str().unwrap_or("UTF-8 error") == INITIAL_APP_FOCUS { + true + } else { + false + }; // note that we are currently assigning all Ux registrations a trust level consistent with a boot context (ultimately trusted) // this needs to be modified later on once we allow post-boot apps to be created let token = context_mgr.register(&gfx, &trng, &status_canvas, &mut canvases, @@ -407,6 +418,22 @@ fn xmain() -> ! { canvases = recompute_canvases(&canvases, Rectangle::new(Point::new(0, 0), screensize)); buffer.replace(Return::UxToken(token)).unwrap(); + + // fire off a thread that deals with activating the initial boot context. You need this because this call has to complete before the context can respond to activation events. + if token.is_some() & init_focus_found { + std::thread::spawn({ + let gam_token = gam_token.clone(); + let conn = CB_TO_MAIN_CONN.load(Ordering::SeqCst); + move || { + let switchapp = SwitchToApp { + token: gam_token, + app_name: String::<128>::from_str(INITIAL_APP_FOCUS), + }; + let buf = Buffer::into_buf(switchapp).or(Err(xous::Error::InternalError))?; + buf.send(conn, Opcode::SwitchToApp.to_u32().unwrap()).or(Err(xous::Error::InternalError)).map(|_|()) + } + }); + } }, Some(Opcode::SetAudioOpcode) => { let buffer = unsafe{ Buffer::from_memory_message(msg.body.memory_message().unwrap()) }; @@ -457,6 +484,7 @@ fn xmain() -> ! { Some(Opcode::RevertFocusNb) => { context_mgr.revert_focus(&gfx, &mut canvases); }, + /* Some(Opcode::RequestFocus) => msg_blocking_scalar_unpack!(msg, t0, t1, t2, t3, { // TODO: add some limitations around who can request focus // for now, it's the boot set so we just trust the requestor @@ -464,7 +492,7 @@ fn xmain() -> ! { // this is a blocking scalar, so return /something/ so we know to move on xous::return_scalar(msg.sender, 1).expect("couldn't confirm focus activation"); - }), + }), */ 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"); xous::return_scalar(msg.sender, height).expect("could not return QueryGlyphProps request"); @@ -496,6 +524,10 @@ fn xmain() -> ! { continue; } } + // this message came from ourselves + if gam_token == switchapp.token { + context_mgr.activate(&gfx, &mut canvases, new_app_token, true); + } } }, Some(Opcode::RaiseMenu) => { diff --git a/services/shellchat/src/main.rs b/services/shellchat/src/main.rs index 7a2d91200..4591c1fb5 100644 --- a/services/shellchat/src/main.rs +++ b/services/shellchat/src/main.rs @@ -94,12 +94,9 @@ impl Repl{ gotinput_id: Some(ShellOpcode::Line.to_u32().unwrap()), audioframe_id: None, rawkeys_id: None, - focuschange_id: None, // shellchat will keep running in the background even if it has lost focus + focuschange_id: Some(ShellOpcode::ChangeFocus.to_u32().unwrap()), }).expect("couldn't register Ux context for shellchat"); - // we should be the first app running, so get the focus - gam.request_focus(token.unwrap()).expect("couldn't take focus"); - let content = gam.request_content_canvas(token.unwrap()).expect("couldn't get content canvas"); log::trace!("content canvas {:?}", content); let screensize = gam.get_canvas_bounds(content).expect("couldn't get dimensions of content canvas"); @@ -278,6 +275,8 @@ enum ShellOpcode { Line = 0, // make sure we occupy opcodes with discriminants < 1000, as the rest are used for callbacks /// redraw our UI Redraw, + /// change focus + ChangeFocus, /// exit the application Quit, } @@ -301,6 +300,7 @@ fn xmain() -> ! { let mut update_repl = false; let mut was_callback = false; + let mut allow_redraw = true; log::trace!("starting main loop"); loop { let msg = xous::receive_message(shch_sid).unwrap(); @@ -315,9 +315,21 @@ fn xmain() -> ! { was_callback = false; } Some(ShellOpcode::Redraw) => { - log::trace!("got Redraw"); - repl.redraw().expect("REPL couldn't redraw"); + if allow_redraw { + repl.redraw().expect("REPL couldn't redraw"); + } } + Some(ShellOpcode::ChangeFocus) => xous::msg_scalar_unpack!(msg, new_state_code, _, _, _, { + let new_state = gam::FocusState::convert_focus_change(new_state_code); + match new_state { + gam::FocusState::Background => { + allow_redraw = false; + } + gam::FocusState::Foreground => { + allow_redraw = true; + } + } + }), Some(ShellOpcode::Quit) => { log::error!("got Quit"); break;