Skip to content

Commit 61187b3

Browse files
authored
Merge pull request #5176 from Alexays/fix/review-lowsev
fix: resource-leak / correctness cleanups from the 0.15.0..master review (low severity)
2 parents e92fe4b + a600fba commit 61187b3

3 files changed

Lines changed: 105 additions & 58 deletions

File tree

src/AGraph.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,17 @@ AGraph::AGraph(const Json::Value& config, const std::string& name, const std::st
7979

8080
// Make the GtkBuilder and check for errors in his parsing
8181
if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) {
82+
g_object_unref(builder);
8283
throw std::runtime_error("Error found in the file " + menuFile);
8384
}
8485

8586
menu_ = gtk_builder_get_object(builder, "menu");
8687
if (menu_ == nullptr) {
88+
g_object_unref(builder);
8789
throw std::runtime_error("Failed to get 'menu' object from GtkBuilder");
8890
}
91+
// Keep the menu alive after dropping the transient GtkBuilder.
92+
g_object_ref(menu_);
8993
submenus_ = std::map<std::string, GtkMenuItem*>();
9094
menuActionsMap_ = std::map<std::string, std::string>();
9195

@@ -98,6 +102,7 @@ AGraph::AGraph(const Json::Value& config, const std::string& name, const std::st
98102
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
99103
(gpointer)menuActionsMap_[key].c_str());
100104
}
105+
g_object_unref(builder);
101106
} catch (std::runtime_error& e) {
102107
spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what());
103108
}

src/ALabel.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,9 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
123123
}
124124
submenus_[key] = GTK_MENU_ITEM(item);
125125
menuActionsMap_[key] = it->asString();
126-
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
127-
(gpointer)g_strdup(menuActionsMap_[key].c_str()));
126+
g_signal_connect_data(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
127+
g_strdup(menuActionsMap_[key].c_str()), (GClosureNotify)g_free,
128+
(GConnectFlags)0);
128129
}
129130
g_object_unref(builder);
130131
} catch (std::runtime_error& e) {
@@ -183,7 +184,9 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_
183184
if (!threshold.isObject() || !threshold["icon"].isString() || !threshold["max"].isUInt()) {
184185
static bool warned = false;
185186
if (!warned) {
186-
spdlog::warn("format-icons: skipping invalid threshold object, expected {\"icon\": \"...\", \"max\": N}");
187+
spdlog::warn(
188+
"format-icons: skipping invalid threshold object, expected {\"icon\": \"...\", "
189+
"\"max\": N}");
187190
warned = true;
188191
}
189192
continue;
@@ -229,7 +232,9 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector<std::string>&
229232
if (!threshold.isObject() || !threshold["icon"].isString() || !threshold["max"].isUInt()) {
230233
static bool warned = false;
231234
if (!warned) {
232-
spdlog::warn("format-icons: skipping invalid threshold object, expected {\"icon\": \"...\", \"max\": N}");
235+
spdlog::warn(
236+
"format-icons: skipping invalid threshold object, expected {\"icon\": \"...\", "
237+
"\"max\": N}");
233238
warned = true;
234239
}
235240
continue;

src/util/audio_backend.cpp

Lines changed: 91 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
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

324348
uint16_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

486520
void 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

495530
void 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

504540
void 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

Comments
 (0)