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"] }
font-loader = "0.11.0"
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 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::{
config::{Button, ConfigError, DeviceConfig, Space},
modules::{retrieve_module_from_name, start_module, HostEvent},
modules::{start_module, HostEvent, MODULE_REGISTRY},
unwrap_or_error,
};
use clru::CLruCache;
@ -141,7 +141,7 @@ impl Device {
.as_ref()
.expect("Runtime has to be created before module can be spawned");
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
let ser = self.serial.clone();
@ -150,7 +150,7 @@ impl Device {
let image_cache = self.image_cache.clone();
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.

View file

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

View file

@ -17,6 +17,8 @@ use ::image::DynamicImage;
use async_trait::async_trait;
pub use deck_driver as streamdeck;
use futures_util::Future;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::{error::Error, pin::Pin, sync::Arc};
use streamdeck::info::ImageFormat;
use streamdeck::info::Kind;
@ -25,6 +27,8 @@ use streamdeck::StreamDeckError;
use tokio::sync::{mpsc, Mutex};
use tracing::{debug, error, trace};
pub static MODULE_REGISTRY: Lazy<ModuleRegistry> = Lazy::new(|| ModuleRegistry::default());
/// Events that are coming from the host
#[derive(Clone, Copy, Debug)]
pub enum HostEvent {
@ -39,12 +43,34 @@ pub type ModuleFuture =
Pin<Box<dyn Future<Output = Result<ModuleObject, ButtonConfigError>> + Send>>;
pub type ModuleInitFunction = fn(Arc<Button>, ModuleCache) -> ModuleFuture;
pub fn retrieve_module_from_name(name: &str) -> Option<ModuleInitFunction> {
match name {
"space" => Some(Space::new as ModuleInitFunction),
"counter" => Some(Counter::new as ModuleInitFunction),
"image" => Some(Image::new as ModuleInitFunction),
_ => None,
pub type ModuleMap = HashMap<&'static str, ModuleInitFunction>;
/// Registry of available modules
pub struct ModuleRegistry {
modules: ModuleMap,
}
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;
// 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 {
Ok(m) => m,
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
/// is checked this function gets called. It therefore should validate the most efficient
/// things first.
///
/// This function should **not** panic as the panic will not be catched and therefore would be
/// not noticed.
async fn new(
config: Arc<Button>,
mut cache: ModuleCache,
@ -206,6 +236,8 @@ pub trait Module: Sync + Send {
Self: Sized;
/// Function for actually running the module and interacting with the device. Errors that
/// happen here should be mostly prevented.
///
/// TODO: The return error is not sent anywhere and is just a panic
async fn run(
&mut self,
device: DeviceAccess,