diff options
Diffstat (limited to 'pulse/src')
| -rw-r--r-- | pulse/src/communication.rs | 28 | ||||
| -rw-r--r-- | pulse/src/config.rs | 12 | ||||
| -rw-r--r-- | pulse/src/runnable/mod.rs (renamed from pulse/src/runnable.rs) | 14 | ||||
| -rw-r--r-- | pulse/src/runnable/pulse.rs | 80 |
4 files changed, 112 insertions, 22 deletions
diff --git a/pulse/src/communication.rs b/pulse/src/communication.rs index c8578b7..cfb800e 100644 --- a/pulse/src/communication.rs +++ b/pulse/src/communication.rs @@ -1,5 +1,5 @@ use std::sync::mpsc::*; -use crate::runnable::*; +use crate::runnable::pulse::*; use swaystatus_plugin::*; pub enum MessagesFromMain { @@ -9,21 +9,27 @@ pub enum MessagesFromMain { pub struct SenderForMain { sender : Sender<MessagesFromMain>, - pulse_loop : std::sync::Arc<PulseMainLoop> + pulse_waker : PulseWakeUp } impl<'p> SenderForMain { - pub fn new(sender : Sender<MessagesFromMain>, pulse_loop : std::sync::Arc<PulseMainLoop>) -> Self { + pub fn new(sender : Sender<MessagesFromMain>, pulse_waker : PulseWakeUp) -> Self { SenderForMain{ sender, - pulse_loop + pulse_waker } } - fn send(&self, message : MessagesFromMain) -> Result<(), PluginCommunicationError> - { - //TODO!!!! - Ok(()) + fn send(&self, message : MessagesFromMain) -> Result<(), PluginCommunicationError> { + if let Ok(_) = self.sender.send(message) { + //The cool thing about pulse using poll() is that poll() also wakes up if started after + //the actual wake up call. So no need to worry about races, this is inherently sane! + self.pulse_waker.wake_up()?; + Ok(()) + } + else { + Err(PluginCommunicationError{}) + } } } @@ -35,3 +41,9 @@ impl MsgMainToModule for SenderForMain { self.send(MessagesFromMain::Refresh) } } + +impl From<PulseWakeUpError> for PluginCommunicationError { + fn from(error : PulseWakeUpError) -> Self { + PluginCommunicationError {} + } +} diff --git a/pulse/src/config.rs b/pulse/src/config.rs index 77b2a22..efe5803 100644 --- a/pulse/src/config.rs +++ b/pulse/src/config.rs @@ -5,7 +5,7 @@ use swaystatus_plugin::*; #[derive(Serialize, Deserialize)] #[serde(tag = "Sink")] -enum Sink { +pub enum Sink { Default, Specific { sink_name : String @@ -14,7 +14,7 @@ enum Sink { #[derive(Serialize, Deserialize)] #[serde(tag = "Format")] -enum Volume { +pub enum Volume { Off, Numeric { label : String @@ -27,7 +27,7 @@ enum Volume { #[derive(Serialize, Deserialize)] #[serde(tag = "Format")] -enum Balance { +pub enum Balance { Off, Numeric { label : String @@ -42,9 +42,9 @@ enum Balance { #[derive(Serialize, Deserialize)] #[serde(rename_all = "PascalCase", default)] pub struct PulseVolumeConfig { - sink : Sink, - volume : Volume, - balance : Balance + pub sink : Sink, + pub volume : Volume, + pub balance : Balance } impl Default for PulseVolumeConfig { diff --git a/pulse/src/runnable.rs b/pulse/src/runnable/mod.rs index d77a132..612bca9 100644 --- a/pulse/src/runnable.rs +++ b/pulse/src/runnable/mod.rs @@ -3,24 +3,26 @@ use crate::config::*; use swaystatus_plugin::*; use crate::communication::*; +pub mod pulse; +use pulse::Pulse; + pub struct PulseVolumeRunnable<'p> { config : &'p PulseVolumeConfig, to_main : Box<dyn MsgModuleToMain + 'p>, from_main : Receiver<MessagesFromMain>, - pulse : std::sync::Arc<PulseMainLoop> + pulse : Pulse } impl<'p : 's, 's> PulseVolumeRunnable<'p> { pub fn new(config : &'p PulseVolumeConfig, to_main : Box<dyn MsgModuleToMain + 'p>) -> (Self, SenderForMain) { let (s, r) = channel(); - let pulse = std::sync::Arc::new(PulseMainLoop {});//TODO: initialize this properly let result = PulseVolumeRunnable { config, to_main, from_main : r, - pulse: pulse.clone() + pulse: Pulse::init(&config.sink), }; - let sender = SenderForMain::new(s, pulse); + let sender = SenderForMain::new(s, result.pulse.get_wake_up()); (result, sender) } } @@ -30,7 +32,3 @@ impl<'p> SwayStatusModuleRunnable for PulseVolumeRunnable<'p> { //TODO } } - -pub struct PulseMainLoop { - //TODO! -} diff --git a/pulse/src/runnable/pulse.rs b/pulse/src/runnable/pulse.rs new file mode 100644 index 0000000..388617f --- /dev/null +++ b/pulse/src/runnable/pulse.rs @@ -0,0 +1,80 @@ +//! This module encapsulates all interaction with pulseaudio. +//! It is the only place in this plugin that explicitly includes unsafe code. + +use std::sync::{Arc, Weak}; +use crate::config::Sink; +use std::marker::PhantomData; + +pub(super) struct Pulse { + main_loop : Arc<PulseMainLoop>, //Beware: Never ever clone this except for PulseWakeUp! + + //Pulse is _definitely_ not sync. It is supposed to be Send though. + marker : PhantomData<std::cell::RefCell<i8>> +} + +//In general it's a really stupid idea to Send Arcs of non-Sync types around. +//However this Arc is only cloned under one very specific condition: creating a PulseWakeUp. +//Apart from that one (thread-safe) exception it's actually single-ownership. +unsafe impl Send for Pulse {} + +impl Pulse { + pub fn init(config : &Sink) -> Self { + //TODO:Use the sink parameter. + Pulse { + //TODO: Initialize main_loop on the pulse-side. + main_loop : Arc::new(PulseMainLoop {}), + marker :PhantomData + } + } + pub fn get_wake_up(&self) -> PulseWakeUp { + PulseWakeUp { main_loop : Arc::downgrade(&self.main_loop) } + } +} + +/// Helper to wake up the Pulseaudio main loop. +/// This implements Send and Sync, because it's explicitly meant to allow cross-thread +/// communication. It only exposes the wake up function of pulse, and that function is in itself +/// send and sync (https://github.com/pulseaudio/pulseaudio/blob/master/src/pulse/mainloop.c). +pub struct PulseWakeUp { + main_loop : Weak<PulseMainLoop> +} +unsafe impl Send for PulseWakeUp {} +unsafe impl Sync for PulseWakeUp {} + +impl PulseWakeUp { + pub fn wake_up(&self) -> Result<(), PulseWakeUpError> { + match self.main_loop.upgrade() { + Some(main_loop) => { + main_loop.awaken(); + Ok(()) + } + None => { + Err(PulseWakeUpError {} ) + } + } + } +} + +#[derive(Debug)] +pub struct PulseWakeUpError; +impl std::error::Error for PulseWakeUpError {} +impl std::fmt::Display for PulseWakeUpError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f,"Failed to wake up Pulse main loop. It has been dropped already.") + } +} + +struct PulseMainLoop { + //TODO +} +impl PulseMainLoop { + //this is intentionally all private. Nobody outside this module should call anything on this. + fn awaken(&self) { + //TODO! + } +} +impl Drop for PulseMainLoop { + fn drop(&mut self) { + //TODO: if this gets dropped, free the pulse main loop. + } +} |
