This commit is contained in:
Moritz Ruth 2024-02-20 16:05:32 +01:00
parent 6b4ea3f4ae
commit fa8c988705
Signed by: moritzruth
GPG key ID: C9BBAB79405EE56D
6 changed files with 95 additions and 66 deletions

12
Cargo.lock generated
View file

@ -384,6 +384,7 @@ dependencies = [
"flume",
"humantime-serde",
"is_executable",
"itertools",
"log",
"loupedeck_serial",
"once_cell",
@ -422,7 +423,6 @@ dependencies = [
"derive_more",
"enum-map",
"enum-ordinalize",
"im",
"parse-display 0.8.2",
"rgb",
"serde",
@ -789,7 +789,6 @@ dependencies = [
"bitmaps",
"rand_core",
"rand_xoshiro",
"serde",
"sized-chunks",
"typenum",
"version_check",
@ -848,6 +847,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.10"

View file

@ -34,6 +34,7 @@ walkdir = "2.4.0"
once_cell = "1.19.0"
is_executable = "1.0.1"
rumqttc = "0.23.0"
itertools = "0.12.1"
[workspace]
members = [

View file

@ -11,5 +11,4 @@ derive_more = "0.99.17"
rgb = "0.8.37"
enum-ordinalize = "4.3.0"
enum-map = "3.0.0-beta.2"
im = { version = "15.1.0", features = ["serde"] }
parse-display = "0.8.2"

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use thiserror::Error;
@ -55,8 +57,8 @@ pub enum HandlerCommand {
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct InitialHandlerMessage<KeyConfig: Clone, KnobConfig: Clone> {
pub key_configs: im::HashMap<KeyPath, KeyConfig>,
pub knob_configs: im::HashMap<KnobPath, KnobConfig>,
pub key_configs: HashMap<KeyPath, KeyConfig>,
pub knob_configs: HashMap<KnobPath, KnobConfig>,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]

View file

@ -35,19 +35,19 @@ config.mode = "loop"
config.style.single.icon = "@fad/repeat-one[color=#58fc11]"
config.style.all.icon = "@fad/repeat[color=#58fc11]"
[keys.3x3]
icon = "@ph/timer[color=#ff0000]"
#[keys.3x3]
#icon = "@ph/timer[color=#ff0000]"
#
#handler = "timer"
#config.durations = ["60s", "5m", "10m", "15m", "30m"]
#config.vibrate_when_finished = true
#config.needy = true
handler = "timer"
config.durations = ["60s", "5m", "10m", "15m", "30m"]
config.vibrate_when_finished = true
config.needy = true
[keys.4x3]
icon = "@ph/computer-tower"
label = "Gaming PC"
handler = "home-assistant"
config.mode = "switch"
config.name = "switch.mwin"
config.style.on.icon = "@ph/computer-tower[color=#58fc11]"
#[keys.4x3]
#icon = "@ph/computer-tower"
#label = "Gaming PC"
#
#handler = "home-assistant"
#config.mode = "switch"
#config.name = "switch.mwin"
#config.style.on.icon = "@ph/computer-tower[color=#58fc11]"

View file

@ -7,6 +7,8 @@ use std::sync::Arc;
use color_eyre::eyre::{eyre, WrapErr};
use color_eyre::Result;
use is_executable::IsExecutable;
use itertools::Itertools;
use log::warn;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::process::{ChildStdin, Command};
@ -43,10 +45,46 @@ pub async fn start(
let mut handler_stdin_by_name: HashMap<Box<str>, ChildStdin> = HashMap::with_capacity(handler_names.len());
for handler_name in handler_names {
let handler_name = handler_name.into_string().map_err(|_| eyre!("Command names must be valid Unicode."))?;
let mut handler_name_by_key_path: HashMap<KeyPath, Box<str>> = HashMap::new();
let mut handler_config_by_key_path_by_handler_name: HashMap<Box<str>, HashMap<KeyPath, Arc<toml::Table>>> = HashMap::new();
let mut command = Command::new(handlers_directory.join(&handler_name))
for (path, config) in key_configs {
handler_name_by_key_path.insert(path.clone(), config.handler_name.clone());
handler_config_by_key_path_by_handler_name
.entry(config.handler_name)
.or_default()
.insert(path, Arc::clone(&config.handler_config));
}
let mut handler_name_by_knob_path: HashMap<KnobPath, Box<str>> = HashMap::new();
let mut handler_config_by_knob_path_by_handler_name: HashMap<Box<str>, HashMap<KnobPath, Arc<toml::Table>>> = HashMap::new();
for (path, config) in knob_configs {
handler_name_by_knob_path.insert(path.clone(), config.handler_name.clone());
handler_config_by_knob_path_by_handler_name
.entry(config.handler_name)
.or_default()
.insert(path, Arc::clone(&config.handler_config));
}
for handler_name in handler_names {
let handler_name = handler_name
.into_string()
.map_err(|_| eyre!("Command names must be valid Unicode."))?
.into_boxed_str();
let (key_configs, knob_configs) = match (
handler_config_by_key_path_by_handler_name.remove(&handler_name),
handler_config_by_knob_path_by_handler_name.remove(&handler_name),
) {
(None, None) => {
warn!("Handler '{handler_name}' is not used by any key or knob.");
continue;
}
(a, b) => (a.unwrap_or_default(), b.unwrap_or_default()),
};
let mut command = Command::new(handlers_directory.join(handler_name.to_string()))
.arg("deckster-run")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
@ -56,28 +94,7 @@ pub async fn start(
let mut stdout_lines = BufReader::new(command.stdout.take().unwrap()).lines();
let mut stdin = command.stdin.unwrap();
let initial_handler_message = InitialHandlerMessage {
key_configs: key_configs
.iter()
.filter_map(|(path, c)| {
if *c.handler_name == handler_name {
Some((path.clone(), Arc::clone(&c.handler_config)))
} else {
None
}
})
.collect(),
knob_configs: knob_configs
.iter()
.filter_map(|(path, c)| {
if *c.handler_name == handler_name {
Some((path.clone(), Arc::clone(&c.handler_config)))
} else {
None
}
})
.collect(),
};
let initial_handler_message = InitialHandlerMessage { key_configs, knob_configs };
let serialized_message = serde_json::to_string(&initial_handler_message).unwrap().into_boxed_str().into_boxed_bytes();
@ -93,13 +110,13 @@ pub async fn start(
if let HandlerInitializationError::InvalidConfig { supports_keys, supports_knobs, .. } = error {
if !supports_keys && !initial_handler_message.key_configs.is_empty() {
return Err(eyre!(
"The '{handler_name}' handler does not support keys, but these keys tried to use it: {:?}",
initial_handler_message.key_configs.keys().map(|k| k.to_string()).collect::<String>()
"The '{handler_name}' handler does not support keys, but these keys tried to use it: {}",
initial_handler_message.key_configs.keys().map(|k| k.to_string()).join(", ")
));
} else if !supports_knobs && !initial_handler_message.knob_configs.is_empty() {
return Err(eyre!(
"The '{handler_name}' handler does not support knobs, but these knobs tried to use it: {:?}",
initial_handler_message.knob_configs.keys().map(|k| k.to_string()).collect::<String>()
"The '{handler_name}' handler does not support knobs, but these knobs tried to use it: {}",
initial_handler_message.knob_configs.keys().map(|k| k.to_string()).join(", ")
));
}
};
@ -121,29 +138,31 @@ pub async fn start(
}
});
handler_stdin_by_name.insert(handler_name.into_boxed_str(), stdin);
handler_stdin_by_name.insert(handler_name, stdin);
}
if let Some((handler_name, config_by_key_path)) = handler_config_by_key_path_by_handler_name.drain().next() {
return Err(eyre!(
"There is no executable file named '{handler_name}' in the handlers directory but these keys have it set as their handler: {}",
config_by_key_path.keys().join(", ")
));
}
if let Some((handler_name, config_by_knob_path)) = handler_config_by_knob_path_by_handler_name.drain().next() {
return Err(eyre!(
"There is no executable file named '{handler_name}' in the handlers directory but these knobs have it set as their handler: {}",
config_by_knob_path.keys().join(", ")
));
}
tokio::spawn(async move {
while let Ok(event) = events_receiver.recv().await {
let config = match &event {
HandlerEvent::Key { path, .. } => {
if let Some(config) = key_configs.get(path) {
config
} else {
continue;
}
}
HandlerEvent::Knob { path, .. } => {
if let Some(config) = knob_configs.get(path) {
config
} else {
continue;
}
}
let handler_name = match &event {
HandlerEvent::Key { path, .. } => handler_name_by_key_path.get(path).expect("every key must have a handler"),
HandlerEvent::Knob { path, .. } => handler_name_by_knob_path.get(path).expect("every knob must have a handler"),
};
let handler_stdin = handler_stdin_by_name.get_mut(&config.handler_name).expect("was already checked above");
let handler_stdin = handler_stdin_by_name.get_mut(handler_name).expect("was already checked above");
let serialized_event = serde_json::to_string(&event).unwrap().into_boxed_str().into_boxed_bytes();
handler_stdin.write_all(&serialized_event).await.unwrap();