commit
This commit is contained in:
parent
6b4ea3f4ae
commit
fa8c988705
6 changed files with 95 additions and 66 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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]"
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue