diff --git a/Cargo.lock b/Cargo.lock index 7c9fbb952..e7f98dcf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -548,6 +548,18 @@ dependencies = [ "xous-names", ] +[[package]] +name = "enum_dispatch" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd53b3fde38a39a06b2e66dc282f3e86191e53bd04cc499929c15742beae3df8" +dependencies = [ + "once_cell", + "proc-macro2 1.0.24", + "quote 1.0.8", + "syn 1.0.60", +] + [[package]] name = "env_logger" version = "0.7.1" @@ -602,11 +614,11 @@ dependencies = [ name = "gam" version = "0.1.0" dependencies = [ - "bitflags", "blitstr-ref", "chrono", "com", "content-plugin-api", + "enum_dispatch", "graphics-server", "heapless 0.7.0", "ime-plugin-api", diff --git a/services/gam/Cargo.toml b/services/gam/Cargo.toml index d22bd2ae7..ba5ec6bdb 100644 --- a/services/gam/Cargo.toml +++ b/services/gam/Cargo.toml @@ -6,12 +6,11 @@ name = "gam" version = "0.1.0" [dependencies] -bitflags = "1.2.1" blitstr-ref = {path = "../../imports/blitstr-ref"} com = {path = "../com"} content-plugin-api = {path = "../content-plugin-api"} graphics-server = {path = "../graphics-server"} -heapless = "0.7" +heapless = "0.7" # used for creating a sortable heap of canvas objects ime-plugin-api = {path = "../ime-plugin-api"} ime-plugin-shell = {path = "../ime-plugin-shell"} keyboard = {path = "../keyboard"} @@ -29,6 +28,8 @@ num-derive = {version = "0.3", default-features = false} num-traits = {version = "0.2", default-features = false} rkyv = {version = "0.4.3", default-features = false, features = ["const_generics"]} +enum_dispatch = "0.3.7" # used for trait-based dispatch off of multiple layout objects. + [target.'cfg(not(any(windows,unix)))'.dependencies] utralib = {path = "../../utralib"} diff --git a/services/gam/src/layouts.rs b/services/gam/src/layouts.rs index 5275c1f18..4516a1aa5 100755 --- a/services/gam/src/layouts.rs +++ b/services/gam/src/layouts.rs @@ -5,19 +5,7 @@ use blitstr_ref as blitstr; use blitstr::GlyphStyle; use graphics_server::*; -pub(crate) trait LayoutApi { - type Layout; - - fn init(gfx: &graphics_server::Gfx, trng: &trng::Trng, - base_trust: u8, status_canvas: &Canvas, - canvases: &mut FnvIndexMap) -> Result; - fn clear(&self, gfx: &graphics_server::Gfx, canvases: &mut FnvIndexMap) -> Result<(), xous::Error>; - // for Chats, this resizes the height of the input area; for menus, it resizes the overall height - fn resize_height(&mut self, gfx: &graphics_server::Gfx, new_height: i16, status_canvas: &Canvas, canvases: &mut FnvIndexMap) -> Result; - fn get_input_canvas(&self) -> Option { None } - fn get_prediction_canvas(&self) -> Option { None } - fn get_content_canvas(&self) -> Gid; // layouts always have a content canvas -} +use crate::LayoutApi; #[derive(Debug, Copy, Clone)] // GIDs of canvases that are used the "Chat" layout. @@ -34,10 +22,9 @@ pub(crate) struct ChatLayout { small_height: i16, regular_height: i16, } -impl LayoutApi for ChatLayout { - type Layout = ChatLayout; +impl ChatLayout { // pass in the status canvas so we can size around it, but we can't draw on it - fn init(gfx: &graphics_server::Gfx, trng: &trng::Trng, base_trust: u8, + pub fn init(gfx: &graphics_server::Gfx, trng: &trng::Trng, base_trust: u8, status_canvas: &Canvas, canvases: &mut FnvIndexMap) -> Result { let screensize = gfx.screen_size().expect("Couldn't get screen size"); // get the height of various text regions to compute the layout @@ -77,6 +64,8 @@ impl LayoutApi for ChatLayout { regular_height, }) } +} +impl LayoutApi for ChatLayout { fn clear(&self, gfx: &graphics_server::Gfx, canvases: &mut FnvIndexMap) -> Result<(), xous::Error> { let input_canvas = canvases.get(&self.input).expect("couldn't find input canvas"); let content_canvas = canvases.get(&self.content).expect("couldn't find content canvas"); @@ -147,9 +136,8 @@ pub(crate) struct MenuLayout { screensize: Point, small_height: i16, } -impl LayoutApi for MenuLayout { - type Layout = MenuLayout; - fn init(gfx: &graphics_server::Gfx, trng: &trng::Trng, base_trust: u8, _status_canvas: &Canvas, canvases: &mut FnvIndexMap) -> Result { +impl MenuLayout { + pub fn init(gfx: &graphics_server::Gfx, trng: &trng::Trng, base_trust: u8, _status_canvas: &Canvas, canvases: &mut FnvIndexMap) -> Result { let screensize = gfx.screen_size().expect("Couldn't get screen size"); // get the height of various text regions to compute the layout let small_height: i16 = gfx.glyph_height_hint(GlyphStyle::Small).expect("couldn't get glyph height") as i16; @@ -172,6 +160,8 @@ impl LayoutApi for MenuLayout { small_height, }) } +} +impl LayoutApi for MenuLayout { fn clear(&self, gfx: &graphics_server::Gfx, canvases: &mut FnvIndexMap) -> Result<(), xous::Error> { let menu_canvas = canvases.get(&self.menu).expect("couldn't find menu canvas"); diff --git a/services/gam/src/main.rs b/services/gam/src/main.rs index 5fa95057d..ee9019bf1 100644 --- a/services/gam/src/main.rs +++ b/services/gam/src/main.rs @@ -27,46 +27,28 @@ use xous::{msg_scalar_unpack, msg_blocking_scalar_unpack}; use core::{sync::atomic::{AtomicU32, Ordering}}; +use enum_dispatch::enum_dispatch; + //// todo: +// - create menu server // - move vibe call to the GAM, reduce keyboard connections to 1 // - add auth tokens to audio streams, so less trusted processes can make direct connections to the codec and reduce latency -// - create menu server -// - macro-ize the unwrapping of the UxLayout enum +#[enum_dispatch] +pub(crate) trait LayoutApi { + fn clear(&self, gfx: &graphics_server::Gfx, canvases: &mut FnvIndexMap) -> Result<(), xous::Error>; + // for Chats, this resizes the height of the input area; for menus, it resizes the overall height + fn resize_height(&mut self, gfx: &graphics_server::Gfx, new_height: i16, status_canvas: &Canvas, canvases: &mut FnvIndexMap) -> Result; + fn get_input_canvas(&self) -> Option { None } + fn get_prediction_canvas(&self) -> Option { None } + fn get_content_canvas(&self) -> Gid; // layouts always have a content canvas +} + +#[enum_dispatch(LayoutApi)] #[derive(Debug, Copy, Clone)] pub(crate) enum UxLayout { - Chat(ChatLayout), - Menu(MenuLayout), -} -pub(crate) fn uxlayout_get_content_canvas(ul: UxLayout) -> Gid { - match ul { - UxLayout::Chat(layout) => layout.get_content_canvas(), - UxLayout::Menu(layout) => layout.get_content_canvas(), - } -} -pub(crate) fn uxlayout_resize_height(ul: &mut UxLayout, gfx: &graphics_server::Gfx, new_height: i16, status_canvas: &Canvas, canvases: &mut FnvIndexMap) -> Result { - match ul { - UxLayout::Chat(layout) => layout.resize_height(gfx, new_height, status_canvas, canvases), - UxLayout::Menu(layout) => layout.resize_height(gfx, new_height, status_canvas, canvases), - } -} -pub(crate) fn uxlayout_clear(ul: UxLayout, gfx: &graphics_server::Gfx, canvases: &mut FnvIndexMap) -> Result<(), xous::Error> { - match ul { - UxLayout::Chat(layout) => layout.clear(gfx, canvases), - UxLayout::Menu(layout) => layout.clear(gfx, canvases), - } -} -pub(crate) fn uxlayout_get_input_canvas(ul: UxLayout) -> Option { - match ul { - UxLayout::Chat(layout) => layout.get_input_canvas(), - UxLayout::Menu(layout) => layout.get_input_canvas(), - } -} -pub(crate) fn uxlayout_get_prediction_canvas(ul: UxLayout) -> Option { - match ul { - UxLayout::Chat(layout) => layout.get_prediction_canvas(), - UxLayout::Menu(layout) => layout.get_prediction_canvas(), - } + ChatLayout, + MenuLayout, } #[derive(Debug, Copy, Clone)] @@ -136,7 +118,7 @@ impl ContextManager { let chatlayout = ChatLayout::init(&gfx, &trng, BOOT_CONTEXT_TRUSTLEVEL, &status_canvas, canvases).expect("couldn't create chat layout"); let ux_context = UxContext { - layout: UxLayout::Chat(chatlayout), + layout: UxLayout::ChatLayout(chatlayout), predictor: registration.predictor, app_token: token, gam_token: [trng.get_u32().unwrap(), trng.get_u32().unwrap(), trng.get_u32().unwrap(), trng.get_u32().unwrap(), ], @@ -159,7 +141,7 @@ impl ContextManager { let menulayout = MenuLayout::init(&gfx, &trng, BOOT_CONTEXT_TRUSTLEVEL, &status_canvas, canvases).expect("couldn't create menu layout"); let ux_context = UxContext { - layout: UxLayout::Menu(menulayout), + layout: UxLayout::MenuLayout(menulayout), predictor: None, app_token: token, gam_token: [trng.get_u32().unwrap(), trng.get_u32().unwrap(), trng.get_u32().unwrap(), trng.get_u32().unwrap(), ], @@ -196,7 +178,7 @@ impl ContextManager { for maybe_context in self.contexts.iter() { if let Some(context) = maybe_context { if context.app_token == token { - return Some(uxlayout_get_content_canvas(context.layout)); + return Some(context.layout.get_content_canvas()); } } } @@ -212,7 +194,7 @@ impl ContextManager { for maybe_context in self.contexts.iter_mut() { if let Some(context) = maybe_context { if context.gam_token == gam_token { - let result = uxlayout_resize_height(&mut context.layout, gfx, new_height, status_canvas, canvases).expect("couldn't adjust height of active Ux context"); + let result = context.layout.resize_height(gfx, new_height, status_canvas, canvases).expect("couldn't adjust height of active Ux context"); return Some(result) } } @@ -230,14 +212,14 @@ impl ContextManager { if let Some(context) = maybe_context { if context.app_token == token { let descriptor = ImefDescriptor { - input_canvas: uxlayout_get_input_canvas(context.layout), - prediction_canvas: uxlayout_get_prediction_canvas(context.layout), + input_canvas: context.layout.get_input_canvas(), + prediction_canvas: context.layout.get_prediction_canvas(), predictor: context.predictor, token: context.gam_token, }; imef.connect_backend(descriptor).expect("couldn't connect IMEF to the current app"); if clear { - uxlayout_clear(context.layout, gfx, canvases).expect("can't clear on context activation"); + context.layout.clear(gfx, canvases).expect("can't clear on context activation"); } // now update the IMEF area, since we're initialized // note: we may need to skip this call if the context does not utilize a predictor...