Compare commits

...

2 commits

Author SHA1 Message Date
Fl1tzi 42dce219f2 Update README.md 2023-12-23 22:32:36 +00:00
Fl1tzi 4536e0a8cd
implement module registry instead of pattern matching solution 2023-12-23 23:13:59 +01:00
5 changed files with 51 additions and 13 deletions

View file

@ -20,3 +20,4 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] }
font-loader = "0.11.0" font-loader = "0.11.0"
clru = "0.6.1" clru = "0.6.1"
once_cell = "1.19.0"

View file

@ -6,4 +6,10 @@ This is a long term project. See [v0.1](https://codeberg.org/Fl1tzi/dach-decker/
# Microdeck # Microdeck
Microdeck is a software to configure a Stream Deck with efficient modules. Microdeck is an open-source software designed for configuring Stream Decks with ease and versatility. It aims to address common pain points found in existing solutions by offering improved rendering speeds, greater customization options, and a focus on stability.
## Key features
- (TODO) **User-friendly Interface:** An intuitive interface allows users to quickly set up and manage their Stream Deck configurations.
- **Independent Modules:** Customize your setup with independently operating modules, enabling tailored functionality according to specific needs or preferences.
- **Optimized Performance:** Efficient rendering ensures smooth operation and minimal lag during usage.

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
config::{Button, ConfigError, DeviceConfig, Space}, config::{Button, ConfigError, DeviceConfig, Space},
modules::{retrieve_module_from_name, start_module, HostEvent}, modules::{start_module, HostEvent, MODULE_REGISTRY},
unwrap_or_error, unwrap_or_error,
}; };
use clru::CLruCache; use clru::CLruCache;
@ -141,7 +141,7 @@ impl Device {
.as_ref() .as_ref()
.expect("Runtime has to be created before module can be spawned"); .expect("Runtime has to be created before module can be spawned");
let (module_sender, module_receiver) = mpsc::channel(4); let (module_sender, module_receiver) = mpsc::channel(4);
if let Some(module) = retrieve_module_from_name(&btn.module) { if let Some(module) = MODULE_REGISTRY.get_module(&btn.module) {
{ {
// initialize the module // initialize the module
let ser = self.serial.clone(); let ser = self.serial.clone();
@ -150,7 +150,7 @@ impl Device {
let image_cache = self.image_cache.clone(); let image_cache = self.image_cache.clone();
runtime.spawn(async move { runtime.spawn(async move {
start_module(ser, b, module, dev, module_receiver, image_cache).await start_module(ser, b, *module, dev, module_receiver, image_cache).await
}); });
} }
// if the receiver already dropped the listener then just directly insert none. // if the receiver already dropped the listener then just directly insert none.

View file

@ -8,7 +8,6 @@ use std::{
collections::HashMap, collections::HashMap,
process::exit, process::exit,
sync::{Arc, Mutex, OnceLock}, sync::{Arc, Mutex, OnceLock},
thread,
time::Duration, time::Duration,
}; };
use tracing::{debug, error, info, trace, warn}; use tracing::{debug, error, info, trace, warn};

View file

@ -17,6 +17,8 @@ use ::image::DynamicImage;
use async_trait::async_trait; use async_trait::async_trait;
pub use deck_driver as streamdeck; pub use deck_driver as streamdeck;
use futures_util::Future; use futures_util::Future;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::{error::Error, pin::Pin, sync::Arc}; use std::{error::Error, pin::Pin, sync::Arc};
use streamdeck::info::ImageFormat; use streamdeck::info::ImageFormat;
use streamdeck::info::Kind; use streamdeck::info::Kind;
@ -25,6 +27,8 @@ use streamdeck::StreamDeckError;
use tokio::sync::{mpsc, Mutex}; use tokio::sync::{mpsc, Mutex};
use tracing::{debug, error, trace}; use tracing::{debug, error, trace};
pub static MODULE_REGISTRY: Lazy<ModuleRegistry> = Lazy::new(|| ModuleRegistry::default());
/// Events that are coming from the host /// Events that are coming from the host
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum HostEvent { pub enum HostEvent {
@ -39,12 +43,34 @@ pub type ModuleFuture =
Pin<Box<dyn Future<Output = Result<ModuleObject, ButtonConfigError>> + Send>>; Pin<Box<dyn Future<Output = Result<ModuleObject, ButtonConfigError>> + Send>>;
pub type ModuleInitFunction = fn(Arc<Button>, ModuleCache) -> ModuleFuture; pub type ModuleInitFunction = fn(Arc<Button>, ModuleCache) -> ModuleFuture;
pub fn retrieve_module_from_name(name: &str) -> Option<ModuleInitFunction> { pub type ModuleMap = HashMap<&'static str, ModuleInitFunction>;
match name {
"space" => Some(Space::new as ModuleInitFunction), /// Registry of available modules
"counter" => Some(Counter::new as ModuleInitFunction), pub struct ModuleRegistry {
"image" => Some(Image::new as ModuleInitFunction), modules: ModuleMap,
_ => None, }
impl Default for ModuleRegistry {
fn default() -> Self {
let mut modules = ModuleMap::new();
modules.insert("space", Space::new as ModuleInitFunction);
modules.insert("image", Image::new as ModuleInitFunction);
modules.insert("counter", Counter::new as ModuleInitFunction);
ModuleRegistry { modules }
}
}
impl ModuleRegistry {
/// Retrieve a module from the registry
pub fn get_module(&self, name: &str) -> Option<&ModuleInitFunction> {
self.modules.get(name)
}
// TODO: Idk if the &&str will be fine in the future
/// List all available modules
#[allow(dead_code)]
pub fn list_modules(&self) -> Vec<&&str> {
self.modules.keys().collect()
} }
} }
@ -67,9 +93,10 @@ pub async fn start_module(
); );
let da = DeviceAccess::new(device, button.index).await; let da = DeviceAccess::new(device, button.index).await;
// run init first // init
// //
// panic should be prevented by the config being checked before running // This function should be called after the config was checked,
// otherwise it will panic and the module wont be started.
let mut module = match module_init_function(button, mc).await { let mut module = match module_init_function(button, mc).await {
Ok(m) => m, Ok(m) => m,
Err(e) => panic!("{}", e), Err(e) => panic!("{}", e),
@ -198,6 +225,9 @@ pub trait Module: Sync + Send {
/// Function for validating configuration and creating module instance. Every time the config /// Function for validating configuration and creating module instance. Every time the config
/// is checked this function gets called. It therefore should validate the most efficient /// is checked this function gets called. It therefore should validate the most efficient
/// things first. /// things first.
///
/// This function should **not** panic as the panic will not be catched and therefore would be
/// not noticed.
async fn new( async fn new(
config: Arc<Button>, config: Arc<Button>,
mut cache: ModuleCache, mut cache: ModuleCache,
@ -206,6 +236,8 @@ pub trait Module: Sync + Send {
Self: Sized; Self: Sized;
/// Function for actually running the module and interacting with the device. Errors that /// Function for actually running the module and interacting with the device. Errors that
/// happen here should be mostly prevented. /// happen here should be mostly prevented.
///
/// TODO: The return error is not sent anywhere and is just a panic
async fn run( async fn run(
&mut self, &mut self,
device: DeviceAccess, device: DeviceAccess,