From 6457d779ec253ade253fc49f5891b5479b270a6d Mon Sep 17 00:00:00 2001 From: Andreas Grois Date: Sun, 9 May 2021 13:22:40 +0200 Subject: 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. --- pulse/src/runnable/mod.rs | 106 +++++++++++++++++++++++++++++++++++++++++++- pulse/src/runnable/pulse.rs | 56 +++++++++++++++++++---- 2 files changed, 152 insertions(+), 10 deletions(-) (limited to 'pulse/src') 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(&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, 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, 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 { + let converted = std::ffi::CString::new(s)?; + Ok(SinkHandle { + sink : converted + }) + } +} + +pub(super) struct IterationResult { + pub default_sink : Option, + pub volume : Option, + pub state : Option +} + +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] } -- cgit v1.2.3