Switch to client/controller architecture
All checks were successful
Continuous integration / Build and test (push) Successful in 46s
All checks were successful
Continuous integration / Build and test (push) Successful in 46s
This commit is contained in:
parent
bc488b7565
commit
db797623ea
9 changed files with 362 additions and 249 deletions
|
@ -13,7 +13,8 @@ pub struct MidiClient {
|
|||
pub out_channel: Sender<KeyEvent>,
|
||||
}
|
||||
|
||||
pub type KeyEvent = (NanoKeys, u8);
|
||||
pub type KeyEvent = (NanoKeys, KeyState);
|
||||
pub type KeyState = u8;
|
||||
|
||||
impl InteractionServer for MidiClient {
|
||||
fn start(self) -> JoinHandle<()> {
|
|
@ -93,7 +93,7 @@ pub fn write_default(path: &PathBuf) -> () {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum MprisAction {
|
||||
Play,
|
||||
Pause,
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
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;
|
||||
}
|
57
src/controllers/exec.rs
Normal file
57
src/controllers/exec.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use crate::interaction_server::InteractionServer;
|
||||
use crossbeam_channel::Receiver;
|
||||
use std::process::{Command, Output};
|
||||
use log::{error, info};
|
||||
use std::{
|
||||
sync::Arc,
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
|
||||
pub type ExecMessage = (Arc<String>, Arc<Option<Vec<String>>>);
|
||||
|
||||
pub struct ExecController {
|
||||
pub in_channel: Receiver<ExecMessage>,
|
||||
}
|
||||
|
||||
impl InteractionServer for ExecController {
|
||||
fn start(self) -> JoinHandle<()> {
|
||||
thread::spawn(move || {
|
||||
self.exec();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecController {
|
||||
pub fn new(in_channel: Receiver<ExecMessage>) -> ExecController {
|
||||
ExecController { in_channel }
|
||||
}
|
||||
|
||||
fn exec(self) {
|
||||
loop {
|
||||
let event_in = self.in_channel.recv();
|
||||
match event_in {
|
||||
Ok((command, options)) => {
|
||||
parse_exec_action(&*command, &*options);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed receiving event in controller thread: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
197
src/controllers/mpris.rs
Normal file
197
src/controllers/mpris.rs
Normal file
|
@ -0,0 +1,197 @@
|
|||
use crate::config::MprisAction;
|
||||
use crossbeam_channel::Receiver;
|
||||
use itertools::Itertools;
|
||||
use log::{error, warn};
|
||||
use mpris::Player;
|
||||
use mpris::PlayerFinder;
|
||||
use regex::Regex;
|
||||
use std::sync::Arc;
|
||||
use std::thread::{self, JoinHandle};
|
||||
|
||||
use crate::interaction_server::InteractionServer;
|
||||
|
||||
pub type MprisMessage = (Arc<Option<Vec<MprisId>>>, MprisAction, MprisState);
|
||||
pub type MprisState = u8;
|
||||
pub type MprisId = String;
|
||||
|
||||
pub struct MprisController {
|
||||
pub in_channel: Receiver<MprisMessage>,
|
||||
}
|
||||
|
||||
impl InteractionServer for MprisController {
|
||||
fn start(self) -> JoinHandle<()> {
|
||||
thread::spawn(move || {
|
||||
self.exec();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl MprisController {
|
||||
pub fn new(in_channel: Receiver<MprisMessage>) -> MprisController {
|
||||
MprisController { in_channel }
|
||||
}
|
||||
|
||||
fn exec(self) {
|
||||
loop {
|
||||
let event_in = self.in_channel.recv();
|
||||
match event_in {
|
||||
Ok((ids, action, state)) => {
|
||||
parse_mpris_action(state, ids, &action);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed receiving event in controller thread: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_mpris_action(state: MprisState, ids: Arc<Option<Vec<MprisId>>>, action: &MprisAction) {
|
||||
match action {
|
||||
MprisAction::Play => {
|
||||
execute_binary_functionality(ids, state, |player_id: Option<&str>| play(player_id))
|
||||
}
|
||||
MprisAction::Pause => {
|
||||
execute_binary_functionality(ids, state, |player_id: Option<&str>| pause(player_id))
|
||||
}
|
||||
MprisAction::PlayPause => {
|
||||
execute_binary_functionality(ids, state, |player_id: Option<&str>| {
|
||||
play_pause(player_id)
|
||||
})
|
||||
}
|
||||
MprisAction::Stop => {
|
||||
execute_binary_functionality(ids, state, |player_id: Option<&str>| stop(player_id))
|
||||
}
|
||||
MprisAction::Next => {
|
||||
execute_binary_functionality(ids, state, |player_id: Option<&str>| next(player_id))
|
||||
}
|
||||
MprisAction::Previous => {
|
||||
execute_binary_functionality(ids, state, |player_id: Option<&str>| previous(player_id))
|
||||
}
|
||||
MprisAction::Volume => {
|
||||
let volume = state as f64 / 100.0;
|
||||
match &*ids {
|
||||
Some(some_ids) => some_ids
|
||||
.iter()
|
||||
.for_each(|id| set_volume(Option::Some(id), volume)),
|
||||
None => set_volume(Option::None, volume),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_binary_functionality<F>(player_ids: Arc<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 is_logical_true(state: u8) -> bool {
|
||||
return state == 127;
|
||||
}
|
||||
|
||||
pub fn list_players() -> () {
|
||||
println!("All available MPRIS players:");
|
||||
PlayerFinder::new()
|
||||
.expect("Could not connect to D-Bus")
|
||||
.find_all()
|
||||
.unwrap_or_else(|_| Vec::new())
|
||||
.iter()
|
||||
.map(|p| p.identity().to_owned())
|
||||
.dedup()
|
||||
.for_each(|i| println!("{}", i));
|
||||
}
|
||||
|
||||
fn for_all_players<F>(player_id: &str, function: F)
|
||||
where
|
||||
F: Fn(&Player) -> (),
|
||||
{
|
||||
let player_filter = Regex::new(player_id).expect("Creating RegEx failed");
|
||||
PlayerFinder::new()
|
||||
.expect("Could not connect to D-Bus")
|
||||
.find_all()
|
||||
.unwrap_or_else(|_| Vec::new())
|
||||
.into_iter()
|
||||
.filter(|player| player_filter.is_match(player.identity()))
|
||||
.into_iter()
|
||||
.for_each(|player| {
|
||||
function(&player);
|
||||
});
|
||||
}
|
||||
|
||||
fn for_active_player<F>(function: F)
|
||||
where
|
||||
F: Fn(&Player) -> (),
|
||||
{
|
||||
let player = PlayerFinder::new()
|
||||
.expect("Could not connect to D-Bus")
|
||||
.find_active();
|
||||
match player {
|
||||
Ok(p) => {
|
||||
function(&p);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Could not find an active player: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_functionality<F>(player_id: Option<&str>, exec_closure: F)
|
||||
where
|
||||
F: Fn(&Player) -> (),
|
||||
{
|
||||
match player_id {
|
||||
Some(id) => for_all_players(id, exec_closure),
|
||||
None => for_active_player(exec_closure),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.play();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn pause(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.pause();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn play_pause(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.play_pause();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn stop(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.stop();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn next(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.next();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn previous(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.previous();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_volume(player_id: Option<&str>, volume: f64) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.set_volume(volume);
|
||||
});
|
||||
}
|
|
@ -17,7 +17,9 @@ use std::thread::{self, JoinHandle};
|
|||
const APPLICATION_NAME: &str = "picoKontroller";
|
||||
const CONTEXT_NAME: &str = "picoKontrollerContext";
|
||||
|
||||
pub type PulseMessage = (Arc<Vec<String>>, PulseAction, u8);
|
||||
pub type PulseMessage = (Arc<Vec<PulseId>>, PulseAction, PulseState);
|
||||
pub type PulseState = u8;
|
||||
pub type PulseId = String;
|
||||
|
||||
pub struct PulseController {
|
||||
pub in_channel: Receiver<PulseMessage>,
|
||||
|
@ -121,8 +123,8 @@ impl PulseController {
|
|||
}
|
||||
|
||||
fn parse_pulse_action(
|
||||
state: u8,
|
||||
ids: Arc<Vec<String>>,
|
||||
state: PulseState,
|
||||
ids: Arc<Vec<PulseId>>,
|
||||
action: &PulseAction,
|
||||
mainloop: Arc<RefCell<Mainloop>>,
|
||||
context: Arc<RefCell<Context>>,
|
30
src/main.rs
30
src/main.rs
|
@ -1,15 +1,22 @@
|
|||
mod config;
|
||||
mod controller;
|
||||
mod router;
|
||||
mod interaction_server;
|
||||
mod midi_client;
|
||||
mod mpris_client;
|
||||
mod pulse_controller;
|
||||
|
||||
mod controllers {
|
||||
pub mod mpris;
|
||||
pub mod pulse;
|
||||
pub mod exec;
|
||||
}
|
||||
|
||||
mod clients {
|
||||
pub mod midi;
|
||||
}
|
||||
|
||||
use clap::Parser;
|
||||
use crossbeam_channel::unbounded;
|
||||
use directories::ProjectDirs;
|
||||
use interaction_server::InteractionServer;
|
||||
use midi_client::MidiClient;
|
||||
use clients::midi::MidiClient;
|
||||
use std::{path::PathBuf, process::exit, sync::Arc};
|
||||
|
||||
#[derive(Parser)]
|
||||
|
@ -17,10 +24,7 @@ use std::{path::PathBuf, process::exit, sync::Arc};
|
|||
struct Cli {
|
||||
#[arg(long, help = "List all available MIDI devices.")]
|
||||
list_midi: bool,
|
||||
#[arg(
|
||||
long,
|
||||
help = "List all available MPRIS players."
|
||||
)]
|
||||
#[arg(long, help = "List all available MPRIS players.")]
|
||||
list_mpris: bool,
|
||||
}
|
||||
|
||||
|
@ -29,10 +33,10 @@ fn main() {
|
|||
let cli = Cli::parse();
|
||||
|
||||
if cli.list_midi {
|
||||
midi_client::list_ports();
|
||||
clients::midi::list_ports();
|
||||
exit(0);
|
||||
} else if cli.list_mpris {
|
||||
mpris_client::list_players();
|
||||
controllers::mpris::list_players();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
@ -41,10 +45,10 @@ fn main() {
|
|||
let cfg: config::Config = config::init(path);
|
||||
let arc_cfg = Arc::new(cfg);
|
||||
|
||||
let (sender, receiver) = unbounded::<midi_client::KeyEvent>();
|
||||
let (sender, receiver) = unbounded::<clients::midi::KeyEvent>();
|
||||
let midi = MidiClient::new(arc_cfg.general.midi_device.clone(), sender);
|
||||
|
||||
controller::run(receiver, Arc::clone(&arc_cfg));
|
||||
router::run(receiver, Arc::clone(&arc_cfg));
|
||||
|
||||
let server = midi.start();
|
||||
let _ = server.join();
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
use itertools::Itertools;
|
||||
use log::warn;
|
||||
use mpris::Player;
|
||||
use mpris::PlayerFinder;
|
||||
use regex::Regex;
|
||||
|
||||
pub fn list_players() -> () {
|
||||
println!("All available MPRIS players:");
|
||||
PlayerFinder::new()
|
||||
.expect("Could not connect to D-Bus")
|
||||
.find_all()
|
||||
.unwrap_or_else(|_| Vec::new())
|
||||
.iter()
|
||||
.map(|p| p.identity().to_owned())
|
||||
.dedup()
|
||||
.for_each(|i| {println!("{}",i)});
|
||||
}
|
||||
|
||||
fn for_all_players<F>(player_id: &str, function: F)
|
||||
where
|
||||
F: Fn(&Player) -> (),
|
||||
{
|
||||
let player_filter = Regex::new(player_id).expect("Creating RegEx failed");
|
||||
PlayerFinder::new()
|
||||
.expect("Could not connect to D-Bus")
|
||||
.find_all()
|
||||
.unwrap_or_else(|_| Vec::new())
|
||||
.into_iter()
|
||||
.filter(|player| player_filter.is_match(player.identity()))
|
||||
.into_iter()
|
||||
.for_each(|player| {
|
||||
function(&player);
|
||||
});
|
||||
}
|
||||
|
||||
fn for_active_player<F>(function: F)
|
||||
where
|
||||
F: Fn(&Player) -> (),
|
||||
{
|
||||
let player = PlayerFinder::new()
|
||||
.expect("Could not connect to D-Bus")
|
||||
.find_active();
|
||||
match player {
|
||||
Ok(p) => {
|
||||
function(&p);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Could not find an active player: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_functionality<F>(player_id: Option<&str>, exec_closure: F)
|
||||
where
|
||||
F: Fn(&Player) -> (),
|
||||
{
|
||||
match player_id {
|
||||
Some(id) => for_all_players(id, exec_closure),
|
||||
None => for_active_player(exec_closure),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.play();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn pause(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.pause();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn play_pause(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.play_pause();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn stop(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.stop();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn next(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.next();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn previous(player_id: Option<&str>) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.previous();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_volume(player_id: Option<&str>, volume: f64) -> () {
|
||||
execute_functionality(player_id, |player: &Player| {
|
||||
let _ = player.set_volume(volume);
|
||||
});
|
||||
}
|
83
src/router.rs
Normal file
83
src/router.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use crate::config::NanoKeys;
|
||||
use crate::config::{Config, KeyMapVariant};
|
||||
use crate::controllers::exec::{ExecController, ExecMessage};
|
||||
use crate::interaction_server::InteractionServer;
|
||||
use crate::clients::midi::KeyEvent;
|
||||
use crate::controllers::mpris::{MprisController, MprisMessage};
|
||||
use crate::controllers::pulse::{PulseController, PulseMessage};
|
||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
use log::{error, info, warn};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
pub fn run(in_channel: Receiver<KeyEvent>, config: Arc<Config>) {
|
||||
let (pulse_sender, pulse_receiver) = unbounded::<PulseMessage>();
|
||||
let (mpris_sender, mpris_receiver) = unbounded::<MprisMessage>();
|
||||
let (exec_sender, exec_receiver) = unbounded::<ExecMessage>();
|
||||
|
||||
let pulse_interactor = PulseController::new(pulse_receiver);
|
||||
let mpris_interactor = MprisController::new(mpris_receiver);
|
||||
let exec_interactor = ExecController::new(exec_receiver);
|
||||
|
||||
pulse_interactor.start();
|
||||
mpris_interactor.start();
|
||||
exec_interactor.start();
|
||||
|
||||
thread::spawn(move || {
|
||||
exec(in_channel, pulse_sender, mpris_sender, exec_sender, config);
|
||||
});
|
||||
}
|
||||
|
||||
fn exec(
|
||||
in_channel: Receiver<KeyEvent>,
|
||||
pulse_channel: Sender<PulseMessage>,
|
||||
mpris_channel: Sender<MprisMessage>,
|
||||
exec_channel: Sender<ExecMessage>,
|
||||
config: Arc<Config>,
|
||||
) {
|
||||
loop {
|
||||
let event_in = in_channel.recv();
|
||||
match event_in {
|
||||
Ok((key, state)) => {
|
||||
eval(key, state, &config, &pulse_channel, &mpris_channel, &exec_channel);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed receiving event in controller thread: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eval(
|
||||
key: NanoKeys,
|
||||
state: u8,
|
||||
config: &Arc<Config>,
|
||||
pulse_channel: &Sender<PulseMessage>,
|
||||
mpris_channel: &Sender<MprisMessage>,
|
||||
exec_channel: &Sender<ExecMessage>,
|
||||
) {
|
||||
match config.keymap.get(&key) {
|
||||
Some(actions) => {
|
||||
info!(
|
||||
"Registered actions for [{:?} | {}]: {:?}",
|
||||
key, state, actions
|
||||
);
|
||||
for action in actions {
|
||||
match action {
|
||||
KeyMapVariant::Mpris { ids, action } => {
|
||||
let _ = mpris_channel.send((Arc::new(ids.to_owned()), action.clone(), state));
|
||||
}
|
||||
KeyMapVariant::PulseAudio { ids, action } => {
|
||||
let _ = pulse_channel.send((Arc::new(ids.to_owned()), action.clone(), state));
|
||||
}
|
||||
KeyMapVariant::Exec { command, args } => {
|
||||
let _ = exec_channel.send((Arc::new(command.clone()), Arc::new(args.clone())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
warn!("Midi input {:?} not mapped", key);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue