mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
change the notification modal to accept the primary message and an
Option of independent text to display as a qrcode
pub fn show_notification(&self, notification: &str, qrtext:Option<&str>) -> Result<(), xous::Error> {}
221 lines
9.2 KiB
Rust
221 lines
9.2 KiB
Rust
use super::*;
|
|
use gam::*;
|
|
use gam::menu::*;
|
|
use gam::menu::api::DrawStyle;
|
|
use locales::t;
|
|
|
|
#[derive(PartialEq, Eq)]
|
|
enum BallMode {
|
|
Random,
|
|
Tilt
|
|
}
|
|
|
|
const BALL_RADIUS: i16 = 10;
|
|
const MOMENTUM_LIMIT: i32 = 8;
|
|
const BORDER_WIDTH: i16 = 5;
|
|
pub(crate) struct Ball {
|
|
gam: gam::Gam,
|
|
gid: Gid,
|
|
screensize: Point,
|
|
// our security token for making changes to our record on the GAM
|
|
_token: [u32; 4],
|
|
ball: Circle,
|
|
momentum: Point,
|
|
trng: trng::Trng,
|
|
modals: modals::Modals,
|
|
mode: BallMode,
|
|
com: com::Com,
|
|
}
|
|
|
|
impl Ball {
|
|
pub(crate) fn new(sid: xous::SID) -> Self {
|
|
let xns = xous_names::XousNames::new().expect("couldn't connect to Xous Namespace Server");
|
|
let gam = gam::Gam::new(&xns).expect("can't connect to Graphical Abstraction Manager");
|
|
|
|
let token = gam.register_ux(UxRegistration {
|
|
app_name: xous_ipc::String::<128>::from_str(gam::APP_NAME_BALL),
|
|
ux_type: gam::UxType::Framebuffer,
|
|
predictor: None,
|
|
listener: sid.to_array(), // note disclosure of our SID to the GAM -- the secret is now shared with the GAM!
|
|
redraw_id: AppOp::Redraw.to_u32().unwrap(),
|
|
gotinput_id: None,
|
|
audioframe_id: None,
|
|
focuschange_id: Some(AppOp::FocusChange.to_u32().unwrap()),
|
|
rawkeys_id: Some(AppOp::Rawkeys.to_u32().unwrap()),
|
|
}).expect("couldn't register Ux context for shellchat");
|
|
|
|
let gid = gam.request_content_canvas(token.unwrap()).expect("couldn't get content canvas");
|
|
let screensize = gam.get_canvas_bounds(gid).expect("couldn't get dimensions of content canvas");
|
|
|
|
gam.draw_rectangle(gid,
|
|
Rectangle::new_coords_with_style(0, 0, screensize.x, screensize.y,
|
|
DrawStyle::new(PixelColor::Light, PixelColor::Dark, 2))
|
|
).expect("couldn't draw our rectangle");
|
|
|
|
let trng = trng::Trng::new(&xns).unwrap();
|
|
let x = ((trng.get_u32().unwrap() / 2) as i32) % (MOMENTUM_LIMIT * 2) - MOMENTUM_LIMIT;
|
|
let y = ((trng.get_u32().unwrap() / 2) as i32) % (MOMENTUM_LIMIT * 2) - MOMENTUM_LIMIT;
|
|
|
|
let mut ball = Circle::new(Point::new(screensize.x / 2, screensize.y / 2), BALL_RADIUS);
|
|
ball.style = DrawStyle::new(PixelColor::Dark, PixelColor::Dark, 1);
|
|
gam.draw_circle(gid, ball).expect("couldn't erase ball's previous position");
|
|
let modals = modals::Modals::new(&xns).unwrap();
|
|
let com = com::Com::new(&xns).unwrap();
|
|
Ball {
|
|
gid,
|
|
gam,
|
|
screensize,
|
|
_token: token.unwrap(),
|
|
ball,
|
|
momentum: Point::new(x as i16, y as i16),
|
|
trng,
|
|
modals,
|
|
mode: BallMode::Random,
|
|
com,
|
|
}
|
|
}
|
|
pub(crate) fn update(&mut self) {
|
|
// send a list of objects to draw to the GAM, to avoid race conditions in between operations
|
|
let mut draw_list = GamObjectList::new(self.gid);
|
|
|
|
// clear the previous location of the ball
|
|
self.ball.style = DrawStyle::new(PixelColor::Light, PixelColor::Light, 1);
|
|
draw_list.push(GamObjectType::Circ(self.ball)).unwrap();
|
|
if self.mode == BallMode::Tilt {
|
|
let (x, y, _z, _id) = self.com.gyro_read_blocking().unwrap();
|
|
let ix = x as i16;
|
|
let iy = y as i16;
|
|
log::debug!("x: {}, y: {}", ix, iy);
|
|
// negative x => tilt to right
|
|
// positive x => tilt to left
|
|
// negative y => tilt toward top
|
|
// positive y => tilt toward bottom
|
|
self.momentum = Point::new(
|
|
-(ix / 200),
|
|
iy / 200
|
|
);
|
|
}
|
|
// update the ball position based on the momentum vector
|
|
self.ball.translate(self.momentum);
|
|
|
|
// check if the ball hits the wall, if so, snap its position to the wall
|
|
let mut hit_right = false;
|
|
let mut hit_left = false;
|
|
let mut hit_top = false;
|
|
let mut hit_bott = false;
|
|
if self.ball.center.x + (BALL_RADIUS + BORDER_WIDTH) >= self.screensize.x {
|
|
hit_right = true;
|
|
self.ball.center.x = self.screensize.x - (BALL_RADIUS + BORDER_WIDTH);
|
|
}
|
|
if self.ball.center.x - (BALL_RADIUS + BORDER_WIDTH) <= 0 {
|
|
hit_left = true;
|
|
self.ball.center.x = BALL_RADIUS + BORDER_WIDTH;
|
|
}
|
|
if self.ball.center.y + (BALL_RADIUS + BORDER_WIDTH) >= self.screensize.y {
|
|
hit_bott = true;
|
|
self.ball.center.y = self.screensize.y - (BALL_RADIUS + BORDER_WIDTH);
|
|
}
|
|
if self.ball.center.y - (BALL_RADIUS + BORDER_WIDTH) <= 0 {
|
|
hit_top = true;
|
|
self.ball.center.y = BALL_RADIUS + BORDER_WIDTH;
|
|
}
|
|
|
|
if (hit_right || hit_left || hit_bott || hit_top) && (self.mode == BallMode::Random) {
|
|
let mut x = ((self.trng.get_u32().unwrap() / 2) as i32) % (MOMENTUM_LIMIT * 2) - MOMENTUM_LIMIT;
|
|
let mut y = ((self.trng.get_u32().unwrap() / 2) as i32) % (MOMENTUM_LIMIT * 2) - MOMENTUM_LIMIT;
|
|
if hit_right {
|
|
x = - x.abs();
|
|
}
|
|
if hit_left {
|
|
x = x.abs();
|
|
}
|
|
if hit_top {
|
|
y = y.abs();
|
|
}
|
|
if hit_bott {
|
|
y = - y.abs();
|
|
}
|
|
self.momentum = Point::new(x as i16, y as i16);
|
|
}
|
|
|
|
// draw the new location for the ball
|
|
self.ball.style = DrawStyle::new(PixelColor::Dark, PixelColor::Dark, 1);
|
|
draw_list.push(GamObjectType::Circ(self.ball)).unwrap();
|
|
self.gam.draw_list(draw_list).expect("couldn't execute draw list");
|
|
log::trace!("ball app redraw##");
|
|
self.gam.redraw().unwrap();
|
|
}
|
|
pub(crate) fn focus(&mut self) {
|
|
// draw the background entirely
|
|
self.gam.draw_rectangle(self.gid,
|
|
Rectangle::new_coords_with_style(0, 0, self.screensize.x, self.screensize.y,
|
|
DrawStyle::new(PixelColor::Light, PixelColor::Dark, BORDER_WIDTH))
|
|
).expect("couldn't draw our rectangle");
|
|
}
|
|
pub(crate) fn rawkeys(&mut self, keys: [char; 4]) {
|
|
log::debug!("got rawkey {:?}", keys); // you could use the raw keypresses, but modals are easier...
|
|
let mut note = String::new();
|
|
use std::fmt::Write;
|
|
write!(note, "{}'{}'.\n\n{}",
|
|
t!("ballapp.notification_a", xous::LANG),
|
|
keys[0],
|
|
t!("ballapp.notification_b", xous::LANG),
|
|
).unwrap();
|
|
self.modals.show_notification(¬e, None).unwrap();
|
|
self.modals.add_list_item(t!("ballapp.random", xous::LANG)).unwrap();
|
|
self.modals.add_list_item(t!("ballapp.tilt", xous::LANG)).unwrap();
|
|
let mode = self.modals.get_radiobutton(t!("ballapp.mode_prompt", xous::LANG)).unwrap();
|
|
if mode == t!("ballapp.random", xous::LANG) {
|
|
self.mode = BallMode::Random;
|
|
} else if mode == t!("ballapp.tilt", xous::LANG) {
|
|
self.mode = BallMode::Tilt;
|
|
} else {
|
|
log::warn!("got an unexpected response from the radio button function: {}", mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn ball_pump_thread(cid_to_main: xous::CID, pump_sid: xous::SID) {
|
|
let _ = std::thread::spawn({
|
|
let cid_to_main = cid_to_main; // kind of redundant but I like making the closure captures explicit
|
|
let sid = pump_sid;
|
|
move || {
|
|
let tt = ticktimer_server::Ticktimer::new().unwrap();
|
|
let cid_to_self = xous::connect(sid).unwrap();
|
|
let mut run = true;
|
|
loop {
|
|
// this blocks the process until a message is received, descheduling it from the run queue
|
|
let msg = xous::receive_message(sid).unwrap();
|
|
match FromPrimitive::from_usize(msg.body.id()) {
|
|
Some(PumpOp::Run) => {
|
|
run = true;
|
|
xous::send_message(
|
|
cid_to_self,
|
|
Message::new_scalar(PumpOp::Pump.to_usize().unwrap(), 0, 0, 0, 0)
|
|
).expect("couldn't pump the main loop event thread");
|
|
},
|
|
Some(PumpOp::Stop) => run = false,
|
|
Some(PumpOp::Pump) => {
|
|
xous::send_message(
|
|
cid_to_main,
|
|
Message::new_blocking_scalar(AppOp::Pump.to_usize().unwrap(), 0, 0, 0, 0)
|
|
).expect("couldn't pump the main loop event thread");
|
|
if run {
|
|
tt.sleep_ms(BALL_UPDATE_RATE_MS).unwrap();
|
|
xous::send_message(
|
|
cid_to_self,
|
|
Message::new_scalar(PumpOp::Pump.to_usize().unwrap(), 0, 0, 0, 0)
|
|
).expect("couldn't pump the main loop event thread");
|
|
}
|
|
}
|
|
Some(PumpOp::Quit) => {
|
|
xous::return_scalar(msg.sender, 1).expect("couldn't ack the quit message");
|
|
break;
|
|
}
|
|
_ => log::error!("Got unrecognized message: {:?}", msg),
|
|
}
|
|
}
|
|
xous::destroy_server(sid).ok();
|
|
}
|
|
});
|
|
}
|