mirror of
https://codeberg.org/Fl1tzi/microdeck.git
synced 2024-05-08 14:40:44 +00:00
memory improvements
This commit is contained in:
parent
d216b86354
commit
366b08c850
|
@ -11,7 +11,7 @@ toml = "0.7.2"
|
|||
tokio = { version = "1", features = ["full"] }
|
||||
log = "0.4"
|
||||
dirs = "4.0.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
image = "0.24.5"
|
||||
async-trait = "0.1.66"
|
||||
futures-util = "0.3.27"
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use crate::{
|
||||
modules::{retrieve_module_from_name, start_module, HostEvent},
|
||||
Button, ConfigError, DeviceConfig,
|
||||
skip_if_none, unwrap_or_error
|
||||
};
|
||||
use deck_driver as streamdeck;
|
||||
use hidapi::HidApi;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::{self, Display},
|
||||
sync::Arc,
|
||||
fmt::Display,
|
||||
sync::Arc, cell::Cell,
|
||||
};
|
||||
use streamdeck::{
|
||||
asynchronous::{AsyncStreamDeck, ButtonStateUpdate},
|
||||
|
@ -16,24 +17,12 @@ use streamdeck::{
|
|||
};
|
||||
use tokio::{
|
||||
process::Command,
|
||||
sync::{
|
||||
mpsc::{self, error::TrySendError},
|
||||
Mutex,
|
||||
},
|
||||
task::JoinHandle,
|
||||
sync::mpsc::{self, error::TrySendError},
|
||||
runtime::Runtime,
|
||||
};
|
||||
use tracing::{debug, error, info_span, trace};
|
||||
|
||||
macro_rules! skip_if_none {
|
||||
($res:expr) => {
|
||||
match $res {
|
||||
Some(v) => v,
|
||||
None => continue,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub type ModuleController = (Button, JoinHandle<()>, mpsc::Sender<HostEvent>);
|
||||
pub type ModuleController = (Arc<Button>, mpsc::Sender<HostEvent>);
|
||||
|
||||
pub enum DeviceError {
|
||||
DriverError(StreamDeckError),
|
||||
|
@ -53,7 +42,8 @@ impl Display for DeviceError {
|
|||
pub struct Device {
|
||||
modules: HashMap<u8, ModuleController>,
|
||||
device: Arc<AsyncStreamDeck>,
|
||||
is_dropped: bool,
|
||||
modules_runtime: Option<Runtime>,
|
||||
config: DeviceConfig,
|
||||
serial: String,
|
||||
}
|
||||
|
||||
|
@ -61,7 +51,7 @@ impl Device {
|
|||
pub async fn new(
|
||||
serial: String,
|
||||
kind: Kind,
|
||||
device_conf: &DeviceConfig,
|
||||
device_conf: DeviceConfig,
|
||||
hid: &HidApi,
|
||||
) -> Result<Device, DeviceError> {
|
||||
// connect to deck or continue to next
|
||||
|
@ -89,23 +79,38 @@ impl Device {
|
|||
Ok(Device {
|
||||
modules: HashMap::new(),
|
||||
device: deck,
|
||||
is_dropped: false,
|
||||
modules_runtime: None,
|
||||
config: device_conf,
|
||||
serial,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_module(&mut self, btn: &Button) -> Result<(), DeviceError> {
|
||||
pub async fn init_modules(&mut self) {
|
||||
if self.modules_runtime.is_none() {
|
||||
self.modules_runtime = Some(Runtime::new().unwrap());
|
||||
}
|
||||
for i in 0..self.config.buttons.len() {
|
||||
let button = self.config.buttons.get(i).unwrap().to_owned();
|
||||
unwrap_or_error!(self._create_module(button).await);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async fn _create_module(&mut self, btn: Arc<Button>) -> Result<(), DeviceError> {
|
||||
let runtime = self.modules_runtime.as_ref().expect("Runtime has to be created before module can be spawned");
|
||||
let (button_sender, button_receiver) = mpsc::channel(4);
|
||||
if let Some(module) = retrieve_module_from_name(btn.module.clone()) {
|
||||
let b = btn.clone();
|
||||
let ser = self.serial.clone();
|
||||
let rx = Arc::new(Mutex::new(button_receiver));
|
||||
let dev = self.device.clone();
|
||||
let handle = tokio::spawn(async move {
|
||||
start_module(ser, b, module, dev, rx).await;
|
||||
});
|
||||
if let Some(module) = retrieve_module_from_name(&btn.module) {
|
||||
{
|
||||
let ser = self.serial.clone();
|
||||
let dev = self.device.clone();
|
||||
let b = btn.clone();
|
||||
|
||||
runtime.spawn(async move {
|
||||
start_module(ser, b, module, dev, Cell::new(button_receiver)).await
|
||||
});
|
||||
}
|
||||
self.modules
|
||||
.insert(btn.index, (btn.clone(), handle, button_sender));
|
||||
.insert(btn.index, (btn.clone(), button_sender));
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(DeviceError::Config(ConfigError::ModuleDoesNotExist(
|
||||
|
@ -119,20 +124,14 @@ impl Device {
|
|||
self.serial.clone()
|
||||
}
|
||||
|
||||
/// stops all modules of the device
|
||||
fn stop_all_modules(&self) {
|
||||
for (index, (_, handle, _)) in self.modules.iter() {
|
||||
trace!("Destroying module {}", index);
|
||||
handle.abort();
|
||||
fn drop(&mut self) {
|
||||
if let Some(handle) = self.modules_runtime.take() {
|
||||
handle.shutdown_background();
|
||||
}
|
||||
}
|
||||
|
||||
fn drop(&mut self) {
|
||||
self.is_dropped = true
|
||||
}
|
||||
|
||||
pub fn is_dropped(&self) -> bool {
|
||||
self.is_dropped
|
||||
self.modules_runtime.is_none()
|
||||
}
|
||||
|
||||
pub fn has_modules(&self) -> bool {
|
||||
|
@ -153,7 +152,7 @@ impl Device {
|
|||
if let Some(on_click) = &options.0.on_click {
|
||||
execute_sh(on_click).await;
|
||||
} else {
|
||||
send_to_channel(&options.2, HostEvent::ButtonPressed).await;
|
||||
send_to_channel(&options.1, HostEvent::ButtonPressed).await;
|
||||
}
|
||||
}
|
||||
ButtonStateUpdate::ButtonUp(i) => {
|
||||
|
@ -161,7 +160,7 @@ impl Device {
|
|||
if let Some(on_release) = &options.0.on_release {
|
||||
execute_sh(on_release).await;
|
||||
} else {
|
||||
send_to_channel(&options.2, HostEvent::ButtonReleased).await;
|
||||
send_to_channel(&options.1, HostEvent::ButtonReleased).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +169,6 @@ impl Device {
|
|||
Err(e) => match e {
|
||||
StreamDeckError::HidError(e) => {
|
||||
error!("Shutting down device because of: {e}");
|
||||
self.stop_all_modules();
|
||||
self.drop();
|
||||
break;
|
||||
}
|
||||
|
|
47
src/main.rs
47
src/main.rs
|
@ -1,5 +1,5 @@
|
|||
use deck_driver as streamdeck;
|
||||
use device::{Device, DeviceError};
|
||||
use device::Device;
|
||||
use hidapi::HidApi;
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
|
@ -11,6 +11,7 @@ use std::{
|
|||
path::PathBuf,
|
||||
process::exit,
|
||||
time::Duration,
|
||||
sync::Arc
|
||||
};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use tracing_subscriber::{
|
||||
|
@ -23,7 +24,27 @@ mod device;
|
|||
mod modules;
|
||||
|
||||
/// The name of the folder which holds the config
|
||||
pub const CONFIG_FOLDER_NAME: &'static str = "dach-decker";
|
||||
pub const CONFIG_FOLDER_NAME: &'static str = "virtual-deck";
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! skip_if_none {
|
||||
($res:expr) => {
|
||||
match $res {
|
||||
Some(v) => v,
|
||||
None => continue,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unwrap_or_error {
|
||||
($res:expr) => {
|
||||
match $res {
|
||||
Ok(v) => v,
|
||||
Err(e) => error!("{}", e)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The config structure
|
||||
#[derive(Deserialize, Debug)]
|
||||
|
@ -83,15 +104,7 @@ fn main() {
|
|||
};
|
||||
debug!("{:#?}", config);
|
||||
|
||||
// ------ START APPLICATION
|
||||
|
||||
let hid = match streamdeck::new_hidapi() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("HidApi Error:\n{}", e);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
let hid = streamdeck::new_hidapi().expect("Could not create HidApi");
|
||||
// lets start some async
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
|
@ -130,7 +143,7 @@ pub async fn start(config: Config, mut hid: HidApi) {
|
|||
config.device.iter().find(|d| d.serial == hw_device.1)
|
||||
{
|
||||
// start the device and its listener
|
||||
if let Some(device) = start_device(hw_device, &hid, device_config).await
|
||||
if let Some(device) = start_device(hw_device, &hid, device_config.clone()).await
|
||||
{
|
||||
devices.insert(device.serial(), device);
|
||||
}
|
||||
|
@ -151,17 +164,19 @@ pub async fn start(config: Config, mut hid: HidApi) {
|
|||
pub async fn start_device(
|
||||
device: (streamdeck::info::Kind, String),
|
||||
hid: &HidApi,
|
||||
device_config: &DeviceConfig,
|
||||
device_config: DeviceConfig,
|
||||
) -> Option<Device> {
|
||||
match Device::new(device.1, device.0, device_config, &hid).await {
|
||||
Ok(mut device) => {
|
||||
info!("Connected");
|
||||
// start all modules or print out the error
|
||||
device_config.buttons.iter().for_each(|button| {
|
||||
/* device_config.buttons.iter().for_each(|button| {
|
||||
device
|
||||
.create_module(&button)
|
||||
.unwrap_or_else(|e| error!("{}", e))
|
||||
});
|
||||
});*/
|
||||
device.init_modules().await;
|
||||
device.key_listener().await;
|
||||
if !device.has_modules() {
|
||||
warn!("All modules have failed to start");
|
||||
}
|
||||
|
@ -199,7 +214,7 @@ pub struct DeviceConfig {
|
|||
pub serial: String,
|
||||
#[serde(default = "default_brightness")]
|
||||
pub brightness: u8,
|
||||
pub buttons: Vec<Button>,
|
||||
pub buttons: Vec<Arc<Button>>,
|
||||
}
|
||||
|
||||
fn default_brightness() -> u8 {
|
||||
|
|
|
@ -8,6 +8,7 @@ pub use deck_driver as streamdeck;
|
|||
use futures_util::Future;
|
||||
use image::DynamicImage;
|
||||
use lazy_static::lazy_static;
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashMap;
|
||||
use std::pin::Pin;
|
||||
use std::{error::Error, sync::Arc};
|
||||
|
@ -16,7 +17,6 @@ use streamdeck::info::Kind;
|
|||
use streamdeck::AsyncStreamDeck;
|
||||
pub use streamdeck::StreamDeckError;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
lazy_static! {
|
||||
|
@ -37,9 +37,9 @@ pub enum HostEvent {
|
|||
}
|
||||
|
||||
pub type ModuleFuture = Pin<Box<dyn Future<Output = Result<(), ReturnError>> + Send>>;
|
||||
pub type ModuleFunction = fn(DeviceAccess, ChannelReceiver, Button) -> ModuleFuture;
|
||||
pub type ModuleFunction = fn(DeviceAccess, ChannelReceiver, Arc<Button>) -> ModuleFuture;
|
||||
|
||||
pub fn retrieve_module_from_name(name: String) -> Option<ModuleFunction> {
|
||||
pub fn retrieve_module_from_name(name: &String) -> Option<ModuleFunction> {
|
||||
MODULE_MAP.get(name.as_str()).copied()
|
||||
}
|
||||
|
||||
|
@ -48,10 +48,10 @@ pub fn retrieve_module_from_name(name: String) -> Option<ModuleFunction> {
|
|||
pub async fn start_module(
|
||||
// Just for logging purpose
|
||||
serial: String,
|
||||
button: Button,
|
||||
button: Arc<Button>,
|
||||
module_function: ModuleFunction,
|
||||
device: Arc<AsyncStreamDeck>,
|
||||
br: Arc<Mutex<mpsc::Receiver<HostEvent>>>,
|
||||
br: Cell<mpsc::Receiver<HostEvent>>
|
||||
) {
|
||||
debug!("STARTED");
|
||||
let da = DeviceAccess::new(device, button.index).await;
|
||||
|
@ -110,13 +110,13 @@ impl DeviceAccess {
|
|||
}
|
||||
|
||||
pub type ReturnError = Box<dyn Error + Send + Sync>;
|
||||
pub type ChannelReceiver = Arc<Mutex<mpsc::Receiver<HostEvent>>>;
|
||||
pub type ChannelReceiver = Cell<mpsc::Receiver<HostEvent>>;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Module {
|
||||
async fn run(
|
||||
device: DeviceAccess,
|
||||
button_receiver: ChannelReceiver,
|
||||
config: Button,
|
||||
receiver: ChannelReceiver,
|
||||
config: Arc<Button>,
|
||||
) -> Result<(), ReturnError>;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use super::DeviceAccess;
|
|||
use super::Module;
|
||||
use super::ReturnError;
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Blank;
|
||||
|
||||
|
@ -12,7 +13,7 @@ impl Module for Blank {
|
|||
async fn run(
|
||||
_streamdeck: DeviceAccess,
|
||||
_button_receiver: ChannelReceiver,
|
||||
_config: Button,
|
||||
_config: Arc<Button>,
|
||||
) -> Result<(), ReturnError> {
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::Button;
|
||||
|
||||
|
@ -18,20 +18,18 @@ impl Module for Counter {
|
|||
async fn run(
|
||||
streamdeck: DeviceAccess,
|
||||
button_receiver: ChannelReceiver,
|
||||
config: Button,
|
||||
_config: Arc<Button>,
|
||||
) -> Result<(), ReturnError> {
|
||||
let options = config.options.unwrap_or_default();
|
||||
let mut button_receiver = button_receiver.into_inner();
|
||||
|
||||
let font_data: &[u8] = include_bytes!("../../fonts/SpaceGrotesk.ttf");
|
||||
let font: Font<'static> = Font::try_from_bytes(font_data).unwrap();
|
||||
|
||||
let (h, w) = streamdeck.resolution();
|
||||
|
||||
let mut stream = button_receiver.lock().await;
|
||||
|
||||
let mut counter: u32 = 0;
|
||||
loop {
|
||||
if let Some(event) = stream.recv().await {
|
||||
if let Some(event) = button_receiver.recv().await {
|
||||
match event {
|
||||
HostEvent::ButtonPressed => {
|
||||
counter += 1;
|
||||
|
|
Loading…
Reference in a new issue