From ea722615c46d07f5859d2c6a78caebf1f069bc2e Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Mon, 10 Aug 2020 19:23:16 -0400 Subject: [PATCH 01/19] Allow enabing pango markup in the taskbar string The fix for taskbar tooltips in 6a2d214b55 was incomplete: it causes the label to contain escaped titles. Use set_markup so that GTK decodes markup again, but only if requested by the user (disabling markup is needed if using format strings like "{title:.15}" to avoid terminating the string in the middle of an XML entity). --- man/waybar-wlr-taskbar.5.scd | 5 ++++ src/modules/wlr/taskbar.cpp | 51 ++++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index f044412..55d2afd 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -32,6 +32,11 @@ Addressed by *wlr/taskbar* default: 16 ++ The size of the icon. +*markup*: ++ + typeof: bool ++ + default: false ++ + If set to true, pango markup will be accepted in format and tooltip-format. + *tooltip*: ++ typeof: bool ++ default: true ++ diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index f7e11df..57c88fe 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -306,7 +306,7 @@ std::string Task::state_string(bool shortened) const void Task::handle_title(const char *title) { - title_ = Glib::Markup::escape_text(title); + title_ = title; } void Task::handle_app_id(const char *app_id) @@ -460,38 +460,51 @@ bool Task::operator!=(const Task &o) const void Task::update() { + bool markup = config_["markup"].isBool() ? config_["markup"].asBool() : false; + std::string title = title_; + std::string app_id = app_id_; + if (markup) { + title = Glib::Markup::escape_text(title); + app_id = Glib::Markup::escape_text(app_id); + } if (!format_before_.empty()) { - text_before_.set_label( - fmt::format(format_before_, - fmt::arg("title", title_), - fmt::arg("app_id", app_id_), + auto txt = fmt::format(format_before_, + fmt::arg("title", title), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true)) - ) - ); + ); + if (markup) + text_before_.set_markup(txt); + else + text_before_.set_label(txt); text_before_.show(); } if (!format_after_.empty()) { - text_after_.set_label( - fmt::format(format_after_, - fmt::arg("title", title_), - fmt::arg("app_id", app_id_), + auto txt = fmt::format(format_after_, + fmt::arg("title", title), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true)) - ) - ); + ); + if (markup) + text_after_.set_markup(txt); + else + text_after_.set_label(txt); text_after_.show(); } if (!format_tooltip_.empty()) { - button_.set_tooltip_markup( - fmt::format(format_tooltip_, - fmt::arg("title", title_), - fmt::arg("app_id", app_id_), + auto txt = fmt::format(format_tooltip_, + fmt::arg("title", title), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true)) - ) - ); + ); + if (markup) + button_.set_tooltip_markup(txt); + else + button_.set_tooltip_text(txt); } } From ba78199dd1a8bc7e240ea560e70d6626500eadf1 Mon Sep 17 00:00:00 2001 From: Tamir Zahavi-Brunner Date: Fri, 28 Aug 2020 01:16:41 +0300 Subject: [PATCH 02/19] custom: Fix "restart-interval" This commit fixes the issue where the process would restart immediately and the thread would sleep after the process has restarted, and not before. Fixes #621 --- src/modules/custom.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 5643160..5ee9bb5 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -50,7 +50,6 @@ void waybar::modules::Custom::continuousWorker() { thread_ = [this, cmd] { char* buff = nullptr; size_t len = 0; - bool restart = false; if (getline(&buff, &len, fp_) == -1) { int exit_code = 1; if (fp_) { @@ -63,8 +62,8 @@ void waybar::modules::Custom::continuousWorker() { spdlog::error("{} stopped unexpectedly, is it endless?", name_); } if (config_["restart-interval"].isUInt()) { - restart = true; pid_ = -1; + thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt())); fp_ = util::command::open(cmd, pid_); if (!fp_) { throw std::runtime_error("Unable to open " + cmd); @@ -83,9 +82,6 @@ void waybar::modules::Custom::continuousWorker() { output_ = {0, output}; dp.emit(); } - if (restart) { - thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt())); - } }; } From 1b22e2b3200de7fdb94e52e33f3e8846587c98db Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Sat, 29 Aug 2020 22:56:26 -0700 Subject: [PATCH 03/19] style(workspaces): align text with other modules Currently, the bottom border on workspace buttons eats into the box size and causes the text to sit higher than in other modules. This is ugly when there are other modules (like the window title) right next to the workspace module. To fix the issue, create the bottom border using an inset box-shadow, which doesn't affect the box's content sizing. --- resources/style.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/style.css b/resources/style.css index e21ae00..c454bff 100644 --- a/resources/style.css +++ b/resources/style.css @@ -41,19 +41,19 @@ window#waybar.chromium { padding: 0 5px; background-color: transparent; color: #ffffff; - border-bottom: 3px solid transparent; + /* Use box-shadow instead of border so the text isn't offset */ + box-shadow: inset 0 -3px transparent; } /* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ #workspaces button:hover { background: rgba(0, 0, 0, 0.2); - box-shadow: inherit; - border-bottom: 3px solid #ffffff; + box-shadow: inset 0 -3px #ffffff; } #workspaces button.focused { background-color: #64727D; - border-bottom: 3px solid #ffffff; + box-shadow: inset 0 -3px #ffffff; } #workspaces button.urgent { From 225a0eccddd777c474d5efb720611dd5589df914 Mon Sep 17 00:00:00 2001 From: MusiKid Date: Wed, 2 Sep 2020 14:25:57 +0200 Subject: [PATCH 04/19] Add support for memory tooltip --- src/modules/memory/common.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index 4875ec8..a332d58 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -36,7 +36,17 @@ auto waybar::modules::Memory::update() -> void { fmt::arg("used", used_ram_gigabytes), fmt::arg("avail", available_ram_gigabytes))); if (tooltipEnabled()) { - label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1)); + if (config_["tooltip-format"].isString()) { + auto tooltip_format = config_["tooltip-format"].asString(); + label_.set_tooltip_text(fmt::format(tooltip_format, + used_ram_percentage, + fmt::arg("total", total_ram_gigabytes), + fmt::arg("percentage", used_ram_percentage), + fmt::arg("used", used_ram_gigabytes), + fmt::arg("avail", available_ram_gigabytes))); + } else { + label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1)); + } } event_box_.show(); } else { From 98b6d7f283f6cefa2fb8e43f0d175041dcb1c943 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Sun, 6 Sep 2020 21:48:42 +0200 Subject: [PATCH 05/19] Fix non-standard usage of Fixes the following build warning with musl libc: In file included from ../src/util/rfkill.cpp:24: /usr/include/sys/poll.h:1:2: warning: #warning redirecting incorrect #include to [-Wcpp] 1 | #warning redirecting incorrect #include to | ^~~~~~~ --- src/util/rfkill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/rfkill.cpp b/src/util/rfkill.cpp index f987f4c..82d29e9 100644 --- a/src/util/rfkill.cpp +++ b/src/util/rfkill.cpp @@ -20,8 +20,8 @@ #include #include +#include #include -#include #include #include From 9e3e4368c733b9d13d78253aacb37173b7ec83cf Mon Sep 17 00:00:00 2001 From: Tamir Zahavi-Brunner Date: Sun, 6 Sep 2020 22:47:34 +0300 Subject: [PATCH 06/19] custom: Add "exec-on-event" config This config allows disabling the default behavior of re-executing the script whenever an event that has a command set is triggered. Fixes #841 --- include/modules/custom.hpp | 1 + man/waybar-custom.5.scd | 6 ++++++ src/modules/custom.cpp | 10 ++++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/modules/custom.hpp b/include/modules/custom.hpp index b8dad9d..7c77145 100644 --- a/include/modules/custom.hpp +++ b/include/modules/custom.hpp @@ -22,6 +22,7 @@ class Custom : public ALabel { void continuousWorker(); void parseOutputRaw(); void parseOutputJson(); + void handleEvent(); bool handleScroll(GdkEventScroll* e); bool handleToggle(GdkEventButton* const& e); diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 121585a..3e820c6 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -22,6 +22,12 @@ Addressed by *custom/* The path to a script, which determines if the script in *exec* should be executed. *exec* will be executed if the exit code of *exec-if* equals 0. +*exec-on-event*: ++ + typeof: bool ++ + default: true ++ + If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after + executing the event command. + *return-type*: ++ typeof: string ++ See *return-type* diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 5ee9bb5..92eedaa 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -91,15 +91,21 @@ void waybar::modules::Custom::refresh(int sig) { } } +void waybar::modules::Custom::handleEvent() { + if (!config_["exec-on-event"].isBool() || config_["exec-on-event"].asBool()) { + thread_.wake_up(); + } +} + bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) { auto ret = ALabel::handleScroll(e); - thread_.wake_up(); + handleEvent(); return ret; } bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) { auto ret = ALabel::handleToggle(e); - thread_.wake_up(); + handleEvent(); return ret; } From c65167022223abc8e86650924c7da7e1b1a4b19d Mon Sep 17 00:00:00 2001 From: koffeinfriedhof Date: Sun, 13 Sep 2020 17:32:00 +0200 Subject: [PATCH 07/19] Added song position and queue length. --- man/waybar-mpd.5.scd | 4 ++++ resources/config | 2 +- src/modules/mpd.cpp | 7 +++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index 1ee7a98..e8105de 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -148,6 +148,10 @@ Addressed by *mpd* *{totalTime}*: The length of the current song. To format as a date/time (see example configuration) +*{songPosition}*: The position of the current song. + +*{queueLength}*: The length of the current queue. + *{stateIcon}*: The icon corresponding the playing or paused status of the player (see *state-icons* option) *{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option) diff --git a/resources/config b/resources/config index 832f76c..36c4f96 100644 --- a/resources/config +++ b/resources/config @@ -27,7 +27,7 @@ "format": "{}" }, "mpd": { - "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ", + "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ ", "format-disconnected": "Disconnected ", "format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ", "unknown-tag": "N/A", diff --git a/src/modules/mpd.cpp b/src/modules/mpd.cpp index d2877f3..26878b1 100644 --- a/src/modules/mpd.cpp +++ b/src/modules/mpd.cpp @@ -133,6 +133,7 @@ void waybar::modules::MPD::setLabel() { auto format = format_; std::string artist, album_artist, album, title, date; + int song_pos, queue_length; std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; @@ -161,6 +162,8 @@ void waybar::modules::MPD::setLabel() { album = getTag(MPD_TAG_ALBUM); title = getTag(MPD_TAG_TITLE); date = getTag(MPD_TAG_DATE); + song_pos = mpd_status_get_song_pos(status_.get()); + queue_length = mpd_status_get_queue_length(status_.get()); elapsedTime = std::chrono::seconds(mpd_status_get_elapsed_time(status_.get())); totalTime = std::chrono::seconds(mpd_status_get_total_time(status_.get())); } @@ -184,6 +187,8 @@ void waybar::modules::MPD::setLabel() { fmt::arg("date", Glib::Markup::escape_text(date).raw()), fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), + fmt::arg("songPosition", song_pos), + fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon), @@ -200,6 +205,8 @@ void waybar::modules::MPD::setLabel() { fmt::arg("album", album), fmt::arg("title", title), fmt::arg("date", date), + fmt::arg("songPosition", song_pos), + fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon), From 95f505a457def2f148d6aa9795dd3f4e6264c061 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 21 Sep 2020 10:56:40 +0200 Subject: [PATCH 08/19] revert: restore eventfd --- include/modules/network.hpp | 2 ++ src/modules/network.cpp | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 4441dc0..c02d3c5 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -54,6 +54,8 @@ class Network : public ALabel { struct sockaddr_nl nladdr_ = {0}; struct nl_sock* sock_ = nullptr; struct nl_sock* ev_sock_ = nullptr; + int efd_; + int ev_fd_; int nl80211_id_; std::mutex mutex_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 3914120..74ae913 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -83,6 +83,8 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf : ALabel(config, "network", id, "{ifname}", 60), ifid_(-1), family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), + efd_(-1), + ev_fd_(-1), cidr_(-1), signal_strength_dbm_(0), signal_strength_(0), @@ -119,6 +121,12 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf } waybar::modules::Network::~Network() { + if (ev_fd_ > -1) { + close(ev_fd_); + } + if (efd_ > -1) { + close(efd_); + } if (ev_sock_ != nullptr) { nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK); if (family_ == AF_INET) { @@ -150,6 +158,30 @@ void waybar::modules::Network::createEventSocket() { } else { nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); } + efd_ = epoll_create1(EPOLL_CLOEXEC); + if (efd_ < 0) { + throw std::runtime_error("Can't create epoll"); + } + { + ev_fd_ = eventfd(0, EFD_NONBLOCK); + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = EPOLLIN | EPOLLET; + event.data.fd = ev_fd_; + if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) { + throw std::runtime_error("Can't add epoll event"); + } + } + { + auto fd = nl_socket_get_fd(ev_sock_); + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; + event.data.fd = fd; + if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) { + throw std::runtime_error("Can't add epoll event"); + } + } } void waybar::modules::Network::createInfoSocket() { @@ -192,6 +224,19 @@ void waybar::modules::Network::worker() { #else spdlog::warn("Waybar has been built without rfkill support."); #endif + thread_ = [this] { + std::array events{}; + + int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1); + if (ec > 0) { + for (auto i = 0; i < ec; i++) { + if (events[i].data.fd != nl_socket_get_fd(ev_sock_) || nl_recvmsgs_default(ev_sock_) < 0) { + thread_.stop(); + break; + } + } + } + }; } const std::string waybar::modules::Network::getNetworkState() const { From 6db795401a6c060a1e9c6d2a72be3caa69ad8ed0 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 21 Sep 2020 12:18:42 +0200 Subject: [PATCH 09/19] chore: v0.9.4 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 16da957..acc8a47 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.3', + version: '0.9.4', license: 'MIT', default_options : [ 'cpp_std=c++17', From 12016d35bb09b9628a0a37272557fad4e4b4a7c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorben=20G=C3=BCnther?= Date: Mon, 21 Sep 2020 13:17:11 +0200 Subject: [PATCH 10/19] disk module: add state for percentage_used --- man/waybar-disk.5.scd | 4 ++++ src/modules/disk.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index 1a9320c..431d7c8 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -31,6 +31,10 @@ Addressed by *disk* typeof: integer ++ Positive value to rotate the text label. +*states*: ++ + typeof: array ++ + A number of disk utilization states which get activated on certain percentage thresholds (percentage_used). See *waybar-states(5)*. + *max-length*: ++ typeof: integer ++ The maximum length in character the module should display. diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index 59ffea6..83d612b 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -47,13 +47,14 @@ auto waybar::modules::Disk::update() -> void { auto free = pow_format(stats.f_bavail * stats.f_frsize, "B", true); auto used = pow_format((stats.f_blocks - stats.f_bavail) * stats.f_frsize, "B", true); auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true); + auto percentage_used = (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks; label_.set_markup(fmt::format(format_ , stats.f_bavail * 100 / stats.f_blocks , fmt::arg("free", free) , fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks) , fmt::arg("used", used) - , fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks) + , fmt::arg("percentage_used", percentage_used) , fmt::arg("total", total) , fmt::arg("path", path_) )); @@ -67,12 +68,13 @@ auto waybar::modules::Disk::update() -> void { , fmt::arg("free", free) , fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks) , fmt::arg("used", used) - , fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks) + , fmt::arg("percentage_used", percentage_used) , fmt::arg("total", total) , fmt::arg("path", path_) )); } event_box_.show(); + getState(percentage_used); // Call parent update ALabel::update(); } From 7ba14c2097c43866fca5e20ccb736f7ca854fcfd Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Sat, 26 Sep 2020 15:55:06 -0400 Subject: [PATCH 11/19] Fix "on-click-backward" when "on-click-forward" is not present --- src/AModule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 10bd077..7da942e 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -44,9 +44,9 @@ bool AModule::handleToggle(GdkEventButton* const& e) { format = config_["on-click-middle"].asString(); } else if (config_["on-click-right"].isString() && e->button == 3) { format = config_["on-click-right"].asString(); - } else if (config_["on-click-forward"].isString() && e->button == 8) { + } else if (config_["on-click-backward"].isString() && e->button == 8) { format = config_["on-click-backward"].asString(); - } else if (config_["on-click-backward"].isString() && e->button == 9) { + } else if (config_["on-click-forward"].isString() && e->button == 9) { format = config_["on-click-forward"].asString(); } if (!format.empty()) { From 83d679bf72da86c9121b285e5ee6323fb3cbd1a1 Mon Sep 17 00:00:00 2001 From: lrhel Date: Sat, 26 Sep 2020 23:06:12 +0000 Subject: [PATCH 12/19] Add format-icons for workspace's name entry --- src/modules/sway/workspaces.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 17a0be2..8d78bf5 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -283,6 +283,8 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node return config_["format-icons"]["persistent"].asString(); } else if (config_["format-icons"][key].isString()) { return config_["format-icons"][key].asString(); + } else if (config_["format-icons"][trimWorkspaceName(key)].isString()) { + return config_["format-icons"][trimWorkspaceName(key)].asString(); } } return name; From e9b5be9adbfcc8982c8fc1117d92a250a682c8a1 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 29 Sep 2020 22:28:39 +0200 Subject: [PATCH 13/19] fix: add global /etc/xdg/waybar back. fixes #714 --- src/client.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index 316e7ec..70cc22c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -162,6 +162,7 @@ std::tuple waybar::Client::getConfigs( "$XDG_CONFIG_HOME/waybar/config", "$HOME/.config/waybar/config", "$HOME/waybar/config", + "/etc/xdg/waybar/config", SYSCONFDIR "/xdg/waybar/config", "./resources/config", }) @@ -170,6 +171,7 @@ std::tuple waybar::Client::getConfigs( "$XDG_CONFIG_HOME/waybar/style.css", "$HOME/.config/waybar/style.css", "$HOME/waybar/style.css", + "/etc/xdg/waybar/style.css", SYSCONFDIR "/xdg/waybar/style.css", "./resources/style.css", }) From 73681a30e5ad70a3308b883deedd2b5ad694d8c6 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 29 Sep 2020 22:31:28 +0200 Subject: [PATCH 14/19] man: add the prefixed path were config is loaded --- man/{waybar.5.scd => waybar.5.scd.in} | 1 + meson.build | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) rename man/{waybar.5.scd => waybar.5.scd.in} (99%) diff --git a/man/waybar.5.scd b/man/waybar.5.scd.in similarity index 99% rename from man/waybar.5.scd rename to man/waybar.5.scd.in index cd64f7d..430b9fc 100644 --- a/man/waybar.5.scd +++ b/man/waybar.5.scd.in @@ -14,6 +14,7 @@ Valid locations for this file are: - *~/.config/waybar/config* - *~/waybar/config* - */etc/xdg/waybar/config* +- *@sysconfdir@/xdg/waybar/config* A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config Also a minimal example configuration can be found on the at the bottom of this man page. diff --git a/meson.build b/meson.build index acc8a47..5ac0986 100644 --- a/meson.build +++ b/meson.build @@ -9,6 +9,8 @@ project( ], ) +fs = import('fs') + compiler = meson.get_compiler('cpp') cpp_args = [] @@ -256,9 +258,20 @@ scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_opti if scdoc.found() scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) sh = find_program('sh', native: true) + + main_manpage = configure_file( + input: 'man/waybar.5.scd.in', + output: 'waybar.5.scd', + configuration: { + 'sysconfdir': join_paths(prefix, sysconfdir) + } + ) + + main_manpage_path = join_paths(meson.build_root(), '@0@'.format(main_manpage)) + mandir = get_option('mandir') man_files = [ - 'waybar.5.scd', + main_manpage_path, 'waybar-backlight.5.scd', 'waybar-battery.5.scd', 'waybar-clock.5.scd', @@ -281,14 +294,18 @@ if scdoc.found() 'waybar-bluetooth.5.scd', ] - foreach filename : man_files - topic = filename.split('.')[-3].split('/')[-1] - section = filename.split('.')[-2] + foreach file : man_files + path = '@0@'.format(file) + basename = fs.name(path) + + topic = basename.split('.')[-3].split('/')[-1] + section = basename.split('.')[-2] output = '@0@.@1@'.format(topic, section) custom_target( output, - input: 'man/@0@'.format(filename), + # drops the 'man' if `path` is an absolute path + input: join_paths('man', path), output: output, command: [ sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output) From e4427cb017f235cda521c4a024bc4da55bf7c976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Rolim?= Date: Sun, 6 Sep 2020 14:44:13 -0300 Subject: [PATCH 15/19] sndio: Add module. - can control sndio: change volume, toggle mute - appearance is somewhat dynamic: takes muted status into account - uses polling inside sleeper thread to update values - uses sioctl_* functions, requires sndio>=1.7.0. --- README.md | 1 + include/factory.hpp | 3 + include/modules/sndio.hpp | 29 +++++++ man/waybar-sndio.5.scd | 83 +++++++++++++++++++ meson.build | 20 +++++ meson_options.txt | 1 + src/factory.cpp | 5 ++ src/modules/sndio.cpp | 167 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 309 insertions(+) create mode 100644 include/modules/sndio.hpp create mode 100644 man/waybar-sndio.5.scd create mode 100644 src/modules/sndio.cpp diff --git a/README.md b/README.md index 74f4660..b104ade 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ libnl [Network module] libappindicator-gtk3 [Tray module] libdbusmenu-gtk3 [Tray module] libmpdclient [MPD module] +libsndio [sndio module] ``` **Build dependencies** diff --git a/include/factory.hpp b/include/factory.hpp index ebc2359..3efe6cb 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -39,6 +39,9 @@ #ifdef HAVE_LIBMPDCLIENT #include "modules/mpd.hpp" #endif +#ifdef HAVE_LIBSNDIO +#include "modules/sndio.hpp" +#endif #include "bar.hpp" #include "modules/custom.hpp" #include "modules/temperature.hpp" diff --git a/include/modules/sndio.hpp b/include/modules/sndio.hpp new file mode 100644 index 0000000..f14d062 --- /dev/null +++ b/include/modules/sndio.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class Sndio : public ALabel { + public: + Sndio(const std::string&, const Json::Value&); + ~Sndio(); + auto update() -> void; + auto set_desc(struct sioctl_desc *, unsigned int) -> void; + auto put_val(unsigned int, unsigned int) -> void; + bool handleScroll(GdkEventScroll *); + bool handleToggle(GdkEventButton* const&); + + private: + util::SleeperThread thread_; + struct sioctl_hdl *hdl_; + std::vector pfds_; + unsigned int addr_; + unsigned int volume_, old_volume_, maxval_; + bool muted_; +}; + +} // namespace waybar::modules diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd new file mode 100644 index 0000000..a61c332 --- /dev/null +++ b/man/waybar-sndio.5.scd @@ -0,0 +1,83 @@ +waybar-sndio(5) + +# NAME + +waybar - sndio module + +# DESCRIPTION + +The *sndio* module displays the current volume reported by sndio(7). + +Additionally, you can control the volume by scrolling *up* or *down* while the +cursor is over the module, and clicking on the module toggles mute. + +# CONFIGURATION + +*format*: ++ + typeof: string ++ + default: {volume}% ++ + The format for how information should be displayed. + +*rotate*: ++ + typeof: integer ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*scroll-step*: ++ + typeof: int ++ + default: 5 ++ + The speed in which to change the volume when scrolling. + +*on-click*: ++ + typeof: string ++ + Command to execute when clicked on the module. + This replaces the default behaviour of toggling mute. + +*on-click-middle*: ++ + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. + +*on-click-right*: ++ + typeof: string ++ + Command to execute when you right clicked on the module. + +*on-update*: ++ + typeof: string ++ + Command to execute when the module is updated. + +*on-scroll-up*: ++ + typeof: string ++ + Command to execute when scrolling up on the module. + This replaces the default behaviour of volume control. + +*on-scroll-down*: ++ + typeof: string ++ + Command to execute when scrolling down on the module. + This replaces the default behaviour of volume control. + +*smooth-scrolling-threshold*: ++ + typeof: double ++ + Threshold to be used when scrolling. + +# FORMAT REPLACEMENTS + +*{volume}*: Volume in percentage. + +*{raw_value}*: Volume as value reported by sndio. + +# EXAMPLES + +``` +"sndio": { + "format": "{raw_value} 🎜", + "scroll-step": 3 +} +``` + +# STYLE + +- *#sndio* +- *#sndio.muted* diff --git a/meson.build b/meson.build index 5ac0986..023894c 100644 --- a/meson.build +++ b/meson.build @@ -96,6 +96,19 @@ 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')) + +libsndio = compiler.find_library('sndio', required: get_option('sndio')) +if libsndio.found() + if not compiler.has_function('sioctl_open', prefix: '#include ', dependencies: libsndio) + if get_option('sndio').enabled() + error('libsndio is too old, required >=1.7.0') + else + warning('libsndio is too old, required >=1.7.0') + libsndio = dependency('', required: false) + endif + endif +endif + gtk_layer_shell = dependency('gtk-layer-shell-0', required: get_option('gtk-layer-shell'), fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep']) @@ -207,6 +220,11 @@ if gtk_layer_shell.found() add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp') endif +if libsndio.found() + add_project_arguments('-DHAVE_LIBSNDIO', language: 'cpp') + src_files += 'src/modules/sndio.cpp' +endif + if get_option('rfkill').enabled() if is_linux add_project_arguments('-DWANT_RFKILL', language: 'cpp') @@ -241,6 +259,7 @@ executable( libepoll, libmpdclient, gtk_layer_shell, + libsndio, tz_dep ], include_directories: [include_directories('include')], @@ -292,6 +311,7 @@ if scdoc.found() 'waybar-states.5.scd', 'waybar-wlr-taskbar.5.scd', 'waybar-bluetooth.5.scd', + 'waybar-sndio.5.scd', ] foreach file : man_files diff --git a/meson_options.txt b/meson_options.txt index de47da7..cb5581b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -8,3 +8,4 @@ option('man-pages', type: 'feature', value: 'auto', description: 'Generate and i option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon') option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support') option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL') +option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio') diff --git a/src/factory.cpp b/src/factory.cpp index af93b20..8a5e825 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -76,6 +76,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "mpd") { return new waybar::modules::MPD(id, config_[name]); } +#endif +#ifdef HAVE_LIBSNDIO + if (ref == "sndio") { + return new waybar::modules::Sndio(id, config_[name]); + } #endif if (ref == "temperature") { return new waybar::modules::Temperature(id, config_[name]); diff --git a/src/modules/sndio.cpp b/src/modules/sndio.cpp new file mode 100644 index 0000000..e72c56f --- /dev/null +++ b/src/modules/sndio.cpp @@ -0,0 +1,167 @@ +#include "modules/sndio.hpp" +#include +#include +#include +#include + +namespace waybar::modules { + +void ondesc(void *arg, struct sioctl_desc *d, int curval) { + auto self = static_cast(arg); + if (d == NULL) { + // d is NULL when the list is done + return; + } + self->set_desc(d, curval); +} + +void onval(void *arg, unsigned int addr, unsigned int val) { + auto self = static_cast(arg); + self->put_val(addr, val); +} + +Sndio::Sndio(const std::string &id, const Json::Value &config) + : ALabel(config, "sndio", id, "{volume}%"), + hdl_(nullptr), + pfds_(0), + addr_(0), + volume_(0), + old_volume_(0), + maxval_(0), + muted_(false) { + hdl_ = sioctl_open(SIO_DEVANY, SIOCTL_READ | SIOCTL_WRITE, 0); + if (hdl_ == nullptr) { + throw std::runtime_error("sioctl_open() failed."); + } + + if(sioctl_ondesc(hdl_, ondesc, this) == 0) { + throw std::runtime_error("sioctl_ondesc() failed."); + } + + sioctl_onval(hdl_, onval, this); + + pfds_.reserve(sioctl_nfds(hdl_)); + + event_box_.show(); + + event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK | Gdk::BUTTON_PRESS_MASK); + event_box_.signal_scroll_event().connect( + sigc::mem_fun(*this, &Sndio::handleScroll)); + event_box_.signal_button_press_event().connect( + sigc::mem_fun(*this, &Sndio::handleToggle)); + + thread_ = [this] { + dp.emit(); + + int nfds = sioctl_pollfd(hdl_, pfds_.data(), POLLIN); + if (nfds == 0) { + throw std::runtime_error("sioctl_pollfd() failed."); + } + while (poll(pfds_.data(), nfds, -1) < 0) { + if (errno != EINTR) { + throw std::runtime_error("poll() failed."); + } + } + + int revents = sioctl_revents(hdl_, pfds_.data()); + if (revents & POLLHUP) { + throw std::runtime_error("disconnected!"); + } + }; +} + +Sndio::~Sndio() { + sioctl_close(hdl_); +} + +auto Sndio::update() -> void { + auto format = format_; + unsigned int vol = 100. * static_cast(volume_) / static_cast(maxval_); + + if (volume_ == 0) { + label_.get_style_context()->add_class("muted"); + } else { + label_.get_style_context()->remove_class("muted"); + } + + label_.set_markup(fmt::format(format, + fmt::arg("volume", vol), + fmt::arg("raw_value", volume_))); + + ALabel::update(); +} + +auto Sndio::set_desc(struct sioctl_desc *d, unsigned int val) -> void { + std::string name{d->func}; + std::string node_name{d->node0.name}; + + if (name == "level" && node_name == "output" && d->type == SIOCTL_NUM) { + // store addr for output.level value, used in put_val + addr_ = d->addr; + maxval_ = d->maxval; + volume_ = val; + } +} + +auto Sndio::put_val(unsigned int addr, unsigned int val) -> void { + if (addr == addr_) { + volume_ = val; + } +} + +bool Sndio::handleScroll(GdkEventScroll *e) { + // change the volume only when no user provided + // events are configured + if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { + return AModule::handleScroll(e); + } + + auto dir = AModule::getScrollDir(e); + if (dir == SCROLL_DIR::NONE) { + return true; + } + + int step = 5; + if (config_["scroll-step"].isInt()) { + step = config_["scroll-step"].asInt(); + } + + int new_volume = volume_; + if (muted_) { + new_volume = old_volume_; + } + + if (dir == SCROLL_DIR::UP) { + new_volume += step; + } else if (dir == SCROLL_DIR::DOWN) { + new_volume -= step; + } + new_volume = std::clamp(new_volume, 0, static_cast(maxval_)); + + // quits muted mode if volume changes + muted_ = false; + + sioctl_setval(hdl_, addr_, new_volume); + + return true; +} + +bool Sndio::handleToggle(GdkEventButton* const& e) { + // toggle mute only when no user provided events are configured + if (config_["on-click"].isString()) { + return AModule::handleToggle(e); + } + + muted_ = !muted_; + if (muted_) { + // store old volume to be able to restore it later + old_volume_ = volume_; + sioctl_setval(hdl_, addr_, 0); + } else { + sioctl_setval(hdl_, addr_, old_volume_); + } + + return true; +} + +} /* namespace waybar::modules */ From 1f66b06f93ca740767acc6925ae66c50e5e9a453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Rolim?= Date: Wed, 9 Sep 2020 14:48:27 -0300 Subject: [PATCH 16/19] Dockerfiles/alpine: add sndio-dev. --- Dockerfiles/alpine | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/alpine b/Dockerfiles/alpine index 7b71837..21d1cbb 100644 --- a/Dockerfiles/alpine +++ b/Dockerfiles/alpine @@ -2,4 +2,4 @@ FROM alpine:latest -RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev scdoc +RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc From aa625f51967ad7bb6a71ed018e814892332441e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Rolim?= Date: Sun, 6 Sep 2020 15:15:58 -0300 Subject: [PATCH 17/19] .travis.yml: add sndio to FreeBSD run. Also add necessary environment variables and move to /latest, which has sndio-1.7.0. --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 62f7863..abc739c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,9 @@ jobs: compiler: clang env: before_install: - - sudo pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu + - export CPPFLAGS+=-isystem/usr/local/include LDFLAGS+=-L/usr/local/lib # sndio + - sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf + - sudo pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu sndio libfmt libmpdclient libudev-devd meson pulseaudio scdoc spdlog script: - meson build -Dman-pages=enabled From 22e46ea6cc64e35f7ab1536d3b43a6a523658242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Rolim?= Date: Sun, 4 Oct 2020 02:53:21 -0300 Subject: [PATCH 18/19] sndio: Add reconnection support. --- include/modules/sndio.hpp | 1 + src/modules/sndio.cpp | 62 ++++++++++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/include/modules/sndio.hpp b/include/modules/sndio.hpp index f14d062..32ed706 100644 --- a/include/modules/sndio.hpp +++ b/include/modules/sndio.hpp @@ -18,6 +18,7 @@ class Sndio : public ALabel { bool handleToggle(GdkEventButton* const&); private: + auto connect_to_sndio() -> void; util::SleeperThread thread_; struct sioctl_hdl *hdl_; std::vector pfds_; diff --git a/src/modules/sndio.cpp b/src/modules/sndio.cpp index e72c56f..34c46bd 100644 --- a/src/modules/sndio.cpp +++ b/src/modules/sndio.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace waybar::modules { @@ -20,8 +21,25 @@ void onval(void *arg, unsigned int addr, unsigned int val) { self->put_val(addr, val); } +auto Sndio::connect_to_sndio() -> void { + hdl_ = sioctl_open(SIO_DEVANY, SIOCTL_READ | SIOCTL_WRITE, 0); + if (hdl_ == nullptr) { + throw std::runtime_error("sioctl_open() failed."); + } + + if (sioctl_ondesc(hdl_, ondesc, this) == 0) { + throw std::runtime_error("sioctl_ondesc() failed."); + } + + if (sioctl_onval(hdl_, onval, this) == 0) { + throw std::runtime_error("sioctl_onval() failed."); + } + + pfds_.reserve(sioctl_nfds(hdl_)); +} + Sndio::Sndio(const std::string &id, const Json::Value &config) - : ALabel(config, "sndio", id, "{volume}%"), + : ALabel(config, "sndio", id, "{volume}%", 1), hdl_(nullptr), pfds_(0), addr_(0), @@ -29,18 +47,7 @@ Sndio::Sndio(const std::string &id, const Json::Value &config) old_volume_(0), maxval_(0), muted_(false) { - hdl_ = sioctl_open(SIO_DEVANY, SIOCTL_READ | SIOCTL_WRITE, 0); - if (hdl_ == nullptr) { - throw std::runtime_error("sioctl_open() failed."); - } - - if(sioctl_ondesc(hdl_, ondesc, this) == 0) { - throw std::runtime_error("sioctl_ondesc() failed."); - } - - sioctl_onval(hdl_, onval, this); - - pfds_.reserve(sioctl_nfds(hdl_)); + connect_to_sndio(); event_box_.show(); @@ -65,7 +72,28 @@ Sndio::Sndio(const std::string &id, const Json::Value &config) int revents = sioctl_revents(hdl_, pfds_.data()); if (revents & POLLHUP) { - throw std::runtime_error("disconnected!"); + spdlog::warn("sndio disconnected!"); + sioctl_close(hdl_); + hdl_ = nullptr; + + // reconnection loop + while (thread_.isRunning()) { + try { + connect_to_sndio(); + } catch(std::runtime_error const& e) { + // avoid leaking hdl_ + if (hdl_) { + sioctl_close(hdl_); + hdl_ = nullptr; + } + // rate limiting for the retries + thread_.sleep_for(interval_); + continue; + } + + spdlog::warn("sndio reconnected!"); + break; + } } }; } @@ -116,6 +144,9 @@ bool Sndio::handleScroll(GdkEventScroll *e) { return AModule::handleScroll(e); } + // only try to talk to sndio if connected + if (hdl_ == nullptr) return true; + auto dir = AModule::getScrollDir(e); if (dir == SCROLL_DIR::NONE) { return true; @@ -152,6 +183,9 @@ bool Sndio::handleToggle(GdkEventButton* const& e) { return AModule::handleToggle(e); } + // only try to talk to sndio if connected + if (hdl_ == nullptr) return true; + muted_ = !muted_; if (muted_) { // store old volume to be able to restore it later From e9b2d275c836a08d8d5eae4cf2766f362c3cb572 Mon Sep 17 00:00:00 2001 From: Christoffer Noerbjerg Date: Sun, 11 Oct 2020 22:36:30 +0200 Subject: [PATCH 19/19] added module group selectors for styling --- src/bar.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bar.cpp b/src/bar.cpp index 45e3420..8af6b97 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -24,6 +24,9 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) window.get_style_context()->add_class(output->name); window.get_style_context()->add_class(config["name"].asString()); window.get_style_context()->add_class(config["position"].asString()); + left_.get_style_context()->add_class("modules-left"); + center_.get_style_context()->add_class("modules-center"); + right_.get_style_context()->add_class("modules-right"); if (config["position"] == "right" || config["position"] == "left") { height_ = 0;