@@ -6,19 +6,19 @@ mod source_output;
66use crate :: channels:: SyncSenderExt ;
77use crate :: { APP_ID , arc_mut, lock, register_client, spawn_blocking} ;
88use libpulse_binding:: callbacks:: ListResult ;
9- use libpulse_binding:: context:: introspect:: { Introspector , ServerInfo } ;
9+ use libpulse_binding:: context:: introspect:: ServerInfo ;
1010use libpulse_binding:: context:: subscribe:: { Facility , InterestMaskSet , Operation } ;
1111use libpulse_binding:: context:: { Context , FlagSet , State } ;
12- use libpulse_binding:: mainloop:: standard :: { IterateResult , Mainloop } ;
12+ use libpulse_binding:: mainloop:: threaded :: Mainloop ;
1313use libpulse_binding:: proplist:: Proplist ;
1414use libpulse_binding:: volume:: { ChannelVolumes , Volume } ;
1515pub use sink:: Sink ;
1616pub use sink_input:: SinkInput ;
1717pub use source:: Source ;
1818pub use source_output:: SourceOutput ;
19-
20- use std:: fmt:: { Debug , Formatter } ;
19+ use std:: fmt:: Debug ;
2120use std:: ops:: { Deref , DerefMut } ;
21+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
2222use std:: sync:: { Arc , Mutex } ;
2323use tokio:: sync:: broadcast;
2424use tracing:: { debug, error, info, trace, warn} ;
@@ -129,14 +129,31 @@ pub enum Event {
129129 RemoveOutput ( u32 ) ,
130130}
131131
132+ #[ derive( Debug , Clone ) ]
133+ pub enum Request {
134+ SinkVolume ( String , VolumeLevels ) ,
135+ SinkMuted ( String , bool ) ,
136+ SinkDefault ( String ) ,
137+
138+ SinkInputVolume ( u32 , VolumeLevels ) ,
139+ SinkInputMuted ( u32 , bool ) ,
140+
141+ SourceVolume ( String , VolumeLevels ) ,
142+ SourceMuted ( String , bool ) ,
143+ SourceDefault ( String ) ,
144+
145+ SourceOutputVolume ( u32 , VolumeLevels ) ,
146+ SourceOutputMuted ( u32 , bool ) ,
147+ }
148+
132149#[ derive( Debug ) ]
133150pub struct Client {
134- connection : Arc < Mutex < ConnectionState > > ,
135-
136151 data : Data ,
137152
138153 tx : broadcast:: Sender < Event > ,
139154 _rx : broadcast:: Receiver < Event > ,
155+
156+ req_tx : std:: sync:: mpsc:: Sender < Request > ,
140157}
141158
142159#[ derive( Debug , Default , Clone ) ]
@@ -150,41 +167,20 @@ struct Data {
150167 default_source_name : Arc < Mutex < Option < String > > > ,
151168}
152169
153- pub enum ConnectionState {
154- Disconnected ,
155- Connected {
156- context : Arc < Mutex < Context > > ,
157- introspector : Introspector ,
158- } ,
159- }
160-
161- impl Debug for ConnectionState {
162- fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
163- write ! (
164- f,
165- "{}" ,
166- match self {
167- Self :: Disconnected => "Disconnected" ,
168- Self :: Connected { .. } => "Connected" ,
169- }
170- )
171- }
172- }
173-
174170impl Client {
175- pub fn new ( ) -> Self {
171+ pub fn new ( req_tx : std :: sync :: mpsc :: Sender < Request > ) -> Self {
176172 let ( tx, rx) = broadcast:: channel ( 32 ) ;
177173
178174 Self {
179- connection : arc_mut ! ( ConnectionState :: Disconnected ) ,
180175 data : Data :: default ( ) ,
181176 tx,
182177 _rx : rx,
178+ req_tx,
183179 }
184180 }
185181
186182 /// Starts the client.
187- fn run ( & self ) {
183+ fn run ( & self , rx : std :: sync :: mpsc :: Receiver < Request > ) {
188184 let Some ( mut proplist) = Proplist :: new ( ) else {
189185 error ! ( "Failed to create PA proplist" ) ;
190186 return ;
@@ -199,45 +195,74 @@ impl Client {
199195 return ;
200196 } ;
201197
202- let Some ( context) = Context :: new_with_proplist ( & mainloop, "Ironbar Context" , & proplist)
203- else {
198+ let Some ( context) = Context :: new_with_proplist ( & mainloop, "Ironbar" , & proplist) else {
204199 error ! ( "Failed to create PA context" ) ;
205200 return ;
206201 } ;
207202
208203 let context = arc_mut ! ( context) ;
209204
210- let state_callback = Box :: new ( {
205+ lock ! ( context ) . set_state_callback ( Some ( Box :: new ( {
211206 let context = context. clone ( ) ;
212207 let data = self . data . clone ( ) ;
213208 let tx = self . tx . clone ( ) ;
214-
215209 move || on_state_change ( & context, & data, & tx)
216- } ) ;
210+ } ) ) ) ;
217211
218- lock ! ( context) . set_state_callback ( Some ( state_callback) ) ;
212+ if let Err ( err) = mainloop. start ( ) {
213+ error ! ( "Failed to start PA mainloop: {err:?}" ) ;
214+ return ;
215+ }
219216
217+ mainloop. lock ( ) ;
220218 if let Err ( err) = lock ! ( context) . connect ( None , FlagSet :: NOAUTOSPAWN , None ) {
221219 error ! ( "{err:?}" ) ;
220+ mainloop. unlock ( ) ;
221+ mainloop. stop ( ) ;
222+ return ;
222223 }
224+ mainloop. unlock ( ) ;
223225
224- let introspector = lock ! ( context) . introspect ( ) ;
226+ let mut introspector = lock ! ( context) . introspect ( ) ;
225227
226- {
227- let mut inner = lock ! ( self . connection) ;
228- * inner = ConnectionState :: Connected {
229- context,
230- introspector,
231- } ;
232- }
233-
234- loop {
235- match mainloop. iterate ( true ) {
236- IterateResult :: Success ( _) => { }
237- IterateResult :: Err ( err) => error ! ( "{err:?}" ) ,
238- IterateResult :: Quit ( _) => break ,
228+ for req in rx {
229+ mainloop. lock ( ) ;
230+ match req {
231+ Request :: SinkVolume ( name, levels) => {
232+ introspector. set_sink_volume_by_name ( & name, & levels. into ( ) , None ) ;
233+ }
234+ Request :: SinkMuted ( name, muted) => {
235+ introspector. set_sink_mute_by_name ( & name, muted, None ) ;
236+ }
237+ Request :: SinkDefault ( name) => {
238+ lock ! ( context) . set_default_sink ( & name, |_| { } ) ;
239+ }
240+ Request :: SinkInputVolume ( index, levels) => {
241+ introspector. set_sink_input_volume ( index, & levels. into ( ) , None ) ;
242+ }
243+ Request :: SinkInputMuted ( index, muted) => {
244+ introspector. set_sink_input_mute ( index, muted, None ) ;
245+ }
246+ Request :: SourceVolume ( name, levels) => {
247+ introspector. set_source_volume_by_name ( & name, & levels. into ( ) , None ) ;
248+ }
249+ Request :: SourceMuted ( name, muted) => {
250+ introspector. set_source_mute_by_name ( & name, muted, None ) ;
251+ }
252+ Request :: SourceDefault ( name) => {
253+ lock ! ( context) . set_default_source ( & name, |_| { } ) ;
254+ }
255+ Request :: SourceOutputVolume ( index, levels) => {
256+ introspector. set_source_output_volume ( index, & levels. into ( ) , None ) ;
257+ }
258+ Request :: SourceOutputMuted ( index, muted) => {
259+ introspector. set_source_output_mute ( index, muted, None ) ;
260+ }
239261 }
262+ mainloop. unlock ( ) ;
240263 }
264+
265+ mainloop. stop ( ) ;
241266 }
242267
243268 /// Gets an event receiver.
@@ -248,12 +273,14 @@ impl Client {
248273
249274/// Creates a new Pulse volume client.
250275pub fn create_client ( ) -> Arc < Client > {
251- let client = Arc :: new ( Client :: new ( ) ) ;
276+ let ( req_tx, req_rx) = std:: sync:: mpsc:: channel ( ) ;
277+
278+ let client = Arc :: new ( Client :: new ( req_tx) ) ;
252279
253280 {
254281 let client = client. clone ( ) ;
255282 spawn_blocking ( move || {
256- client. run ( ) ;
283+ client. run ( req_rx ) ;
257284 } ) ;
258285 }
259286
@@ -392,6 +419,14 @@ fn on_server_event(
392419 default_source : & Arc < Mutex < Option < String > > > ,
393420 tx : & broadcast:: Sender < Event > ,
394421) {
422+ // introspection will return latest result -
423+ // avoid multiple duplicate calls
424+ static RUNNING : AtomicBool = AtomicBool :: new ( false ) ;
425+
426+ if RUNNING . swap ( true , Ordering :: Relaxed ) {
427+ return ;
428+ }
429+
395430 lock ! ( context) . introspect ( ) . get_server_info ( {
396431 let sinks = sinks. clone ( ) ;
397432 let default_sink = default_sink. clone ( ) ;
@@ -402,6 +437,8 @@ fn on_server_event(
402437 move |info| {
403438 set_default_sink ( info, & sinks, & default_sink, & tx) ;
404439 set_default_source ( info, & sources, & default_source, & tx) ;
440+
441+ RUNNING . store ( false , Ordering :: Relaxed ) ;
405442 }
406443 } ) ;
407444}
0 commit comments