diff --git a/Cargo.lock b/Cargo.lock index 471b542..92afa25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -624,6 +624,19 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "execute" +version = "0.1.0" +dependencies = [ + "clap", + "color-eyre", + "deckster_mode", + "env_logger", + "log", + "serde", + "tokio", +] + [[package]] name = "eyre" version = "0.6.11" diff --git a/examples/full/key-pages/default.toml b/examples/full/key-pages/default.toml index 5260e22..21a2a4b 100644 --- a/examples/full/key-pages/default.toml +++ b/examples/full/key-pages/default.toml @@ -58,4 +58,11 @@ config.mode = "toggle" config.entity_id = "light.moritz_zimmer_stehlampe" config.style.on.icon = "@ph/computer-tower[color=#58fc11]" config.style.disconnected.icon = "@ph/computer-tower[alpha=0.2]" -config.disconnected_state = "disconnected" \ No newline at end of file +config.disconnected_state = "disconnected" + +[keys.4x1] +label = "9" + +handler = "execute" +config.program = "wtype" +config.args = ["9"] \ No newline at end of file diff --git a/handlers/execute/Cargo.toml b/handlers/execute/Cargo.toml new file mode 100644 index 0000000..98b8dc2 --- /dev/null +++ b/handlers/execute/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "execute" +version = "0.1.0" +edition = "2021" + +[dependencies] +deckster_mode = { path = "../../crates/deckster_mode" } +clap = { version = "4.4.18", features = ["derive"] } +color-eyre = "0.6.2" +serde = { version = "1.0.196", features = ["derive"] } +env_logger = "0.11.1" +log = "0.4.20" +tokio = { version = "1.35.1", features = ["macros", "parking_lot", "rt", "process", "sync"] } \ No newline at end of file diff --git a/handlers/execute/src/handler.rs b/handlers/execute/src/handler.rs new file mode 100644 index 0000000..e32e337 --- /dev/null +++ b/handlers/execute/src/handler.rs @@ -0,0 +1,82 @@ +use deckster_mode::shared::handler_communication::{HandlerEvent, HandlerInitializationError, InitialHandlerMessage, KeyEvent}; +use deckster_mode::shared::path::KeyPath; +use deckster_mode::DecksterHandler; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; +use std::thread; +use tokio::process::Command; +use tokio::sync::broadcast; +use tokio::task::LocalSet; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct KeyConfig { + program: Box, + #[serde(default)] + args: Box<[String]>, +} + +pub struct Handler { + events_sender: broadcast::Sender<(KeyPath, KeyEvent)>, +} + +impl Handler { + pub fn new(data: InitialHandlerMessage<(), KeyConfig, ()>) -> Result { + let (events_sender, _) = broadcast::channel::<(KeyPath, KeyEvent)>(5); + + thread::spawn({ + let events_sender = events_sender.clone(); + + move || { + let runtime = tokio::runtime::Builder::new_current_thread().enable_io().build().unwrap(); + let task_set = LocalSet::new(); + + for (path, config) in data.key_configs { + task_set.spawn_local(manage_key(events_sender.subscribe(), path, config)); + } + + runtime.block_on(task_set) + } + }); + + Ok(Handler { events_sender }) + } +} + +impl DecksterHandler for Handler { + fn handle(&mut self, event: HandlerEvent) { + if let HandlerEvent::Key { path, event } = event { + self.events_sender.send((path, event)).unwrap(); + } + } +} + +pub async fn manage_key(mut events: broadcast::Receiver<(KeyPath, KeyEvent)>, path: KeyPath, config: KeyConfig) { + let mut command = Command::new(config.program.as_ref()); + let command = command.args(config.args.clone().iter()); + + while let Ok((p, event)) = events.recv().await { + if p != path { + continue; + } + + if event == KeyEvent::Press { + tokio::spawn({ + let future = command.status(); + let path = path.clone(); + + async move { + let result = future.await; + + match result { + Ok(status) => { + if !status.success() { + log::error!("Process failed with status (key at {}): {}", &path, status) + } + } + Err(error) => log::error!("Failed to spawn process (key at {}): {}", &path, error), + } + } + }); + } + } +} diff --git a/handlers/execute/src/main.rs b/handlers/execute/src/main.rs new file mode 100644 index 0000000..e6118ea --- /dev/null +++ b/handlers/execute/src/main.rs @@ -0,0 +1,26 @@ +use clap::Parser; +use color_eyre::Result; + +use crate::handler::Handler; + +mod handler; + +#[derive(Debug, Parser)] +#[command(name = "execute")] +enum CliCommand { + #[command(name = "deckster-run", hide = true)] + Run, +} + +fn main() -> Result<()> { + env_logger::init(); + let command = CliCommand::parse(); + + match command { + CliCommand::Run => { + deckster_mode::run(Handler::new)?; + } + } + + Ok(()) +}