mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
Merge branch 'pddb' into main
This commit is contained in:
commit
7ef4e4dc41
11 changed files with 152 additions and 25 deletions
|
|
@ -174,6 +174,14 @@ perform the Xous firmware upgrade. This requires running manual update commands,
|
|||
- Autobacklight feature: selectable feature to automatically raise the backlight when keys are pressed. Thanks to gsora for the feature!
|
||||
- More importantly, this adds `crossbeam` into the kernel. This is a somewhat heavyweight item and is not recommended for use in core
|
||||
services, but by including it to drive the auto-backlight feature, we get regular code coverage of the condvar pathway.
|
||||
- Fix PDDB FSCB generation problems
|
||||
- some issue were found in free space cache generation, these are now fixed
|
||||
- fuzzer added to push many more corner cases than before -- disk is filled to ~75% capacity with dozens of Bases,
|
||||
triggering roughly 4 FSCB ops, and then all Bases and keys are verified to be correct.
|
||||
- Optimize GAM performance
|
||||
- context switches that revert to a prior app screen are restored from a stashed bitmap instead of a full redraw. This
|
||||
prevents old menus, contexts, and defacements from "piling up" when an app is blocked due to an exceptional condition
|
||||
being raised (such as the PDDB requesting a full basis unlock to generate the FSCB)
|
||||
|
||||
## Roadmap to 1.0
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ fn main() -> ! {
|
|||
|
||||
// this is the main event loop for the app.
|
||||
let mut allow_redraw = true;
|
||||
let mut into_allow_redraw = false;
|
||||
loop {
|
||||
let msg = xous::receive_message(sid).unwrap();
|
||||
match FromPrimitive::from_usize(msg.body.id()) {
|
||||
|
|
@ -65,6 +66,10 @@ fn main() -> ! {
|
|||
}
|
||||
}
|
||||
Some(AppOp::Pump) => { // this is a blocking scalar so that pump calls don't initiate faster than the app can draw
|
||||
if into_allow_redraw {
|
||||
ball.focus();
|
||||
into_allow_redraw = false;
|
||||
}
|
||||
if allow_redraw {
|
||||
ball.update();
|
||||
}
|
||||
|
|
@ -81,6 +86,7 @@ fn main() -> ! {
|
|||
}),
|
||||
Some(AppOp::FocusChange) => xous::msg_scalar_unpack!(msg, new_state_code, _, _, _, {
|
||||
let new_state = gam::FocusState::convert_focus_change(new_state_code);
|
||||
log::info!("focus change: {:?}", new_state);
|
||||
match new_state {
|
||||
gam::FocusState::Background => {
|
||||
allow_redraw = false; // this instantly terminates future updates, even if Pump messages are in our input queue
|
||||
|
|
@ -90,6 +96,7 @@ fn main() -> ! {
|
|||
).expect("couldn't send stop message to the pump thread");
|
||||
}
|
||||
gam::FocusState::Foreground => {
|
||||
into_allow_redraw = true;
|
||||
allow_redraw = true;
|
||||
xous::send_message(
|
||||
cid_to_pump,
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ fn main() -> ! {
|
|||
Some(VaultOp::ChangeFocus) => xous::msg_scalar_unpack!(msg, new_state_code, _, _, _, {
|
||||
let new_state = gam::FocusState::convert_focus_change(new_state_code);
|
||||
vaultux.change_focus_to(&new_state);
|
||||
log::info!("change focus: {:?}", new_state);
|
||||
log::debug!("change focus: {:?}", new_state);
|
||||
match new_state {
|
||||
gam::FocusState::Background => {
|
||||
allow_redraw = false;
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ pub(crate) struct ContextManager {
|
|||
contexts: HashMap::<[u32; 4], UxContext>,
|
||||
focused_context: Option<[u32; 4]>, // app_token of the app that has I/O focus, if any
|
||||
last_context: Option<[u32; 4]>, // previously focused context, if any
|
||||
context_stack: Vec::<[u32; 4]>,
|
||||
imef: ime_plugin_api::ImeFrontEnd,
|
||||
imef_active: bool,
|
||||
kbd: keyboard::Keyboard,
|
||||
|
|
@ -112,6 +113,7 @@ impl ContextManager {
|
|||
ContextManager {
|
||||
tm: TokenManager::new(&xns),
|
||||
contexts: HashMap::new(),
|
||||
context_stack: Vec::new(),
|
||||
focused_context: None,
|
||||
last_context: None,
|
||||
imef,
|
||||
|
|
@ -325,7 +327,10 @@ impl ContextManager {
|
|||
token: [u32; 4],
|
||||
clear: bool,
|
||||
) -> Result<(), xous::Error> {
|
||||
self.notify_app_switch(token).ok();
|
||||
|
||||
let mut leaving_visibility: bool = false;
|
||||
let stack_on_entry = self.context_stack.len();
|
||||
{
|
||||
// using a temp copy of the old focus, check if we need to update any visibility state
|
||||
let maybe_leaving_focused_context = if let Some(focused_token) = self.focused_context {
|
||||
|
|
@ -357,22 +362,27 @@ impl ContextManager {
|
|||
(leaving_focused_context.layout.behavior() == LayoutBehavior::App) {
|
||||
context.layout.set_visibility_state(true, canvases);
|
||||
leaving_visibility = false;
|
||||
self.context_stack.pop();
|
||||
self.context_stack.push(token);
|
||||
} 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;
|
||||
self.context_stack.push(token);
|
||||
} 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;
|
||||
self.context_stack.pop();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// there was no current focus, just make the activation visible
|
||||
log::debug!("setting first-time visibility to context {:?}", token);
|
||||
context.layout.set_visibility_state(true, canvases);
|
||||
self.context_stack.push(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -448,12 +458,27 @@ impl ContextManager {
|
|||
self.last_context = self.focused_context;
|
||||
self.focused_context = Some(last_token);
|
||||
}
|
||||
log::trace!("context stack: {:x?}", self.context_stack);
|
||||
if self.context_stack.len() > 1 { // we've now got a stack of contexts, start stashing copies
|
||||
log::trace!("stashing");
|
||||
gfx.stash(true);
|
||||
}
|
||||
// run the defacement before we redraw all the canvases
|
||||
if deface(gfx, &self.trng, canvases) {
|
||||
log::trace!("activate triggered a defacement");
|
||||
}
|
||||
log::trace!("activate redraw");
|
||||
self.redraw().expect("couldn't redraw the currently focused app");
|
||||
if self.context_stack.len() > 0 && stack_on_entry > 1 {
|
||||
if self.context_stack[self.context_stack.len() - 1] == token {
|
||||
// if we're returning to the previous context, just pop the image
|
||||
log::trace!("popping");
|
||||
gfx.pop(true);
|
||||
} else {
|
||||
self.redraw().expect("couldn't redraw the currently focused app");
|
||||
}
|
||||
} else {
|
||||
self.redraw().expect("couldn't redraw the currently focused app");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -477,19 +502,24 @@ impl ContextManager {
|
|||
}
|
||||
}
|
||||
pub(crate) fn notify_app_switch(&self, new_app_token: [u32; 4]) -> Result<(), xous::Error> {
|
||||
if let Some(old_context) = self.get_context_by_token(self.focused_context.unwrap()) {
|
||||
if let Some(focuschange_id) = old_context.focuschange_id {
|
||||
log::trace!("Background focus change to {}, id {}", old_context.listener, old_context.redraw_id);
|
||||
xous::send_message(old_context.listener,
|
||||
xous::Message::new_scalar(focuschange_id as usize, gam::FocusState::Background as usize, 0, 0, 0)
|
||||
).map(|_| ())?;
|
||||
} else {
|
||||
return Err(xous::Error::ServerNotFound);
|
||||
log::trace!("notify app switch to {:?}", new_app_token);
|
||||
if let Some(current_focus) = self.focused_context {
|
||||
if let Some(old_context) = self.get_context_by_token(current_focus) {
|
||||
if let Some(focuschange_id) = old_context.focuschange_id {
|
||||
log::trace!("Background focus change to {}, id {} / {:?}", old_context.listener, old_context.redraw_id, current_focus);
|
||||
xous::send_message(old_context.listener,
|
||||
xous::Message::new_scalar(focuschange_id as usize, gam::FocusState::Background as usize, 0, 0, 0)
|
||||
).map(|_| ())?;
|
||||
} else {
|
||||
// don't return an error -- this just means that the listener doesn't recognize focus changes. This should not
|
||||
// deprive the later app of a notification that it is coming into focus!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(new_context) = self.get_context_by_token(new_app_token) {
|
||||
if let Some(focuschange_id) = new_context.focuschange_id {
|
||||
log::trace!("Foreground focus change to {}, id {}", new_context.listener, new_context.redraw_id);
|
||||
log::trace!("Foreground focus change to {}, id {} / {:?}", new_context.listener, new_context.redraw_id, new_app_token);
|
||||
xous::send_message(new_context.listener,
|
||||
xous::Message::new_scalar(focuschange_id as usize, gam::FocusState::Foreground as usize, 0, 0, 0)
|
||||
).map(|_| ())?;
|
||||
|
|
@ -622,7 +652,7 @@ impl ContextManager {
|
|||
pub(crate) fn toggle_menu_mode(&mut self, token: [u32; 4]) {
|
||||
if let Some(context) = self.contexts.get_mut(&token) {
|
||||
context.imef_menu_mode = !context.imef_menu_mode;
|
||||
log::info!("menu mode for token {:?} is now {}", token, context.imef_menu_mode);
|
||||
log::debug!("menu mode for token {:?} is now {}", token, context.imef_menu_mode);
|
||||
}
|
||||
}
|
||||
pub(crate) fn raise_menu(&mut self,
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ fn wrapped_main() -> ! {
|
|||
let status_canvas = Canvas::new(
|
||||
Rectangle::new_coords(
|
||||
0, 0, screensize.x,
|
||||
// note: if this gets modified, the "pop" routine in gfx/backend/betrusted.rs also needs to be updated
|
||||
gfx.glyph_height_hint(GlyphStyle::Cjk).expect("couldn't get glyph height") as i16 * 2),
|
||||
255, &trng, None, crate::api::CanvasType::Status
|
||||
).expect("couldn't create status canvas");
|
||||
|
|
@ -567,8 +568,6 @@ fn wrapped_main() -> ! {
|
|||
for switchers in authorized_switchers {
|
||||
if let Some(auth_token) = context_mgr.find_app_token_by_name(switchers) {
|
||||
if auth_token == switchapp.token {
|
||||
context_mgr.notify_app_switch(new_app_token)
|
||||
.unwrap_or_else(|_| {log::warn!("Application does not recognize focus changes")});
|
||||
match context_mgr.activate(&gfx, &mut canvases, new_app_token, false) {
|
||||
Ok(_) => (),
|
||||
Err(_) => log::warn!("failed to switch to {}, silent error!", switchapp.app_name.as_str().unwrap()),
|
||||
|
|
|
|||
|
|
@ -108,6 +108,11 @@ pub(crate) enum Opcode {
|
|||
BulkReadFonts,
|
||||
RestartBulkRead,
|
||||
|
||||
/// sling the framebuffer into and out of the suspend/resume area, abusing this
|
||||
/// to help accelerate redraws between modal swaps.
|
||||
Stash,
|
||||
Pop,
|
||||
|
||||
/// generates a test pattern
|
||||
TestPattern,
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::api::Point;
|
||||
use crate::api::{LINES, WIDTH};
|
||||
use susres::{ManagedMem, RegManager, RegOrField, SuspendResume};
|
||||
use susres::{RegManager, RegOrField, SuspendResume};
|
||||
use utralib::generated::*;
|
||||
use xous::MemoryRange;
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ const CONFIG_CLOCK_FREQUENCY: u32 = 100_000_000;
|
|||
pub struct XousDisplay {
|
||||
fb: MemoryRange,
|
||||
hwfb: MemoryRange,
|
||||
srfb: ManagedMem<{ utralib::generated::HW_MEMLCD_MEM_LEN / core::mem::size_of::<u32>() }>,
|
||||
srfb: [u32; FB_SIZE],
|
||||
csr: utralib::CSR<u32>,
|
||||
susres: RegManager<{ utra::memlcd::MEMLCD_NUMREGS }>,
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ impl XousDisplay {
|
|||
hwfb,
|
||||
csr: CSR::new(control.as_mut_ptr() as *mut u32),
|
||||
susres: RegManager::new(control.as_mut_ptr() as *mut u32),
|
||||
srfb: ManagedMem::new(fb),
|
||||
srfb: [0u32; FB_SIZE],
|
||||
};
|
||||
|
||||
display.set_clock(CONFIG_CLOCK_FREQUENCY);
|
||||
|
|
@ -101,18 +101,38 @@ impl XousDisplay {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn stash(&mut self) {
|
||||
let fb = unsafe{core::slice::from_raw_parts_mut(self.fb.as_mut_ptr() as *mut u32, FB_SIZE)};
|
||||
for lines in 0..FB_LINES {
|
||||
// set the dirty bits prior to stashing the frame buffer
|
||||
fb[lines * FB_WIDTH_WORDS + (FB_WIDTH_WORDS - 1)] |= 0x1_0000;
|
||||
}
|
||||
let srfb_ptr = self.srfb.as_mut_ptr() as *mut u32;
|
||||
for (index, &src) in fb.iter().enumerate() {
|
||||
unsafe{srfb_ptr.add(index).write_volatile(src)};
|
||||
}
|
||||
}
|
||||
pub fn pop(&mut self) {
|
||||
let fb: &mut [u32] = self.fb.as_slice_mut();
|
||||
// skip copying the status bar, so that the status info is not overwritten by the pop.
|
||||
// this is "fixed" at 32 pixels high (2 * Cjk glyph height hint) per line 79 in gam/src/main.rs
|
||||
fb[FB_WIDTH_WORDS * 32..FB_SIZE].copy_from_slice(&self.srfb[FB_WIDTH_WORDS * 32..FB_SIZE]);
|
||||
self.redraw();
|
||||
}
|
||||
|
||||
pub fn suspend(&mut self) {
|
||||
while self.busy() {
|
||||
// just wait until any pending FB operations are done
|
||||
}
|
||||
let fb: *mut [u32; FB_SIZE] = self.fb.as_mut_ptr() as *mut [u32; FB_SIZE];
|
||||
let fb = unsafe{core::slice::from_raw_parts_mut(self.fb.as_mut_ptr() as *mut u32, FB_SIZE)};
|
||||
for lines in 0..FB_LINES {
|
||||
// set the dirty bits prior to stashing the frame buffer
|
||||
unsafe {
|
||||
(*fb)[lines * FB_WIDTH_WORDS + (FB_WIDTH_WORDS - 1)] |= 0x1_0000;
|
||||
}
|
||||
fb[lines * FB_WIDTH_WORDS + (FB_WIDTH_WORDS - 1)] |= 0x1_0000;
|
||||
}
|
||||
let srfb_ptr = self.srfb.as_mut_ptr() as *mut u32;
|
||||
for (index, &src) in fb.iter().enumerate() {
|
||||
unsafe{srfb_ptr.add(index).write_volatile(src)};
|
||||
}
|
||||
self.srfb.suspend();
|
||||
self.susres.suspend();
|
||||
|
||||
let note = crate::sleep_note::LOGO_MAP;
|
||||
|
|
@ -154,7 +174,8 @@ impl XousDisplay {
|
|||
}
|
||||
pub fn resume(&mut self) {
|
||||
self.susres.resume();
|
||||
self.srfb.resume();
|
||||
let fb: &mut [u32] = self.fb.as_slice_mut();
|
||||
fb[..FB_SIZE].copy_from_slice(&self.srfb);
|
||||
|
||||
self.redraw();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ const LIGHT_COLOUR: u32 = 0x1B1B19;
|
|||
pub struct XousDisplay {
|
||||
native_buffer: Vec<u32>, //[u32; WIDTH * HEIGHT],
|
||||
emulated_buffer: [u32; FB_SIZE],
|
||||
srfb: [u32; FB_SIZE],
|
||||
window: Window,
|
||||
devboot: bool,
|
||||
}
|
||||
|
|
@ -70,6 +71,7 @@ impl XousDisplay {
|
|||
native_buffer,
|
||||
window,
|
||||
emulated_buffer: [0u32; FB_SIZE],
|
||||
srfb: [0u32; FB_SIZE],
|
||||
devboot: true,
|
||||
}
|
||||
}
|
||||
|
|
@ -82,6 +84,15 @@ impl XousDisplay {
|
|||
pub fn suspend(&self) {}
|
||||
pub fn resume(&self) {}
|
||||
|
||||
pub fn stash(&mut self) {
|
||||
self.srfb.copy_from_slice(&self.emulated_buffer);
|
||||
}
|
||||
pub fn pop(&mut self) {
|
||||
self.emulated_buffer[FB_WIDTH_WORDS*32..].copy_from_slice(&self.srfb[FB_WIDTH_WORDS*32..]);
|
||||
self.redraw();
|
||||
self.update();
|
||||
}
|
||||
|
||||
pub fn screen_size(&self) -> Point {
|
||||
Point::new(WIDTH as i16, HEIGHT as i16)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,6 +268,38 @@ impl Gfx {
|
|||
)
|
||||
.expect("couldn't self test");
|
||||
}
|
||||
|
||||
pub fn stash(&self, blocking: bool) {
|
||||
if blocking {
|
||||
send_message(
|
||||
self.conn,
|
||||
Message::new_blocking_scalar(Opcode::Stash.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
)
|
||||
.expect("couldn't stash");
|
||||
} else {
|
||||
send_message(
|
||||
self.conn,
|
||||
Message::new_scalar(Opcode::Stash.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
)
|
||||
.expect("couldn't stash");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(&self, blocking: bool) {
|
||||
if blocking {
|
||||
send_message(
|
||||
self.conn,
|
||||
Message::new_blocking_scalar(Opcode::Pop.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
)
|
||||
.expect("couldn't stash");
|
||||
} else {
|
||||
send_message(
|
||||
self.conn,
|
||||
Message::new_scalar(Opcode::Pop.to_usize().unwrap(), 0, 0, 0, 0)
|
||||
)
|
||||
.expect("couldn't stash");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
|
|
|||
|
|
@ -606,6 +606,20 @@ fn wrapped_main() -> ! {
|
|||
|
||||
xous::return_scalar(msg.sender, duration).expect("couldn't ack test pattern");
|
||||
}),
|
||||
Some(Opcode::Stash) => {
|
||||
display.stash();
|
||||
match msg.body { // ack the message if it's a blocking scalar
|
||||
xous::Message::BlockingScalar(_) => xous::return_scalar(msg.sender, 1).unwrap(),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
Some(Opcode::Pop) => {
|
||||
display.pop();
|
||||
match msg.body { // ack the message if it's a blocking scalar
|
||||
xous::Message::BlockingScalar(_) => xous::return_scalar(msg.sender, 1).unwrap(),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
Some(Opcode::Quit) => break,
|
||||
None => {
|
||||
log::error!("received opcode scalar that is not handled");
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ impl<'a> ShellCmdApi<'a> for PddbCmd {
|
|||
"dictlist" => {
|
||||
match self.pddb.list_dict(None) {
|
||||
Ok(list) => {
|
||||
let checked_len = if list.len() > 6 {
|
||||
let checked_len = if list.len() > 10 {
|
||||
write!(ret, "First 10 dicts of {}:", list.len()).unwrap();
|
||||
10
|
||||
} else {
|
||||
|
|
@ -594,7 +594,7 @@ impl<'a> ShellCmdApi<'a> for PddbCmd {
|
|||
// 128k chunks of junk
|
||||
const JUNK_CHUNK: usize = 131072;
|
||||
log::info!("fill junk");
|
||||
for index in 0..28 { // write ~3 megs of junk, should trigger FSCB unlock at least once...
|
||||
for index in 0..17 { // write ~2 megs of junk, should trigger FSCB unlock at least once...
|
||||
let mut junk = Vec::<u8>::new();
|
||||
// 128k chunk of junk
|
||||
for i in 0..JUNK_CHUNK {
|
||||
|
|
|
|||
Loading…
Reference in a new issue