mpd: Implement idle events for Playing state

This commit is contained in:
Desuwa
2026-05-16 15:41:09 -07:00
parent 05945748dc
commit 546b53d675
2 changed files with 102 additions and 16 deletions
+4
View File
@@ -82,6 +82,7 @@ class Idle : public State {
class Playing : public State { class Playing : public State {
Context* const ctx_; Context* const ctx_;
sigc::connection timer_connection_; sigc::connection timer_connection_;
sigc::connection idle_connection_;
public: public:
Playing(Context* const ctx) : ctx_{ctx} {} Playing(Context* const ctx) : ctx_{ctx} {}
@@ -98,7 +99,10 @@ class Playing : public State {
Playing(Playing const&) = delete; Playing(Playing const&) = delete;
Playing& operator=(Playing const&) = delete; Playing& operator=(Playing const&) = delete;
void timer() noexcept;
void idle() noexcept;
bool on_timer(); bool on_timer();
bool on_io(Glib::IOCondition const&);
}; };
class Paused : public State { class Paused : public State {
+98 -16
View File
@@ -19,13 +19,13 @@ auto format_as(enum mpd_idle val) {
namespace waybar::modules::detail { namespace waybar::modules::detail {
#define IDLE_RUN_NOIDLE_AND_CMD(...) \ #define RUN_NOIDLE_AND_CMD(STATE, ...) \
if (idle_connection_.connected()) { \ if (idle_connection_.connected()) { \
idle_connection_.disconnect(); \ idle_connection_.disconnect(); \
auto conn = ctx_->connection().get(); \ auto conn = ctx_->connection().get(); \
if (!mpd_run_noidle(conn)) { \ if (!mpd_run_noidle(conn)) { \
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { \ if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { \
spdlog::error("mpd: Idle: failed to unregister for IDLE events"); \ spdlog::error("mpd: STATE: failed to unregister for IDLE events"); \
ctx_->checkErrors(conn); \ ctx_->checkErrors(conn); \
} \ } \
} \ } \
@@ -33,24 +33,23 @@ namespace waybar::modules::detail {
} }
void Idle::play() { void Idle::play() {
IDLE_RUN_NOIDLE_AND_CMD(mpd_run_play(conn)); RUN_NOIDLE_AND_CMD(Idle, mpd_run_play(conn));
ctx_->setState(std::make_unique<Playing>(ctx_)); ctx_->setState(std::make_unique<Playing>(ctx_));
} }
void Idle::pause() { void Idle::pause() {
IDLE_RUN_NOIDLE_AND_CMD(mpd_run_pause(conn, true)); RUN_NOIDLE_AND_CMD(Idle, mpd_run_pause(conn, true));
ctx_->setState(std::make_unique<Paused>(ctx_)); ctx_->setState(std::make_unique<Paused>(ctx_));
} }
void Idle::stop() { void Idle::stop() {
IDLE_RUN_NOIDLE_AND_CMD(mpd_run_stop(conn)); RUN_NOIDLE_AND_CMD(Idle, mpd_run_stop(conn));
ctx_->setState(std::make_unique<Stopped>(ctx_)); ctx_->setState(std::make_unique<Stopped>(ctx_));
} }
#undef IDLE_RUN_NOIDLE_AND_CMD
void Idle::update() noexcept { void Idle::update() noexcept {
// This is intentionally blank. // This is intentionally blank.
@@ -118,18 +117,50 @@ bool Idle::on_io(Glib::IOCondition const&) {
} }
void Playing::entry() noexcept { void Playing::entry() noexcept {
sigc::slot<bool> timer_slot = sigc::mem_fun(*this, &Playing::on_timer); timer();
timer_connection_ = Glib::signal_timeout().connect_seconds(timer_slot, 1); idle();
spdlog::debug("mpd: Playing: enabled 1 second periodic timer."); spdlog::debug("mpd: Playing: enabled 1 second periodic timer.");
} }
void Playing::exit() noexcept { void Playing::exit() noexcept {
if (idle_connection_.connected()) {
idle_connection_.disconnect();
spdlog::debug("mpd: Playing: unwatching FD");
}
if (timer_connection_.connected()) { if (timer_connection_.connected()) {
timer_connection_.disconnect(); timer_connection_.disconnect();
spdlog::debug("mpd: Playing: disabled 1 second periodic timer."); spdlog::debug("mpd: Playing: disabled 1 second periodic timer.");
} }
} }
void Playing::timer() noexcept {
if (timer_connection_.connected()) {
timer_connection_.disconnect();
}
sigc::slot<bool> timer_slot = sigc::mem_fun(*this, &Playing::on_timer);
timer_connection_ = Glib::signal_timeout().connect_seconds(timer_slot, 1);
}
void Playing::idle() noexcept {
auto conn = ctx_->connection().get();
assert(conn != nullptr);
if (!mpd_send_idle_mask(
conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) {
ctx_->checkErrors(conn);
spdlog::error("mpd: Playing: failed to register for IDLE events");
} else if (!idle_connection_.connected()) {
spdlog::trace("mpd: Playing: watching FD");
sigc::slot<bool, Glib::IOCondition const&> idle_slot = sigc::mem_fun(*this, &Playing::on_io);
idle_connection_ =
Glib::signal_io().connect(idle_slot, mpd_connection_get_fd(conn),
Glib::IO_IN | Glib::IO_PRI | Glib::IO_ERR | Glib::IO_HUP);
}
}
bool Playing::on_timer() { bool Playing::on_timer() {
// Attempt to connect with MPD. // Attempt to connect with MPD.
try { try {
@@ -141,6 +172,8 @@ bool Playing::on_timer() {
return false; return false;
} }
RUN_NOIDLE_AND_CMD(Playing);
ctx_->fetchState(); ctx_->fetchState();
if (!ctx_->is_playing()) { if (!ctx_->is_playing()) {
@@ -154,6 +187,50 @@ bool Playing::on_timer() {
ctx_->queryMPD(); ctx_->queryMPD();
ctx_->emit(); ctx_->emit();
idle();
} catch (std::exception const& e) {
spdlog::warn("mpd: Playing: error: {}", e.what());
ctx_->setState(std::make_unique<Disconnected>(ctx_));
return false;
}
return true;
}
bool Playing::on_io(Glib::IOCondition const&) {
auto conn = ctx_->connection().get();
// callback should do this:
enum mpd_idle events = mpd_recv_idle(conn, /* ignore_timeout?= */ false);
spdlog::debug("mpd: Playing: recv_idle events -> {}", events);
mpd_response_finish(conn);
try {
ctx_->checkErrors(conn);
ctx_->fetchState();
if (!ctx_->is_playing()) {
if (ctx_->is_paused()) {
ctx_->setState(std::make_unique<Paused>(ctx_));
} else {
ctx_->setState(std::make_unique<Stopped>(ctx_));
}
return false;
}
ctx_->queryMPD();
ctx_->emit();
if (!mpd_send_idle_mask(
conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) {
ctx_->checkErrors(conn);
spdlog::error("mpd: Playing: failed to register for IDLE events");
}
// Defer the next timer
timer();
} catch (std::exception const& e) { } catch (std::exception const& e) {
spdlog::warn("mpd: Playing: error: {}", e.what()); spdlog::warn("mpd: Playing: error: {}", e.what());
ctx_->setState(std::make_unique<Disconnected>(ctx_)); ctx_->setState(std::make_unique<Disconnected>(ctx_));
@@ -164,21 +241,25 @@ bool Playing::on_timer() {
} }
void Playing::stop() { void Playing::stop() {
if (timer_connection_.connected()) { RUN_NOIDLE_AND_CMD(Playing,
timer_connection_.disconnect(); if (timer_connection_.connected()) {
timer_connection_.disconnect();
mpd_run_stop(ctx_->connection().get()); mpd_run_stop(ctx_->connection().get());
} }
);
ctx_->setState(std::make_unique<Stopped>(ctx_)); ctx_->setState(std::make_unique<Stopped>(ctx_));
} }
void Playing::pause() { void Playing::pause() {
if (timer_connection_.connected()) { RUN_NOIDLE_AND_CMD(Playing,
timer_connection_.disconnect(); if (timer_connection_.connected()) {
timer_connection_.disconnect();
mpd_run_pause(ctx_->connection().get(), true); mpd_run_pause(ctx_->connection().get(), true);
} }
);
ctx_->setState(std::make_unique<Paused>(ctx_)); ctx_->setState(std::make_unique<Paused>(ctx_));
} }
@@ -387,4 +468,5 @@ bool Disconnected::on_timer() {
void Disconnected::update() noexcept { ctx_->do_update(); } void Disconnected::update() noexcept { ctx_->do_update(); }
#undef RUN_NOIDLE_AND_CMD
} // namespace waybar::modules::detail } // namespace waybar::modules::detail