aboutsummaryrefslogtreecommitdiff
path: root/pulse/src
diff options
context:
space:
mode:
authorAndreas Grois <andi@grois.info>2021-05-09 13:22:40 +0200
committerAndreas Grois <andi@grois.info>2021-05-09 13:22:40 +0200
commit6457d779ec253ade253fc49f5891b5479b270a6d (patch)
tree39adb3132c9d7439b62ad5c496c19ba6dfc7b7be /pulse/src
parent13573941cca891bcf7fa5b61b5816178658bc339 (diff)
Unfinished (doesn't compile) implementation of pulse runnable loop.
This is mainly as a note to my future self. A barebone implementation of the logic for the pulse runnable loop, where all details (meaning: functions to be called) are still not implemented - not even as signatures.
Diffstat (limited to 'pulse/src')
-rw-r--r--pulse/src/runnable/mod.rs106
-rw-r--r--pulse/src/runnable/pulse.rs56
2 files changed, 152 insertions, 10 deletions
diff --git a/pulse/src/runnable/mod.rs b/pulse/src/runnable/mod.rs
index 149e994..7286c31 100644
--- a/pulse/src/runnable/mod.rs
+++ b/pulse/src/runnable/mod.rs
@@ -2,9 +2,10 @@ use std::sync::mpsc::*;
use crate::config::*;
use swaystatus_plugin::*;
use crate::communication::*;
+use std::convert::TryFrom;
pub mod pulse;
-use pulse::{Pulse,MainLoopCreationError};
+use pulse::{Pulse,MainLoopCreationError, PulseContext, PaContextState, SinkHandle};
pub struct PulseVolumeRunnable<'p> {
config : &'p PulseVolumeConfig,
@@ -25,10 +26,111 @@ impl<'p : 's, 's> PulseVolumeRunnable<'p> {
let sender = SenderForMain::new(s, result.pulse.as_ref().map_or(None,|x| Some(x.get_wake_up())));
(result, sender)
}
+ fn send_error_to_main<E>(&self, err : E) where E : std::error::Error {
+ self.to_main.send_update(Err(PluginError::ShowInsteadOfText(String::from("Error")))).expect("Tried to tell main thread that an error occured. Main thread isn't listening any more.");
+ self.to_main.send_update(Err(PluginError::PrintToStdErr(err.to_string()))).expect("Tried to tell main thread that an error occured. Main thread isn't listening any more.");
+ }
+
}
impl<'p> SwayStatusModuleRunnable for PulseVolumeRunnable<'p> {
fn run(&self) {
- //TODO
+ let pulse = match &self.pulse {
+ Err(x) => {
+ self.send_error_to_main(x);
+ return;
+ }
+ Ok(x) => x
+ };
+ let context = match PulseContext::create(pulse) {
+ Err(x) => {
+ self.send_error_to_main(x);
+ return;
+ }
+ Ok(x) => x
+ };
+ let mut context_state = PaContextState::Unconnected;
+ let mut curr_default_sink = None;
+ let mut curr_volume = None;
+ let mut sink_we_care_about = match self.config.sink {
+ crate::config::Sink::Default => { None }
+ crate::config::Sink::Specific { sink_name } => {
+ Some(match SinkHandle::try_from(&*sink_name) {
+ Ok(x) => {x}
+ Err(e) => {
+ self.send_error_to_main(e);
+ return;
+ }
+ })
+ }
+ };
+ loop {
+ match context_state {
+ PaContextState::Unconnected => {
+ if let crate::config::Sink::Default = self.config.sink {
+ sink_we_care_about = None;
+ }
+ context.connect();
+ }
+ PaContextState::Failed | PaContextState::Terminated => {
+ match self.from_main.recv_timeout(std::time::Duration::from_secs(1)) {
+ Ok(x) => {
+ if let MessagesFromMain::Quit = x {
+ break;
+ }
+ }
+ Err(e) => {
+ if let RecvTimeoutError::Disconnected = e {
+ break;
+ }
+ }
+ }
+ if let crate::config::Sink::Default = self.config.sink {
+ sink_we_care_about = None;
+ }
+ context.connect();
+ }
+ PaContextState::Ready => {
+ if sink_we_care_about.is_none() {
+ //this may trigger several redundant refreshes, but it _should_ only happen
+ //during startup, so we don't really care.
+ context.refresh_default_sink();
+ }
+ }
+ _ => {}
+ }
+
+ let Pulse::IterationResult { default_sink, volume, state } = context.iterate(sink_we_care_about);
+ context_state = state.unwrap_or(context_state);
+ if default_sink.is_some() && default_sink != curr_default_sink {
+ curr_default_sink = default_sink;
+ if let crate::config::Sink::Default = self.config.sink {
+ sink_we_care_about = curr_default_sink;
+ context.refresh_volume();
+ }
+ }
+ if volume.is_some() && volume != curr_volume {
+ curr_volume = volume;
+ self.send_updated_volume_to_main(curr_volume);
+ }
+ match self.from_main.try_recv() {
+ Ok(x) => match x {
+ MessagesFromMain::Quit => {
+ break;
+ }
+ MessagesFromMain::Refresh => {
+ context.refresh_volume();
+ if let crate::config::Sink::Default = self.config.sink {
+ context.refresh_default_sink();
+ }
+ }
+ }
+ Err(e) => {
+ if let TryRecvError::Disconnected = e {
+ break;
+ }
+ }
+ }
+ }
}
}
diff --git a/pulse/src/runnable/pulse.rs b/pulse/src/runnable/pulse.rs
index 7687c86..64a7d33 100644
--- a/pulse/src/runnable/pulse.rs
+++ b/pulse/src/runnable/pulse.rs
@@ -5,6 +5,7 @@ use std::sync::{Arc, Weak};
use std::ffi::{c_void, CString};
use libc::{c_int, size_t};
use std::os::raw::c_char;
+use std::convert::TryFrom;
use std::marker::PhantomData;
@@ -32,8 +33,17 @@ impl Pulse {
pub fn get_wake_up(&self) -> PulseWakeUp {
PulseWakeUp { main_loop : Arc::downgrade(&self.main_loop) }
}
- pub fn create_context<'c>(&'c self) -> Result<PulseContext<'c>, PulseContextCreationError> {
- let api = self.main_loop.get_api();
+
+}
+
+pub(super) struct PulseContext<'c> {
+ context : *mut PaContext, //Can actually never be null, the nullptr case is handled by create, which returns an Err instead of a PulseContext then.
+ main : &'c Pulse
+}
+
+impl<'c> PulseContext<'c> {
+ pub(super) fn create<'m>(pulse : &'m Pulse) -> Result<PulseContext<'m>, PulseContextCreationError> {
+ let api = pulse.main_loop.get_api();
if api.is_null() {
Err(PulseContextCreationError::FailedToGetPulseApi)
}
@@ -44,17 +54,12 @@ impl Pulse {
Err(PulseContextCreationError::ContextNewFailed)
}
else {
- Ok(PulseContext { context, _marker : PhantomData })
+ Ok(PulseContext { context, main : pulse})
}
}
}
}
-pub struct PulseContext<'c> {
- context : *mut PaContext, //Can actually never be null, the nullptr case is handled by create, which returns an Err instead of a PulseContext then.
- _marker : PhantomData<&'c PulseMainLoop>
-}
-
impl<'c> Drop for PulseContext<'c> {
fn drop(&mut self) {
unsafe {pa_context_unref(self.context)}
@@ -159,6 +164,41 @@ impl std::fmt::Display for MainLoopCreationError {
}
impl std::error::Error for MainLoopCreationError {}
+pub(super) struct SinkHandle {
+ sink: CString
+}
+
+impl TryFrom<&str> for SinkHandle {
+ type Error = std::ffi::NulError;
+ fn try_from(s : &str) -> Result<Self, Self::Error> {
+ let converted = std::ffi::CString::new(s)?;
+ Ok(SinkHandle {
+ sink : converted
+ })
+ }
+}
+
+pub(super) struct IterationResult {
+ pub default_sink : Option<SinkHandle>,
+ pub volume : Option<Volume>,
+ pub state : Option<PaContextState>
+}
+
+pub(super) struct Volume {
+ pub volume : f32,
+ pub balance : f32
+}
+
+#[repr(C)] pub(super) enum PaContextState {
+ Unconnected,
+ Connecting,
+ Authorizing,
+ SettingName,
+ Ready,
+ Failed,
+ Terminated
+}
+
#[repr(C)] struct PaMainloop { _private: [u8; 0] }
#[repr(C)] struct PaContext { _private: [u8; 0] }