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