aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Grois <andi@grois.info>2021-05-02 16:01:55 +0200
committerAndreas Grois <andi@grois.info>2021-05-02 16:01:55 +0200
commitd2fd2da41dbc9c2f19711c14cd68142d5484eb40 (patch)
tree799ae403bd62b37a5011680745d3c2e10d71f5b9
parent18a96163d730b7e44270603a4c6de071b725db5f (diff)
Factor out all unsafe code into its own module (unfinished)
-rw-r--r--pulse/src/communication.rs28
-rw-r--r--pulse/src/config.rs12
-rw-r--r--pulse/src/runnable/mod.rs (renamed from pulse/src/runnable.rs)14
-rw-r--r--pulse/src/runnable/pulse.rs80
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.
+ }
+}