Compare commits

...

15 Commits

Author SHA1 Message Date
409
cdb2cd5144 Merge branch 'dev' 2024-07-03 14:42:48 +02:00
409
7d810a603a docs: add hint to byte 5 in the table 2024-07-03 14:41:03 +02:00
409
0107ad88f3 Squashed commit of the following:
commit edef922902
Author: 409 <bloecker.s@gmail.com>
Date:   Mon Jul 1 13:22:02 2024 +0200

    feat: automatically select new sink input if the current selection is
    none
2024-07-01 13:26:08 +02:00
409
edef922902 feat: automatically select new sink input if the current selection is
none
2024-07-01 13:22:02 +02:00
409
f31e7ee894 chore: README.md 2024-06-30 13:52:58 +02:00
409
a08728250a Merge branch 'dev' 2024-06-29 22:20:29 +02:00
409
3f083d3ee5 feat: --silent flag to disable notifications 2024-06-29 22:19:53 +02:00
409
94338d2e37 fix: sluggish controls 2024-06-29 22:08:09 +02:00
409
0e87327f06 Merge branch 'dev' 2024-06-29 21:36:44 +02:00
409
4db37a1107 feat: GetCurrentOutput instruction 2024-06-29 21:36:08 +02:00
409
13d2579419 Merge branch 'dev' 2024-06-29 13:45:44 +02:00
409
eeea6938ad Merge branch 'dev' 2024-06-29 13:07:05 +02:00
409
28ac91bca2 Merge branch 'dev' 2024-06-29 13:05:37 +02:00
409
cb413e8774 Merge branch 'dev' 2024-06-29 12:50:21 +02:00
409
20083d1f60 Squashed commit of the following:
commit 2c3dc3a3c9
Author: 409 <bloecker.s@gmail.com>
Date:   Sat Jun 29 12:37:17 2024 +0200

    feat!: search playerctl players using simsearch
2024-06-29 12:43:10 +02:00
5 changed files with 116 additions and 33 deletions

View File

