44#include < pulse/def.h>
55#include < pulse/error.h>
66#include < pulse/introspect.h>
7+ #include < pulse/operation.h>
78#include < pulse/subscribe.h>
89#include < pulse/volume.h>
910#include < spdlog/spdlog.h>
@@ -104,16 +105,17 @@ void AudioBackend::contextStateCb(pa_context* c, void* data) {
104105 }
105106 break ;
106107 case PA_CONTEXT_READY :
107- pa_context_get_server_info (c, serverInfoCb, data);
108+ if ( auto * o = pa_context_get_server_info (c, serverInfoCb, data)) pa_operation_unref (o );
108109 pa_context_set_subscribe_callback (c, subscribeCb, data);
109- pa_context_subscribe (c,
110- static_cast <enum pa_subscription_mask>(
111- static_cast <int >(PA_SUBSCRIPTION_MASK_SERVER ) |
112- static_cast <int >(PA_SUBSCRIPTION_MASK_SINK ) |
113- static_cast <int >(PA_SUBSCRIPTION_MASK_SINK_INPUT ) |
114- static_cast <int >(PA_SUBSCRIPTION_MASK_SOURCE ) |
115- static_cast <int >(PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT )),
116- nullptr , nullptr );
110+ if (auto * o = pa_context_subscribe (c,
111+ static_cast <enum pa_subscription_mask>(
112+ static_cast <int >(PA_SUBSCRIPTION_MASK_SERVER ) |
113+ static_cast <int >(PA_SUBSCRIPTION_MASK_SINK ) |
114+ static_cast <int >(PA_SUBSCRIPTION_MASK_SINK_INPUT ) |
115+ static_cast <int >(PA_SUBSCRIPTION_MASK_SOURCE ) |
116+ static_cast <int >(PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT )),
117+ nullptr , nullptr ))
118+ pa_operation_unref (o);
117119 break ;
118120 case PA_CONTEXT_FAILED :
119121 if (pa_context_errno (c) != PA_ERR_CONNECTIONREFUSED ) {
@@ -164,15 +166,18 @@ void AudioBackend::subscribeCb(pa_context* context, pa_subscription_event_type_t
164166 return ;
165167 }
166168 if (facility == PA_SUBSCRIPTION_EVENT_SERVER ) {
167- pa_context_get_server_info (context, serverInfoCb, data);
169+ if ( auto * o = pa_context_get_server_info (context, serverInfoCb, data)) pa_operation_unref (o );
168170 } else if (facility == PA_SUBSCRIPTION_EVENT_SINK ) {
169- pa_context_get_sink_info_by_index (context, idx, sinkInfoCb, data);
171+ if (auto * o = pa_context_get_sink_info_by_index (context, idx, sinkInfoCb, data))
172+ pa_operation_unref (o);
170173 } else if (facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT ) {
171- pa_context_get_sink_info_list (context, sinkInfoCb, data);
174+ if ( auto * o = pa_context_get_sink_info_list (context, sinkInfoCb, data)) pa_operation_unref (o );
172175 } else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE ) {
173- pa_context_get_source_info_by_index (context, idx, sourceInfoCb, data);
176+ if (auto * o = pa_context_get_source_info_by_index (context, idx, sourceInfoCb, data))
177+ pa_operation_unref (o);
174178 } else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT ) {
175- pa_context_get_source_info_list (context, sourceInfoCb, data);
179+ if (auto * o = pa_context_get_source_info_list (context, sourceInfoCb, data))
180+ pa_operation_unref (o);
176181 }
177182}
178183
@@ -184,7 +189,9 @@ void AudioBackend::volumeModifyCb(pa_context* c, int success, void* data) {
184189 if (success != 0 ) {
185190 if ((backend->context_ != nullptr ) &&
186191 pa_context_get_state (backend->context_ ) == PA_CONTEXT_READY ) {
187- pa_context_get_sink_info_by_index (backend->context_ , backend->sink_idx_ , sinkInfoCb, data);
192+ if (auto * o = pa_context_get_sink_info_by_index (backend->context_ , backend->sink_idx_ ,
193+ sinkInfoCb, data))
194+ pa_operation_unref (o);
188195 }
189196 } else {
190197 spdlog::debug (" Volume modification failed" );
@@ -196,8 +203,9 @@ void AudioBackend::sourceVolumeModifyCb(pa_context* c, int success, void* data)
196203 if (success != 0 ) {
197204 if ((backend->context_ != nullptr ) &&
198205 pa_context_get_state (backend->context_ ) == PA_CONTEXT_READY ) {
199- pa_context_get_source_info_by_index (backend->context_ , backend->source_idx_ , sourceInfoCb,
200- data);
206+ if (auto * o = pa_context_get_source_info_by_index (backend->context_ , backend->source_idx_ ,
207+ sourceInfoCb, data))
208+ pa_operation_unref (o);
201209 }
202210 } else {
203211 spdlog::debug (" Source volume modification failed" );
@@ -231,29 +239,45 @@ void AudioBackend::sinkInfoCb(pa_context* /*context*/, const pa_sink_info* i, in
231239 }
232240 }
233241
234- if (const auto mapping = backend->sink_mapping_ .find (backend->current_sink_name_ );
242+ // Resolve which sink to report deterministically, independent of the order in
243+ // which PulseAudio enumerates sinks during a sink_info_list sweep.
244+ //
245+ // When the default sink has an explicit mapping, the mapped target sink is the
246+ // definitive selection ("the sink named by the value is considered to be the
247+ // current sink instead of the one named by the key"): only that target sink may
248+ // write the reported volume/mute state. The mapping is keyed on the stable
249+ // default_sink_name (set once per server-info update) rather than on the mutable
250+ // current_sink_name_. Previously the mapping override and the "pick a running
251+ // sink" fallback both mutated current_sink_name_ mid-sweep, so the mapping could
252+ // be overridden by whichever running sink happened to be enumerated, and the
253+ // reported volume depended on enumeration order.
254+ if (const auto mapping = backend->sink_mapping_ .find (backend->default_sink_name );
235255 mapping != backend->sink_mapping_ .end ()) {
236- if (i->name == mapping->second ) {
237- backend->current_sink_name_ = i->name ;
256+ if (i->name != mapping->second ) {
257+ // A mapping is in effect but this is not the mapped target sink; ignore it so
258+ // it can never override the target's reported volume.
259+ return ;
238260 }
239- }
240-
241- backend->default_sink_running_ = backend->default_sink_name == i->name &&
242- (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE );
261+ backend->current_sink_name_ = i->name ;
262+ backend->current_sink_running_ = (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE );
263+ } else {
264+ backend->default_sink_running_ = backend->default_sink_name == i->name &&
265+ (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE );
243266
244- if (i->name != backend->default_sink_name && i->name != backend->current_sink_name_ &&
245- !backend->default_sink_running_ ) {
246- return ;
247- }
267+ if (i->name != backend->default_sink_name && i->name != backend->current_sink_name_ &&
268+ !backend->default_sink_running_ ) {
269+ return ;
270+ }
248271
249- if (backend->current_sink_name_ == i->name ) {
250- backend->current_sink_running_ = (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE );
251- }
272+ if (backend->current_sink_name_ == i->name ) {
273+ backend->current_sink_running_ = (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE );
274+ }
252275
253- if (!backend->current_sink_running_ &&
254- (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE )) {
255- backend->current_sink_name_ = i->name ;
256- backend->current_sink_running_ = true ;
276+ if (!backend->current_sink_running_ &&
277+ (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE )) {
278+ backend->current_sink_name_ = i->name ;
279+ backend->current_sink_running_ = true ;
280+ }
257281 }
258282
259283 if (backend->current_sink_name_ == i->name ) {
@@ -317,8 +341,8 @@ void AudioBackend::serverInfoCb(pa_context* context, const pa_server_info* i, vo
317341 backend->default_sink_name = i->default_sink_name ? i->default_sink_name : " " ;
318342 backend->default_source_name_ = i->default_source_name ? i->default_source_name : " " ;
319343
320- pa_context_get_sink_info_list (context, sinkInfoCb, data);
321- pa_context_get_source_info_list (context, sourceInfoCb, data);
344+ if ( auto * o = pa_context_get_sink_info_list (context, sinkInfoCb, data)) pa_operation_unref (o );
345+ if ( auto * o = pa_context_get_source_info_list (context, sourceInfoCb, data)) pa_operation_unref (o );
322346}
323347
324348uint16_t AudioBackend::getVolume (PulseaudioTarget target) const {
@@ -368,10 +392,13 @@ void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t m
368392 // Apply the volume change
369393 pa_threaded_mainloop_lock (mainloop_);
370394 if (is_source) {
371- pa_context_set_source_volume_by_index (context_, source_idx_, &pa_volume, sourceVolumeModifyCb,
372- this );
395+ if (auto * o = pa_context_set_source_volume_by_index (context_, source_idx_, &pa_volume,
396+ sourceVolumeModifyCb, this ))
397+ pa_operation_unref (o);
373398 } else {
374- pa_context_set_sink_volume_by_index (context_, sink_idx_, &pa_volume, volumeModifyCb, this );
399+ if (auto * o = pa_context_set_sink_volume_by_index (context_, sink_idx_, &pa_volume,
400+ volumeModifyCb, this ))
401+ pa_operation_unref (o);
375402 }
376403 pa_threaded_mainloop_unlock (mainloop_);
377404}
@@ -412,10 +439,13 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
412439 // No need to continue with volume change if we had to create a new structure
413440 pa_threaded_mainloop_lock (mainloop_);
414441 if (is_source) {
415- pa_context_set_source_volume_by_index (context_, source_idx_, &pa_volume, sourceVolumeModifyCb,
416- this );
442+ if (auto * o = pa_context_set_source_volume_by_index (context_, source_idx_, &pa_volume,
443+ sourceVolumeModifyCb, this ))
444+ pa_operation_unref (o);
417445 } else {
418- pa_context_set_sink_volume_by_index (context_, sink_idx_, &pa_volume, volumeModifyCb, this );
446+ if (auto * o = pa_context_set_sink_volume_by_index (context_, sink_idx_, &pa_volume,
447+ volumeModifyCb, this ))
448+ pa_operation_unref (o);
419449 }
420450 pa_threaded_mainloop_unlock (mainloop_);
421451 return ;
@@ -458,10 +488,13 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
458488 // Apply the volume change
459489 pa_threaded_mainloop_lock (mainloop_);
460490 if (is_source) {
461- pa_context_set_source_volume_by_index (context_, source_idx_, &pa_volume, sourceVolumeModifyCb,
462- this );
491+ if (auto * o = pa_context_set_source_volume_by_index (context_, source_idx_, &pa_volume,
492+ sourceVolumeModifyCb, this ))
493+ pa_operation_unref (o);
463494 } else {
464- pa_context_set_sink_volume_by_index (context_, sink_idx_, &pa_volume, volumeModifyCb, this );
495+ if (auto * o = pa_context_set_sink_volume_by_index (context_, sink_idx_, &pa_volume,
496+ volumeModifyCb, this ))
497+ pa_operation_unref (o);
465498 }
466499 pa_threaded_mainloop_unlock (mainloop_);
467500}
@@ -478,35 +511,39 @@ void AudioBackend::toggleSinkMute() {
478511 if (context_ == nullptr || pa_context_get_state (context_) != PA_CONTEXT_READY ) return ;
479512 muted_ = !muted_;
480513 pa_threaded_mainloop_lock (mainloop_);
481- pa_context_set_sink_mute_by_index (context_, sink_idx_, static_cast <int >(muted_), nullptr ,
482- nullptr );
514+ if (auto * o = pa_context_set_sink_mute_by_index (context_, sink_idx_, static_cast <int >(muted_),
515+ nullptr , nullptr ))
516+ pa_operation_unref (o);
483517 pa_threaded_mainloop_unlock (mainloop_);
484518}
485519
486520void AudioBackend::toggleSinkMute (bool mute) {
487521 if (context_ == nullptr || pa_context_get_state (context_) != PA_CONTEXT_READY ) return ;
488522 muted_ = mute;
489523 pa_threaded_mainloop_lock (mainloop_);
490- pa_context_set_sink_mute_by_index (context_, sink_idx_, static_cast <int >(muted_), nullptr ,
491- nullptr );
524+ if (auto * o = pa_context_set_sink_mute_by_index (context_, sink_idx_, static_cast <int >(muted_),
525+ nullptr , nullptr ))
526+ pa_operation_unref (o);
492527 pa_threaded_mainloop_unlock (mainloop_);
493528}
494529
495530void AudioBackend::toggleSourceMute () {
496531 if (context_ == nullptr || pa_context_get_state (context_) != PA_CONTEXT_READY ) return ;
497532 source_muted_ = !source_muted_;
498533 pa_threaded_mainloop_lock (mainloop_);
499- pa_context_set_source_mute_by_index (context_, source_idx_, static_cast <int >(source_muted_),
500- nullptr , nullptr );
534+ if (auto * o = pa_context_set_source_mute_by_index (
535+ context_, source_idx_, static_cast <int >(source_muted_), nullptr , nullptr ))
536+ pa_operation_unref (o);
501537 pa_threaded_mainloop_unlock (mainloop_);
502538}
503539
504540void AudioBackend::toggleSourceMute (bool mute) {
505541 if (context_ == nullptr || pa_context_get_state (context_) != PA_CONTEXT_READY ) return ;
506542 source_muted_ = mute;
507543 pa_threaded_mainloop_lock (mainloop_);
508- pa_context_set_source_mute_by_index (context_, source_idx_, static_cast <int >(source_muted_),
509- nullptr , nullptr );
544+ if (auto * o = pa_context_set_source_mute_by_index (
545+ context_, source_idx_, static_cast <int >(source_muted_), nullptr , nullptr ))
546+ pa_operation_unref (o);
510547 pa_threaded_mainloop_unlock (mainloop_);
511548}
512549
0 commit comments