picoKontroller/src/controller.rs

128 lines
4.3 KiB
Rust

use crate::config::NanoKeys;
use crate::config::{Config, KeyMapVariant, MprisAction};
use crate::interaction_server::InteractionServer;
use crate::midi_client::KeyEvent;
use crate::mpris_client;
use crate::pulse_controller::{PulseController, PulseMessage};
use crossbeam_channel::{unbounded, Receiver, Sender};
use log::{error, info, warn};
use std::process::{Command, Output};
use std::sync::Arc;
use std::thread;
pub fn run(in_channel: Receiver<KeyEvent>, config: Arc<Config>) {
let (sender, receiver) = unbounded::<PulseMessage>();
let pulse = PulseController::new(receiver);
pulse.start();
thread::spawn(move || {
exec(in_channel, sender, config);
});
}
fn exec(in_channel: Receiver<KeyEvent>, pulse_channel: Sender<PulseMessage>, config: Arc<Config>) {
loop {
let event_in = in_channel.recv();
match event_in {
Ok((key, state)) => {
eval(key, state, &config, &pulse_channel);
}
Err(err) => {
error!("Failed receiving event in controller thread: {err}");
}
}
}
}
fn execute_binary_functionality<F>(player_ids: &Option<Vec<String>>, state: u8, exec_closure: F)
where
F: Fn(Option<&str>) -> (),
{
if is_logical_true(state) {
return;
}
match player_ids {
Some(ids) => ids.iter().for_each(|id| exec_closure(Option::Some(id))),
None => exec_closure(Option::None),
}
}
fn eval(key: NanoKeys, state: u8, config: &Arc<Config>, pulse_channel: &Sender<PulseMessage>) {
match config.keymap.get(&key) {
Some(actions) => {
info!(
"Registered actions for [{:?} | {}]: {:?}",
key, state, actions
);
for action in actions {
match action {
KeyMapVariant::Mpris { ids, action } => parse_mpris_action(state, ids, action),
KeyMapVariant::PulseAudio { ids, action } => {
//TODO: maybe clean up?
let _ = pulse_channel.send((Arc::new(ids.to_vec()), action.clone(), state));
}
KeyMapVariant::Exec { command, args } => parse_exec_action(command, args),
}
}
}
None => {
warn!("Midi input {:?} not mapped", key);
}
}
}
fn parse_mpris_action(state: u8, ids: &Option<Vec<String>>, action: &MprisAction) {
match action {
MprisAction::Play => execute_binary_functionality(ids, state, |player_id: Option<&str>| {
mpris_client::play(player_id)
}),
MprisAction::Pause => {
execute_binary_functionality(ids, state, |player_id: Option<&str>| {
mpris_client::pause(player_id)
})
}
MprisAction::PlayPause => {
execute_binary_functionality(ids, state, |player_id: Option<&str>| {
mpris_client::play_pause(player_id)
})
}
MprisAction::Stop => execute_binary_functionality(ids, state, |player_id: Option<&str>| {
mpris_client::stop(player_id)
}),
MprisAction::Next => execute_binary_functionality(ids, state, |player_id: Option<&str>| {
mpris_client::next(player_id)
}),
MprisAction::Previous => {
execute_binary_functionality(ids, state, |player_id: Option<&str>| {
mpris_client::previous(player_id)
})
}
MprisAction::Volume => {
let volume = state as f64 / 100.0;
match ids {
Some(some_ids) => some_ids
.iter()
.for_each(|id| mpris_client::set_volume(Option::Some(id), volume)),
None => mpris_client::set_volume(Option::None, volume),
}
}
}
}
fn parse_exec_action(command: &str, args: &Option<Vec<String>>) {
let output: Result<Output, std::io::Error> = match args {
Some(some_args) => Command::new(command).args(some_args).output(),
None => Command::new(command).output(),
};
match output {
Ok(out) => {
info!("{:?}", out);
}
Err(err) => {
error!("{:?}", err);
}
}
}
fn is_logical_true(state: u8) -> bool {
return state == 127;
}