diff --git a/.direnv/flake-profile b/.direnv/flake-profile index 399b3c4..f9f4da1 120000 --- a/.direnv/flake-profile +++ b/.direnv/flake-profile @@ -1 +1 @@ -flake-profile-17-link \ No newline at end of file +flake-profile-18-link \ No newline at end of file diff --git a/clover-hub/src/server/appd/mod.rs b/clover-hub/src/server/appd/mod.rs index 7a3d5a2..3a95fcb 100644 --- a/clover-hub/src/server/appd/mod.rs +++ b/clover-hub/src/server/appd/mod.rs @@ -98,20 +98,36 @@ pub async fn appd_main( apps_initialized, init_apps.len() ); - init_user.send( + match init_user.send( &"nexus://com.reboot-codes.clover.appd/status".to_string(), &"incomplete-init".to_string(), &None, - ); + ) { + Err(e) => { + error!( + "Error when letting peers know about incomplete init state: {}", + e + ); + } + _ => {} + } } else { if apps_initialized != 0 { info!("Initialized all {} apps!", apps_initialized); } - init_user.send( + match init_user.send( &"nexus://com.reboot-codes.clover.appd/status".to_string(), &"finished-init".to_string(), &None, - ); + ) { + Err(e) => { + error!( + "Error when letting peers know about complete init state: {}", + e + ); + } + _ => {} + } } }) .await; diff --git a/clover-hub/src/server/inference_engine/mod.rs b/clover-hub/src/server/inference_engine/mod.rs index 954a328..243c55c 100644 --- a/clover-hub/src/server/inference_engine/mod.rs +++ b/clover-hub/src/server/inference_engine/mod.rs @@ -3,6 +3,7 @@ pub mod ipc; use ipc::handle_ipc_msg; use log::{ debug, + error, info, }; use nexus::{ @@ -60,11 +61,19 @@ pub async fn inference_engine_main( cancellation_tokens .0 .run_until_cancelled(async move { - init_user.send( + match init_user.send( &"nexus://com.reboot-codes.clover.inference-engine/status".to_string(), &"finished-init".to_string(), &None, - ); + ) { + Err(e) => { + error!( + "Error when letting peers know about complete init status: {}", + e + ); + } + _ => {} + } }) .await; diff --git a/clover-hub/src/server/mod.rs b/clover-hub/src/server/mod.rs index 3768c99..1e68609 100644 --- a/clover-hub/src/server/mod.rs +++ b/clover-hub/src/server/mod.rs @@ -47,12 +47,12 @@ pub async fn server_main( let warehouse_store = WarehouseStore::new(None); // TODO: Let each process run independantly of eachother using nexus - let warehouse_setup_store = Arc::new(warehouse_store.clone()); match setup_warehouse(data_dir.clone(), warehouse_setup_store).await { Ok(_) => { match connect_db(warehouse_store.clone()).await { Ok(_) => { + debug!("Initalizing Stores..."); let renderer_store = RendererStore::new(Some(warehouse_store.config.clone())); let modman_store = ModManStore::new(Some(warehouse_store.config.clone())); let inference_engine_store = @@ -66,7 +66,9 @@ pub async fn server_main( .await .primary_api_key .clone(); + debug!("Stores initalized!"); + debug!("Creating master Nexus user..."); // Add users to nexus let (mut nexus_store, master_user_config) = NexusStore::new(&"Owner".to_string(), &primary_api_key.clone()).await; @@ -75,7 +77,9 @@ pub async fn server_main( "Master User api key: {}", master_user_config.api_keys[0].clone() ); + debug!("Created master Nexus user!"); + debug!("Creating Warehouse Nexus user..."); let warehouse_user_config = Arc::new( nexus_store .add_user( @@ -86,6 +90,14 @@ pub async fn server_main( .await .unwrap(), ); + debug!("Creating Warehouse NexusUser object..."); + let (warehouse_user, from_warehouse, to_warehouse) = nexus_store + .connect_user(&warehouse_user_config.api_keys[0].clone()) + .await + .unwrap(); + debug!("Created Warehouse Nexus user!"); + + debug!("Creating Renderer Nexus user..."); let renderer_user_config = Arc::new( nexus_store .add_user( @@ -96,6 +108,14 @@ pub async fn server_main( .await .unwrap(), ); + debug!("Creating Renderer NexusUser object..."); + let (renderer_user, from_renderer, to_renderer) = nexus_store + .connect_user(&renderer_user_config.api_keys[0].clone()) + .await + .unwrap(); + debug!("Created Renderer Nexus user!"); + + debug!("Creating Modman Nexus user..."); let modman_user_config = Arc::new( nexus_store .add_user( @@ -106,6 +126,14 @@ pub async fn server_main( .await .unwrap(), ); + debug!("Creating ModMan NexusUser object..."); + let (modman_user, from_modman, to_modman) = nexus_store + .connect_user(&modman_user_config.api_keys[0].clone()) + .await + .unwrap(); + debug!("Created Modman Nexus user!"); + + debug!("Creating Inference Engine Nexus user..."); let inference_engine_user_config = Arc::new( nexus_store .add_user( @@ -116,6 +144,14 @@ pub async fn server_main( .await .unwrap(), ); + debug!("Creating InferenceEngine NexusUser object..."); + let (inference_engine_user, from_inference_engine, to_inference_engine) = nexus_store + .connect_user(&inference_engine_user_config.api_keys[0].clone()) + .await + .unwrap(); + debug!("Created Inference Engine Nexus user!"); + + debug!("Creating AppDaemon Nexus user..."); let appd_user_config = Arc::new( nexus_store .add_user( @@ -126,30 +162,15 @@ pub async fn server_main( .await .unwrap(), ); - - // Create NexusUser objects - let (warehouse_user, from_warehouse, to_warehouse) = nexus_store - .connect_user(&warehouse_user_config.api_keys[0].clone()) - .await - .unwrap(); - let (renderer_user, from_renderer, to_renderer) = nexus_store - .connect_user(&renderer_user_config.api_keys[0].clone()) - .await - .unwrap(); - let (modman_user, from_modman, to_modman) = nexus_store - .connect_user(&modman_user_config.api_keys[0].clone()) - .await - .unwrap(); - let (inference_engine_user, from_inference_engine, to_inference_engine) = nexus_store - .connect_user(&inference_engine_user_config.api_keys[0].clone()) - .await - .unwrap(); + debug!("Creating AppDaemon NexusUser object..."); let (appd_user, from_appd, to_appd) = nexus_store .connect_user(&appd_user_config.api_keys[0].clone()) .await .unwrap(); + debug!("Created AppDaemon Nexus user!"); // Start Nexus + debug!("Starting Nexus..."); let nexus_port = Arc::new(port); let nexus_store_clone = Arc::new(nexus_store.clone()); let nexus_tokens = (CancellationToken::new(), CancellationToken::new()); @@ -182,6 +203,7 @@ pub async fn server_main( }); // Start Warehouse + debug!("Starting Warehouse..."); let warehouse_store_clone = Arc::new(warehouse_store.clone()); let warehouse_tokens = (CancellationToken::new(), CancellationToken::new()); let warehouse_tokens_clone = warehouse_tokens.clone(); @@ -194,21 +216,24 @@ pub async fn server_main( .await; }); - // Start Renderer - let renderer_tokens = (CancellationToken::new(), CancellationToken::new()); - let renderer_tokens_clone = renderer_tokens.clone(); - let renderer_handle = tokio::task::spawn(async move { - renderer_main(renderer_store, renderer_user, renderer_tokens_clone).await; - }); - // Start ModMan + debug!("Starting Modman..."); let modman_tokens = (CancellationToken::new(), CancellationToken::new()); let modman_tokens_clone = modman_tokens.clone(); let modman_handle = tokio::task::spawn(async move { modman_main(modman_store, modman_user, modman_tokens_clone).await; }); + // Start Renderer + debug!("Starting Renderer..."); + let renderer_tokens = (CancellationToken::new(), CancellationToken::new()); + let renderer_tokens_clone = renderer_tokens.clone(); + let renderer_handle = tokio::task::spawn(async move { + renderer_main(renderer_store, renderer_user, renderer_tokens_clone).await; + }); + // Start InferenceEngine + debug!("Starting Inference Engine..."); let inference_engine_tokens = (CancellationToken::new(), CancellationToken::new()); let inference_engine_tokens_clone = inference_engine_tokens.clone(); let inference_engine_handle = tokio::task::spawn(async move { @@ -221,6 +246,7 @@ pub async fn server_main( }); // Start AppDaemon + debug!("Starting AppDaemon..."); let appd_tokens = (CancellationToken::new(), CancellationToken::new()); let appd_tokens_clone = appd_tokens.clone(); let appd_handle = tokio::task::spawn(async move { @@ -238,14 +264,14 @@ pub async fn server_main( inference_engine_tokens.0.cancel(); tokio::select! { _ = inference_engine_tokens.1.cancelled() => { - info!("Shutting down ModMan..."); - modman_tokens.0.cancel(); + info!("Shutting down Renderer..."); + renderer_tokens.0.cancel(); tokio::select! { - _ = modman_tokens.1.cancelled() => { - info!("Shutting down Renderer..."); - renderer_tokens.0.cancel(); + _ = renderer_tokens.1.cancelled() => { + info!("Shutting down ModMan..."); + modman_tokens.0.cancel(); tokio::select! { - _ = renderer_tokens.1.cancelled() => { + _ = modman_tokens.1.cancelled() => { info!("Shutting down Warehouse..."); warehouse_tokens.0.cancel(); tokio::select! { diff --git a/clover-hub/src/server/modman/busses/mod.rs b/clover-hub/src/server/modman/busses/mod.rs index de9bb3f..7ea069d 100644 --- a/clover-hub/src/server/modman/busses/mod.rs +++ b/clover-hub/src/server/modman/busses/mod.rs @@ -2,7 +2,10 @@ pub mod models; pub mod proxies; use super::models::ModManStore; -use log::info; +use log::{ + error, + info, +}; use models::Bus; use nexus::server::websockets::WsIn; use nexus::{ @@ -24,7 +27,12 @@ pub async fn start_busses( let proxy_user = user.clone(); handles.push(tokio::task::spawn(async move { while let Ok(raw_msg) = rx.recv().await { - proxy_user.send(&raw_msg.kind, &raw_msg.message, &raw_msg.replying_to); + match proxy_user.send(&raw_msg.kind, &raw_msg.message, &raw_msg.replying_to) { + Err(e) => { + error!("Error when proxying message: {}", e); + } + _ => {} + } } })); diff --git a/clover-hub/src/server/modman/components/audio/models.rs b/clover-hub/src/server/modman/components/audio/models.rs index be3ac89..bd01515 100644 --- a/clover-hub/src/server/modman/components/audio/models.rs +++ b/clover-hub/src/server/modman/components/audio/models.rs @@ -13,13 +13,13 @@ use serde::{ }; use strum::VariantNames; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct AudioOutputComponent { pub connection: ConnectionType, pub gesture_config: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct AudioInputComponent { pub connection: ConnectionType, } diff --git a/clover-hub/src/server/modman/components/movement/models.rs b/clover-hub/src/server/modman/components/movement/models.rs index be18406..60c1ad1 100644 --- a/clover-hub/src/server/modman/components/movement/models.rs +++ b/clover-hub/src/server/modman/components/movement/models.rs @@ -122,7 +122,7 @@ pub enum DegreesOfFreedomGestureParams { SixDegrees(GestureParamsPosition6D), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct MovementComponent { /// The inital position of this component, also determines how many degrees of freedom it uses. **NON OPTIONAL!** pub inital_position: DegreesOfFreedom, diff --git a/clover-hub/src/server/modman/components/sensors/models.rs b/clover-hub/src/server/modman/components/sensors/models.rs index 38bbae6..38e2205 100644 --- a/clover-hub/src/server/modman/components/sensors/models.rs +++ b/clover-hub/src/server/modman/components/sensors/models.rs @@ -8,12 +8,12 @@ use serde::{ }; use strum::VariantNames; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct InputSensorComponent { pub connection: ConnectionType, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct OutputSensorComponent { pub gesture_config: Option, pub connection: ConnectionType, diff --git a/clover-hub/src/server/modman/components/video/cameras/models.rs b/clover-hub/src/server/modman/components/video/cameras/models.rs index 46f6636..a5a3905 100644 --- a/clover-hub/src/server/modman/components/video/cameras/models.rs +++ b/clover-hub/src/server/modman/components/video/cameras/models.rs @@ -25,7 +25,7 @@ pub enum ConnectionType { Stream(StreamingConnection), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct CameraComponent { pub connection: ConnectionType, pub max_resolution: VideoResolution, diff --git a/clover-hub/src/server/modman/components/video/displays/impls.rs b/clover-hub/src/server/modman/components/video/displays/impls.rs index b2591bb..b444d19 100644 --- a/clover-hub/src/server/modman/components/video/displays/impls.rs +++ b/clover-hub/src/server/modman/components/video/displays/impls.rs @@ -10,14 +10,14 @@ impl CloverComponentTrait for PhysicalDisplayComponent { &mut self, store: Arc, ) -> Result<(), anyhow::Error> { - todo!() + Ok(()) } async fn deinit( &mut self, store: Arc, ) -> Result<(), anyhow::Error> { - todo!() + Ok(()) } } @@ -26,13 +26,13 @@ impl CloverComponentTrait for VirtualDisplayComponent { &mut self, store: Arc, ) -> Result<(), anyhow::Error> { - todo!() + Ok(()) } async fn deinit( &mut self, store: Arc, ) -> Result<(), anyhow::Error> { - todo!() + Ok(()) } } diff --git a/clover-hub/src/server/modman/components/video/displays/models.rs b/clover-hub/src/server/modman/components/video/displays/models.rs index fbc1131..ffc4ff7 100644 --- a/clover-hub/src/server/modman/components/video/displays/models.rs +++ b/clover-hub/src/server/modman/components/video/displays/models.rs @@ -19,17 +19,18 @@ use strum::VariantNames; pub trait DisplayComponent {} -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct PhysicalDisplayComponent { /// Required to initialize rendering pipeline for this display. pub resolution: VideoResolution, /// Required to calculate PPI (and optionally pixel aspect ratio). pub size: DisplaySize, pub connection: ConnectionType, - /// If configured, the Entity ID of the VDisplay this physical display is a part of, like for a video wall. - pub virtual_display: Option, + /// The id of the VDisplay this physical display is a part of, like for a video wall. + pub virtual_display: Option, /// If configured, how the display should react to gesture events. Will be overriden if this display is part of a virtual display. pub gesture_config: Option, + pub internal: bool, } impl DisplayComponent for PhysicalDisplayComponent {} @@ -41,6 +42,7 @@ pub struct VirtualDisplayComponent { pub resolution: VideoResolution, /// If configured, how the display should react to gesture events. pub gesture_config: Option, + pub internal: bool, } impl DisplayComponent for VirtualDisplayComponent {} @@ -51,11 +53,11 @@ pub struct DisplayPosition { pub y: f64, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] /// All measurements in CM. Only one is required for square pixels. Otherwise, use both values for rectangular pixels (e.g. 1:2 pixel aspect ratio vs 1:1). pub struct DisplaySize { - pub height: Option>, - pub width: Option>, + pub height: Option, + pub width: Option, } #[derive(Debug, Clone, Serialize, Deserialize, VariantNames)] diff --git a/clover-hub/src/server/modman/ipc/mod.rs b/clover-hub/src/server/modman/ipc/mod.rs index 5a2aac3..816f650 100644 --- a/clover-hub/src/server/modman/ipc/mod.rs +++ b/clover-hub/src/server/modman/ipc/mod.rs @@ -7,19 +7,21 @@ use log::{ error, warn, }; -use nexus::server::models::IPCMessageWithId; -use serde::{ - Deserialize, - Serialize, +use nexus::{ + server::models::IPCMessageWithId, + user::NexusUser, }; -use strum::VariantNames; use tokio::sync::broadcast::Sender; use url::Url; use crate::{ - server::modman::models::{ - GestureCommand, - ModManStore, + server::{ + modman::models::{ + CloverComponent, + GestureCommand, + ModManStore, + }, + renderer::system_ui::AnyDisplayComponent, }, utils::deserialize_base64, }; @@ -32,6 +34,7 @@ use std::{ pub enum Events { Status, Gesture, + InitDisplays, None, } @@ -42,6 +45,7 @@ impl FromStr for Events { match input { "/gesture" => Ok(Events::Gesture), "/status" => Ok(Events::Status), + "/init-displays" => Ok(Events::InitDisplays), "" => Ok(Events::None), "/" => Ok(Events::None), _ => Err(anyhow!("String \"{}\" not part of enum!", input)), @@ -49,7 +53,11 @@ impl FromStr for Events { } } -pub async fn handle_ipc_msg(store: ModManStore, ipc_rx: Sender) { +pub async fn handle_ipc_msg( + store: ModManStore, + ipc_rx: Sender, + user: Arc, +) { let store_arc = Arc::new(store.clone()); while let Ok(msg) = ipc_rx.subscribe().recv().await { @@ -108,6 +116,56 @@ pub async fn handle_ipc_msg(store: ModManStore, ipc_rx: Sender } } } + Events::InitDisplays => { + for (_module_id, module_config) in store.modules.lock().await.iter() { + if module_config.initialized { + for (component_id, _is_critical) in &module_config.components { + match store.components.lock().await.get(component_id) { + Some(component_entry) => { + let component_config = component_entry.1.clone(); + + match component_config { + CloverComponent::PhysicalDisplayComponent(physical_display_config) => { + match user.send( + &"nexus://com.reboot-codes.clover.renderer/register-display" + .to_string(), + &serde_json_lenient::to_string(&AnyDisplayComponent::Physical( + physical_display_config, + )) + .unwrap(), + &None, + ) { + Err(e) => { + error!("Error when attempting to send registered display registration to peers: {}", e); + } + _ => {} + } + } + CloverComponent::VirtualDisplayComponent(virtual_display_config) => { + match user.send( + &"nexus://com.reboot-codes.clover.renderer/register-display" + .to_string(), + &serde_json_lenient::to_string(&AnyDisplayComponent::Virtual( + virtual_display_config, + )) + .unwrap(), + &None, + ) { + Err(e) => { + error!("Error when attempting to send registered display registration to peers: {}", e); + } + _ => {} + } + } + _ => {} + } + } + None => todo!(), + } + } + } + } + } _ => { debug!("Blank event... doing nothing."); } diff --git a/clover-hub/src/server/modman/mod.rs b/clover-hub/src/server/modman/mod.rs index 9c37783..984a5f1 100644 --- a/clover-hub/src/server/modman/mod.rs +++ b/clover-hub/src/server/modman/mod.rs @@ -9,6 +9,7 @@ use busses::start_busses; use ipc::handle_ipc_msg; use log::{ debug, + error, info, warn, }; @@ -68,9 +69,46 @@ pub async fn modman_main( cancellation_tokens .0 .run_until_cancelled(async move { - let modules = init_store.modules.lock().await; + let config = init_store.config.lock().await; + let static_modules = &config.modman.static_modules; + let static_components = &config.modman.static_components; + let mut modules = init_store.modules.lock().await; let mut modules_initalized: usize = 0; + debug!("Checking for statically defined modules to init..."); + if static_modules.len() > 0 { + debug!( + "Adding {} statically defined module(s) to init queue...", + static_modules.len() + ); + for (module_id, module) in static_modules { + modules.insert(module_id.clone(), module.clone()); + } + } else { + debug!("No statically defined modules to put into store, skipping!"); + } + + debug!("Checking for statically defined components to init..."); + if static_components.len() > 0 { + debug!( + "Adding {} statically defined components(s) to init queue...", + static_components.len() + ); + let mut components = init_store.components.lock().await; + + for (component_id, component) in static_components { + components.insert( + component_id.clone(), + Arc::new((component.0.clone(), component.1.clone())), + ); + } + } else { + debug!("No statically defined components to put into store, skipping!"); + } + + drop(config); + + info!("Initalizing modules..."); if modules.len() > 0 { // Initialize modules that were registered already via configuration and persistence. for (id, module) in modules.iter() { @@ -82,7 +120,7 @@ pub async fn modman_main( } } } else { - info!("No pre-configured modules to initialize."); + info!("No static modules to initialize."); } if modules_initalized != modules.len() { @@ -91,20 +129,36 @@ pub async fn modman_main( modules_initalized, modules.len() ); - init_user.send( + match init_user.send( &"nexus://com.reboot-codes.clover.appd/status".to_string(), &"incomplete-init".to_string(), &None, - ); + ) { + Err(e) => { + error!( + "Error when letting peers know about incomplete init state: {}", + e + ); + } + _ => {} + } } else { if modules_initalized != 0 { info!("Initalized all {} module(s)", modules_initalized); } - init_user.send( + match init_user.send( &"nexus://com.reboot-codes.clover.modman/status".to_string(), &"finished-init".to_string(), &None, - ); + ) { + Err(e) => { + error!( + "Error when letting peers know about complete init state: {}", + e + ); + } + _ => {} + } } }) .await; @@ -112,12 +166,13 @@ pub async fn modman_main( let ipc_recv_token = cancellation_tokens.0.clone(); let (ipc_rx, ipc_handle) = user.subscribe(); let ipc_recv_store = store.clone(); + let ipc_user = Arc::new(user.clone()); let ipc_recv_handle = tokio::task::spawn(async move { tokio::select! { _ = ipc_recv_token.cancelled() => { debug!("ipc_recv exited"); }, - _ = handle_ipc_msg(ipc_recv_store, ipc_rx) => {} + _ = handle_ipc_msg(ipc_recv_store, ipc_rx, ipc_user) => {} } }); diff --git a/clover-hub/src/server/modman/models.rs b/clover-hub/src/server/modman/models.rs index 9c414f5..6d2f663 100644 --- a/clover-hub/src/server/modman/models.rs +++ b/clover-hub/src/server/modman/models.rs @@ -33,12 +33,13 @@ use super::components::models::CloverComponentTrait; // TODO: Define defaults via `Default` trait impl. /// Modules contain [Components](CloverComponent). -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Module { pub module_type: String, pub module_name: String, pub custom_name: Option, pub initialized: bool, + /// Component ID and if it's critical pub components: Vec<(String, bool)>, pub registered_by: String, } @@ -70,7 +71,7 @@ pub struct CloverComponentMeta { } /// Enum with all known clover component types, technically a valid "component" ([see the Component Trait](CloverComponentTrait)) itself. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum CloverComponent { AudioInputComponent(AudioInputComponent), AudioOutputComponent(AudioOutputComponent), @@ -240,12 +241,126 @@ pub struct GestureOverride { pub gesture_parameters: Option, } -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ModManConfig { pub uart_ports: Vec<(String, u32)>, pub restart_gestures: bool, pub gesture_states: HashMap, pub gestures_bg_by_default: bool, + pub static_modules: HashMap, + pub static_components: HashMap, +} + +impl Default for ModManConfig { + /// Ensure that there is a display if the compositor was compiled in + /// and there wasn't a display defined in the config/disabled explicitly. + fn default() -> Self { + let mut static_modules = HashMap::new(); + let mut static_components = HashMap::new(); + + #[cfg(feature = "compositor")] + { + use std::num::NonZero; + + use crate::server::modman::components::video::{ + displays::{ + self, + models::{ + DirectConnection, + DisplaySize, + }, + }, + VideoResolution, + }; + + let internal_display_id = uuid::Uuid::new_v4().to_string(); + let external_display_id = uuid::Uuid::new_v4().to_string(); + + static_modules.insert( + uuid::Uuid::new_v4().to_string(), + Module { + module_type: "com.reboot-codes.clover.debug-display".to_string(), + module_name: "Debug Displays".to_string(), + custom_name: Default::default(), + initialized: false, + components: vec![ + (internal_display_id.clone(), true), + (external_display_id.clone(), true), + ], + registered_by: "com.reboot-codes.clover.modman.default".to_string(), + }, + ); + + static_components.insert( + internal_display_id.clone(), + ( + CloverComponentMeta { + name: "Debug Internal Display".to_string(), + critical: true, + location: "none".to_string(), + base_gesture_parameters: HashMap::new(), + internal: true, + }, + CloverComponent::PhysicalDisplayComponent(PhysicalDisplayComponent { + resolution: VideoResolution { + width: NonZero::new(500).unwrap(), + height: NonZero::new(200).unwrap(), + }, + size: DisplaySize { + height: Some(2.0), + width: None, + }, + connection: displays::models::ConnectionType::Direct(DirectConnection { + display_id: "@primary".to_string(), + windowed: true, + }), + virtual_display: None, + gesture_config: None, + internal: true, + }), + ), + ); + + static_components.insert( + external_display_id.clone(), + ( + CloverComponentMeta { + name: "Debug External Display".to_string(), + critical: true, + location: "none".to_string(), + base_gesture_parameters: HashMap::new(), + internal: false, + }, + CloverComponent::PhysicalDisplayComponent(PhysicalDisplayComponent { + resolution: VideoResolution { + width: NonZero::new(500).unwrap(), + height: NonZero::new(200).unwrap(), + }, + size: DisplaySize { + height: Some(2.0), + width: None, + }, + connection: displays::models::ConnectionType::Direct(DirectConnection { + display_id: "@primary".to_string(), + windowed: true, + }), + virtual_display: None, + gesture_config: None, + internal: false, + }), + ), + ); + } + + Self { + static_components, + static_modules, + uart_ports: Default::default(), + restart_gestures: Default::default(), + gesture_states: Default::default(), + gestures_bg_by_default: Default::default(), + } + } } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/clover-hub/src/server/modman/modules.rs b/clover-hub/src/server/modman/modules.rs index 4b3e757..a80a93e 100644 --- a/clover-hub/src/server/modman/modules.rs +++ b/clover-hub/src/server/modman/modules.rs @@ -66,7 +66,12 @@ pub async fn init_component( } } } - None => todo!(), + None => { + failiure = Some(anyhow::anyhow!( + "Unable to find component {} in store!", + component_id.clone() + )); + } } match failiure { @@ -146,6 +151,8 @@ pub async fn init_module(store: &ModManStore, id: String, module: Module) -> (bo } else { error!("Module: {}, failed to initialize!", id.clone()); } + } else { + initialized_module = true; } } } diff --git a/clover-hub/src/server/renderer/ipc.rs b/clover-hub/src/server/renderer/ipc.rs index 5f00fc7..2759e9e 100644 --- a/clover-hub/src/server/renderer/ipc.rs +++ b/clover-hub/src/server/renderer/ipc.rs @@ -1,10 +1,17 @@ use anyhow::anyhow; use log::debug; use nexus::server::models::IPCMessageWithId; -use std::str::FromStr; +use queues::Queue; +use std::sync::Mutex as StdMutex; +use std::{ + str::FromStr, + sync::Arc, +}; use tokio::sync::broadcast::Sender; use url::Url; +use crate::server::renderer::system_ui::AnyDisplayComponent; + #[derive(Debug, PartialEq)] pub enum Events { None, @@ -22,7 +29,10 @@ impl FromStr for Events { } } -pub async fn handle_ipc_msg(ipc_rx: Sender) { +pub async fn handle_ipc_msg( + ipc_rx: Sender, + display_registration_queue: Arc>>, +) { while let Ok(msg) = ipc_rx.subscribe().recv().await { let kind = Url::parse(&msg.kind.clone()).unwrap(); diff --git a/clover-hub/src/server/renderer/mod.rs b/clover-hub/src/server/renderer/mod.rs index 2db0169..34fe8e7 100644 --- a/clover-hub/src/server/renderer/mod.rs +++ b/clover-hub/src/server/renderer/mod.rs @@ -5,6 +5,7 @@ pub mod system_ui; use self::system_ui::system_ui_main; use crate::utils::RecvSync; use ipc::handle_ipc_msg; +use log::error; use log::{ debug, info, @@ -16,6 +17,7 @@ use nexus::{ }; use queues::*; use std::sync::Arc; +use std::sync::Mutex as StdMutex; use system_ui::SystemUIIPC; use tokio::sync::Mutex; use tokio_util::sync::CancellationToken; @@ -32,6 +34,7 @@ pub async fn gen_user() -> UserConfig { ], allowed_events_from: vec![ "^nexus://com.reboot-codes.clover.renderer(\\.(.*))*(\\/.*)*$".to_string(), + "^nexus://com.reboot-codes.clover.modman(\\.(.*))*(\\/.*)*$".to_string(), ], echo: false, proxy: false, @@ -62,21 +65,17 @@ pub async fn renderer_main( ) { info!("Starting Renderer..."); + let display_registration_queue = Arc::new(StdMutex::new(queue![])); + let (bevy_cancel_tx, bevy_cancel_rx) = std::sync::mpsc::channel(); - let mut system_ui_ipc = SystemUIIPC { + let system_ui_ipc = SystemUIIPC { exit_channel: RecvSync(bevy_cancel_rx), - display_registration_queue: queue![], + display_registration_queue: display_registration_queue.clone(), }; - for (_display_id, display) in store.config.lock().await.renderer.virtual_displays.clone() { - system_ui_ipc - .display_registration_queue - .add(system_ui::AnyDisplayComponent::Virtual(display)) - .unwrap(); - } - // TODO: Add this as a CLI/Config option! - std::thread::spawn(|| system_ui_main(system_ui_ipc, Some(false))); + let custom_bevy_ipc = system_ui_ipc; + std::thread::spawn(move || system_ui_main(custom_bevy_ipc, Some(false))); // let display_handles = Arc::new(HashMap::new()); @@ -84,14 +83,38 @@ pub async fn renderer_main( cancellation_tokens .0 .run_until_cancelled(async move { - let _ = init_user.send( + match init_user.send( &"nexus://com.reboot-codes.clover.renderer/status".to_string(), &"finished-init".to_string(), &None, - ); + ) { + Err(e) => { + error!( + "Error when letting peers know about completed init state: {}", + e + ); + } + _ => {} + } + + info!("Requesting displays to setup in SystemUI from ModMan..."); + match init_user.send( + &"nexus://com.reboot-codes.clover.modman/init-displays".to_string(), + &"first-time".to_string(), + &None, + ) { + Err(e) => { + error!( + "Error when requesting display configurations from peers: {}", + e + ); + } + _ => {} + } }) .await; + let ipc_display_registration_queue = display_registration_queue.clone(); let ipc_recv_token = cancellation_tokens.0.clone(); let (ipc_rx, ipc_handle) = user.subscribe(); let ipc_recv_handle = tokio::task::spawn(async move { @@ -99,7 +122,7 @@ pub async fn renderer_main( _ = ipc_recv_token.cancelled() => { debug!("ipc_recv exited"); }, - _ = handle_ipc_msg(ipc_rx) => {} + _ = handle_ipc_msg(ipc_rx, ipc_display_registration_queue) => {} } }); diff --git a/clover-hub/src/server/renderer/models.rs b/clover-hub/src/server/renderer/models.rs index 2e2030f..0fb827a 100644 --- a/clover-hub/src/server/renderer/models.rs +++ b/clover-hub/src/server/renderer/models.rs @@ -1,51 +1,7 @@ -use std::{ - collections::HashMap, - num::NonZero, -}; - use serde::{ Deserialize, Serialize, }; -use crate::server::modman::components::video::{ - displays::models::{ - DisplayPosition, - VirtualDisplayComponent, - }, - VideoResolution, -}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct RendererConfig { - pub virtual_displays: HashMap, -} - -impl Default for RendererConfig { - /// Ensure that there is a display if the compositor was compiled in - /// and there wasn't a display defined in the config/disabled explicitly. - fn default() -> Self { - let mut virtual_displays = HashMap::new(); - - #[cfg(feature = "compositor")] - { - let mut displays = HashMap::new(); - - displays.insert("0".to_string(), DisplayPosition { x: 0.0, y: 0.0 }); - - virtual_displays.insert( - "debug_display".to_string(), - VirtualDisplayComponent { - displays, - resolution: VideoResolution { - height: NonZero::new(200).unwrap(), - width: NonZero::new(500).unwrap(), - }, - gesture_config: None, - }, - ); - } - - Self { virtual_displays } - } -} +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct RendererConfig {} diff --git a/clover-hub/src/server/renderer/system_ui/mod.rs b/clover-hub/src/server/renderer/system_ui/mod.rs index 4c8de9d..1acb0c7 100644 --- a/clover-hub/src/server/renderer/system_ui/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/mod.rs @@ -1,16 +1,18 @@ pub mod plugins; pub mod systems; +use std::sync::{ + Arc, + Mutex, +}; + use crate::server::modman::components::video::displays::models::{ PhysicalDisplayComponent, VirtualDisplayComponent, }; use crate::utils::RecvSync; use bevy::{ - app::{ - App, - Startup, - }, + app::App, diagnostic::{ FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin, @@ -19,12 +21,13 @@ use bevy::{ prelude::*, }; use queues::*; -use systems::{ - displays::display_registrar, - setup::setup, +use serde::{ + Deserialize, + Serialize, }; +use systems::displays::display_registrar; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum AnyDisplayComponent { Physical(PhysicalDisplayComponent), Virtual(VirtualDisplayComponent), @@ -33,7 +36,7 @@ pub enum AnyDisplayComponent { #[derive(Resource)] pub struct SystemUIIPC { pub exit_channel: RecvSync, - pub display_registration_queue: Queue, + pub display_registration_queue: Arc>>, } pub fn shutdown_system( @@ -53,7 +56,7 @@ pub fn system_ui_main(custom_bevy_ipc: SystemUIIPC, disable_winit: Option) app .insert_resource(custom_bevy_ipc) - .add_systems(Startup, setup) + // .add_systems(Startup, setup) .add_systems(Update, display_registrar) .add_systems(Update, shutdown_system); @@ -79,6 +82,8 @@ pub fn system_ui_main(custom_bevy_ipc: SystemUIIPC, disable_winit: Option) update_scroll_position, }; + use crate::server::renderer::system_ui::systems::simulated_controls::sim_setup; + let mut modded_winit: WinitPlugin = Default::default(); modded_winit.run_on_any_thread = true; @@ -87,8 +92,8 @@ pub fn system_ui_main(custom_bevy_ipc: SystemUIIPC, disable_winit: Option) DefaultPlugins .set(WindowPlugin { primary_window: Some(Window { - title: "Clover Simulated Controls".into(), - resolution: (440.0, 75.0).into(), + title: "Clover".into(), + resolution: (100.0, 100.0).into(), present_mode: PresentMode::AutoVsync, // Tells Wasm to resize the window according to the available canvas fit_canvas_to_parent: true, @@ -109,6 +114,7 @@ pub fn system_ui_main(custom_bevy_ipc: SystemUIIPC, disable_winit: Option) LogDiagnosticsPlugin::default(), FrameTimeDiagnosticsPlugin, )) + .add_systems(Startup, sim_setup) .add_systems(Update, (make_visible, update_scroll_position)); } } diff --git a/clover-hub/src/server/renderer/system_ui/systems/displays/mod.rs b/clover-hub/src/server/renderer/system_ui/systems/displays/mod.rs index 30802c3..1a306af 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/displays/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/displays/mod.rs @@ -6,10 +6,8 @@ use crate::server::{ modman::components::video::displays::models::VirtualDisplayComponent, renderer::system_ui::systems::view_management::Composition, }; -use bevy::math::f32; use bevy::prelude::*; use bevy::render::camera::RenderTarget; -use bevy::window::WindowResolution; #[cfg(feature = "compositor")] use bevy::window::{ Monitor, @@ -19,17 +17,42 @@ use bevy::window::{ use log::warn; use queues::*; +#[derive(Component)] +#[cfg(feature = "compositor")] +pub struct DisplayWindow { + pub id: String, +} + +#[derive(Component)] +pub struct DisplayCamera { + pub id: String, +} + +#[derive(Component)] +pub struct DisplayComposition { + pub id: String, +} + +#[derive(Component)] +pub struct VirtualDisplayID { + pub id: String, +} + pub fn display_registrar( mut commands: Commands, - mut ipc: ResMut, + ipc: Res, #[cfg(feature = "compositor")] monitor_entities_query: Query<(Entity, &Monitor)>, - vdisplay_query: Query<(Entity, &mut VirtualDisplayComponent)>, + vdisplay_query: Query<(Entity, &mut VirtualDisplayComponent, &VirtualDisplayID)>, ) { - let display_queue = &mut ipc.display_registration_queue; + let ipc_display_reg_queue_clone = ipc.display_registration_queue.clone(); + let mut display_queue = ipc_display_reg_queue_clone.lock().unwrap(); match display_queue.remove() { - Ok(any_display) => { - debug!("Found display to register: {:?}", any_display.clone()); + Ok(display_config) => { + let display_id = display_config.0; + let any_display = display_config.1; + + debug!("Found display to register: {}", display_id.clone()); match any_display { AnyDisplayComponent::Physical(physical_display_component) => { @@ -72,30 +95,49 @@ pub fn display_registrar( match physical_display_component.virtual_display { Some(vdisplay_id) => { - match vdisplay_query.get(Entity::from_bits(vdisplay_id)) { - Ok(vdisplay) => { - // TODO: Create a camera for this display. - - commands.get_entity(vdisplay.0).unwrap().insert(Window { - title: "Clover SystemUI".into(), + for ( + queried_vdisplay_entity, + _queried_vdisplay_component, + queried_vdisplay_id + ) in &vdisplay_query { + if vdisplay_id == queried_vdisplay_id.id { + use bevy::window::WindowTheme; + + let window = commands.get_entity(queried_vdisplay_entity).unwrap().insert((Window { + title: format!("Clover SystemUI: Virtual Display: {}", display_id.clone()), mode: windowed, + window_theme: Some(WindowTheme::Dark), position, ..Default::default() - }); + }, DisplayWindow { id: vdisplay_id.to_string() })).id(); + + commands.spawn(( + Camera3d::default(), + Camera { + target: RenderTarget::Window(WindowRef::Entity(window)), + ..default() + }, + Transform::from_xyz(6.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y), + DisplayCamera { id: vdisplay_id.to_string() } + )); + + break; } - Err(e) => {} } } None => { - // TODO: Spawn a composition. Create a camera for this display. + // TODO: Spawn a composition for this display. + + use bevy::window::WindowTheme; let window = commands - .spawn(Window { - title: format!("Clover SystemUI {}", direct_connection.display_id.clone()), + .spawn((Window { + title: format!("Clover SystemUI: Display: {}", display_id.clone()), mode: windowed, + window_theme: Some(WindowTheme::Dark), position, ..Default::default() - }) + }, DisplayWindow { id: display_id.clone() })) .id(); commands.spawn(( @@ -105,11 +147,10 @@ pub fn display_registrar( ..default() }, Transform::from_xyz(6.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y), + DisplayCamera { id: display_id.clone() } )); } } - - // TODO: Show new window! } crate::server::modman::components::video::displays::models::ConnectionType::ModManProxy( proxied_connection, @@ -120,32 +161,12 @@ pub fn display_registrar( } } AnyDisplayComponent::Virtual(virtual_display_component) => { - debug!("Spawing VDisplay: {:?}", virtual_display_component.clone()); - // TODO: Spawn a composition. Create a camera for this display. - - let window = commands - .spawn(Window { - title: "Clover SystemUI: Virtual Display".to_owned(), - resolution: WindowResolution::new( - virtual_display_component.resolution.width.get() as f32, - virtual_display_component.resolution.height.get() as f32, - ), - ..Default::default() - }) - .id(); - - commands.spawn(( - Camera3d::default(), - Camera { - target: RenderTarget::Window(WindowRef::Entity(window)), - ..default() - }, - Transform::from_xyz(6.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y), - )); - - // TODO: Show new window! - - debug!("Done spawning VDisplay!"); + debug!("Spawing VDisplay: {}", display_id.clone()); + // TODO: Spawn a composition for this display. + + commands.spawn((virtual_display_component, VirtualDisplayID { id: display_id.clone() })); + + debug!("Done spawning VDisplay: {}!", display_id.clone()); } } } diff --git a/clover-hub/src/server/renderer/system_ui/systems/setup.rs b/clover-hub/src/server/renderer/system_ui/systems/setup.rs index 381a076..8b13789 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/setup.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/setup.rs @@ -1,87 +1 @@ -use bevy::prelude::*; -#[cfg(feature = "compositor")] -use bevy::window::Monitor; -use std::env; -pub fn sim_button(parent: &mut ChildBuilder<'_>, color: Color, text: &str) { - let button_id = text.to_owned(); - - parent - .spawn(( - Node { - width: Val::Px(50.0), - height: Val::Px(50.0), - justify_content: JustifyContent::Center, - align_items: AlignItems::Center, - ..default() - }, - BackgroundColor(color), - )) - .with_children(|button| { - button.spawn(( - Text::new(text), - TextFont { - font_size: 10.0, - ..Default::default() - }, - TextColor(Color::srgb(0.0, 0.0, 0.0)), - )); - }) - .observe(move |_: Trigger>| { - debug!("{} was clicked!", button_id); - }); -} - -pub fn setup( - mut commands: Commands, - #[cfg(feature = "compositor")] monitor_entities_query: Query<(Entity, &Monitor)>, -) { - #[cfg(feature = "compositor")] - { - debug!("Available Monitors:"); - for (monitor_eid, monitor) in &monitor_entities_query { - debug!( - "Monitor {} with EntityID of {}.", - monitor.name.clone().unwrap_or("unknown".into()), - monitor_eid.to_bits() - ); - } - - if &env::var("CLOVER_SIMULATED_CONTROLS").unwrap_or("false".to_string()) == "true" { - // Camera - commands.spawn((Camera2d::default(), IsDefaultUiCamera)); - - // root node - commands - .spawn(Node { - width: Val::Percent(100.0), - height: Val::Percent(100.0), - justify_content: JustifyContent::SpaceBetween, - ..default() - }) - .insert(PickingBehavior { - should_block_lower: false, - is_hoverable: false, - }) - .with_children(|parent| { - // Directions - sim_button(parent, Color::srgb(0.65, 0.65, 0.65), "Up"); - sim_button(parent, Color::srgb(0.65, 0.65, 0.65), "Down"); - sim_button(parent, Color::srgb(0.65, 0.65, 0.65), "Left"); - sim_button(parent, Color::srgb(0.65, 0.65, 0.65), "Right"); - - // Primary - sim_button(parent, Color::srgb(0.1, 0.65, 0.1), "1"); - - // Seccondary - sim_button(parent, Color::srgb(0.1, 0.1, 0.65), "2"); - - // Back - sim_button(parent, Color::srgb(0.65, 0.1, 0.1), "Back"); - - // Home - sim_button(parent, Color::srgb(0.65, 0.65, 0.1), "Home"); - }); - } - } -} diff --git a/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs b/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs index d72104f..0369172 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "compositor")] +use bevy::window::Monitor; use bevy::{ core::FrameCount, ecs::system::Query, @@ -9,6 +11,148 @@ use bevy::{ prelude::*, window::Window, }; +use bevy::{ + render::camera::RenderTarget, + window::{ + PresentMode, + PrimaryWindow, + WindowRef, + WindowTheme, + }, +}; + +pub fn sim_button(parent: &mut ChildBuilder<'_>, color: Color, text: &str) { + let button_id = text.to_owned(); + + parent + .spawn(( + Node { + width: Val::Px(50.0), + height: Val::Px(50.0), + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..default() + }, + BackgroundColor(color), + )) + .with_children(|button| { + button.spawn(( + Text::new(text), + TextFont { + font_size: 10.0, + ..Default::default() + }, + TextColor(Color::srgb(0.0, 0.0, 0.0)), + )); + }) + .observe(move |_: Trigger>| { + debug!("{} was clicked!", button_id); + }); +} + +#[derive(Component)] +#[cfg(feature = "compositor")] +pub struct SimulatedControlsWindow {} + +#[derive(Component)] +#[cfg(feature = "compositor")] +pub struct SimulatedControlsCamera {} + +#[derive(Component)] +#[cfg(feature = "compositor")] +pub struct SimulatedControlsRootNode {} + +pub fn sim_setup( + mut commands: Commands, + #[cfg(feature = "compositor")] monitor_entities_query: Query<(Entity, &Monitor)>, +) { + #[cfg(feature = "compositor")] + { + debug!("Available Monitors:"); + for (monitor_eid, monitor) in &monitor_entities_query { + debug!( + "Monitor {} with EntityID of {}.", + monitor.name.clone().unwrap_or("unknown".into()), + monitor_eid.to_bits() + ); + } + + debug!("Spawning simulated controls window..."); + + let ui_window = commands + .spawn(( + Window { + title: "Clover Simulated Controls".into(), + resolution: (440.0, 75.0).into(), + present_mode: PresentMode::AutoVsync, + // Tells Wasm to resize the window according to the available canvas + fit_canvas_to_parent: true, + // Tells Wasm not to override default event handling, like F5, Ctrl+R etc. + prevent_default_event_handling: false, + window_theme: Some(WindowTheme::Dark), + enabled_buttons: bevy::window::EnabledButtons { + maximize: false, + ..Default::default() + }, + visible: true, + ..Default::default() + }, + SimulatedControlsWindow {}, + )) + .id(); + + // Camera + let ui_camera = commands + .spawn(( + Camera2d::default(), + IsDefaultUiCamera, + SimulatedControlsCamera {}, + Camera { + target: RenderTarget::Window(WindowRef::Entity(ui_window)), + ..default() + }, + )) + .id(); + + // root node + commands + .spawn(( + Node { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + justify_content: JustifyContent::SpaceBetween, + ..default() + }, + TargetCamera(ui_camera), + SimulatedControlsRootNode {}, + )) + .insert(PickingBehavior { + should_block_lower: false, + is_hoverable: false, + }) + .with_children(|parent| { + // Directions + sim_button(parent, Color::srgb(0.65, 0.65, 0.65), "Up"); + sim_button(parent, Color::srgb(0.65, 0.65, 0.65), "Down"); + sim_button(parent, Color::srgb(0.65, 0.65, 0.65), "Left"); + sim_button(parent, Color::srgb(0.65, 0.65, 0.65), "Right"); + + // Primary + sim_button(parent, Color::srgb(0.1, 0.65, 0.1), "1"); + + // Seccondary + sim_button(parent, Color::srgb(0.1, 0.1, 0.65), "2"); + + // Back + sim_button(parent, Color::srgb(0.65, 0.1, 0.1), "Back"); + + // Home + sim_button(parent, Color::srgb(0.65, 0.65, 0.1), "Home"); + }); + + debug!("Spawned simulated controls window!"); + } +} pub fn make_visible(mut windows: Query<&mut Window>, frames: Res) { // info!("Frame: {}", frames.0); diff --git a/clover-hub/src/server/warehouse/mod.rs b/clover-hub/src/server/warehouse/mod.rs index a703392..7941fdc 100644 --- a/clover-hub/src/server/warehouse/mod.rs +++ b/clover-hub/src/server/warehouse/mod.rs @@ -84,7 +84,7 @@ pub async fn setup_warehouse(data_dir: String, store: Arc) -> Re Ok(mut file) => { match file .write_all( - serde_json_lenient::to_string::(&Default::default()) + serde_json_lenient::to_string_pretty::(&Default::default()) .unwrap() .as_bytes(), ) @@ -250,11 +250,19 @@ pub async fn warehouse_main( } } - let _ = init_user.send( + match init_user.send( &"nexus://com.reboot-codes.clover.warehouse/status".to_string(), &"finished-init".to_string(), &None, - ); + ) { + Err(e) => { + error!( + "Error when letting peers know about completed init state: {}", + e + ); + } + _ => {} + } }) .await; @@ -285,7 +293,7 @@ pub async fn warehouse_main( match config_file .write_all( - serde_json_lenient::to_string::(&config.clone() as &Config) + serde_json_lenient::to_string_pretty::(&config.clone() as &Config) .unwrap() .as_bytes(), ) diff --git a/dev.sh b/dev.sh index ea364c7..faaefc7 100755 --- a/dev.sh +++ b/dev.sh @@ -1 +1 @@ -CLOVER_LOG="clover=debug,bevy=debug,nexus=debug" CLOVER_SIMULATED_CONTROLS="true" CLOVER_MASTER_PRINT="true" cargo run --bin="clover-hub" run server +CLOVER_LOG="clover=debug,nexus=debug" CLOVER_SIMULATED_CONTROLS="true" CLOVER_MASTER_PRINT="true" cargo run --bin="clover-hub" run server diff --git a/flake.nix b/flake.nix index 2e27899..633e91f 100644 --- a/flake.nix +++ b/flake.nix @@ -23,7 +23,7 @@ pkg-config (rust-bin.selectLatestNightlyWith (toolchain: toolchain.default.override { extensions = [ "rust-src" ]; - targets = [ "arm-unknown-linux-gnueabihf" "x86_64-pc-windows-gnu" ]; + targets = [ "arm-unknown-linux-gnueabihf" "aarch64-unknown-linux-gnu" "x86_64-pc-windows-gnu" ]; })) alsa-lib udev diff --git a/todo.md b/todo.md index 924f8b6..c72388d 100644 --- a/todo.md +++ b/todo.md @@ -2,6 +2,7 @@ - [ ] CloverHub - [ ] Server + - [ ] Move to MQTT/RabbitMQ - [ ] Renderer - [ ] SystemUI - [ ] Custom Audio Plugin (using ModMan proxies).