Fix button action handling to consistently use the active player

This commit is contained in:
BlueManCZ
2026-02-11 12:09:20 +01:00
parent 0a50e82d0d
commit a871d90161
2 changed files with 37 additions and 32 deletions

View File

@ -78,6 +78,7 @@ class Mpris : public ALabel {
PlayerctlPlayerManager* manager;
PlayerctlPlayer* player;
PlayerctlPlayer* last_active_player_ = nullptr;
std::string lastStatus;
std::string lastPlayer;

View File

@ -178,6 +178,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
}
Mpris::~Mpris() {
if (last_active_player_ && last_active_player_ != player) g_object_unref(last_active_player_);
if (manager != nullptr) g_object_unref(manager);
if (player != nullptr) g_object_unref(player);
}
@ -486,12 +487,11 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
char* player_status = nullptr;
auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED;
// When using playerctld and the most active player is ignored, we create a
// direct connection to the first non-ignored player for correct metadata.
PlayerctlPlayer* fallback_player = nullptr;
waybar::util::ScopeGuard fallback_deleter([&fallback_player]() {
if (fallback_player) g_object_unref(fallback_player);
});
// Clean up previous fallback player
if (last_active_player_ && last_active_player_ != player) {
g_object_unref(last_active_player_);
last_active_player_ = nullptr;
}
std::string player_name = player_;
if (player_name == "playerctld") {
@ -501,8 +501,8 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
}
// > get the list of players [..] in order of activity
// https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249
PlayerctlPlayerName* best = nullptr;
PlayerctlPlayerName* first_valid = nullptr;
PlayerctlPlayer* first_valid_player = nullptr;
std::string first_valid_name;
for (auto* p = g_list_first(players); p != nullptr; p = p->next) {
auto* pn = static_cast<PlayerctlPlayerName*>(p->data);
std::string name = pn->name;
@ -511,35 +511,37 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
spdlog::warn("mpris[{}]: ignoring player update", name);
continue;
}
if (!first_valid) first_valid = pn;
// Check if this player is currently playing
auto* tmp = playerctl_player_new_from_name(pn, &error);
if (error || !tmp) continue;
if (!first_valid_player) {
first_valid_player = tmp;
first_valid_name = name;
}
PlayerctlPlaybackStatus status;
g_object_get(tmp, "playback-status", &status, NULL);
if (status == PLAYERCTL_PLAYBACK_STATUS_PLAYING) {
best = pn;
g_object_unref(tmp);
if (tmp != first_valid_player) g_object_unref(first_valid_player);
last_active_player_ = tmp;
player_name = name;
break;
}
g_object_unref(tmp);
if (tmp != first_valid_player) g_object_unref(tmp);
}
if (!best) best = first_valid;
if (!best) return std::nullopt;
player_name = best->name;
if (best != static_cast<PlayerctlPlayerName*>(g_list_first(players)->data)) {
fallback_player = playerctl_player_new_from_name(best, &error);
if (error || !fallback_player) return std::nullopt;
if (!last_active_player_) {
if (!first_valid_player) return std::nullopt;
last_active_player_ = first_valid_player;
player_name = first_valid_name;
}
} else if (std::any_of(ignored_players_.begin(), ignored_players_.end(),
[&](const std::string& pn) { return player_name == pn; })) {
spdlog::warn("mpris[{}]: ignoring player update", player_name);
return std::nullopt;
} else {
last_active_player_ = player;
}
auto* source_player = fallback_player ? fallback_player : player;
g_object_get(source_player, "status", &player_status, "playback-status", &player_playback_status,
NULL);
g_object_get(last_active_player_, "status", &player_status, "playback-status",
&player_playback_status, NULL);
// make status lowercase
player_status[0] = std::tolower(player_status[0]);
@ -554,28 +556,29 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
.length = std::nullopt,
};
if (auto* artist_ = playerctl_player_get_artist(source_player, &error)) {
if (auto* artist_ = playerctl_player_get_artist(last_active_player_, &error)) {
spdlog::debug("mpris[{}]: artist = {}", info.name, artist_);
info.artist = artist_;
g_free(artist_);
}
if (error) goto errorexit;
if (auto* album_ = playerctl_player_get_album(source_player, &error)) {
if (auto* album_ = playerctl_player_get_album(last_active_player_, &error)) {
spdlog::debug("mpris[{}]: album = {}", info.name, album_);
info.album = album_;
g_free(album_);
}
if (error) goto errorexit;
if (auto* title_ = playerctl_player_get_title(source_player, &error)) {
if (auto* title_ = playerctl_player_get_title(last_active_player_, &error)) {
spdlog::debug("mpris[{}]: title = {}", info.name, title_);
info.title = title_;
g_free(title_);
}
if (error) goto errorexit;
if (auto* length_ = playerctl_player_print_metadata_prop(source_player, "mpris:length", &error)) {
if (auto* length_ =
playerctl_player_print_metadata_prop(last_active_player_, "mpris:length", &error)) {
spdlog::debug("mpris[{}]: mpris:length = {}", info.name, length_);
auto len = std::chrono::microseconds(std::strtol(length_, nullptr, 10));
auto len_h = std::chrono::duration_cast<std::chrono::hours>(len);
@ -587,7 +590,7 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
if (error) goto errorexit;
{
auto position_ = playerctl_player_get_position(source_player, &error);
auto position_ = playerctl_player_get_position(last_active_player_, &error);
if (error) {
// it's fine to have an error here because not all players report a position
g_error_free(error);
@ -639,12 +642,13 @@ bool Mpris::handleToggle(GdkEventButton* const& e) {
});
// Command pattern: encapsulate each button's action
auto* target = last_active_player_ ? last_active_player_ : player;
const ButtonAction actions[] = {
{1, "on-click", [&]() { playerctl_player_play_pause(player, &error); }},
{2, "on-click-middle", [&]() { playerctl_player_previous(player, &error); }},
{3, "on-click-right", [&]() { playerctl_player_next(player, &error); }},
{8, "on-click-backward", [&]() { playerctl_player_previous(player, &error); }},
{9, "on-click-forward", [&]() { playerctl_player_next(player, &error); }},
{1, "on-click", [&]() { playerctl_player_play_pause(target, &error); }},
{2, "on-click-middle", [&]() { playerctl_player_previous(target, &error); }},
{3, "on-click-right", [&]() { playerctl_player_next(target, &error); }},
{8, "on-click-backward", [&]() { playerctl_player_previous(target, &error); }},
{9, "on-click-forward", [&]() { playerctl_player_next(target, &error); }},
};
for (const auto& action : actions) {