Skip to content

Commit d1e9ee0

Browse files
committed
wip pulse crash fix
1 parent 742fe7f commit d1e9ee0

5 files changed

Lines changed: 197 additions & 157 deletions

File tree

src/clients/volume/mod.rs

Lines changed: 89 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@ mod source_output;
66
use crate::channels::SyncSenderExt;
77
use crate::{APP_ID, arc_mut, lock, register_client, spawn_blocking};
88
use libpulse_binding::callbacks::ListResult;
9-
use libpulse_binding::context::introspect::{Introspector, ServerInfo};
9+
use libpulse_binding::context::introspect::ServerInfo;
1010
use libpulse_binding::context::subscribe::{Facility, InterestMaskSet, Operation};
1111
use libpulse_binding::context::{Context, FlagSet, State};
12-
use libpulse_binding::mainloop::standard::{IterateResult, Mainloop};
12+
use libpulse_binding::mainloop::threaded::Mainloop;
1313
use libpulse_binding::proplist::Proplist;
1414
use libpulse_binding::volume::{ChannelVolumes, Volume};
1515
pub use sink::Sink;
1616
pub use sink_input::SinkInput;
1717
pub use source::Source;
1818
pub use source_output::SourceOutput;
19-
20-
use std::fmt::{Debug, Formatter};
19+
use std::fmt::Debug;
2120
use std::ops::{Deref, DerefMut};
21+
use std::sync::atomic::{AtomicBool, Ordering};
2222
use std::sync::{Arc, Mutex};
2323
use tokio::sync::broadcast;
2424
use 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)]
133150
pub 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-
174170
impl 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.
250275
pub 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
}

src/clients/volume/sink.rs

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
use std::sync::{Arc, Mutex};
2-
1+
use super::{ArcMutVec, Client, Event, HasIndex, PulseObject, Request, VolumeLevels};
2+
use crate::channels::SyncSenderExt;
3+
use crate::lock;
34
use libpulse_binding::context::Context;
45
use libpulse_binding::context::introspect::SinkInfo;
56
use libpulse_binding::context::subscribe::Operation;
67
use libpulse_binding::def::SinkState;
8+
use std::sync::atomic::{AtomicBool, Ordering};
9+
use std::sync::{Arc, Mutex};
710
use tokio::sync::broadcast;
811
use tracing::{debug, instrument};
912

10-
use super::{ArcMutVec, Client, ConnectionState, Event, HasIndex, PulseObject, VolumeLevels};
11-
use crate::lock;
12-
1313
#[derive(Debug, Clone)]
1414
pub struct Sink {
1515
index: u32,
@@ -90,37 +90,35 @@ impl Client {
9090

9191
#[instrument(level = "trace")]
9292
pub fn set_default_sink(&self, name: &str) {
93-
if let ConnectionState::Connected { context, .. } = &*lock!(self.connection) {
94-
lock!(context).set_default_sink(name, |_| {});
95-
}
93+
self.req_tx
94+
.send_expect(Request::SinkDefault(name.to_string()));
9695
}
9796

9897
#[instrument(level = "trace")]
9998
pub fn set_sink_volume(&self, name: &str, volume: f64) {
100-
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
101-
let Some(mut volume_levels) = ({
102-
let sinks = self.sinks();
103-
lock!(sinks).iter().find_map(|s| {
104-
if s.name == name {
105-
Some(s.volume.clone())
106-
} else {
107-
None
108-
}
109-
})
110-
}) else {
111-
return;
112-
};
113-
114-
volume_levels.set_percent(volume);
115-
introspector.set_sink_volume_by_name(name, &volume_levels.into(), None);
116-
}
99+
let Some(mut volume_levels) = ({
100+
let sinks = self.sinks();
101+
lock!(sinks).iter().find_map(|s| {
102+
if s.name == name {
103+
Some(s.volume.clone())
104+
} else {
105+
None
106+
}
107+
})
108+
}) else {
109+
return;
110+
};
111+
112+
volume_levels.set_percent(volume);
113+
114+
self.req_tx
115+
.send_expect(Request::SinkVolume(name.to_string(), volume_levels));
117116
}
118117

119118
#[instrument(level = "trace")]
120119
pub fn set_sink_muted(&self, name: &str, muted: bool) {
121-
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
122-
introspector.set_sink_mute_by_name(name, muted, None);
123-
}
120+
self.req_tx
121+
.send_expect(Request::SinkMuted(name.to_string(), muted));
124122
}
125123
}
126124

@@ -133,6 +131,14 @@ impl Sink {
133131
op: Operation,
134132
i: u32,
135133
) {
134+
// introspection will return latest result -
135+
// avoid multiple duplicate calls
136+
static RUNNING: AtomicBool = AtomicBool::new(false);
137+
138+
if RUNNING.swap(true, Ordering::Relaxed) {
139+
return;
140+
}
141+
136142
let introspect = lock!(context).introspect();
137143

138144
match op {
@@ -152,7 +158,10 @@ impl Sink {
152158
let default_sink = default_sink.clone();
153159
let tx = tx.clone();
154160

155-
move |info| Self::update(info, &sinks, Some(&default_sink), &tx)
161+
move |info| {
162+
Self::update(info, &sinks, Some(&default_sink), &tx);
163+
RUNNING.store(false, Ordering::Relaxed);
164+
}
156165
});
157166
}
158167
Operation::Removed => {

0 commit comments

Comments
 (0)