From 85b6e6f7096414f850a5272c0b4cc0ed197d19fa Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 16 Apr 2019 16:32:05 +0200 Subject: [PATCH 01/15] chore: Add EditorConfig file --- .editorconfig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8c2ecb0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig configuration for Waybar +# http://EditorConfig.org + +# Top-most EditorConfig file +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +[*.{build,css}] +indent_style = space +indent_size = 4 + +[*.{hpp,cpp}] +indent_style = space +indent_size = 2 From 06aff70e2e80f19e63497ca56f5594c1f4bfe82e Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 16 Apr 2019 16:34:37 +0200 Subject: [PATCH 02/15] feat: Add basic support for MPD --- README.md | 2 + include/factory.hpp | 3 + include/modules/mpd.hpp | 39 ++++++++++ meson.build | 9 ++- meson_options.txt | 1 + resources/style.css | 13 ++++ src/factory.cpp | 5 ++ src/modules/mpd.cpp | 164 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 include/modules/mpd.hpp create mode 100644 src/modules/mpd.cpp diff --git a/README.md b/README.md index 717c80c..e4464a2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ - Memory - Cpu load average - Temperature +- MPD - Custom scripts - Multiple output configuration - And much more customizations @@ -49,6 +50,7 @@ libpulse [Pulseaudio module] libnl [Network module] sway [Sway modules] libdbusmenu-gtk3 [Tray module] +libmpdclient [MPD module] ``` Contributions welcome! - have fun :)
diff --git a/include/factory.hpp b/include/factory.hpp index f896d05..2f55046 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -23,6 +23,9 @@ #ifdef HAVE_LIBPULSE #include "modules/pulseaudio.hpp" #endif +#ifdef HAVE_LIBMPDCLIENT +#include "modules/mpd.hpp" +#endif #include "modules/temperature.hpp" #include "modules/custom.hpp" diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp new file mode 100644 index 0000000..8623c41 --- /dev/null +++ b/include/modules/mpd.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include "ALabel.hpp" + +namespace waybar::modules { + +class MPD : public ALabel { + public: + MPD(const std::string&, const Json::Value&); + auto update() -> void; + private: + std::thread worker(); + void setLabel(); + void tryConnect(); + void checkErrors(); + void fetchState(); + void waitForEvent(); + + std::thread worker_; + + using unique_connection = std::unique_ptr; + using unique_status = std::unique_ptr; + using unique_song = std::unique_ptr; + + // Not using unique_ptr since we don't manage the pointer + // (It's either nullptr, or from the config) + const char* server; + + unique_connection connection_; + unique_status status_; + unique_song song_; + + bool stopped_; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index 9438518..2898e4e 100644 --- a/meson.build +++ b/meson.build @@ -56,6 +56,7 @@ libnl = dependency('libnl-3.0', required: get_option('libnl')) libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl')) libpulse = dependency('libpulse', required: get_option('pulseaudio')) libudev = dependency('libudev', required: get_option('libudev')) +libmpdclient = dependency('libmpdclient', required: get_option('mpd')) src_files = files( 'src/factory.cpp', @@ -107,6 +108,11 @@ if libudev.found() src_files += 'src/modules/backlight.cpp' endif +if libmpdclient.found() + add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') + src_files += 'src/modules/mpd.cpp' +endif + subdir('protocol') executable( @@ -128,7 +134,8 @@ executable( libnl, libnlgen, libpulse, - libudev + libudev, + libmpdclient ], include_directories: [include_directories('include')], install: true, diff --git a/meson_options.txt b/meson_options.txt index 4c2f03a..f49d9c1 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,4 +2,5 @@ option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl suppo option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features') option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio') option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray') +option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon') option('out', type: 'string', value : '/', description: 'output prefix directory') diff --git a/resources/style.css b/resources/style.css index 2a6f9be..8de2cdc 100644 --- a/resources/style.css +++ b/resources/style.css @@ -138,3 +138,16 @@ window#waybar.hidded { background-color: #ecf0f1; color: #2d3436; } + +#mpd { + background-color: #ba2880; +} + +#mpd.disconnected { + background: #f53c3c; +} + +#mpd.stopped { + background: #90b1b1; + color: #2a5c45; +} diff --git a/src/factory.cpp b/src/factory.cpp index 11e3db2..067ecac 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -56,6 +56,11 @@ waybar::IModule* waybar::Factory::makeModule(const std::string &name) const return new waybar::modules::Pulseaudio(id, config_[name]); } #endif + #ifdef HAVE_LIBMPDCLIENT + if (ref == "mpd") { + return new waybar::modules::MPD(id, config_[name]); + } + #endif if (ref == "temperature") { return new waybar::modules::Temperature(id, config_[name]); } diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp new file mode 100644 index 0000000..8502080 --- /dev/null +++ b/src/modules/mpd.cpp @@ -0,0 +1,164 @@ +#include "modules/mpd.hpp" + +#include + +waybar::modules::MPD::MPD(const std::string& id, const Json::Value &config) + : ALabel(config, "{album} - {artist} - {title}", 2), + server(nullptr), + connection_(nullptr, &mpd_connection_free), + status_(nullptr, &mpd_status_free), + song_(nullptr, &mpd_song_free) { + label_.set_name("mpd"); + if (!id.empty()) { + label_.get_style_context()->add_class(id); + } + + if (!config["server"].isNull()) { + server = config["server"].asCString(); + } + + worker_ = worker(); +} + +auto waybar::modules::MPD::update() -> void { + tryConnect(); + + if (connection_ != nullptr) { + try { + fetchState(); + } catch (std::exception e) { + stopped_ = true; + } + } + + setLabel(); +} + +std::thread waybar::modules::MPD::worker() { + return std::thread([this] () { + while (true) { + if (connection_ == nullptr) { + // Retry periodically if no connection + update(); + std::this_thread::sleep_for(std::chrono::seconds(2)); + } else { + // Else, update on any event + waitForEvent(); + update(); + } + } + }); +} + +void waybar::modules::MPD::setLabel() { + if (connection_ == nullptr) { + label_.get_style_context()->add_class("disconnected"); + // In the case connection closed while MPD is stopped + label_.get_style_context()->remove_class("stopped"); + + auto format = config_["format-disconnected"].isString() ? + config_["format-disconnected"].asString() : "disconnected"; + label_.set_markup(format); + return; + } else { + label_.get_style_context()->remove_class("disconnected"); + } + + auto format = format_; + + std::string artist, album_artist, album, title, date; + + if (stopped_) { + format = config_["format-stopped"].isString() ? + config_["format-stopped"].asString() : "stopped"; + label_.get_style_context()->add_class("stopped"); + } else { + label_.get_style_context()->remove_class("stopped"); + + artist = mpd_song_get_tag(song_.get(), MPD_TAG_ARTIST, 0); + album_artist = mpd_song_get_tag(song_.get(), MPD_TAG_ALBUM_ARTIST, 0); + album = mpd_song_get_tag(song_.get(), MPD_TAG_ALBUM, 0); + title = mpd_song_get_tag(song_.get(), MPD_TAG_TITLE, 0); + date = mpd_song_get_tag(song_.get(), MPD_TAG_DATE, 0); + } + + label_.set_markup(fmt::format(format, + fmt::arg("artist", artist), + fmt::arg("album-artist", album_artist), + fmt::arg("album", album), + fmt::arg("title", title), + fmt::arg("date", date))); + + if (tooltipEnabled()) { + std::string tooltip_format; + tooltip_format = config_["tooltip-format"].isString() ? + config_["tooltip-format"].asString() : "MPD"; + auto tooltip_text = fmt::format(tooltip_format, + fmt::arg("artist", artist), + fmt::arg("album-artist", album_artist), + fmt::arg("album", album), + fmt::arg("title", title), + fmt::arg("date", date)); + label_.set_tooltip_text(tooltip_text); + } +} + +void waybar::modules::MPD::tryConnect() { + if (connection_ != nullptr) { + return; + } + + connection_ = unique_connection( + mpd_connection_new(server, config_["port"].asUInt(), 5'000), + &mpd_connection_free); + + if (connection_ == nullptr) { + std::cerr << "Failed to connect to MPD" << std::endl; + return; + } + + try { + checkErrors(); + } catch (std::runtime_error e) { + std::cerr << "Failed to connect to MPD: " << e.what() << std::endl; + connection_.reset(); + } + +} + +void waybar::modules::MPD::checkErrors() { + auto conn = connection_.get(); + + switch (mpd_connection_get_error(conn)) { + case MPD_ERROR_SUCCESS: + return; + case MPD_ERROR_CLOSED: + std::cerr << "Connection to MPD closed" << std::endl; + mpd_connection_clear_error(conn); + connection_.reset(); + return; + default: + auto error_message = mpd_connection_get_error_message(conn); + mpd_connection_clear_error(conn); + throw std::runtime_error(std::string(error_message)); + } +} + +void waybar::modules::MPD::fetchState() { + status_ = unique_status(mpd_run_status(connection_.get()), &mpd_status_free); + checkErrors(); + mpd_state state = mpd_status_get_state(status_.get()); + checkErrors(); + stopped_ = state == MPD_STATE_UNKNOWN || state == MPD_STATE_STOP; + + mpd_send_current_song(connection_.get()); + song_ = unique_song(mpd_recv_song(connection_.get()), &mpd_song_free); + mpd_response_finish(connection_.get()); + checkErrors(); +} + +void waybar::modules::MPD::waitForEvent() { + auto conn = connection_.get(); + mpd_run_idle_mask(conn, MPD_IDLE_PLAYER /* | MPD_IDLE_OPTIONS */); + checkErrors(); +} From 8c9dd94670172c9386570e3ef52c95df8789b954 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 17 Apr 2019 10:46:08 +0200 Subject: [PATCH 03/15] feat(mpd): Add support for setting tooltip label when disconnected --- src/modules/mpd.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index 8502080..d120876 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -59,6 +59,14 @@ void waybar::modules::MPD::setLabel() { auto format = config_["format-disconnected"].isString() ? config_["format-disconnected"].asString() : "disconnected"; label_.set_markup(format); + + if (tooltipEnabled()) { + std::string tooltip_format; + tooltip_format = config_["tooltip-format-disconnected"].isString() ? + config_["tooltip-format-disconnected"].asString() : "MPD (disconnected)"; + // Nothing to format + label_.set_tooltip_text(tooltip_format); + } return; } else { label_.get_style_context()->remove_class("disconnected"); @@ -92,7 +100,7 @@ void waybar::modules::MPD::setLabel() { if (tooltipEnabled()) { std::string tooltip_format; tooltip_format = config_["tooltip-format"].isString() ? - config_["tooltip-format"].asString() : "MPD"; + config_["tooltip-format"].asString() : "MPD (connected)"; auto tooltip_text = fmt::format(tooltip_format, fmt::arg("artist", artist), fmt::arg("album-artist", album_artist), From 557b786ce096309fecc6daf1786b990ebe434170 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 17 Apr 2019 11:01:35 +0200 Subject: [PATCH 04/15] feat(mpd): Allow for specifying the reconnect interval --- include/modules/mpd.hpp | 3 ++- src/modules/mpd.cpp | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp index 8623c41..5ae0f9e 100644 --- a/include/modules/mpd.hpp +++ b/include/modules/mpd.hpp @@ -27,7 +27,8 @@ class MPD : public ALabel { // Not using unique_ptr since we don't manage the pointer // (It's either nullptr, or from the config) - const char* server; + const char* server_; + unsigned port_; unique_connection connection_; unique_status status_; diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index d120876..f46393f 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -3,8 +3,9 @@ #include waybar::modules::MPD::MPD(const std::string& id, const Json::Value &config) - : ALabel(config, "{album} - {artist} - {title}", 2), - server(nullptr), + : ALabel(config, "{album} - {artist} - {title}", 5), + server_(nullptr), + port_(config["port"].asUInt()), connection_(nullptr, &mpd_connection_free), status_(nullptr, &mpd_status_free), song_(nullptr, &mpd_song_free) { @@ -14,7 +15,7 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value &config) } if (!config["server"].isNull()) { - server = config["server"].asCString(); + server_ = config["server"].asCString(); } worker_ = worker(); @@ -40,7 +41,7 @@ std::thread waybar::modules::MPD::worker() { if (connection_ == nullptr) { // Retry periodically if no connection update(); - std::this_thread::sleep_for(std::chrono::seconds(2)); + std::this_thread::sleep_for(interval_); } else { // Else, update on any event waitForEvent(); @@ -117,7 +118,7 @@ void waybar::modules::MPD::tryConnect() { } connection_ = unique_connection( - mpd_connection_new(server, config_["port"].asUInt(), 5'000), + mpd_connection_new(server_, port_, 5'000), &mpd_connection_free); if (connection_ == nullptr) { From cbfcec486721367f6715e09873f4ef65a2ec2ea4 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 17 Apr 2019 13:55:08 +0200 Subject: [PATCH 05/15] feat(mpd): Add support for play/pause icons --- include/modules/mpd.hpp | 6 +++++- src/modules/mpd.cpp | 42 +++++++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp index 5ae0f9e..4916970 100644 --- a/include/modules/mpd.hpp +++ b/include/modules/mpd.hpp @@ -14,8 +14,11 @@ class MPD : public ALabel { private: std::thread worker(); void setLabel(); + std::string getStateIcon(); + void tryConnect(); void checkErrors(); + void fetchState(); void waitForEvent(); @@ -28,10 +31,11 @@ class MPD : public ALabel { // Not using unique_ptr since we don't manage the pointer // (It's either nullptr, or from the config) const char* server_; - unsigned port_; + const unsigned port_; unique_connection connection_; unique_status status_; + mpd_state state_; unique_song song_; bool stopped_; diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index f46393f..1da6877 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -77,6 +77,7 @@ void waybar::modules::MPD::setLabel() { std::string artist, album_artist, album, title, date; + std::string stateIcon = ""; if (stopped_) { format = config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped"; @@ -84,6 +85,8 @@ void waybar::modules::MPD::setLabel() { } else { label_.get_style_context()->remove_class("stopped"); + stateIcon = getStateIcon(); + artist = mpd_song_get_tag(song_.get(), MPD_TAG_ARTIST, 0); album_artist = mpd_song_get_tag(song_.get(), MPD_TAG_ALBUM_ARTIST, 0); album = mpd_song_get_tag(song_.get(), MPD_TAG_ALBUM, 0); @@ -91,12 +94,14 @@ void waybar::modules::MPD::setLabel() { date = mpd_song_get_tag(song_.get(), MPD_TAG_DATE, 0); } + // TODO: format can fail label_.set_markup(fmt::format(format, fmt::arg("artist", artist), - fmt::arg("album-artist", album_artist), + fmt::arg("albumArtist", album_artist), fmt::arg("album", album), fmt::arg("title", title), - fmt::arg("date", date))); + fmt::arg("date", date), + fmt::arg("stateIcon", stateIcon))); if (tooltipEnabled()) { std::string tooltip_format; @@ -104,14 +109,39 @@ void waybar::modules::MPD::setLabel() { config_["tooltip-format"].asString() : "MPD (connected)"; auto tooltip_text = fmt::format(tooltip_format, fmt::arg("artist", artist), - fmt::arg("album-artist", album_artist), + fmt::arg("albumArtist", album_artist), fmt::arg("album", album), fmt::arg("title", title), - fmt::arg("date", date)); + fmt::arg("date", date), + fmt::arg("stateIcon", stateIcon)); label_.set_tooltip_text(tooltip_text); } } +std::string waybar::modules::MPD::getStateIcon() { + if (!config_["state-icons"].isObject()) { + std::cerr << "No state icons defined" << std::endl; + return ""; + } + + if (connection_ == nullptr) { + std::cerr << "MPD: Trying to fetch state icon while disconnected" << std::endl; + return ""; + } + + if (stopped_) { + std::cerr << "MPD: Trying to fetch state icon while stopped" << std::endl; + return ""; + } + + if (state_ == MPD_STATE_PLAY) { + return config_["state-icons"]["playing"].asString(); + } else { + // MPD_STATE_PAUSE + return config_["state-icons"]["paused"].asString(); + } +} + void waybar::modules::MPD::tryConnect() { if (connection_ != nullptr) { return; @@ -156,9 +186,9 @@ void waybar::modules::MPD::checkErrors() { void waybar::modules::MPD::fetchState() { status_ = unique_status(mpd_run_status(connection_.get()), &mpd_status_free); checkErrors(); - mpd_state state = mpd_status_get_state(status_.get()); + state_ = mpd_status_get_state(status_.get()); checkErrors(); - stopped_ = state == MPD_STATE_UNKNOWN || state == MPD_STATE_STOP; + stopped_ = state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP; mpd_send_current_song(connection_.get()); song_ = unique_song(mpd_recv_song(connection_.get()), &mpd_song_free); From 07dab2baec768276718d7e88f872ba497b1f8395 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 17 Apr 2019 15:09:06 +0200 Subject: [PATCH 06/15] feat(mpd): Add support for options (random, repeat, etc.) --- include/modules/mpd.hpp | 3 ++- src/modules/mpd.cpp | 46 ++++++++++++++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp index 4916970..fa9662c 100644 --- a/include/modules/mpd.hpp +++ b/include/modules/mpd.hpp @@ -15,6 +15,7 @@ class MPD : public ALabel { std::thread worker(); void setLabel(); std::string getStateIcon(); + std::string getOptionIcon(std::string optionName, bool activated); void tryConnect(); void checkErrors(); @@ -26,7 +27,7 @@ class MPD : public ALabel { using unique_connection = std::unique_ptr; using unique_status = std::unique_ptr; - using unique_song = std::unique_ptr; + using unique_song = std::unique_ptr; // Not using unique_ptr since we don't manage the pointer // (It's either nullptr, or from the config) diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index 1da6877..aafc473 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -94,6 +94,15 @@ void waybar::modules::MPD::setLabel() { date = mpd_song_get_tag(song_.get(), MPD_TAG_DATE, 0); } + bool consumeActivated = mpd_status_get_consume(status_.get()); + std::string consumeIcon = getOptionIcon("consume", consumeActivated); + bool randomActivated = mpd_status_get_random(status_.get()); + std::string randomIcon = getOptionIcon("random", randomActivated); + bool repeatActivated = mpd_status_get_repeat(status_.get()); + std::string repeatIcon = getOptionIcon("repeat", repeatActivated); + bool singleActivated = mpd_status_get_single(status_.get()); + std::string singleIcon = getOptionIcon("single", singleActivated); + // TODO: format can fail label_.set_markup(fmt::format(format, fmt::arg("artist", artist), @@ -101,7 +110,11 @@ void waybar::modules::MPD::setLabel() { fmt::arg("album", album), fmt::arg("title", title), fmt::arg("date", date), - fmt::arg("stateIcon", stateIcon))); + fmt::arg("stateIcon", stateIcon), + fmt::arg("consumeIcon", consumeIcon), + fmt::arg("randomIcon", randomIcon), + fmt::arg("repeatIcon", repeatIcon), + fmt::arg("singleIcon", singleIcon))); if (tooltipEnabled()) { std::string tooltip_format; @@ -113,14 +126,17 @@ void waybar::modules::MPD::setLabel() { fmt::arg("album", album), fmt::arg("title", title), fmt::arg("date", date), - fmt::arg("stateIcon", stateIcon)); + fmt::arg("stateIcon", stateIcon), + fmt::arg("consumeIcon", consumeIcon), + fmt::arg("randomIcon", randomIcon), + fmt::arg("repeatIcon", repeatIcon), + fmt::arg("singleIcon", singleIcon)); label_.set_tooltip_text(tooltip_text); } } std::string waybar::modules::MPD::getStateIcon() { if (!config_["state-icons"].isObject()) { - std::cerr << "No state icons defined" << std::endl; return ""; } @@ -142,6 +158,23 @@ std::string waybar::modules::MPD::getStateIcon() { } } +std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) { + if (!config_[optionName + "-icons"].isObject()) { + return ""; + } + + if (connection_ == nullptr) { + std::cerr << "MPD: Trying to fetch option icon while disconnected" << std::endl; + return ""; + } + + if (activated) { + return config_[optionName + "-icons"]["on"].asString(); + } else { + return config_[optionName + "-icons"]["off"].asString(); + } +} + void waybar::modules::MPD::tryConnect() { if (connection_ != nullptr) { return; @@ -190,14 +223,13 @@ void waybar::modules::MPD::fetchState() { checkErrors(); stopped_ = state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP; - mpd_send_current_song(connection_.get()); - song_ = unique_song(mpd_recv_song(connection_.get()), &mpd_song_free); - mpd_response_finish(connection_.get()); + song_ = unique_song(mpd_run_current_song(connection_.get()), &mpd_song_free); checkErrors(); } void waybar::modules::MPD::waitForEvent() { auto conn = connection_.get(); - mpd_run_idle_mask(conn, MPD_IDLE_PLAYER /* | MPD_IDLE_OPTIONS */); + // Wait for a player (play/pause), option (random, shuffle, etc.), or playlist change + mpd_run_idle_mask(conn, static_cast(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_PLAYLIST)); checkErrors(); } From 80a12d023819266231af12cf3bfb645778bf83bb Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 17 Apr 2019 16:16:39 +0200 Subject: [PATCH 07/15] feat(mpd): play/pause on click & stop on right-click --- include/modules/mpd.hpp | 5 +++++ src/modules/mpd.cpp | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp index fa9662c..3bcd6ea 100644 --- a/include/modules/mpd.hpp +++ b/include/modules/mpd.hpp @@ -23,6 +23,8 @@ class MPD : public ALabel { void fetchState(); void waitForEvent(); + bool handlePlayPause(GdkEventButton* const&); + std::thread worker_; using unique_connection = std::unique_ptr; @@ -35,6 +37,9 @@ class MPD : public ALabel { const unsigned port_; unique_connection connection_; + // Use two connections because the main one will be blocking most of the time. + // Used to send play/pause events and poll elapsed time. + unique_connection alternate_connection_; unique_status status_; mpd_state state_; unique_song song_; diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index aafc473..478fd5a 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -7,6 +7,7 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value &config) server_(nullptr), port_(config["port"].asUInt()), connection_(nullptr, &mpd_connection_free), + alternate_connection_(nullptr, &mpd_connection_free), status_(nullptr, &mpd_status_free), song_(nullptr, &mpd_song_free) { label_.set_name("mpd"); @@ -19,6 +20,10 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value &config) } worker_ = worker(); + + event_box_.add_events(Gdk::BUTTON_PRESS_MASK); + event_box_.signal_button_press_event().connect( + sigc::mem_fun(*this, &MPD::handlePlayPause)); } auto waybar::modules::MPD::update() -> void { @@ -184,8 +189,14 @@ void waybar::modules::MPD::tryConnect() { mpd_connection_new(server_, port_, 5'000), &mpd_connection_free); - if (connection_ == nullptr) { + alternate_connection_ = unique_connection( + mpd_connection_new(server_, port_, 5'000), + &mpd_connection_free); + + if (connection_ == nullptr || alternate_connection_ == nullptr) { std::cerr << "Failed to connect to MPD" << std::endl; + connection_.reset(); + alternate_connection_.reset(); return; } @@ -194,6 +205,7 @@ void waybar::modules::MPD::tryConnect() { } catch (std::runtime_error e) { std::cerr << "Failed to connect to MPD: " << e.what() << std::endl; connection_.reset(); + alternate_connection_.reset(); } } @@ -208,6 +220,7 @@ void waybar::modules::MPD::checkErrors() { std::cerr << "Connection to MPD closed" << std::endl; mpd_connection_clear_error(conn); connection_.reset(); + alternate_connection_.reset(); return; default: auto error_message = mpd_connection_get_error_message(conn); @@ -233,3 +246,21 @@ void waybar::modules::MPD::waitForEvent() { mpd_run_idle_mask(conn, static_cast(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_PLAYLIST)); checkErrors(); } + +bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) { + if (e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS || alternate_connection_ == nullptr) { + return false; + } + + if (e->button == 1) { + if (stopped_) { + mpd_run_play(alternate_connection_.get()); + } else { + mpd_run_toggle_pause(alternate_connection_.get()); + } + } else if (e->button == 3) { + mpd_run_stop(alternate_connection_.get()); + } + + return true; +} From 235997fa7330b044471ebb25b2bf045ab04342e7 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Thu, 18 Apr 2019 11:17:08 +0200 Subject: [PATCH 08/15] feat(mpd): Add support for elapsed and total time --- include/modules/mpd.hpp | 30 ++++++++---- src/modules/mpd.cpp | 100 +++++++++++++++++++++++++--------------- 2 files changed, 85 insertions(+), 45 deletions(-) diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp index 3bcd6ea..adb688a 100644 --- a/include/modules/mpd.hpp +++ b/include/modules/mpd.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -12,20 +13,27 @@ class MPD : public ALabel { MPD(const std::string&, const Json::Value&); auto update() -> void; private: - std::thread worker(); + std::thread periodic_updater(); void setLabel(); std::string getStateIcon(); std::string getOptionIcon(std::string optionName, bool activated); - void tryConnect(); - void checkErrors(); + std::thread event_listener(); + // Assumes `connection_lock_` is locked + void tryConnect(); + // If checking errors on the main connection, make sure to lock it using + // `connection_lock_` before calling checkErrors + void checkErrors(mpd_connection* conn); + + // Assumes `connection_lock_` is locked void fetchState(); void waitForEvent(); bool handlePlayPause(GdkEventButton* const&); - std::thread worker_; + bool stopped(); + bool playing(); using unique_connection = std::unique_ptr; using unique_status = std::unique_ptr; @@ -36,15 +44,21 @@ class MPD : public ALabel { const char* server_; const unsigned port_; + // We need a mutex here because we can trigger updates from multiple thread: + // the event based updates, the periodic updates needed for the elapsed time, + // and the click play/pause feature + std::mutex connection_lock_; unique_connection connection_; - // Use two connections because the main one will be blocking most of the time. - // Used to send play/pause events and poll elapsed time. + // The alternate connection will be used to wait for events: since it will + // be blocking (idle) we can't send commands via this connection + // + // No lock since only used in the event listener thread unique_connection alternate_connection_; + + // Protect them using the `connection_lock_` unique_status status_; mpd_state state_; unique_song song_; - - bool stopped_; }; } // namespace waybar::modules diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index 478fd5a..79f1615 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -1,5 +1,7 @@ #include "modules/mpd.hpp" +#include + #include waybar::modules::MPD::MPD(const std::string& id, const Json::Value &config) @@ -19,7 +21,7 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value &config) server_ = config["server"].asCString(); } - worker_ = worker(); + event_listener().detach(); event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect( @@ -27,32 +29,45 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value &config) } auto waybar::modules::MPD::update() -> void { + std::lock_guard guard(connection_lock_); tryConnect(); if (connection_ != nullptr) { try { + bool wasPlaying = playing(); fetchState(); + if (!wasPlaying && playing()) { + periodic_updater().detach(); + } } catch (std::exception e) { - stopped_ = true; + state_ = MPD_STATE_UNKNOWN; } } setLabel(); } -std::thread waybar::modules::MPD::worker() { - return std::thread([this] () { - while (true) { - if (connection_ == nullptr) { - // Retry periodically if no connection - update(); - std::this_thread::sleep_for(interval_); - } else { - // Else, update on any event - waitForEvent(); - update(); - } +std::thread waybar::modules::MPD::event_listener() { + return std::thread([this] { + while (true) { + if (connection_ == nullptr) { + // Retry periodically if no connection + update(); + std::this_thread::sleep_for(interval_); + } else { + waitForEvent(); + dp.emit(); } + } + }); +} + +std::thread waybar::modules::MPD::periodic_updater() { + return std::thread([this] { + while (connection_ != nullptr && playing()) { + dp.emit(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } }); } @@ -81,9 +96,10 @@ void waybar::modules::MPD::setLabel() { auto format = format_; std::string artist, album_artist, album, title, date; + std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; - if (stopped_) { + if (stopped()) { format = config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped"; label_.get_style_context()->add_class("stopped"); @@ -97,6 +113,8 @@ void waybar::modules::MPD::setLabel() { album = mpd_song_get_tag(song_.get(), MPD_TAG_ALBUM, 0); title = mpd_song_get_tag(song_.get(), MPD_TAG_TITLE, 0); date = mpd_song_get_tag(song_.get(), MPD_TAG_DATE, 0); + elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get())); + totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get())); } bool consumeActivated = mpd_status_get_consume(status_.get()); @@ -115,6 +133,8 @@ void waybar::modules::MPD::setLabel() { fmt::arg("album", album), fmt::arg("title", title), fmt::arg("date", date), + fmt::arg("elapsedTime", elapsedTime), + fmt::arg("totalTime", totalTime), fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon), @@ -150,15 +170,14 @@ std::string waybar::modules::MPD::getStateIcon() { return ""; } - if (stopped_) { + if (stopped()) { std::cerr << "MPD: Trying to fetch state icon while stopped" << std::endl; return ""; } - if (state_ == MPD_STATE_PLAY) { + if (playing()) { return config_["state-icons"]["playing"].asString(); } else { - // MPD_STATE_PAUSE return config_["state-icons"]["paused"].asString(); } } @@ -201,18 +220,15 @@ void waybar::modules::MPD::tryConnect() { } try { - checkErrors(); + checkErrors(connection_.get()); } catch (std::runtime_error e) { std::cerr << "Failed to connect to MPD: " << e.what() << std::endl; connection_.reset(); alternate_connection_.reset(); } - } -void waybar::modules::MPD::checkErrors() { - auto conn = connection_.get(); - +void waybar::modules::MPD::checkErrors(mpd_connection* conn) { switch (mpd_connection_get_error(conn)) { case MPD_ERROR_SUCCESS: return; @@ -230,37 +246,47 @@ void waybar::modules::MPD::checkErrors() { } void waybar::modules::MPD::fetchState() { - status_ = unique_status(mpd_run_status(connection_.get()), &mpd_status_free); - checkErrors(); - state_ = mpd_status_get_state(status_.get()); - checkErrors(); - stopped_ = state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP; + auto conn = connection_.get(); + status_ = unique_status(mpd_run_status(conn), &mpd_status_free); + checkErrors(conn); + state_ = mpd_status_get_state(status_.get()); + checkErrors(conn); - song_ = unique_song(mpd_run_current_song(connection_.get()), &mpd_song_free); - checkErrors(); + song_ = unique_song(mpd_run_current_song(conn), &mpd_song_free); + checkErrors(conn); } void waybar::modules::MPD::waitForEvent() { - auto conn = connection_.get(); + auto conn = alternate_connection_.get(); // Wait for a player (play/pause), option (random, shuffle, etc.), or playlist change mpd_run_idle_mask(conn, static_cast(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_PLAYLIST)); - checkErrors(); + checkErrors(conn); } bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) { - if (e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS || alternate_connection_ == nullptr) { + if (e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS || connection_ == nullptr) { return false; } if (e->button == 1) { - if (stopped_) { - mpd_run_play(alternate_connection_.get()); + std::lock_guard guard(connection_lock_); + if (stopped()) { + mpd_run_play(connection_.get()); } else { - mpd_run_toggle_pause(alternate_connection_.get()); + mpd_run_toggle_pause(connection_.get()); } } else if (e->button == 3) { - mpd_run_stop(alternate_connection_.get()); + std::lock_guard guard(connection_lock_); + mpd_run_stop(connection_.get()); } return true; } + +bool waybar::modules::MPD::stopped() { + return connection_ == nullptr || state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP; +} + +bool waybar::modules::MPD::playing() { + return connection_ != nullptr && state_ == MPD_STATE_PLAY; +} From cd92b475ad69c95ca6fbb01d5075b408d7890900 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Thu, 18 Apr 2019 11:31:47 +0200 Subject: [PATCH 09/15] chore: Add clang-format configuration and format MPD module --- .clang-format | 5 +- include/modules/mpd.hpp | 87 ++++++++++++++++---------------- src/modules/mpd.cpp | 109 +++++++++++++++++++--------------------- 3 files changed, 100 insertions(+), 101 deletions(-) diff --git a/.clang-format b/.clang-format index 4bd6b39..c428ccc 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,6 @@ --- BasedOnStyle: Google -ColumnLimit: '100' - +AlignConsecutiveDeclarations: true +BinPackArguments: false +ColumnLimit: 100 ... diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp index adb688a..3f2d64f 100644 --- a/include/modules/mpd.hpp +++ b/include/modules/mpd.hpp @@ -1,64 +1,65 @@ #pragma once -#include -#include #include #include +#include +#include #include "ALabel.hpp" namespace waybar::modules { class MPD : public ALabel { - public: - MPD(const std::string&, const Json::Value&); - auto update() -> void; - private: - std::thread periodic_updater(); - void setLabel(); - std::string getStateIcon(); - std::string getOptionIcon(std::string optionName, bool activated); + public: + MPD(const std::string&, const Json::Value&); + auto update() -> void; - std::thread event_listener(); + private: + std::thread periodic_updater(); + void setLabel(); + std::string getStateIcon(); + std::string getOptionIcon(std::string optionName, bool activated); - // Assumes `connection_lock_` is locked - void tryConnect(); - // If checking errors on the main connection, make sure to lock it using - // `connection_lock_` before calling checkErrors - void checkErrors(mpd_connection* conn); + std::thread event_listener(); - // Assumes `connection_lock_` is locked - void fetchState(); - void waitForEvent(); + // Assumes `connection_lock_` is locked + void tryConnect(); + // If checking errors on the main connection, make sure to lock it using + // `connection_lock_` before calling checkErrors + void checkErrors(mpd_connection* conn); - bool handlePlayPause(GdkEventButton* const&); + // Assumes `connection_lock_` is locked + void fetchState(); + void waitForEvent(); - bool stopped(); - bool playing(); + bool handlePlayPause(GdkEventButton* const&); - using unique_connection = std::unique_ptr; - using unique_status = std::unique_ptr; - using unique_song = std::unique_ptr; + bool stopped(); + bool playing(); - // Not using unique_ptr since we don't manage the pointer - // (It's either nullptr, or from the config) - const char* server_; - const unsigned port_; + using unique_connection = std::unique_ptr; + using unique_status = std::unique_ptr; + using unique_song = std::unique_ptr; - // We need a mutex here because we can trigger updates from multiple thread: - // the event based updates, the periodic updates needed for the elapsed time, - // and the click play/pause feature - std::mutex connection_lock_; - unique_connection connection_; - // The alternate connection will be used to wait for events: since it will - // be blocking (idle) we can't send commands via this connection - // - // No lock since only used in the event listener thread - unique_connection alternate_connection_; + // Not using unique_ptr since we don't manage the pointer + // (It's either nullptr, or from the config) + const char* server_; + const unsigned port_; - // Protect them using the `connection_lock_` - unique_status status_; - mpd_state state_; - unique_song song_; + // We need a mutex here because we can trigger updates from multiple thread: + // the event based updates, the periodic updates needed for the elapsed time, + // and the click play/pause feature + std::mutex connection_lock_; + unique_connection connection_; + // The alternate connection will be used to wait for events: since it will + // be blocking (idle) we can't send commands via this connection + // + // No lock since only used in the event listener thread + unique_connection alternate_connection_; + + // Protect them using the `connection_lock_` + unique_status status_; + mpd_state state_; + unique_song song_; }; } // namespace waybar::modules diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index 79f1615..246be9f 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -1,10 +1,9 @@ #include "modules/mpd.hpp" #include - #include -waybar::modules::MPD::MPD(const std::string& id, const Json::Value &config) +waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) : ALabel(config, "{album} - {artist} - {title}", 5), server_(nullptr), port_(config["port"].asUInt()), @@ -24,8 +23,7 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value &config) event_listener().detach(); event_box_.add_events(Gdk::BUTTON_PRESS_MASK); - event_box_.signal_button_press_event().connect( - sigc::mem_fun(*this, &MPD::handlePlayPause)); + event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &MPD::handlePlayPause)); } auto waybar::modules::MPD::update() -> void { @@ -77,14 +75,16 @@ void waybar::modules::MPD::setLabel() { // In the case connection closed while MPD is stopped label_.get_style_context()->remove_class("stopped"); - auto format = config_["format-disconnected"].isString() ? - config_["format-disconnected"].asString() : "disconnected"; + auto format = config_["format-disconnected"].isString() + ? config_["format-disconnected"].asString() + : "disconnected"; label_.set_markup(format); if (tooltipEnabled()) { std::string tooltip_format; - tooltip_format = config_["tooltip-format-disconnected"].isString() ? - config_["tooltip-format-disconnected"].asString() : "MPD (disconnected)"; + tooltip_format = config_["tooltip-format-disconnected"].isString() + ? config_["tooltip-format-disconnected"].asString() + : "MPD (disconnected)"; // Nothing to format label_.set_tooltip_text(tooltip_format); } @@ -95,67 +95,67 @@ void waybar::modules::MPD::setLabel() { auto format = format_; - std::string artist, album_artist, album, title, date; + std::string artist, album_artist, album, title, date; std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; if (stopped()) { - format = config_["format-stopped"].isString() ? - config_["format-stopped"].asString() : "stopped"; + format = + config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped"; label_.get_style_context()->add_class("stopped"); } else { label_.get_style_context()->remove_class("stopped"); stateIcon = getStateIcon(); - artist = mpd_song_get_tag(song_.get(), MPD_TAG_ARTIST, 0); + artist = mpd_song_get_tag(song_.get(), MPD_TAG_ARTIST, 0); album_artist = mpd_song_get_tag(song_.get(), MPD_TAG_ALBUM_ARTIST, 0); - album = mpd_song_get_tag(song_.get(), MPD_TAG_ALBUM, 0); - title = mpd_song_get_tag(song_.get(), MPD_TAG_TITLE, 0); - date = mpd_song_get_tag(song_.get(), MPD_TAG_DATE, 0); - elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get())); - totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get())); + album = mpd_song_get_tag(song_.get(), MPD_TAG_ALBUM, 0); + title = mpd_song_get_tag(song_.get(), MPD_TAG_TITLE, 0); + date = mpd_song_get_tag(song_.get(), MPD_TAG_DATE, 0); + elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get())); + totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get())); } - bool consumeActivated = mpd_status_get_consume(status_.get()); + bool consumeActivated = mpd_status_get_consume(status_.get()); std::string consumeIcon = getOptionIcon("consume", consumeActivated); - bool randomActivated = mpd_status_get_random(status_.get()); + bool randomActivated = mpd_status_get_random(status_.get()); std::string randomIcon = getOptionIcon("random", randomActivated); - bool repeatActivated = mpd_status_get_repeat(status_.get()); + bool repeatActivated = mpd_status_get_repeat(status_.get()); std::string repeatIcon = getOptionIcon("repeat", repeatActivated); - bool singleActivated = mpd_status_get_single(status_.get()); + bool singleActivated = mpd_status_get_single(status_.get()); std::string singleIcon = getOptionIcon("single", singleActivated); // TODO: format can fail label_.set_markup(fmt::format(format, - fmt::arg("artist", artist), - fmt::arg("albumArtist", album_artist), - fmt::arg("album", album), - fmt::arg("title", title), - fmt::arg("date", date), - fmt::arg("elapsedTime", elapsedTime), - fmt::arg("totalTime", totalTime), - fmt::arg("stateIcon", stateIcon), - fmt::arg("consumeIcon", consumeIcon), - fmt::arg("randomIcon", randomIcon), - fmt::arg("repeatIcon", repeatIcon), - fmt::arg("singleIcon", singleIcon))); + fmt::arg("artist", artist), + fmt::arg("albumArtist", album_artist), + fmt::arg("album", album), + fmt::arg("title", title), + fmt::arg("date", date), + fmt::arg("elapsedTime", elapsedTime), + fmt::arg("totalTime", totalTime), + fmt::arg("stateIcon", stateIcon), + fmt::arg("consumeIcon", consumeIcon), + fmt::arg("randomIcon", randomIcon), + fmt::arg("repeatIcon", repeatIcon), + fmt::arg("singleIcon", singleIcon))); if (tooltipEnabled()) { std::string tooltip_format; - tooltip_format = config_["tooltip-format"].isString() ? - config_["tooltip-format"].asString() : "MPD (connected)"; + tooltip_format = config_["tooltip-format"].isString() ? config_["tooltip-format"].asString() + : "MPD (connected)"; auto tooltip_text = fmt::format(tooltip_format, - fmt::arg("artist", artist), - fmt::arg("albumArtist", album_artist), - fmt::arg("album", album), - fmt::arg("title", title), - fmt::arg("date", date), - fmt::arg("stateIcon", stateIcon), - fmt::arg("consumeIcon", consumeIcon), - fmt::arg("randomIcon", randomIcon), - fmt::arg("repeatIcon", repeatIcon), - fmt::arg("singleIcon", singleIcon)); + fmt::arg("artist", artist), + fmt::arg("albumArtist", album_artist), + fmt::arg("album", album), + fmt::arg("title", title), + fmt::arg("date", date), + fmt::arg("stateIcon", stateIcon), + fmt::arg("consumeIcon", consumeIcon), + fmt::arg("randomIcon", randomIcon), + fmt::arg("repeatIcon", repeatIcon), + fmt::arg("singleIcon", singleIcon)); label_.set_tooltip_text(tooltip_text); } } @@ -204,13 +204,10 @@ void waybar::modules::MPD::tryConnect() { return; } - connection_ = unique_connection( - mpd_connection_new(server_, port_, 5'000), - &mpd_connection_free); + connection_ = unique_connection(mpd_connection_new(server_, port_, 5'000), &mpd_connection_free); - alternate_connection_ = unique_connection( - mpd_connection_new(server_, port_, 5'000), - &mpd_connection_free); + alternate_connection_ = + unique_connection(mpd_connection_new(server_, port_, 5'000), &mpd_connection_free); if (connection_ == nullptr || alternate_connection_ == nullptr) { std::cerr << "Failed to connect to MPD" << std::endl; @@ -258,8 +255,10 @@ void waybar::modules::MPD::fetchState() { void waybar::modules::MPD::waitForEvent() { auto conn = alternate_connection_.get(); - // Wait for a player (play/pause), option (random, shuffle, etc.), or playlist change - mpd_run_idle_mask(conn, static_cast(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_PLAYLIST)); + // Wait for a player (play/pause), option (random, shuffle, etc.), or playlist + // change + mpd_run_idle_mask(conn, + static_cast(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_PLAYLIST)); checkErrors(conn); } @@ -287,6 +286,4 @@ bool waybar::modules::MPD::stopped() { return connection_ == nullptr || state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP; } -bool waybar::modules::MPD::playing() { - return connection_ != nullptr && state_ == MPD_STATE_PLAY; -} +bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; } From 22eccc2ac250e71ad14eaac1ca6179fa3d325628 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Thu, 18 Apr 2019 11:39:04 +0200 Subject: [PATCH 10/15] feat(mpd): reset player state when connection drops --- src/modules/mpd.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index 246be9f..0313e12 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -234,6 +234,7 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) { mpd_connection_clear_error(conn); connection_.reset(); alternate_connection_.reset(); + state_ = MPD_STATE_UNKNOWN; return; default: auto error_message = mpd_connection_get_error_message(conn); From ab43d34a1e4a6e69aaab64a639d7be5d65ccf61e Mon Sep 17 00:00:00 2001 From: Minijackson Date: Thu, 18 Apr 2019 11:48:38 +0200 Subject: [PATCH 11/15] refactor(mpd): Add module name to log messages --- include/modules/mpd.hpp | 2 ++ src/modules/mpd.cpp | 13 +++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp index 3f2d64f..b3134f4 100644 --- a/include/modules/mpd.hpp +++ b/include/modules/mpd.hpp @@ -36,6 +36,8 @@ class MPD : public ALabel { bool stopped(); bool playing(); + const std::string module_name_; + using unique_connection = std::unique_ptr; using unique_status = std::unique_ptr; using unique_song = std::unique_ptr; diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index 0313e12..7e4e88b 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -5,6 +5,7 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) : ALabel(config, "{album} - {artist} - {title}", 5), + module_name_(id.empty() ? "mpd" : "mpd#" + id), server_(nullptr), port_(config["port"].asUInt()), connection_(nullptr, &mpd_connection_free), @@ -166,12 +167,12 @@ std::string waybar::modules::MPD::getStateIcon() { } if (connection_ == nullptr) { - std::cerr << "MPD: Trying to fetch state icon while disconnected" << std::endl; + std::cerr << module_name_ << ": Trying to fetch state icon while disconnected" << std::endl; return ""; } if (stopped()) { - std::cerr << "MPD: Trying to fetch state icon while stopped" << std::endl; + std::cerr << module_name_ << ": Trying to fetch state icon while stopped" << std::endl; return ""; } @@ -188,7 +189,7 @@ std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool act } if (connection_ == nullptr) { - std::cerr << "MPD: Trying to fetch option icon while disconnected" << std::endl; + std::cerr << module_name_ << ": Trying to fetch option icon while disconnected" << std::endl; return ""; } @@ -210,7 +211,7 @@ void waybar::modules::MPD::tryConnect() { unique_connection(mpd_connection_new(server_, port_, 5'000), &mpd_connection_free); if (connection_ == nullptr || alternate_connection_ == nullptr) { - std::cerr << "Failed to connect to MPD" << std::endl; + std::cerr << module_name_ << ": Failed to connect to MPD" << std::endl; connection_.reset(); alternate_connection_.reset(); return; @@ -219,7 +220,7 @@ void waybar::modules::MPD::tryConnect() { try { checkErrors(connection_.get()); } catch (std::runtime_error e) { - std::cerr << "Failed to connect to MPD: " << e.what() << std::endl; + std::cerr << module_name_ << ": Failed to connect to MPD: " << e.what() << std::endl; connection_.reset(); alternate_connection_.reset(); } @@ -230,7 +231,7 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) { case MPD_ERROR_SUCCESS: return; case MPD_ERROR_CLOSED: - std::cerr << "Connection to MPD closed" << std::endl; + std::cerr << module_name_ << ": Connection to MPD closed" << std::endl; mpd_connection_clear_error(conn); connection_.reset(); alternate_connection_.reset(); From 38e37f3680081a0e421d640395fa2c8b8b3066c6 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Thu, 18 Apr 2019 14:19:46 +0200 Subject: [PATCH 12/15] chore(mpd): add sample MPD config --- resources/config | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/resources/config b/resources/config index 8b4a4be..c06ec1a 100644 --- a/resources/config +++ b/resources/config @@ -6,7 +6,7 @@ // Choose the order of the modules "modules-left": ["sway/workspaces", "sway/mode", "custom/media"], "modules-center": ["sway/window"], - "modules-right": ["idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "battery", "battery#bat2", "clock", "tray"], + "modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "battery", "battery#bat2", "clock", "tray"], // Modules configuration // "sway/workspaces": { // "disable-scroll": true, @@ -26,6 +26,31 @@ "sway/mode": { "format": "{}" }, + "mpd": { + "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ", + "format-disconnected": "Disconnected ", + "format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ", + "interval": 2, + "consume-icons": { + "on": " " + }, + "random-icons": { + "off": " ", + "on": " " + }, + "repeat-icons": { + "on": " " + }, + "single-icons": { + "on": "1 " + }, + "state-icons": { + "paused": "", + "playing": "" + }, + "tooltip-format": "MPD (connected)", + "tooltip-format-disconnected": "MPD (disconnected)" + } "idle_inhibitor": { "format": "{icon}", "format-icons": { From 0ce8821aec9b4899c75f546dd7d61d5d5c9b84e7 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Thu, 18 Apr 2019 14:30:50 +0200 Subject: [PATCH 13/15] feat(mpd): Add playing / paused classes --- resources/style.css | 6 +++++- src/modules/mpd.cpp | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/resources/style.css b/resources/style.css index 8de2cdc..cca30d9 100644 --- a/resources/style.css +++ b/resources/style.css @@ -140,7 +140,7 @@ window#waybar.hidded { } #mpd { - background-color: #ba2880; + background: #ba2880; } #mpd.disconnected { @@ -151,3 +151,7 @@ window#waybar.hidded { background: #90b1b1; color: #2a5c45; } + +#mpd.paused { + background: #ce68a6; +} diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index 7e4e88b..ae3bc85 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -104,8 +104,17 @@ void waybar::modules::MPD::setLabel() { format = config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped"; label_.get_style_context()->add_class("stopped"); + label_.get_style_context()->remove_class("playing"); + label_.get_style_context()->remove_class("paused"); } else { label_.get_style_context()->remove_class("stopped"); + if (playing()) { + label_.get_style_context()->add_class("playing"); + label_.get_style_context()->remove_class("paused"); + } else { + label_.get_style_context()->add_class("paused"); + label_.get_style_context()->remove_class("playing"); + } stateIcon = getStateIcon(); From 3656035c89da3174a4c07fd6bf2cc5b455e1315a Mon Sep 17 00:00:00 2001 From: Minijackson Date: Thu, 18 Apr 2019 14:57:03 +0200 Subject: [PATCH 14/15] fix(mpd): slightly better and safer error handling --- src/modules/mpd.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index ae3bc85..ae3ca47 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -38,7 +38,8 @@ auto waybar::modules::MPD::update() -> void { if (!wasPlaying && playing()) { periodic_updater().detach(); } - } catch (std::exception e) { + } catch (const std::exception& e) { + std::cerr << module_name_ + ": " + e.what() << std::endl; state_ = MPD_STATE_UNKNOWN; } } @@ -51,7 +52,11 @@ std::thread waybar::modules::MPD::event_listener() { while (true) { if (connection_ == nullptr) { // Retry periodically if no connection - update(); + try { + update(); + } catch (const std::exception& e) { + std::cerr << module_name_ + ": " + e.what() << std::endl; + } std::this_thread::sleep_for(interval_); } else { waitForEvent(); From 3e54c3c6696510405a1949b64e66f069e88df438 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Thu, 18 Apr 2019 14:57:49 +0200 Subject: [PATCH 15/15] fix(mpd): better sample theme --- resources/style.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/style.css b/resources/style.css index cca30d9..c29569a 100644 --- a/resources/style.css +++ b/resources/style.css @@ -140,7 +140,8 @@ window#waybar.hidded { } #mpd { - background: #ba2880; + background: #66cc99; + color: #2a5c45; } #mpd.disconnected { @@ -149,9 +150,8 @@ window#waybar.hidded { #mpd.stopped { background: #90b1b1; - color: #2a5c45; } #mpd.paused { - background: #ce68a6; + background: #51a37a; }