@@ -8,7 +8,7 @@ There is currently no convenient way to install Mixrs. To use this application,
## Requirements
- [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/)
- [playerctl](https://wiki.archlinux.org/title/MPRIS#Playerctl)
- [libnotify](https://gitlab.gnome.org/GNOME/libnotify)
- [libnotify](https://gitlab.gnome.org/GNOME/libnotify) (required unless started with `--silent`)
## Usage
Mixrs will create a unix socket at `/tmp/mixrs` and listen for instructions. Instructions are issued by sending a specific byte to the socket.
@@ -24,7 +24,8 @@ Mixrs will create a unix socket at `/tmp/mixrs` and listen for instructions. Ins
|2|ToggleMuteCurrent|Toggles the current sink input's muted state|
|3|IncreaseCurrent|Increases the current sink input's volume by 5%|
|4|DecreaseCurrent|Decreases the current sink input's volume by 5%|
|5|GetCurrent|Displays the current sink input's name|
|5|GetCurrent|Displays the current sink input's name<br>*Has no effect when using `--silent`*|
|6|PlayPauseCurrent|Tells the current sink input to toggle its `playing` state.<br>*Behavior varies based on the current sink input's player*|
|7|PlayNext|Tells the current sink input to play the next item (e.g. the next song).<br>*Behavior varies based on the current sink input's player*|
|8|PlayPrevious|Tells the current sink input to play the previous item (e.g. the previous song).<br>*Behavior varies based on the current sink input's player*|
|9|GetCurrentOutput|Gets information about the currently selected sink input and sends it through the requesting unix socket|

View File

@@ -9,6 +9,7 @@ pub enum MixerInstruction {
PlayPauseCurrent,
PlayNext,
PlayPrevious,
GetCurrentOutput,
}
impl MixerInstruction {
@@ -23,6 +24,7 @@ impl MixerInstruction {
6 => Some(MixerInstruction::PlayPauseCurrent),
7 => Some(MixerInstruction::PlayNext),
8 => Some(MixerInstruction::PlayPrevious),
9 => Some(MixerInstruction::GetCurrentOutput),
_ => None,
}
}

View File

@@ -1,19 +1,26 @@
mod instructions;
pub mod mixer;
pub mod playerctl;
pub mod pulseaudio;
pub mod utils;
pub mod playerctl;
use mixer::Mixer;
use pulseaudio::PulseInstruction;
use std::sync::mpsc::channel;
use std::{env, sync::mpsc::channel};
fn main() {
let mainloop = pulse::mainloop::standard::Mainloop::new().expect("Error getting PulseAudio main loop");
let mainloop =
pulse::mainloop::standard::Mainloop::new().expect("Error getting PulseAudio main loop");
let args: Vec<String> = env::args().collect();
let silent_mode = match args.iter().nth(1) {
Some(arg) => arg == "--silent",
None => false,
};
let (pulse_ix_tx, pulse_ix_rx) = channel::<PulseInstruction>();
let mut mixer = Mixer::new(mainloop, pulse_ix_tx);
let mut mixer = Mixer::new(mainloop, pulse_ix_tx, silent_mode);
mixer.run(pulse_ix_rx);
}

View File

@@ -4,9 +4,10 @@ use std::{
borrow::{Borrow, BorrowMut},
collections::HashMap,
fs,
io::Read,
os::unix::net::UnixListener,
io::{Read, Write},
os::unix::net::{UnixListener, UnixStream},
path::Path,
process::exit,
sync::{
mpsc::{channel, Receiver, Sender},
Arc, Mutex,
@@ -41,10 +42,15 @@ pub struct Mixer {
selected_index: Arc<Mutex<Option<usize>>>,
mainloop: Mainloop,
context: pulse::context::Context,
silent_mode: bool,
}
impl Mixer {
pub fn new(mut mainloop: Mainloop, pulse_ix_tx: Sender<PulseInstruction>) -> Self {
pub fn new(
mut mainloop: Mainloop,
pulse_ix_tx: Sender<PulseInstruction>,
silent_mode: bool,
) -> Self {
let mut context =
pulse::context::Context::new(&mainloop, "Mixrs").expect("Error creating pulse context");
@@ -104,6 +110,7 @@ impl Mixer {
selected_index,
mainloop,
context,
silent_mode,
}
}
@@ -124,7 +131,7 @@ impl Mixer {
.create_socket_listener()
.expect("Error creating unix socket listener");
let (mixer_tx, mixer_rx) = channel::<MixerInstruction>();
let (mixer_tx, mixer_rx) = channel::<(MixerInstruction, UnixStream)>();
thread::spawn(move || {
for client in listener.incoming() {
@@ -134,7 +141,7 @@ impl Mixer {
stream.read_to_end(&mut buf).expect("Error reading stream");
match MixerInstruction::from_u8(buf[0]) {
Some(ix) => mixer_tx.send(ix).unwrap(),
Some(ix) => mixer_tx.send((ix, stream)).unwrap(),
None => println!("Invalid instruction: {}", buf[0]),
}
}
@@ -181,7 +188,7 @@ impl Mixer {
loop {
match mixer_rx.try_recv() {
Ok(ix) => match ix {
Ok((ix, stream)) => match ix {
MixerInstruction::SelectNext => self.select_next(),
MixerInstruction::SelectPrevious => self.select_previous(),
MixerInstruction::ToggleMuteCurrent => self.toggle_mute_current(),
@@ -191,6 +198,7 @@ impl Mixer {
MixerInstruction::PlayPauseCurrent => self.play_pause_current(),
MixerInstruction::PlayNext => self.play_next_current(),
MixerInstruction::PlayPrevious => self.play_previous_current(),
MixerInstruction::GetCurrentOutput => self.get_current_output(stream),
},
Err(_) => (),
}
@@ -229,6 +237,10 @@ impl Mixer {
let sink_input = result.lock().unwrap().take();
if let Some(sink_input) = sink_input {
self.sink_inputs.insert(sink_index, sink_input);
if self.selected_index.lock().unwrap().is_none() {
self.select_next();
}
}
}
PulseInstruction::RemoveSinkInput(sink_index) => {
@@ -421,15 +433,18 @@ impl Mixer {
.set_sink_input_volume(
*sink_index,
&volume,
Some(Box::new(move |success| {
if success {
let volume = volume_to_percentage(volume);
let _ = send_notification_with_progress(
&format!("{sink_name}: {}%", volume),
volume,
);
}
})),
match self.silent_mode {
true => None,
false => Some(Box::new(move |success| {
if success {
let volume = volume_to_percentage(volume);
let _ = send_notification_with_progress(
&format!("{sink_name}: {}%", volume),
volume,
);
}
})),
},
);
}
@@ -464,19 +479,26 @@ impl Mixer {
.set_sink_input_volume(
*sink_index,
&volume,
Some(Box::new(move |success| {
if success {
let volume = volume_to_percentage(volume);
let _ = send_notification_with_progress(
&format!("{sink_name}: {}%", volume),
volume,
);
}
})),
match self.silent_mode {
true => None,
false => Some(Box::new(move |success| {
if success {
let volume = volume_to_percentage(volume);
let _ = send_notification_with_progress(
&format!("{sink_name}: {}%", volume),
volume,
);
}
})),
},
);
}
pub fn get_current(&self) {
if self.silent_mode {
return;
}
let index_lock = self.selected_index.lock().unwrap();
let Some(index) = *index_lock else {
@@ -495,7 +517,10 @@ impl Mixer {
let _ = send_notification_with_progress(
&format!(
"({}/{}) {}: {}%",
index + 1, sink_inputs_length, &current_sink.name, current_sink_volume_percent
index + 1,
sink_inputs_length,
&current_sink.name,
current_sink_volume_percent
),
current_sink_volume_percent,
);
@@ -560,9 +585,44 @@ impl Mixer {
Err(_) => (),
};
}
pub fn get_current_output(&self, mut stream: UnixStream) {
let index_lock = self.selected_index.lock().unwrap();
let Some(index) = *index_lock else {
return;
};
drop(index_lock);
let Some(sink_index) = self.sink_inputs.keys().nth(index) else {
return;
};
let Some(sink_input) = self.sink_inputs.get(sink_index) else {
return;
};
let _ = stream.write_all(
sink_input
.get_output_data(index, self.sink_inputs.len(), *sink_index)
.as_bytes(),
);
let _ = stream.shutdown(std::net::Shutdown::Both);
}
}
pub fn iterate_mainloop(mainloop: &mut pulse::mainloop::standard::Mainloop) {
mainloop.borrow_mut().iterate(false);
thread::sleep(Duration::from_millis(5));
match mainloop.borrow_mut().iterate(false) {
IterateResult::Success(s) => {
if s == 0 {
thread::sleep(Duration::from_millis(5));
}
}
IterateResult::Quit(_) => exit(0),
IterateResult::Err(e) => {
println!("Err: {:?}", e);
exit(1);
}
};
}

View File

@@ -29,4 +29,17 @@ impl SinkInputMixerData {
pub fn get_volume_percent(&self) -> u8 {
total_volume_to_percentage(self.volume)
}
/// Formats the sink input data to a string separating fields by new lines
pub fn get_output_data(
&self,
selection_index: usize,
sink_count: usize,
sink_index: u32,
) -> String {
format!(
"selection: {}/{sink_count}\nid: {sink_index}\nname: {}\nvolume: {}\nvolume_percentage: {}\nmuted: {}\n",
selection_index + 1, self.name, self.volume, self.get_volume_percent(), self.muted
)
}
}