Enable basic SinkInput midi control
This commit is contained in:
parent
0a663e97f2
commit
6617652839
3 changed files with 73 additions and 24 deletions
|
@ -104,6 +104,19 @@ pub enum MprisAction {
|
||||||
Volume,
|
Volume,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub enum PulseAction {
|
||||||
|
SinkVolume,
|
||||||
|
SinkMute,
|
||||||
|
SinkInputVolume,
|
||||||
|
SinkInputMute,
|
||||||
|
SourceVolume,
|
||||||
|
SourceMute,
|
||||||
|
SourceOutputVolume,
|
||||||
|
SourceOutputMute,
|
||||||
|
DefaultSink,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum KeyMapVariant {
|
pub enum KeyMapVariant {
|
||||||
|
@ -113,6 +126,7 @@ pub enum KeyMapVariant {
|
||||||
},
|
},
|
||||||
PulseAudio {
|
PulseAudio {
|
||||||
ids: Vec<String>,
|
ids: Vec<String>,
|
||||||
|
action: PulseAction,
|
||||||
},
|
},
|
||||||
Exec {
|
Exec {
|
||||||
command: String,
|
command: String,
|
||||||
|
|
|
@ -1,25 +1,28 @@
|
||||||
use crate::config::NanoKeys;
|
use crate::config::NanoKeys;
|
||||||
use crate::config::{Config, KeyMapVariant, MprisAction};
|
use crate::config::{Config, KeyMapVariant, MprisAction, PulseAction};
|
||||||
use crate::midi_client::KeyEvent;
|
use crate::midi_client::KeyEvent;
|
||||||
use crate::mpris_client;
|
use crate::mpris_client;
|
||||||
use crossbeam_channel::Receiver;
|
use crate::pulse_client::{self, PulseMessage};
|
||||||
|
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use std::process::{Command, Output};
|
use std::process::{Command, Output};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
pub fn run(in_channel: Receiver<KeyEvent>, config: Arc<Config>) {
|
pub fn run(in_channel: Receiver<KeyEvent>, config: Arc<Config>) {
|
||||||
|
let (sender, receiver) = unbounded::<PulseMessage>();
|
||||||
|
pulse_client::run(receiver);
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
exec(in_channel, config);
|
exec(in_channel, sender, config);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(in_channel: Receiver<KeyEvent>, config: Arc<Config>) {
|
fn exec(in_channel: Receiver<KeyEvent>, pulse_channel: Sender<PulseMessage>, config: Arc<Config>) {
|
||||||
loop {
|
loop {
|
||||||
let event_in = in_channel.recv();
|
let event_in = in_channel.recv();
|
||||||
match event_in {
|
match event_in {
|
||||||
Ok((key, state)) => {
|
Ok((key, state)) => {
|
||||||
eval(key, state, &config);
|
eval(key, state, &config, &pulse_channel);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed receiving event in controller thread: {err}");
|
error!("Failed receiving event in controller thread: {err}");
|
||||||
|
@ -41,7 +44,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(key: NanoKeys, state: u8, config: &Arc<Config>) {
|
fn eval(key: NanoKeys, state: u8, config: &Arc<Config>, pulse_channel: &Sender<PulseMessage>) {
|
||||||
match config.keymap.get(&key) {
|
match config.keymap.get(&key) {
|
||||||
Some(actions) => {
|
Some(actions) => {
|
||||||
info!(
|
info!(
|
||||||
|
@ -51,7 +54,10 @@ fn eval(key: NanoKeys, state: u8, config: &Arc<Config>) {
|
||||||
for action in actions {
|
for action in actions {
|
||||||
match action {
|
match action {
|
||||||
KeyMapVariant::Mpris { ids, action } => parse_mpris_action(state, ids, action),
|
KeyMapVariant::Mpris { ids, action } => parse_mpris_action(state, ids, action),
|
||||||
KeyMapVariant::PulseAudio { ids } => {}
|
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),
|
KeyMapVariant::Exec { command, args } => parse_exec_action(command, args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
extern crate libpulse_binding as pulse;
|
extern crate libpulse_binding as pulse;
|
||||||
|
|
||||||
|
use crate::config::{Config, KeyMapVariant, PulseAction};
|
||||||
|
use crossbeam_channel::Receiver;
|
||||||
|
use log::{error, info, warn};
|
||||||
use pulse::context::{Context, FlagSet as ContextFlagSet};
|
use pulse::context::{Context, FlagSet as ContextFlagSet};
|
||||||
use pulse::mainloop::threaded::Mainloop;
|
use pulse::mainloop::threaded::Mainloop;
|
||||||
use pulse::proplist::Proplist;
|
use pulse::proplist::Proplist;
|
||||||
|
@ -8,9 +11,16 @@ use regex::Regex;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time;
|
use std::thread;
|
||||||
|
|
||||||
pub fn run() {
|
pub type PulseMessage = (Arc<Vec<String>>, PulseAction, u8);
|
||||||
|
|
||||||
|
pub fn run(in_channel: Receiver<PulseMessage>) {
|
||||||
|
thread::spawn(move || {
|
||||||
|
exec(in_channel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn exec(in_channel: Receiver<PulseMessage>) {
|
||||||
let mut proplist = Proplist::new().unwrap();
|
let mut proplist = Proplist::new().unwrap();
|
||||||
proplist
|
proplist
|
||||||
.set_str(
|
.set_str(
|
||||||
|
@ -82,16 +92,38 @@ pub fn run() {
|
||||||
context.borrow_mut().set_state_callback(None);
|
context.borrow_mut().set_state_callback(None);
|
||||||
mainloop.borrow_mut().unlock();
|
mainloop.borrow_mut().unlock();
|
||||||
|
|
||||||
//---------------------
|
|
||||||
let ml = Arc::clone(&mainloop);
|
|
||||||
let ctx = Arc::clone(&context);
|
|
||||||
|
|
||||||
set_volume_sink_input(ml, ctx, "Firefox", 100);
|
|
||||||
//---------------------
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
//TODO: react to incoming commands here instead of infinite sleep
|
let ml = Arc::clone(&mainloop);
|
||||||
std::thread::sleep(time::Duration::from_secs(u64::MAX));
|
let ctx = Arc::clone(&context);
|
||||||
|
let event_in = in_channel.recv();
|
||||||
|
match event_in {
|
||||||
|
Ok((ids, action, state)) => {
|
||||||
|
parse_pulse_action(state, ids, &action, ml, ctx);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed receiving event in controller thread: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_pulse_action(
|
||||||
|
state: u8,
|
||||||
|
ids: Arc<Vec<String>>,
|
||||||
|
action: &PulseAction,
|
||||||
|
mainloop: Arc<RefCell<Mainloop>>,
|
||||||
|
context: Arc<RefCell<Context>>,
|
||||||
|
) {
|
||||||
|
match action {
|
||||||
|
PulseAction::SinkInputVolume => {
|
||||||
|
for id in ids.iter() {
|
||||||
|
set_volume_sink_input(mainloop.clone(), context.clone(), id, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PulseAction::DefaultSink => {
|
||||||
|
set_default_sink(mainloop.clone(), context.clone(), ids.get(0).unwrap());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +172,6 @@ fn for_all_sink_inputs(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let matches_regex = sink_filter.is_match(&application_name);
|
let matches_regex = sink_filter.is_match(&application_name);
|
||||||
|
|
||||||
println!("{:?}", application_name);
|
|
||||||
if matches_regex {
|
if matches_regex {
|
||||||
function(
|
function(
|
||||||
callback_context.clone(),
|
callback_context.clone(),
|
||||||
|
@ -176,7 +207,7 @@ pub fn set_volume_sink_input(
|
||||||
mainloop: Arc<RefCell<Mainloop>>,
|
mainloop: Arc<RefCell<Mainloop>>,
|
||||||
context: Arc<RefCell<Context>>,
|
context: Arc<RefCell<Context>>,
|
||||||
name: &str,
|
name: &str,
|
||||||
volume: i16,
|
volume: u8,
|
||||||
) {
|
) {
|
||||||
for_all_sink_inputs(
|
for_all_sink_inputs(
|
||||||
mainloop,
|
mainloop,
|
||||||
|
@ -197,12 +228,10 @@ pub fn set_volume_sink_input(
|
||||||
}
|
}
|
||||||
|
|
||||||
//taken from https://github.com/jantap/rsmixer
|
//taken from https://github.com/jantap/rsmixer
|
||||||
pub fn percent_to_volume(target_percent: i16) -> u32 {
|
pub fn percent_to_volume(target_percent: u8) -> u32 {
|
||||||
let base_delta = (volume::Volume::NORMAL.0 as f32 - volume::Volume::MUTED.0 as f32) / 100.0;
|
let base_delta = (volume::Volume::NORMAL.0 as f32 - volume::Volume::MUTED.0 as f32) / 100.0;
|
||||||
|
|
||||||
if target_percent < 0 {
|
if target_percent == 100 {
|
||||||
volume::Volume::MUTED.0
|
|
||||||
} else if target_percent == 100 {
|
|
||||||
volume::Volume::NORMAL.0
|
volume::Volume::NORMAL.0
|
||||||
} else if target_percent >= 150 {
|
} else if target_percent >= 150 {
|
||||||
(volume::Volume::NORMAL.0 as f32 * 1.5) as u32
|
(volume::Volume::NORMAL.0 as f32 * 1.5) as u32
|
||||||
|
|
Loading…
Reference in a new issue