From 999c1b6b81c5fe29c32e1e4450d49d67d0e6570c Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Sat, 5 Jun 2021 14:50:25 +0200 Subject: [PATCH 01/51] sway-language: ignore events with empty layout --- src/modules/sway/language.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index a318647..4d27fb8 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -45,7 +45,9 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { auto payload = parser_.parse(res.payload)["input"]; if (payload["type"].asString() == "keyboard") { auto layout_name = payload["xkb_active_layout_name"].asString().substr(0,2); - lang_ = Glib::Markup::escape_text(layout_name); + if (!layout_name.empty()) { + lang_ = Glib::Markup::escape_text(layout_name); + } } dp.emit(); } catch (const std::exception& e) { From 23b9923eeba13bdba51b601f2c5c7e4f20b8710a Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sun, 30 May 2021 15:56:56 +0100 Subject: [PATCH 02/51] network: Parse whole RTM_NEWROUTE msg before interpreting it The check to figure out if we have the default route should be after the for loop that parses the route attributes, to avoid acting on incomplete information. We are going to use more fields from the message. --- src/modules/network.cpp | 76 ++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 583daae..3edc4dc 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -623,49 +623,47 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { default: break; } + } - /* If this is the default route, and we know the interface index, - * we can stop parsing this message. - */ - if (has_gateway && !has_destination && temp_idx != -1) { - if (!is_del_event) { - net->ifid_ = temp_idx; + // Check if we have a default route. + if (has_gateway && !has_destination && temp_idx != -1) { + if (!is_del_event) { + net->ifid_ = temp_idx; - spdlog::debug("network: new default route via if{}", temp_idx); + spdlog::debug("network: new default route via if{}", temp_idx); - /* Ask ifname associated with temp_idx as well as carrier status */ - struct ifinfomsg ifinfo_hdr = { - .ifi_family = AF_UNSPEC, - .ifi_index = temp_idx, - }; - int err; - err = nl_send_simple(net->ev_sock_, RTM_GETLINK, NLM_F_REQUEST, - &ifinfo_hdr, sizeof (ifinfo_hdr)); - if (err < 0) { - spdlog::error("network: failed to ask link info: {}", err); - /* Ask for a dump of all links instead */ - net->want_link_dump_ = true; - } - - /* Also ask for the address. Asking for a addresses of a specific - * interface doesn't seems to work so ask for a dump of all - * addresses. */ - net->want_addr_dump_ = true; - net->askForStateDump(); - net->thread_timer_.wake_up(); - } else if (is_del_event && temp_idx == net->ifid_) { - spdlog::debug("network: default route deleted {}/if{}", - net->ifname_, temp_idx); - - net->ifname_.clear(); - net->clearIface(); - net->dp.emit(); - /* Ask for a dump of all routes in case another one is already - * setup. If there's none, there'll be an event with new one - * later. */ - net->want_route_dump_ = true; - net->askForStateDump(); + /* Ask ifname associated with temp_idx as well as carrier status */ + struct ifinfomsg ifinfo_hdr = { + .ifi_family = AF_UNSPEC, + .ifi_index = temp_idx, + }; + int err; + err = nl_send_simple(net->ev_sock_, RTM_GETLINK, NLM_F_REQUEST, + &ifinfo_hdr, sizeof (ifinfo_hdr)); + if (err < 0) { + spdlog::error("network: failed to ask link info: {}", err); + /* Ask for a dump of all links instead */ + net->want_link_dump_ = true; } + + /* Also ask for the address. Asking for a addresses of a specific + * interface doesn't seems to work so ask for a dump of all + * addresses. */ + net->want_addr_dump_ = true; + net->askForStateDump(); + net->thread_timer_.wake_up(); + } else if (is_del_event && temp_idx == net->ifid_) { + spdlog::debug("network: default route deleted {}/if{}", + net->ifname_, temp_idx); + + net->ifname_.clear(); + net->clearIface(); + net->dp.emit(); + /* Ask for a dump of all routes in case another one is already + * setup. If there's none, there'll be an event with new one + * later. */ + net->want_route_dump_ = true; + net->askForStateDump(); } } break; From ce97df34e69689d97adbf481ef65997beb4f3f98 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Sat, 5 Jun 2021 15:44:32 +0100 Subject: [PATCH 03/51] network: Also clear ifname in clearIface() Since we reset `ifid_`, clear `ifname_` as well. --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 3edc4dc..e895569 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -400,6 +400,7 @@ bool waybar::modules::Network::checkInterface(std::string name) { void waybar::modules::Network::clearIface() { ifid_ = -1; + ifname_.clear(); essid_.clear(); ipaddr_.clear(); netmask_.clear(); @@ -656,7 +657,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { spdlog::debug("network: default route deleted {}/if{}", net->ifname_, temp_idx); - net->ifname_.clear(); net->clearIface(); net->dp.emit(); /* Ask for a dump of all routes in case another one is already From efaac20d8200b2249668773fa96ef40ea66fe4fe Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 2 Jun 2021 22:15:34 +0100 Subject: [PATCH 04/51] network: Handle ip route priority When there's a new default route with higher priority, switch to it. --- include/modules/network.hpp | 1 + src/modules/network.cpp | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 2523dc2..009ae5a 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -72,6 +72,7 @@ class Network : public ALabel { int32_t signal_strength_dbm_; uint8_t signal_strength_; uint32_t frequency_; + uint32_t route_priority; util::SleeperThread thread_; util::SleeperThread thread_timer_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index e895569..de023ef 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -573,11 +573,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { bool has_gateway = false; bool has_destination = false; int temp_idx = -1; - - /* If we found the correct answer, skip parsing the attributes. */ - if (!is_del_event && net->ifid_ != -1) { - return NL_OK; - } + uint32_t priority = 0; /* Find the message(s) concerting the main routing table, each message * corresponds to a single routing table entry. @@ -621,6 +617,9 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { /* The output interface index. */ temp_idx = *static_cast(RTA_DATA(attr)); break; + case RTA_PRIORITY: + priority = *(uint32_t*)RTA_DATA(attr); + break; default: break; } @@ -628,10 +627,16 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { // Check if we have a default route. if (has_gateway && !has_destination && temp_idx != -1) { - if (!is_del_event) { + // Check if this is the first default route we see, or if this new + // route have a higher priority. + if (!is_del_event && ((net->ifid_ == -1) || (priority < net->route_priority))) { + // Clear if's state for the case were there is a higher priority + // route on a different interface. + net->clearIface(); net->ifid_ = temp_idx; + net->route_priority = priority; - spdlog::debug("network: new default route via if{}", temp_idx); + spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority); /* Ask ifname associated with temp_idx as well as carrier status */ struct ifinfomsg ifinfo_hdr = { @@ -653,9 +658,10 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->want_addr_dump_ = true; net->askForStateDump(); net->thread_timer_.wake_up(); - } else if (is_del_event && temp_idx == net->ifid_) { - spdlog::debug("network: default route deleted {}/if{}", - net->ifname_, temp_idx); + } else if (is_del_event && temp_idx == net->ifid_ + && net->route_priority == priority) { + spdlog::debug("network: default route deleted {}/if{} metric {}", + net->ifname_, temp_idx, priority); net->clearIface(); net->dp.emit(); From 33617b67f0c342f15a72da413c4e6b0b46f9e196 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 2 Jun 2021 22:16:28 +0100 Subject: [PATCH 05/51] network: Fix one case where default route is deleted without notification When an interface's state is change to "down", all the route associated with it are deleted without an RTM_DELROUTE event. So when this happen, reset the module to start looking for a new external interface / default route. Fixes #1117 --- src/modules/network.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index de023ef..ec221a2 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -1,6 +1,7 @@ #include "modules/network.hpp" #include #include +#include #include #include #include @@ -432,6 +433,20 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { return NL_OK; } + // Check if the interface goes "down" and if we want to detect the + // external interface. + if (net->ifid_ != -1 && !(ifi->ifi_flags & IFF_UP) + && !net->config_["interface"].isString()) { + // The current interface is now down, all the routes associated with + // it have been deleted, so start looking for a new default route. + spdlog::debug("network: if{} down", net->ifid_); + net->clearIface(); + net->dp.emit(); + net->want_route_dump_ = true; + net->askForStateDump(); + return NL_OK; + } + for (; RTA_OK(ifla, attrlen); ifla = RTA_NEXT(ifla, attrlen)) { switch (ifla->rta_type) { case IFLA_IFNAME: From 194f4c2f18799633a1877fb79ea82c47f4aa30c7 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Mon, 7 Jun 2021 19:24:38 +0100 Subject: [PATCH 06/51] network: Fix mix use of default and state specific format Whenever the network module is configured with both "format" and "format-$state" and when the module use "format-$state" once, it override the value that was saved from "format". For example, if both "format" and "format-disconnect" are configured, and only those, as soon as the module show information about a disconnected interface, it will keep showing the format for disconnected, even if the interface is connected again later. Fix that by always setting a value to default_format_ in update() and thus use the intended default format when needed. Fixes #1129 --- src/modules/network.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index ec221a2..7d0f638 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -19,6 +19,7 @@ constexpr const char *NETSTAT_FILE = constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt"; constexpr std::string_view BANDWIDTH_DOWN_TOTAL_KEY = "InOctets"; constexpr std::string_view BANDWIDTH_UP_TOTAL_KEY = "OutOctets"; +constexpr const char *DEFAULT_FORMAT = "{ifname}"; std::ifstream netstat(NETSTAT_FILE); std::optional read_netstat(std::string_view category, std::string_view key) { @@ -82,7 +83,7 @@ std::optional read_netstat(std::string_view category, std::s } // namespace waybar::modules::Network::Network(const std::string &id, const Json::Value &config) - : ALabel(config, "network", id, "{ifname}", 60), + : ALabel(config, "network", id, DEFAULT_FORMAT, 60), ifid_(-1), family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), @@ -323,6 +324,10 @@ auto waybar::modules::Network::update() -> void { } if (config_["format-" + state].isString()) { default_format_ = config_["format-" + state].asString(); + } else if (config_["format"].isString()) { + default_format_ = config_["format"].asString(); + } else { + default_format_ = DEFAULT_FORMAT; } if (config_["tooltip-format-" + state].isString()) { tooltip_format = config_["tooltip-format-" + state].asString(); From 5da268077cb0e53a5ff46fe9ca2dee8bf8e1c041 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 4 Jun 2021 20:22:16 -0700 Subject: [PATCH 07/51] fix(util): protect std::condition_variable methods from pthread_cancel The changes in GCC 11.x made `std::condition_variable` implementation internals `noexcept`. `noexcept` is known to interact particularly bad with `pthread_cancel`, i.e. `__cxxabiv1::__force_unwind` passing through the `noexcept` call stack frame causes a `std::terminate` call and immediate termination of the program Digging through the GCC ML archives[1] lead me to the idea of patching this with a few pthread_setcancelstate's. As bad as the solution is, it seems to be the best we can do within C++17 limits and without major rework. [1]: https://gcc.gnu.org/legacy-ml/gcc/2017-08/msg00156.html --- include/util/sleeper_thread.hpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/include/util/sleeper_thread.hpp b/include/util/sleeper_thread.hpp index 9adbe8f..d1c6ba0 100644 --- a/include/util/sleeper_thread.hpp +++ b/include/util/sleeper_thread.hpp @@ -8,6 +8,20 @@ namespace waybar::util { +/** + * Defer pthread_cancel until the end of a current scope. + * + * Required to protect a scope where it's unsafe to raise `__forced_unwind` exception. + * An example of these is a call of a method marked as `noexcept`; an attempt to cancel within such + * a method may result in a `std::terminate` call. + */ +class CancellationGuard { + int oldstate; +public: + CancellationGuard() { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); } + ~CancellationGuard() { pthread_setcancelstate(oldstate, &oldstate); } +}; + class SleeperThread { public: SleeperThread() = default; @@ -33,14 +47,16 @@ class SleeperThread { bool isRunning() const { return do_run_; } auto sleep_for(std::chrono::system_clock::duration dur) { - std::unique_lock lk(mutex_); + std::unique_lock lk(mutex_); + CancellationGuard cancel_lock; return condvar_.wait_for(lk, dur, [this] { return signal_ || !do_run_; }); } auto sleep_until( std::chrono::time_point time_point) { - std::unique_lock lk(mutex_); + std::unique_lock lk(mutex_); + CancellationGuard cancel_lock; return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; }); } From 14f626d422a77b51bca4bbbe5282ee6e995169e0 Mon Sep 17 00:00:00 2001 From: Oskar Carl Date: Sun, 24 Jan 2021 17:36:30 +0100 Subject: [PATCH 08/51] Add recursive config includes --- include/client.hpp | 1 + src/client.cpp | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/client.hpp b/include/client.hpp index ec3866a..af6eeef 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -39,6 +39,7 @@ class Client { void handleOutput(struct waybar_output &output); bool isValidOutput(const Json::Value &config, struct waybar_output &output); auto setupConfig(const std::string &config_file) -> void; + auto mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void; auto setupCss(const std::string &css_file) -> void; struct waybar_output & getOutput(void *); std::vector getOutputConfigs(struct waybar_output &output); diff --git a/src/client.cpp b/src/client.cpp index ced9e49..752574d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -241,7 +241,24 @@ auto waybar::Client::setupConfig(const std::string &config_file) -> void { } std::string str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); util::JsonParser parser; - config_ = parser.parse(str); + Json::Value tmp_config_ = parser.parse(str); + if (tmp_config_["include"].isArray()) { + for (const auto& include : tmp_config_["include"]) { + spdlog::info("Including resource file: {}", include.asString()); + setupConfig(getValidPath({include.asString()})); + } + } + mergeConfig(config_, tmp_config_); +} + +auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { + for (const auto& key : b_config_.getMemberNames()) { + if (a_config_[key].type() == Json::objectValue && b_config_[key].type() == Json::objectValue) { + mergeConfig(a_config_[key], b_config_[key]); + } else { + a_config_[key] = b_config_[key]; + } + } } auto waybar::Client::setupCss(const std::string &css_file) -> void { From e8278431d281e6bfa2cae15c6874963711a1396b Mon Sep 17 00:00:00 2001 From: Oskar Carl Date: Sun, 24 Jan 2021 18:32:05 +0100 Subject: [PATCH 09/51] Proper formatting --- src/client.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 752574d..7f3229c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -241,9 +241,9 @@ auto waybar::Client::setupConfig(const std::string &config_file) -> void { } std::string str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); util::JsonParser parser; - Json::Value tmp_config_ = parser.parse(str); + Json::Value tmp_config_ = parser.parse(str); if (tmp_config_["include"].isArray()) { - for (const auto& include : tmp_config_["include"]) { + for (const auto &include : tmp_config_["include"]) { spdlog::info("Including resource file: {}", include.asString()); setupConfig(getValidPath({include.asString()})); } @@ -252,7 +252,7 @@ auto waybar::Client::setupConfig(const std::string &config_file) -> void { } auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { - for (const auto& key : b_config_.getMemberNames()) { + for (const auto &key : b_config_.getMemberNames()) { if (a_config_[key].type() == Json::objectValue && b_config_[key].type() == Json::objectValue) { mergeConfig(a_config_[key], b_config_[key]); } else { From e62b634f72a9b91a6f2e94ba04a7ac60bcee23cb Mon Sep 17 00:00:00 2001 From: Oskar Carl Date: Mon, 21 Jun 2021 19:29:09 +0200 Subject: [PATCH 10/51] Workaround for circular imports --- include/client.hpp | 2 +- src/client.cpp | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index af6eeef..a54bf4b 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -38,7 +38,7 @@ class Client { const std::string getValidPath(const std::vector &paths) const; void handleOutput(struct waybar_output &output); bool isValidOutput(const Json::Value &config, struct waybar_output &output); - auto setupConfig(const std::string &config_file) -> void; + auto setupConfig(const std::string &config_file, int depth) -> void; auto mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void; auto setupCss(const std::string &css_file) -> void; struct waybar_output & getOutput(void *); diff --git a/src/client.cpp b/src/client.cpp index 7f3229c..07c0b23 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -234,7 +234,10 @@ std::tuple waybar::Client::getConfigs( return {config_file, css_file}; } -auto waybar::Client::setupConfig(const std::string &config_file) -> void { +auto waybar::Client::setupConfig(const std::string &config_file, int depth) -> void { + if (depth > 100) { + throw std::runtime_error("Aborting due to likely recursive include in config files"); + } std::ifstream file(config_file); if (!file.is_open()) { throw std::runtime_error("Can't open config file"); @@ -245,7 +248,7 @@ auto waybar::Client::setupConfig(const std::string &config_file) -> void { if (tmp_config_["include"].isArray()) { for (const auto &include : tmp_config_["include"]) { spdlog::info("Including resource file: {}", include.asString()); - setupConfig(getValidPath({include.asString()})); + setupConfig(getValidPath({include.asString()}), ++depth); } } mergeConfig(config_, tmp_config_); @@ -337,7 +340,7 @@ int waybar::Client::main(int argc, char *argv[]) { } wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj()); auto [config_file, css_file] = getConfigs(config, style); - setupConfig(config_file); + setupConfig(config_file, 0); setupCss(css_file); bindInterfaces(); gtk_app->hold(); From 982d571b2ea0577b70582e2830ab878eb8070ff3 Mon Sep 17 00:00:00 2001 From: Oskar Carl Date: Wed, 23 Jun 2021 23:08:47 +0200 Subject: [PATCH 11/51] Add include man section --- man/waybar.5.scd.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index fe11c4a..6e6dd13 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -85,6 +85,10 @@ Also a minimal example configuration can be found on the at the bottom of this m Option to disable the use of gtk-layer-shell for popups. Only functional if compiled with gtk-layer-shell support. +*include* ++ + typeof: array ++ + Paths to additional configuration files. In case of duplicate options, the including file's value takes precedence. Make sure to avoid circular imports. + # MODULE FORMAT You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat). From 368e4813de5356332d1167e8200cb5633e772ed6 Mon Sep 17 00:00:00 2001 From: John Helmert III Date: Tue, 29 Jun 2021 21:29:12 -0500 Subject: [PATCH 12/51] libfmt >=8.0.0 compatibility --- include/util/format.hpp | 4 ++++ src/modules/clock.cpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/include/util/format.hpp b/include/util/format.hpp index 288d8f0..543a100 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -35,7 +35,11 @@ namespace fmt { // The rationale for ignoring it is that the only reason to specify // an alignment and a with is to get a fixed width bar, and ">" is // sufficient in this implementation. +#if FMT_VERSION < 80000 width = parse_nonnegative_int(it, end, ctx); +#else + width = detail::parse_nonnegative_int(it, end, -1); +#endif } return it; } diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 22bedc7..82c5701 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -196,6 +196,9 @@ template <> struct fmt::formatter : fmt::formatter { template auto format(const waybar_time& t, FormatContext& ctx) { +#if FMT_VERSION >= 80000 + auto& tm_format = specs; +#endif return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime)); } }; From d3c59c42ef9c3878e0086b0e95100a2f832aa088 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 30 Jun 2021 23:28:01 -0700 Subject: [PATCH 13/51] feat(ci): add GitHub CI jobs on Linux --- .github/workflows/linux.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/linux.yml diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..e550e20 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,25 @@ +name: linux + +on: [push, pull_request] + +jobs: + build: + strategy: + matrix: + distro: + - alpine + - archlinux + - debian + - fedora + - opensuse + + runs-on: ubuntu-latest + container: + image: alexays/waybar:${{ matrix.distro }} + + steps: + - uses: actions/checkout@v2 + - name: configure + run: meson -Dman-pages=enabled build + - name: build + run: ninja -C build From 2a52efa99a7a9604f9d28b0c1bec8e74643c19cf Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 30 Jun 2021 23:50:37 -0700 Subject: [PATCH 14/51] chore: update fedora dockerfile --- Dockerfiles/fedora | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index d75083c..77c77cb 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -1,7 +1,12 @@ # vim: ft=Dockerfile -FROM fedora:32 +FROM fedora:latest -RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel pugixml-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel scdoc -y && \ - dnf group install "C Development Tools and Libraries" -y && \ +RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ + 'pkgconfig(dbusmenu-gtk3-0.4)' 'pkgconfig(fmt)' 'pkgconfig(gdk-pixbuf-2.0)' \ + 'pkgconfig(gio-unix-2.0)' 'pkgconfig(gtk-layer-shell-0)' 'pkgconfig(gtkmm-3.0)' \ + 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ + 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ + 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ + 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' && \ dnf clean all -y From 5420a9104652ece306faefcfe41a6f2191f02295 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Thu, 1 Jul 2021 00:13:05 -0700 Subject: [PATCH 15/51] chore: update FreeBSD action to address ntp sync issue --- .github/workflows/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 7072c49..74422ee 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -8,7 +8,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Test in FreeBSD VM - uses: vmactions/freebsd-vm@v0.0.9 # aka FreeBSD 12.2 + uses: vmactions/freebsd-vm@v0.1.3 # aka FreeBSD 12.2 with: usesh: true prepare: | From 8e1f85e1c30b7431097e7441cfb80db5fccc029b Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 3 Jul 2021 00:27:57 +0200 Subject: [PATCH 16/51] Update archlinux --- Dockerfiles/archlinux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index 0b618ff..e5a53ef 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -1,6 +1,6 @@ # vim: ft=Dockerfile -FROM archlinux/base:latest +FROM archlinux:base-devel RUN pacman -Syu --noconfirm && \ pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm From a8edc0886d6524e3df46e58f98bc86184fa05bb1 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 3 Jul 2021 00:28:43 +0200 Subject: [PATCH 17/51] Delete .travis.yml --- .travis.yml | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index abc739c..0000000 --- a/.travis.yml +++ /dev/null @@ -1,38 +0,0 @@ -language: cpp - -services: - - docker - -git: - submodules: false - -env: - - distro: debian - - distro: archlinux - - distro: fedora - - distro: alpine - - distro: opensuse - -before_install: - - docker pull alexays/waybar:${distro} - - find . -type f \( -name '*.cpp' -o -name '*.h' \) -print0 | xargs -r0 clang-format -i - -script: - - echo FROM alexays/waybar:${distro} > Dockerfile - - echo ADD . /root >> Dockerfile - - docker build -t waybar . - - docker run waybar /bin/sh -c "cd /root && meson build -Dman-pages=enabled && ninja -C build" - -jobs: - include: - - os: freebsd - compiler: clang - env: - before_install: - - 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 - - ninja -C build From 569517c53164a8df6bfa2697bd4b7a97f44d0eb6 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 3 Jul 2021 00:55:59 +0200 Subject: [PATCH 18/51] chore: update freebsd-vm to 0.1.4 --- .github/workflows/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 74422ee..2fde610 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -8,7 +8,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Test in FreeBSD VM - uses: vmactions/freebsd-vm@v0.1.3 # aka FreeBSD 12.2 + uses: vmactions/freebsd-vm@v0.1.4 # aka FreeBSD 12.2 with: usesh: true prepare: | From 78aaa5c1b448dc3d744cd6e8cc1a5ee466774107 Mon Sep 17 00:00:00 2001 From: tiosgz Date: Sat, 10 Jul 2021 20:22:37 +0000 Subject: [PATCH 19/51] Do not fail to parse a multi-bar config --- include/client.hpp | 1 + man/waybar.5.scd.in | 3 ++- src/client.cpp | 48 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index a54bf4b..e7fa1db 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -39,6 +39,7 @@ class Client { void handleOutput(struct waybar_output &output); bool isValidOutput(const Json::Value &config, struct waybar_output &output); auto setupConfig(const std::string &config_file, int depth) -> void; + auto resolveConfigIncludes(Json::Value &config, int depth) -> void; auto mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void; auto setupCss(const std::string &css_file) -> void; struct waybar_output & getOutput(void *); diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 6e6dd13..0168de3 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -86,8 +86,9 @@ Also a minimal example configuration can be found on the at the bottom of this m Only functional if compiled with gtk-layer-shell support. *include* ++ - typeof: array ++ + typeof: string|array ++ Paths to additional configuration files. In case of duplicate options, the including file's value takes precedence. Make sure to avoid circular imports. + For a multi-bar config, specify at least an empty object for each bar also in every file being included. # MODULE FORMAT diff --git a/src/client.cpp b/src/client.cpp index 07c0b23..ff6e7bf 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -245,22 +245,50 @@ auto waybar::Client::setupConfig(const std::string &config_file, int depth) -> v std::string str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); util::JsonParser parser; Json::Value tmp_config_ = parser.parse(str); - if (tmp_config_["include"].isArray()) { - for (const auto &include : tmp_config_["include"]) { - spdlog::info("Including resource file: {}", include.asString()); - setupConfig(getValidPath({include.asString()}), ++depth); + if (tmp_config_.isArray()) { + for (auto &config_part : tmp_config_) { + resolveConfigIncludes(config_part, depth); } + } else { + resolveConfigIncludes(tmp_config_, depth); } mergeConfig(config_, tmp_config_); } -auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { - for (const auto &key : b_config_.getMemberNames()) { - if (a_config_[key].type() == Json::objectValue && b_config_[key].type() == Json::objectValue) { - mergeConfig(a_config_[key], b_config_[key]); - } else { - a_config_[key] = b_config_[key]; +auto waybar::Client::resolveConfigIncludes(Json::Value &config, int depth) -> void { + Json::Value includes = config["include"]; + if (includes.isArray()) { + for (const auto &include : includes) { + spdlog::info("Including resource file: {}", include.asString()); + setupConfig(getValidPath({include.asString()}), ++depth); } + } else if (includes.isString()) { + spdlog::info("Including resource file: {}", includes.asString()); + setupConfig(getValidPath({includes.asString()}), ++depth); + } +} + +auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { + if (!a_config_) { + // For the first config + a_config_ = b_config_; + } else if (a_config_.isObject() && b_config_.isObject()) { + for (const auto &key : b_config_.getMemberNames()) { + if (a_config_[key].isObject() && b_config_[key].isObject()) { + mergeConfig(a_config_[key], b_config_[key]); + } else { + a_config_[key] = b_config_[key]; + } + } + } else if (a_config_.isArray() && b_config_.isArray()) { + // This can happen only on the top-level array of a multi-bar config + for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) { + if (a_config_[i].isObject() && b_config_[i].isObject()) { + mergeConfig(a_config_[i], b_config_[i]); + } + } + } else { + spdlog::error("Cannot merge config, conflicting or invalid JSON types"); } } From 8310700bbb52af17477cedc61ea9f660c7965053 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 13 Jul 2021 04:33:12 +0300 Subject: [PATCH 20/51] Improve sway/language --- include/modules/sway/ipc/ipc.hpp | 4 + include/modules/sway/language.hpp | 40 +++++++- include/util/string.hpp | 15 +++ man/waybar-sway-language.5.scd | 59 +++--------- meson.build | 4 +- src/modules/sway/language.cpp | 148 +++++++++++++++++++++++++----- 6 files changed, 197 insertions(+), 73 deletions(-) create mode 100644 include/util/string.hpp diff --git a/include/modules/sway/ipc/ipc.hpp b/include/modules/sway/ipc/ipc.hpp index 2c5a7a6..5f23d17 100644 --- a/include/modules/sway/ipc/ipc.hpp +++ b/include/modules/sway/ipc/ipc.hpp @@ -29,4 +29,8 @@ enum ipc_command_type { IPC_EVENT_BINDING = ((1 << 31) | 5), IPC_EVENT_SHUTDOWN = ((1 << 31) | 6), IPC_EVENT_TICK = ((1 << 31) | 7), + + // sway-specific event types + IPC_EVENT_BAR_STATE_UPDATE = ((1<<31) | 20), + IPC_EVENT_INPUT = ((1<<31) | 21), }; diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index 7cd6bf6..fb4438b 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -1,6 +1,11 @@ #pragma once #include +#include + +#include +#include + #include "ALabel.hpp" #include "bar.hpp" #include "client.hpp" @@ -16,13 +21,40 @@ class Language : public ALabel, public sigc::trackable { auto update() -> void; private: + struct Layout { + std::string full_name; + std::string short_name; + std::string variant; + }; + + class XKBContext { + public: + XKBContext(); + ~XKBContext(); + auto next_layout() -> Layout*; + private: + rxkb_context* context_ = nullptr; + rxkb_layout* xkb_layout_ = nullptr; + Layout* layout_ = nullptr; + }; + void onEvent(const struct Ipc::ipc_response&); void onCmd(const struct Ipc::ipc_response&); + + auto set_current_layout(std::string current_layout) -> void; + auto init_layouts_map(const std::vector& used_layouts) -> void; + + const static std::string XKB_LAYOUT_NAMES_KEY; + const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY; - std::string lang_; - util::JsonParser parser_; - std::mutex mutex_; - Ipc ipc_; + Layout layout_; + std::map layouts_map_; + XKBContext xkb_context_; + bool is_variant_displayed; + + util::JsonParser parser_; + std::mutex mutex_; + Ipc ipc_; }; } // namespace waybar::modules::sway diff --git a/include/util/string.hpp b/include/util/string.hpp new file mode 100644 index 0000000..d644b4c --- /dev/null +++ b/include/util/string.hpp @@ -0,0 +1,15 @@ +#include + +const std::string WHITESPACE = " \n\r\t\f\v"; + +std::string ltrim(const std::string s) { + size_t begin = s.find_first_not_of(WHITESPACE); + return (begin == std::string::npos) ? "" : s.substr(begin); +} + +std::string rtrim(const std::string s) { + size_t end = s.find_last_not_of(WHITESPACE); + return (end == std::string::npos) ? "" : s.substr(0, end + 1); +} + +std::string trim(const std::string& s) { return rtrim(ltrim(s)); } diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index 769924f..f7f8830 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -15,63 +15,30 @@ Addressed by *sway/language* *format*: ++ typeof: string ++ default: {} ++ - The format, how information should be displayed. On {} data gets inserted. - -*rotate*: ++ - typeof: integer ++ - Positive value to rotate the text label. - -*max-length*: ++ - typeof: integer ++ - The maximum length in character the module should display. - -*min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. - -*align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. - -*on-click*: ++ - typeof: string ++ - Command to execute when clicked on the module. - -*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. - -*on-scroll-down*: ++ - typeof: string ++ - Command to execute when scrolling down on the module. - -*smooth-scrolling-threshold*: ++ - typeof: double ++ - Threshold to be used when scrolling. + The format, how layout should be displayed. *tooltip*: ++ typeof: bool ++ default: true ++ Option to disable tooltip on hover. +# FORMAT REPLACEMENTS + +*{short}*: Short name of layout (e.g. "en"). Equals to {}. + +*{long}*: Long name of layout (e.g. "English (Dvorak)"). + +*{variant}*: Variant of layout (e.g. "Dvorak"). + # EXAMPLES ``` "sway/language": { "format": "{}", - "max-length": 50 +}, + +"sway/language": { + "format": "{short} {variant}", } ``` diff --git a/meson.build b/meson.build index e80448e..1bd1f02 100644 --- a/meson.build +++ b/meson.build @@ -95,6 +95,7 @@ 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')) +xkbregistry = dependency('xkbregistry') libsndio = compiler.find_library('sndio', required: get_option('sndio')) if libsndio.found() @@ -272,7 +273,8 @@ executable( libmpdclient, gtk_layer_shell, libsndio, - tz_dep + tz_dep, + xkbregistry ], include_directories: [include_directories('include')], install: true, diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 4d27fb8..86a8e1e 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -1,10 +1,24 @@ #include "modules/sway/language.hpp" + +#include #include +#include + +#include +#include +#include + +#include "modules/sway/ipc/ipc.hpp" +#include "util/string.hpp" namespace waybar::modules::sway { +const std::string Language::XKB_LAYOUT_NAMES_KEY = "xkb_layout_names"; +const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name"; + Language::Language(const std::string& id, const Json::Value& config) : ALabel(config, "language", id, "{}", 0, true) { + is_variant_displayed = format_.find("{variant}") != std::string::npos; ipc_.subscribe(R"(["input"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd)); @@ -21,18 +35,31 @@ Language::Language(const std::string& id, const Json::Value& config) } void Language::onCmd(const struct Ipc::ipc_response& res) { + if (res.type != static_cast(IPC_GET_INPUTS)) { + return; + } + try { - auto payload = parser_.parse(res.payload); - //Display current layout of a device with a maximum count of layouts, expecting that all will be OK - Json::Value::ArrayIndex maxId = 0, max = 0; - for(Json::Value::ArrayIndex i = 0; i < payload.size(); i++) { - if(payload[i]["xkb_layout_names"].size() > max) { - max = payload[i]["xkb_layout_names"].size(); - maxId = i; + std::lock_guard lock(mutex_); + auto payload = parser_.parse(res.payload); + std::vector used_layouts; + // Display current layout of a device with a maximum count of layouts, expecting that all will + // be OK + Json::ArrayIndex max_id = 0, max = 0; + for (Json::ArrayIndex i = 0; i < payload.size(); i++) { + auto size = payload[i][XKB_LAYOUT_NAMES_KEY].size(); + if (size > max) { + max = size; + max_id = i; } } - auto layout_name = payload[maxId]["xkb_active_layout_name"].asString().substr(0,2); - lang_ = Glib::Markup::escape_text(layout_name); + + for (const auto& layout : payload[max_id][XKB_LAYOUT_NAMES_KEY]) { + used_layouts.push_back(layout.asString()); + } + + init_layouts_map(used_layouts); + set_current_layout(payload[max_id][XKB_ACTIVE_LAYOUT_NAME_KEY].asString()); dp.emit(); } catch (const std::exception& e) { spdlog::error("Language: {}", e.what()); @@ -40,14 +67,15 @@ void Language::onCmd(const struct Ipc::ipc_response& res) { } void Language::onEvent(const struct Ipc::ipc_response& res) { + if (res.type != static_cast(IPC_EVENT_INPUT)) { + return; + } + try { std::lock_guard lock(mutex_); - auto payload = parser_.parse(res.payload)["input"]; + auto payload = parser_.parse(res.payload)["input"]; if (payload["type"].asString() == "keyboard") { - auto layout_name = payload["xkb_active_layout_name"].asString().substr(0,2); - if (!layout_name.empty()) { - lang_ = Glib::Markup::escape_text(layout_name); - } + set_current_layout(payload[XKB_ACTIVE_LAYOUT_NAME_KEY].asString()); } dp.emit(); } catch (const std::exception& e) { @@ -56,17 +84,93 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { } auto Language::update() -> void { - if (lang_.empty()) { - event_box_.hide(); - } else { - label_.set_markup(fmt::format(format_, lang_)); - if (tooltipEnabled()) { - label_.set_tooltip_text(lang_); - } - event_box_.show(); + auto display_layout = trim(fmt::format(format_, + fmt::arg("short", layout_.short_name), + fmt::arg("long", layout_.full_name), + fmt::arg("variant", layout_.variant))); + label_.set_markup(display_layout); + if (tooltipEnabled()) { + label_.set_tooltip_markup(display_layout); } + + event_box_.show(); + // Call parent update ALabel::update(); } +auto Language::set_current_layout(std::string current_layout) -> void { + layout_ = layouts_map_[current_layout]; +} + +auto Language::init_layouts_map(const std::vector& used_layouts) -> void { + std::map> found_by_short_names; + auto layout = xkb_context_.next_layout(); + for (; layout != nullptr; layout = xkb_context_.next_layout()) { + if (std::find(used_layouts.begin(), used_layouts.end(), layout->full_name) == + used_layouts.end()) { + continue; + } + + if (!is_variant_displayed) { + auto short_name = layout->short_name; + if (found_by_short_names.count(short_name) > 0) { + found_by_short_names[short_name].push_back(layout); + } else { + found_by_short_names[short_name] = {layout}; + } + } + + layouts_map_.emplace(layout->full_name, *layout); + } + + if (is_variant_displayed || found_by_short_names.size() == 0) { + return; + } + + std::map short_name_to_number_map; + for (const auto& used_layout_name : used_layouts) { + auto used_layout = &layouts_map_.find(used_layout_name)->second; + auto layouts_with_same_name_list = found_by_short_names[used_layout->short_name]; + spdlog::info("SIZE: " + std::to_string(layouts_with_same_name_list.size())); + if (layouts_with_same_name_list.size() < 2) { + continue; + } + + if (short_name_to_number_map.count(used_layout->short_name) == 0) { + short_name_to_number_map[used_layout->short_name] = 1; + } + + used_layout->short_name = + used_layout->short_name + std::to_string(short_name_to_number_map[used_layout->short_name]++); + } +} + +Language::XKBContext::XKBContext() { + context_ = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES); + rxkb_context_include_path_append_default(context_); + rxkb_context_parse_default_ruleset(context_); +} + +auto Language::XKBContext::next_layout() -> Layout* { + if (xkb_layout_ == nullptr) { + xkb_layout_ = rxkb_layout_first(context_); + } else { + xkb_layout_ = rxkb_layout_next(xkb_layout_); + } + + if (xkb_layout_ == nullptr) { + return nullptr; + } + + auto description = std::string(rxkb_layout_get_description(xkb_layout_)); + auto name = std::string(rxkb_layout_get_name(xkb_layout_)); + auto variant_ = rxkb_layout_get_variant(xkb_layout_); + std::string variant = variant_ == nullptr ? "" : std::string(variant_); + + layout_ = new Layout{description, name, variant}; + return layout_; +} + +Language::XKBContext::~XKBContext() { rxkb_context_unref(context_); } } // namespace waybar::modules::sway From 9cce5ea6b536a0dace941fd233a0c54c3462b9d8 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 13 Jul 2021 04:49:19 +0300 Subject: [PATCH 21/51] Update dockerfiles --- Dockerfiles/alpine | 2 +- Dockerfiles/archlinux | 2 +- Dockerfiles/debian | 2 +- Dockerfiles/fedora | 2 +- Dockerfiles/opensuse | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfiles/alpine b/Dockerfiles/alpine index 21d1cbb..c0e032f 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 sndio-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 libxkbcommon diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index e5a53ef..40a1b2e 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,4 +3,4 @@ FROM archlinux:base-devel RUN pacman -Syu --noconfirm && \ - pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm + pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm libxkbcommon diff --git a/Dockerfiles/debian b/Dockerfiles/debian index cee1744..f7f6e63 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon && \ apt-get clean diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index 77c77cb..470ceb7 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -8,5 +8,5 @@ RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ - 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' && \ + 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbcommon)' && \ dnf clean all -y diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index 5b664fb..eb231fd 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -4,4 +4,4 @@ FROM opensuse/tumbleweed:latest RUN zypper -n up && \ zypper -n install -t pattern devel_C_C++ && \ - zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc + zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon From 9c2b5efe7b5f9acd934595fcd8f27d2404ea0668 Mon Sep 17 00:00:00 2001 From: Patrick Nicolas Date: Thu, 15 Jul 2021 08:52:02 +0200 Subject: [PATCH 22/51] Support per-device icon in pulseaudio --- include/ALabel.hpp | 2 +- include/modules/pulseaudio.hpp | 2 +- man/waybar-pulseaudio.5.scd | 4 ++++ src/ALabel.cpp | 2 +- src/modules/pulseaudio.cpp | 12 +++++++----- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/include/ALabel.hpp b/include/ALabel.hpp index 5b9ac54..d8a5b50 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -14,7 +14,7 @@ class ALabel : public AModule { virtual ~ALabel() = default; virtual auto update() -> void; virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0); - virtual std::string getIcon(uint16_t, std::vector &alts, uint16_t max = 0); + virtual std::string getIcon(uint16_t, const std::vector &alts, uint16_t max = 0); protected: Gtk::Label label_; diff --git a/include/modules/pulseaudio.hpp b/include/modules/pulseaudio.hpp index 5f17620..9b36dfe 100644 --- a/include/modules/pulseaudio.hpp +++ b/include/modules/pulseaudio.hpp @@ -24,7 +24,7 @@ class Pulseaudio : public ALabel { static void volumeModifyCb(pa_context*, int, void*); bool handleScroll(GdkEventScroll* e); - const std::string getPortIcon() const; + const std::vector getPulseIcon() const; pa_threaded_mainloop* mainloop_; pa_mainloop_api* mainloop_api_; diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index d894290..7de1028 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -109,6 +109,9 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu # ICONS: The following strings for *format-icons* are supported. + +- the device name + If they are found in the current PulseAudio port name, the corresponding icons will be selected. - *default* (Shown, when no other port is found) @@ -131,6 +134,7 @@ If they are found in the current PulseAudio port name, the corresponding icons w "format-bluetooth": "{volume}% {icon}", "format-muted": "", "format-icons": { + "alsa_output.pci-0000_00_1f.3.analog-stereo": "", "headphones": "", "handsfree": "", "headset": "", diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 8d9c9b4..dd41a32 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -76,7 +76,7 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_ return ""; } -std::string ALabel::getIcon(uint16_t percentage, std::vector& alts, uint16_t max) { +std::string ALabel::getIcon(uint16_t percentage, const std::vector& alts, uint16_t max) { auto format_icons = config_["format-icons"]; if (format_icons.isObject()) { std::string _alt = "default"; diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 3fbe956..a7cdcec 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -194,15 +194,17 @@ static const std::array ports = { "phone", }; -const std::string waybar::modules::Pulseaudio::getPortIcon() const { +const std::vector waybar::modules::Pulseaudio::getPulseIcon() const { + std::vector res = {default_sink_name_}; std::string nameLC = port_name_ + form_factor_; std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { if (nameLC.find(port) != std::string::npos) { - return port; + res.push_back(port); + return res; } } - return port_name_; + return res; } auto waybar::modules::Pulseaudio::update() -> void { @@ -252,7 +254,7 @@ auto waybar::modules::Pulseaudio::update() -> void { fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_), fmt::arg("source_desc", source_desc_), - fmt::arg("icon", getIcon(volume_, getPortIcon())))); + fmt::arg("icon", getIcon(volume_, getPulseIcon())))); getState(volume_); if (tooltipEnabled()) { @@ -267,7 +269,7 @@ auto waybar::modules::Pulseaudio::update() -> void { fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_), fmt::arg("source_desc", source_desc_), - fmt::arg("icon", getIcon(volume_, getPortIcon())))); + fmt::arg("icon", getIcon(volume_, getPulseIcon())))); } else { label_.set_tooltip_text(desc_); } From 948eba92a540b52443b1e0b0a414b7ec6de83cab Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 13 Jul 2021 04:49:19 +0300 Subject: [PATCH 23/51] Update dockerfiles --- Dockerfiles/alpine | 2 +- Dockerfiles/archlinux | 2 +- Dockerfiles/debian | 2 +- Dockerfiles/fedora | 2 +- Dockerfiles/opensuse | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfiles/alpine b/Dockerfiles/alpine index 21d1cbb..c0e032f 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 sndio-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 libxkbcommon diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index e5a53ef..40a1b2e 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,4 +3,4 @@ FROM archlinux:base-devel RUN pacman -Syu --noconfirm && \ - pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm + pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm libxkbcommon diff --git a/Dockerfiles/debian b/Dockerfiles/debian index cee1744..f7f6e63 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon && \ apt-get clean diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index 77c77cb..470ceb7 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -8,5 +8,5 @@ RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ - 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' && \ + 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbcommon)' && \ dnf clean all -y diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index 5b664fb..eb231fd 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -4,4 +4,4 @@ FROM opensuse/tumbleweed:latest RUN zypper -n up && \ zypper -n install -t pattern devel_C_C++ && \ - zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc + zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon From 2506c0104a12e017721849ff9b5541b5675e4c7a Mon Sep 17 00:00:00 2001 From: Anakael Date: Fri, 16 Jul 2021 11:37:58 +0300 Subject: [PATCH 24/51] Update Dockerfiles/fedora Co-authored-by: Aleksei Bavshin --- Dockerfiles/fedora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index 470ceb7..a61dcd3 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -8,5 +8,5 @@ RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ - 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbcommon)' && \ + 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' && \ dnf clean all -y From 86a43b904214613b2470db65746367b7721bd929 Mon Sep 17 00:00:00 2001 From: Roosembert Palacios Date: Tue, 20 Jul 2021 10:11:55 +0200 Subject: [PATCH 25/51] pulseaudio: Control currently running sink In a system with multiple sinks, the default sink may not always be the once currently being used. It is more useful to control the currently active sink rather than an unused one. This patch does not make any difference if the system only uses the default sink. Signed-off-by: Roosembert Palacios --- include/modules/pulseaudio.hpp | 3 ++- src/modules/pulseaudio.cpp | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/include/modules/pulseaudio.hpp b/include/modules/pulseaudio.hpp index 5f17620..f636c00 100644 --- a/include/modules/pulseaudio.hpp +++ b/include/modules/pulseaudio.hpp @@ -38,7 +38,8 @@ class Pulseaudio : public ALabel { std::string form_factor_; std::string desc_; std::string monitor_; - std::string default_sink_name_; + std::string current_sink_name_; + bool current_sink_running_; // SOURCE uint32_t source_idx_{0}; uint16_t source_volume_; diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 3fbe956..c0299fc 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -151,8 +151,24 @@ void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const p */ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, int /*eol*/, void *data) { + if (i == nullptr) + return; + auto pa = static_cast(data); - if (i != nullptr && pa->default_sink_name_ == i->name) { + if (pa->current_sink_name_ == i->name) { + if (i->state != PA_SINK_RUNNING) { + pa->current_sink_running_ = false; + } else { + pa->current_sink_running_ = true; + } + } + + if (!pa->current_sink_running_ && i->state == PA_SINK_RUNNING) { + pa->current_sink_name_ = i->name; + pa->current_sink_running_ = true; + } + + if (pa->current_sink_name_ == i->name) { pa->pa_volume_ = i->volume; float volume = static_cast(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM}; pa->sink_idx_ = i->index; @@ -175,11 +191,11 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_ void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i, void *data) { auto pa = static_cast(data); - pa->default_sink_name_ = i->default_sink_name; + pa->current_sink_name_ = i->default_sink_name; pa->default_source_name_ = i->default_source_name; - pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data); - pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data); + pa_context_get_sink_info_list(context, sinkInfoCb, data); + pa_context_get_source_info_list(context, sourceInfoCb, data); } static const std::array ports = { From 6f2bfd43bf2a92ffefd880c6923af757b6565665 Mon Sep 17 00:00:00 2001 From: Lars Christensen Date: Tue, 20 Jul 2021 15:25:05 +0200 Subject: [PATCH 26/51] Fix pulseaudio icon name compilation error --- src/modules/pulseaudio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index bd90375..cf42780 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -211,7 +211,7 @@ static const std::array ports = { }; const std::vector waybar::modules::Pulseaudio::getPulseIcon() const { - std::vector res = {default_sink_name_}; + std::vector res = {default_source_name_}; std::string nameLC = port_name_ + form_factor_; std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { From 6dfa31fb1778da2989a28179304f9e1ccf1e3e4e Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 15:05:11 -0500 Subject: [PATCH 27/51] Basic keyboard state module --- README.md | 2 + include/factory.hpp | 3 ++ include/modules/keyboard_state.hpp | 31 +++++++++++++ meson.build | 7 +++ meson_options.txt | 1 + src/factory.cpp | 5 ++ src/modules/keyboard_state.cpp | 73 ++++++++++++++++++++++++++++++ 7 files changed, 122 insertions(+) create mode 100644 include/modules/keyboard_state.hpp create mode 100644 src/modules/keyboard_state.cpp diff --git a/README.md b/README.md index b104ade..37c0cfc 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ libappindicator-gtk3 [Tray module] libdbusmenu-gtk3 [Tray module] libmpdclient [MPD module] libsndio [sndio module] +libevdev [KeyboardState module] ``` **Build dependencies** @@ -86,6 +87,7 @@ sudo apt install \ clang-tidy \ gobject-introspection \ libdbusmenu-gtk3-dev \ + libevdev-dev \ libfmt-dev \ libgirepository1.0-dev \ libgtk-3-dev \ diff --git a/include/factory.hpp b/include/factory.hpp index 1cae68c..4b9f32a 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -38,6 +38,9 @@ #ifdef HAVE_LIBUDEV #include "modules/backlight.hpp" #endif +#ifdef HAVE_LIBEVDEV +#include "modules/keyboard_state.hpp" +#endif #ifdef HAVE_LIBPULSE #include "modules/pulseaudio.hpp" #endif diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp new file mode 100644 index 0000000..99ed602 --- /dev/null +++ b/include/modules/keyboard_state.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#if FMT_VERSION < 60000 +#include +#else +#include +#endif +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +extern "C" { +#include +} + +namespace waybar::modules { + +class KeyboardState : public ALabel { + public: + KeyboardState(const std::string&, const Json::Value&); + ~KeyboardState(); + auto update() -> void; + + private: + std::string dev_path_; + int fd_; + libevdev* dev_; + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index e80448e..7ac6fd6 100644 --- a/meson.build +++ b/meson.build @@ -94,6 +94,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')) +libevdev = dependency('libevdev', required: get_option('libevdev')) libmpdclient = dependency('libmpdclient', required: get_option('mpd')) libsndio = compiler.find_library('sndio', required: get_option('sndio')) @@ -215,6 +216,11 @@ if libudev.found() and (is_linux or libepoll.found()) src_files += 'src/modules/backlight.cpp' endif +if libevdev.found() and (is_linux or libepoll.found()) + add_project_arguments('-DHAVE_LIBEVDEV', language: 'cpp') + src_files += 'src/modules/keyboard_state.cpp' +endif + if libmpdclient.found() add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') src_files += 'src/modules/mpd/mpd.cpp' @@ -270,6 +276,7 @@ executable( libudev, libepoll, libmpdclient, + libevdev, gtk_layer_shell, libsndio, tz_dep diff --git a/meson_options.txt b/meson_options.txt index cb5581b..fefb3dc 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,6 +1,7 @@ option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.') option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features') option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features') +option('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features') option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio') option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit') option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray') diff --git a/src/factory.cpp b/src/factory.cpp index 1f90789..cab2307 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -70,6 +70,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { return new waybar::modules::Backlight(id, config_[name]); } #endif +#ifdef HAVE_LIBEVDEV + if (ref == "keyboard_state") { + return new waybar::modules::KeyboardState(id, config_[name]); + } +#endif #ifdef HAVE_LIBPULSE if (ref == "pulseaudio") { return new waybar::modules::Pulseaudio(id, config_[name]); diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp new file mode 100644 index 0000000..ea9ae57 --- /dev/null +++ b/src/modules/keyboard_state.cpp @@ -0,0 +1,73 @@ +#include "modules/keyboard_state.hpp" +#include + +extern "C" { +#include +#include +#include +} + +waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Json::Value& config) + : ALabel(config, "keyboard_state", id, "{temperatureC}", 1) { + if (config_["device-path"].isString()) { + dev_path_ = config_["device-path"].asString(); + } else { + dev_path_ = ""; + } + + fd_ = open(dev_path_.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY); + if (fd_ < 0) { + throw std::runtime_error("Can't open " + dev_path_); + } + int err = libevdev_new_from_fd(fd_, &dev_); + if (err < 0) { + throw std::runtime_error("Can't create libevdev device"); + } + if (!libevdev_has_event_type(dev_, EV_LED)) { + throw std::runtime_error("Device doesn't support LED events"); + } + if (!libevdev_has_event_code(dev_, EV_LED, LED_NUML) || !libevdev_has_event_code(dev_, EV_LED, LED_CAPSL)) { + throw std::runtime_error("Device doesn't support num lock or caps lock events"); + } + + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +waybar::modules::KeyboardState::~KeyboardState() { + libevdev_free(dev_); + int err = close(fd_); + if (err < 0) { + // Not much we can do, so ignore it. + } +} + +auto waybar::modules::KeyboardState::update() -> void { + int err = LIBEVDEV_READ_STATUS_SUCCESS; + while (err == LIBEVDEV_READ_STATUS_SUCCESS) { + input_event ev; + err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_NORMAL, &ev); + while (err == LIBEVDEV_READ_STATUS_SYNC) { + err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev); + } + } + if (err != -EAGAIN) { + throw std::runtime_error("Failed to sync evdev device"); + } + + int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML); + //int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL); + + std::string text; + if (numl) { + text = fmt::format(format_, "num lock enabled"); + label_.set_markup(text); + } else { + text = fmt::format(format_, "num lock disabled"); + label_.set_markup(text); + } + + ALabel::update(); +} From 642e28166b784244fda45cc90832b75951969372 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 18:46:39 -0500 Subject: [PATCH 28/51] Add more configuaration --- include/modules/keyboard_state.hpp | 22 ++++++++-- resources/config | 13 +++++- src/factory.cpp | 2 +- src/modules/keyboard_state.cpp | 70 ++++++++++++++++++++++++------ 4 files changed, 89 insertions(+), 18 deletions(-) diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index 99ed602..0873ad5 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -6,8 +6,10 @@ #else #include #endif -#include "ALabel.hpp" +#include "AModule.hpp" +#include "bar.hpp" #include "util/sleeper_thread.hpp" +#include extern "C" { #include @@ -15,16 +17,30 @@ extern "C" { namespace waybar::modules { -class KeyboardState : public ALabel { +class KeyboardState : public AModule { public: - KeyboardState(const std::string&, const Json::Value&); + KeyboardState(const std::string&, const waybar::Bar&, const Json::Value&); ~KeyboardState(); auto update() -> void; private: + const Bar& bar_; + Gtk::Box box_; + Gtk::Label numlock_label_; + Gtk::Label capslock_label_; + Gtk::Label scrolllock_label_; + + std::string numlock_format_; + std::string capslock_format_; + std::string scrolllock_format_; + const std::chrono::seconds interval_; + std::string icon_locked_; + std::string icon_unlocked_; + std::string dev_path_; int fd_; libevdev* dev_; + util::SleeperThread thread_; }; diff --git a/resources/config b/resources/config index 13dc94c..e7c91c0 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": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "sway/language", "battery", "battery#bat2", "clock", "tray"], + "modules-right": ["keyboard_state", "mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "sway/language", "battery", "battery#bat2", "clock", "tray"], // Modules configuration // "sway/workspaces": { // "disable-scroll": true, @@ -23,6 +23,16 @@ // "default": "" // } // }, + "keyboard_state": { + "numlock": true, + "format": "{name} {icon}", + "format-icons": { + "locked": "", + "unlocked": "" + }, + "capslock": true, + "device-path": "/dev/input/by-path/platform-i8042-serio-0-event-kbd" + }, "sway/mode": { "format": "{}" }, @@ -145,3 +155,4 @@ // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name } } + diff --git a/src/factory.cpp b/src/factory.cpp index cab2307..6a0147d 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -72,7 +72,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { #endif #ifdef HAVE_LIBEVDEV if (ref == "keyboard_state") { - return new waybar::modules::KeyboardState(id, config_[name]); + return new waybar::modules::KeyboardState(id, bar_, config_[name]); } #endif #ifdef HAVE_LIBPULSE diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index ea9ae57..b51617f 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -7,8 +7,44 @@ extern "C" { #include } -waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Json::Value& config) - : ALabel(config, "keyboard_state", id, "{temperatureC}", 1) { +waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config) + : AModule(config, "keyboard_state", id, false, !config["disable-scroll"].asBool()), + bar_(bar), + box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), + numlock_label_(""), + capslock_label_(""), + numlock_format_(config_["format"].isString() ? config_["format"].asString() + : config_["format"]["numlock"].isString() ? config_["format"]["numlock"].asString() + : "{name} {icon}"), + capslock_format_(config_["format"].isString() ? config_["format"].asString() + : config_["format"]["capslock"].isString() ? config_["format"]["capslock"].asString() + : "{name} {icon}"), + scrolllock_format_(config_["format"].isString() ? config_["format"].asString() + : config_["format"]["scrolllock"].isString() ? config_["format"]["scrolllock"].asString() + : "{name} {icon}"), + interval_(std::chrono::seconds(config_["interval"].isUInt() ? config_["interval"].asUInt() : 1)), + icon_locked_(config_["format-icons"]["locked"].isString() + ? config_["format-icons"]["locked"].asString() + : "locked"), + icon_unlocked_(config_["format-icons"]["unlocked"].isString() + ? config_["format-icons"]["unlocked"].asString() + : "unlocked") + { + box_.set_name("keyboard_state"); + if (config_["numlock"].asBool()) { + box_.pack_end(numlock_label_, false, false, 0); + } + if (config_["capslock"].asBool()) { + box_.pack_end(capslock_label_, false, false, 0); + } + if (config_["scrolllock"].asBool()) { + box_.pack_end(scrolllock_label_, false, false, 0); + } + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + event_box_.add(box_); + if (config_["device-path"].isString()) { dev_path_ = config_["device-path"].asString(); } else { @@ -26,8 +62,10 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Json: if (!libevdev_has_event_type(dev_, EV_LED)) { throw std::runtime_error("Device doesn't support LED events"); } - if (!libevdev_has_event_code(dev_, EV_LED, LED_NUML) || !libevdev_has_event_code(dev_, EV_LED, LED_CAPSL)) { - throw std::runtime_error("Device doesn't support num lock or caps lock events"); + if (!libevdev_has_event_code(dev_, EV_LED, LED_NUML) + || !libevdev_has_event_code(dev_, EV_LED, LED_CAPSL) + || !libevdev_has_event_code(dev_, EV_LED, LED_SCROLLL)) { + throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events"); } thread_ = [this] { @@ -58,16 +96,22 @@ auto waybar::modules::KeyboardState::update() -> void { } int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML); - //int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL); + int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL); + int scrolll = libevdev_get_event_value(dev_, EV_LED, LED_SCROLLL); std::string text; - if (numl) { - text = fmt::format(format_, "num lock enabled"); - label_.set_markup(text); - } else { - text = fmt::format(format_, "num lock disabled"); - label_.set_markup(text); - } + text = fmt::format(numlock_format_, + fmt::arg("icon", numl ? icon_locked_ : icon_unlocked_), + fmt::arg("name", "Num")); + numlock_label_.set_markup(text); + text = fmt::format(capslock_format_, + fmt::arg("icon", capsl ? icon_locked_ : icon_unlocked_), + fmt::arg("name", "Caps")); + capslock_label_.set_markup(text); + text = fmt::format(scrolllock_format_, + fmt::arg("icon", scrolll ? icon_locked_ : icon_unlocked_), + fmt::arg("name", "Scroll")); + scrolllock_label_.set_markup(text); - ALabel::update(); + AModule::update(); } From 40e6360722acc32ea1b266f361666a4cc64d9608 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 18:57:12 -0500 Subject: [PATCH 29/51] Update css class when locked/unlocked --- src/modules/keyboard_state.cpp | 35 +++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index b51617f..516502f 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -99,19 +99,28 @@ auto waybar::modules::KeyboardState::update() -> void { int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL); int scrolll = libevdev_get_event_value(dev_, EV_LED, LED_SCROLLL); - std::string text; - text = fmt::format(numlock_format_, - fmt::arg("icon", numl ? icon_locked_ : icon_unlocked_), - fmt::arg("name", "Num")); - numlock_label_.set_markup(text); - text = fmt::format(capslock_format_, - fmt::arg("icon", capsl ? icon_locked_ : icon_unlocked_), - fmt::arg("name", "Caps")); - capslock_label_.set_markup(text); - text = fmt::format(scrolllock_format_, - fmt::arg("icon", scrolll ? icon_locked_ : icon_unlocked_), - fmt::arg("name", "Scroll")); - scrolllock_label_.set_markup(text); + struct { + bool state; + Gtk::Label& label; + const std::string& format; + const char* name; + } label_states[] = { + {(bool) numl, numlock_label_, numlock_format_, "Num"}, + {(bool) capsl, capslock_label_, capslock_format_, "Caps"}, + {(bool) scrolll, scrolllock_label_, scrolllock_format_, "Scroll"}, + }; + for (auto& label_state : label_states) { + std::string text; + text = fmt::format(label_state.format, + fmt::arg("icon", label_state.state ? icon_locked_ : icon_unlocked_), + fmt::arg("name", label_state.name)); + label_state.label.set_markup(text); + if (label_state.state) { + label_state.label.get_style_context()->add_class("locked"); + } else { + label_state.label.get_style_context()->remove_class("locked"); + } + } AModule::update(); } From 6fdbc27998e16daafa637427c6ecddc53abc31ce Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 19:06:55 -0500 Subject: [PATCH 30/51] Add default style --- resources/config | 2 +- resources/style.css | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/resources/config b/resources/config index e7c91c0..a83edf1 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": ["keyboard_state", "mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "sway/language", "battery", "battery#bat2", "clock", "tray"], + "modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "keyboard_state", "sway/language", "battery", "battery#bat2", "clock", "tray"], // Modules configuration // "sway/workspaces": { // "disable-scroll": true, diff --git a/resources/style.css b/resources/style.css index 63ea871..c1ac5ad 100644 --- a/resources/style.css +++ b/resources/style.css @@ -228,3 +228,19 @@ label:focus { margin: 0 5px; min-width: 16px; } + +#keyboard_state { + background: #97e1ad; + color: #000000; + padding: 0 0px; + margin: 0 5px; + min-width: 16px; +} + +#keyboard_state > label { + padding: 0 5px; +} + +#keyboard_state > label.locked { + background: rgba(0, 0, 0, 0.2); +} From 08e886ebc681849e7b241db71ad7e76a9d0dec43 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 21:05:34 -0500 Subject: [PATCH 31/51] Search for device automatically if none given --- include/modules/keyboard_state.hpp | 3 +- resources/config | 5 +-- src/modules/keyboard_state.cpp | 69 +++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index 0873ad5..d6d98de 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -24,6 +24,8 @@ class KeyboardState : public AModule { auto update() -> void; private: + static auto openDevice(const std::string&) -> std::pair; + const Bar& bar_; Gtk::Box box_; Gtk::Label numlock_label_; @@ -37,7 +39,6 @@ class KeyboardState : public AModule { std::string icon_locked_; std::string icon_unlocked_; - std::string dev_path_; int fd_; libevdev* dev_; diff --git a/resources/config b/resources/config index a83edf1..465f694 100644 --- a/resources/config +++ b/resources/config @@ -25,13 +25,12 @@ // }, "keyboard_state": { "numlock": true, + "capslock": true, "format": "{name} {icon}", "format-icons": { "locked": "", "unlocked": "" - }, - "capslock": true, - "device-path": "/dev/input/by-path/platform-i8042-serio-0-event-kbd" + } }, "sway/mode": { "format": "{}" diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 516502f..9a7802f 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -1,5 +1,6 @@ #include "modules/keyboard_state.hpp" #include +#include extern "C" { #include @@ -28,8 +29,9 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& : "locked"), icon_unlocked_(config_["format-icons"]["unlocked"].isString() ? config_["format-icons"]["unlocked"].asString() - : "unlocked") - { + : "unlocked"), + fd_(0), + dev_(nullptr) { box_.set_name("keyboard_state"); if (config_["numlock"].asBool()) { box_.pack_end(numlock_label_, false, false, 0); @@ -46,26 +48,28 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& event_box_.add(box_); if (config_["device-path"].isString()) { - dev_path_ = config_["device-path"].asString(); + std::string dev_path = config_["device-path"].asString(); + std::tie(fd_, dev_) = openDevice(dev_path); } else { - dev_path_ = ""; - } - - fd_ = open(dev_path_.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY); - if (fd_ < 0) { - throw std::runtime_error("Can't open " + dev_path_); - } - int err = libevdev_new_from_fd(fd_, &dev_); - if (err < 0) { - throw std::runtime_error("Can't create libevdev device"); - } - if (!libevdev_has_event_type(dev_, EV_LED)) { - throw std::runtime_error("Device doesn't support LED events"); - } - if (!libevdev_has_event_code(dev_, EV_LED, LED_NUML) - || !libevdev_has_event_code(dev_, EV_LED, LED_CAPSL) - || !libevdev_has_event_code(dev_, EV_LED, LED_SCROLLL)) { - throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events"); + DIR* dev_dir = opendir("/dev/input"); + if (dev_dir == nullptr) { + throw std::runtime_error("Failed to open /dev/input"); + } + dirent *ep; + while ((ep = readdir(dev_dir))) { + if (ep->d_type != DT_CHR) continue; + std::string dev_path = std::string("/dev/input/") + ep->d_name; + try { + std::tie(fd_, dev_) = openDevice(dev_path); + spdlog::info("Found device {} at '{}'", libevdev_get_name(dev_), dev_path); + break; + } catch (const std::runtime_error& e) { + continue; + } + } + if (dev_ == nullptr) { + throw std::runtime_error("Failed to find keyboard device"); + } } thread_ = [this] { @@ -82,6 +86,29 @@ waybar::modules::KeyboardState::~KeyboardState() { } } +auto waybar::modules::KeyboardState::openDevice(const std::string& path) -> std::pair { + int fd = open(path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY); + if (fd < 0) { + throw std::runtime_error("Can't open " + path); + } + + libevdev* dev; + int err = libevdev_new_from_fd(fd, &dev); + if (err < 0) { + throw std::runtime_error("Can't create libevdev device"); + } + if (!libevdev_has_event_type(dev, EV_LED)) { + throw std::runtime_error("Device doesn't support LED events"); + } + if (!libevdev_has_event_code(dev, EV_LED, LED_NUML) + || !libevdev_has_event_code(dev, EV_LED, LED_CAPSL) + || !libevdev_has_event_code(dev, EV_LED, LED_SCROLLL)) { + throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events"); + } + + return std::make_pair(fd, dev); +} + auto waybar::modules::KeyboardState::update() -> void { int err = LIBEVDEV_READ_STATUS_SUCCESS; while (err == LIBEVDEV_READ_STATUS_SUCCESS) { From 99138ffdcd6e3620a9952d7ca1ceb7b0fc5edfb1 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Thu, 15 Apr 2021 17:41:15 -0400 Subject: [PATCH 32/51] Add man page for keyboard_state module --- man/waybar-keyboard-state.5.scd | 80 +++++++++++++++++++++++++++++++++ man/waybar.5.scd.in | 1 + meson.build | 1 + 3 files changed, 82 insertions(+) create mode 100644 man/waybar-keyboard-state.5.scd diff --git a/man/waybar-keyboard-state.5.scd b/man/waybar-keyboard-state.5.scd new file mode 100644 index 0000000..8e9ca5e --- /dev/null +++ b/man/waybar-keyboard-state.5.scd @@ -0,0 +1,80 @@ +waybar-keyboard-state(5) + +# NAME + +waybar - keyboard_state module + +# DESCRIPTION + +The *keyboard_state* module displays the state of number lock, caps lock, and scroll lock. + +# CONFIGURATION + +*interval*: ++ + typeof: integer ++ + default: 1 ++ + The interval, in seconds, to poll the keyboard state. + +*format*: ++ + typeof: string|object ++ + default: {name} {icon} ++ + The format, how information should be displayed. If a string, the same format is used for all keyboard states. If an object, the fields "numlock", "capslock", and "scrolllock" each specify the format for the corresponding state. Any unspecified states use the default format. + +*format-icons*: ++ + typeof: object ++ + default: {"locked": "locked", "unlocked": "unlocked"} ++ + Based on the keyboard state, the corresponding icon gets selected. The same set of icons is used for number, caps, and scroll lock, but the icon is selected from the set independently for each. See *icons*. + +*numlock*: ++ + typeof: bool ++ + default: false ++ + Display the number lock state. + +*capslock*: ++ + typeof: bool ++ + default: false ++ + Display the caps lock state. + +*scrolllock*: ++ + typeof: bool ++ + default: false ++ + Display the scroll lock state. + +*device-path*: ++ + typeof: string ++ + default: chooses first valid input device ++ + Which libevdev input device to show the state of. Libevdev devices can be found in /dev/input. The device should support number lock, caps lock, and scroll lock events. + +# FORMAT REPLACEMENTS + +*{name}*: Caps, Num, or Scroll. + +*{icon}*: Icon, as defined in *format-icons*. + +# ICONS + +The following *format-icons* can be set. + +- *locked*: Will be shown when the keyboard state is locked. Default "locked". +- *unlocked*: Will be shown when the keyboard state is not locked. Default "unlocked" + +# EXAMPLE: + +``` +"keyboard_state": { + "numlock": true, + "capslock": true, + "format": "{name} {icon}", + "format-icons": { + "locked": "", + "unlocked": "" + } +} +``` + +# STYLE + +- *#keyboard_state* +- *#keyboard_state label* +- *#keyboard_state label.locked* + diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 0168de3..9dc6925 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -208,6 +208,7 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270. - *waybar-custom(5)* - *waybar-disk(5)* - *waybar-idle-inhibitor(5)* +- *waybar-keyboard-state(5)* - *waybar-memory(5)* - *waybar-mpd(5)* - *waybar-network(5)* diff --git a/meson.build b/meson.build index 7ac6fd6..f3b50f7 100644 --- a/meson.build +++ b/meson.build @@ -317,6 +317,7 @@ if scdoc.found() 'waybar-custom.5.scd', 'waybar-disk.5.scd', 'waybar-idle-inhibitor.5.scd', + 'waybar-keyboard-state.5.scd', 'waybar-memory.5.scd', 'waybar-mpd.5.scd', 'waybar-network.5.scd', From 9880c6929f64284aa088a8a662ac3c636176b685 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Tue, 20 Jul 2021 21:24:43 -0400 Subject: [PATCH 33/51] Install libevdev in FreeBSD workflow --- .github/workflows/freebsd.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 2fde610..f02c9b5 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -15,8 +15,9 @@ jobs: export CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib # sndio sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf pkg install -y git # subprojects/date - pkg install -y gtk-layer-shell gtkmm30 jsoncpp libdbusmenu sndio \ - libfmt libmpdclient libudev-devd meson pkgconf pulseaudio scdoc spdlog + pkg install -y evdev-proto gtk-layer-shell gtkmm30 jsoncpp libdbusmenu \ + libevdev libfmt libmpdclient libudev-devd meson pkgconf pulseaudio \ + scdoc sndio spdlog run: | meson build -Dman-pages=enabled ninja -C build From 311c5779ea7729474343490817974a6c89d6fe0c Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Tue, 20 Jul 2021 23:01:39 -0400 Subject: [PATCH 34/51] Remove unused variable --- include/modules/keyboard_state.hpp | 1 - src/modules/keyboard_state.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index d6d98de..1793bfe 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -26,7 +26,6 @@ class KeyboardState : public AModule { private: static auto openDevice(const std::string&) -> std::pair; - const Bar& bar_; Gtk::Box box_; Gtk::Label numlock_label_; Gtk::Label capslock_label_; diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 9a7802f..ca72144 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -10,7 +10,6 @@ extern "C" { waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config) : AModule(config, "keyboard_state", id, false, !config["disable-scroll"].asBool()), - bar_(bar), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), numlock_label_(""), capslock_label_(""), From 1440ed29d45f978164f37dd86c3e0e0bcaadef85 Mon Sep 17 00:00:00 2001 From: Michael Swiger Date: Tue, 20 Jul 2021 22:29:34 -0700 Subject: [PATCH 35/51] Fix blurry tray icons for HiDPI displays --- src/modules/sni/item.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index abf9556..6fca472 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -1,4 +1,5 @@ #include "modules/sni/item.hpp" +#include #include #include #include @@ -252,33 +253,42 @@ Glib::RefPtr Item::extractPixBuf(GVariant* variant) { } void Item::updateImage() { + auto scale_factor = image.get_scale_factor(); + auto scaled_icon_size = icon_size * scale_factor; + image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU); - image.set_pixel_size(icon_size); + image.set_pixel_size(scaled_icon_size); if (!icon_name.empty()) { try { // Try to find icons specified by path and filename std::ifstream temp(icon_name); if (temp.is_open()) { auto pixbuf = Gdk::Pixbuf::create_from_file(icon_name); + if (pixbuf->gobj() != nullptr) { // An icon specified by path and filename may be the wrong size for // the tray - // Keep the aspect ratio and scale to make the height equal to icon_size + // Keep the aspect ratio and scale to make the height equal to scaled_icon_size // If people have non square icons, assume they want it to grow in width not height - int width = icon_size * pixbuf->get_width() / pixbuf->get_height(); + int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height(); - pixbuf = pixbuf->scale_simple(width, icon_size, Gdk::InterpType::INTERP_BILINEAR); - image.set(pixbuf); + pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); + + auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, 0, image.get_window()); + image.set(surface); } } else { - image.set(getIconByName(icon_name, icon_size)); + auto icon_by_name = getIconByName(icon_name, scaled_icon_size); + auto surface = Gdk::Cairo::create_surface_from_pixbuf(icon_by_name, 0, image.get_window()); + image.set(surface); } } catch (Glib::Error& e) { spdlog::error("Item '{}': {}", id, static_cast(e.what())); } } else if (icon_pixmap) { // An icon extracted may be the wrong size for the tray - icon_pixmap = icon_pixmap->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR); + icon_pixmap = icon_pixmap->scale_simple(icon_size, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); + auto surface = Gdk::Cairo::create_surface_from_pixbuf(icon_pixmap, 0, image.get_window()); image.set(icon_pixmap); } } From 67d482d28b46ee6f89d69016ec3451bbe8653f11 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Jul 2021 09:23:52 +0200 Subject: [PATCH 36/51] Update opensuse --- Dockerfiles/opensuse | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index eb231fd..1015e5c 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -3,5 +3,7 @@ FROM opensuse/tumbleweed:latest RUN zypper -n up && \ + zypper -n addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo && \ + zypper -n refresh && \ zypper -n install -t pattern devel_C_C++ && \ zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon From 1f5c07a07f5f35dd531177ad1e86251b0a6565a4 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Jul 2021 09:27:54 +0200 Subject: [PATCH 37/51] Update debian --- Dockerfiles/debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index f7f6e63..7c9e0b0 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev && \ apt-get clean From 7f5fd1ac8648d1301c2840d328567c120317b285 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Jul 2021 09:30:47 +0200 Subject: [PATCH 38/51] Update opensuse --- Dockerfiles/opensuse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index 1015e5c..a88aa7d 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -3,7 +3,7 @@ FROM opensuse/tumbleweed:latest RUN zypper -n up && \ - zypper -n addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo && \ + zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \ zypper -n refresh && \ zypper -n install -t pattern devel_C_C++ && \ zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon From 7729ca34275bbc7a2bcd382a14f5be6cc74396e9 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Jul 2021 10:28:56 +0200 Subject: [PATCH 39/51] Update debian --- Dockerfiles/debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 7c9e0b0..32d7a6f 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry0 && \ apt-get clean From 929fc16994c413923af31d8b4a3d302315943a47 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 23 Jun 2021 00:27:30 -0700 Subject: [PATCH 40/51] fix(tray): ignore unused WindowId property --- include/modules/sni/item.hpp | 1 - src/modules/sni/item.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index 3cbd0b7..11402c1 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -30,7 +30,6 @@ class Item : public sigc::trackable { std::string status; std::string title; - int32_t window_id; std::string icon_name; Glib::RefPtr icon_pixmap; Glib::RefPtr icon_theme; diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 6fca472..0ccba19 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -105,8 +105,6 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { title = get_variant(value); } else if (name == "Status") { status = get_variant(value); - } else if (name == "WindowId") { - window_id = get_variant(value); } else if (name == "IconName") { icon_name = get_variant(value); } else if (name == "IconPixmap") { From 4b6253e81001e285c7e09a7196b3dad98d9aa783 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 23 Jun 2021 00:27:42 -0700 Subject: [PATCH 41/51] refactor(tray): infer changed properties from signal name Comparing two GVariants is too expensive; let's collect the set of properties updated by each signal and apply them unconditionally. --- include/modules/sni/item.hpp | 5 +++- src/modules/sni/item.cpp | 46 ++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index 11402c1..fbb5163 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -11,6 +11,9 @@ #include #include +#include +#include + namespace waybar::modules::SNI { class Item : public sigc::trackable { @@ -64,7 +67,7 @@ class Item : public sigc::trackable { Glib::RefPtr proxy_; Glib::RefPtr cancellable_; - bool update_pending_; + std::set update_pending_; }; } // namespace waybar::modules::SNI diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 0ccba19..d3eed4a 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -1,8 +1,11 @@ #include "modules/sni/item.hpp" + #include #include #include + #include +#include template <> struct fmt::formatter : formatter { @@ -40,8 +43,7 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf object_path(op), icon_size(16), effective_icon_size(0), - icon_theme(Gtk::IconTheme::create()), - update_pending_(false) { + icon_theme(Gtk::IconTheme::create()) { if (config["icon-size"].isUInt()) { icon_size = config["icon-size"].asUInt(); } @@ -148,8 +150,6 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } void Item::getUpdatedProperties() { - update_pending_ = false; - auto params = Glib::VariantContainerBase::create_tuple( {Glib::Variant::create(SNI_INTERFACE_NAME)}); proxy_->call("org.freedesktop.DBus.Properties.GetAll", @@ -166,10 +166,7 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { auto properties = properties_variant.get(); for (const auto& [name, value] : properties) { - Glib::VariantBase old_value; - proxy_->get_cached_property(old_value, name); - if (!old_value || !value.equal(old_value)) { - proxy_->set_cached_property(name, value); + if (update_pending_.count(name.raw())) { setProperty(name, const_cast(value)); } } @@ -181,18 +178,37 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { } catch (const std::exception& err) { spdlog::warn("Failed to update properties: {}", err.what()); } + update_pending_.clear(); } +/** + * Mapping from a signal name to a set of possibly changed properties. + * Commented signals are not handled by the tray module at the moment. + */ +static const std::map> signal2props = { + {"NewTitle", {"Title"}}, + {"NewIcon", {"IconName", "IconPixmap"}}, + // {"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}}, + // {"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}}, + {"NewIconThemePath", {"IconThemePath"}}, + {"NewToolTip", {"ToolTip"}}, + {"NewStatus", {"Status"}}, + // {"XAyatanaNewLabel", {"XAyatanaLabel"}}, +}; + void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, const Glib::VariantContainerBase& arguments) { spdlog::trace("Tray item '{}' got signal {}", id, signal_name); - if (!update_pending_ && signal_name.compare(0, 3, "New") == 0) { - /* Debounce signals and schedule update of all properties. - * Based on behavior of Plasma dataengine for StatusNotifierItem. - */ - update_pending_ = true; - Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties), - UPDATE_DEBOUNCE_TIME); + auto changed = signal2props.find(signal_name.raw()); + if (changed != signal2props.end()) { + if (update_pending_.empty()) { + /* Debounce signals and schedule update of all properties. + * Based on behavior of Plasma dataengine for StatusNotifierItem. + */ + Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &Item::getUpdatedProperties), + UPDATE_DEBOUNCE_TIME); + } + update_pending_.insert(changed->second.begin(), changed->second.end()); } } From 84a8f79bbe18ec17036f7816d29cc6c5df20b0de Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 23 Jun 2021 23:47:35 -0700 Subject: [PATCH 42/51] feat(tray): implement tooltips (text only) for tray items --- include/modules/sni/item.hpp | 6 ++++++ src/modules/sni/item.cpp | 24 ++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index fbb5163..fc4596e 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -16,6 +16,11 @@ namespace waybar::modules::SNI { +struct ToolTip { + Glib::ustring icon_name; + Glib::ustring text; +}; + class Item : public sigc::trackable { public: Item(const std::string&, const std::string&, const Json::Value&); @@ -41,6 +46,7 @@ class Item : public sigc::trackable { std::string attention_movie_name; std::string icon_theme_path; std::string menu; + ToolTip tooltip; DbusmenuGtkMenu* dbus_menu = nullptr; Gtk::Menu* gtk_menu = nullptr; /** diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index d3eed4a..f092183 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -81,7 +82,6 @@ void Item::proxyReady(Glib::RefPtr& result) { return; } this->updateImage(); - // this->event_box.set_tooltip_text(this->title); } catch (const Glib::Error& err) { spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); @@ -91,10 +91,24 @@ void Item::proxyReady(Glib::RefPtr& result) { } template -T get_variant(Glib::VariantBase& value) { +T get_variant(const Glib::VariantBase& value) { return Glib::VariantBase::cast_dynamic>(value).get(); } +template <> +ToolTip get_variant(const Glib::VariantBase& value) { + ToolTip result; + // Unwrap (sa(iiay)ss) + auto container = value.cast_dynamic(value); + result.icon_name = get_variant(container.get_child(0)); + result.text = get_variant(container.get_child(2)); + auto description = get_variant(container.get_child(3)); + if (!description.empty()) { + result.text = fmt::format("{}\n{}", result.text, description); + } + return result; +} + void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { try { spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value); @@ -122,7 +136,10 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } else if (name == "AttentionMovieName") { attention_movie_name = get_variant(value); } else if (name == "ToolTip") { - // TODO: tooltip + tooltip = get_variant(value); + if (!tooltip.text.empty()) { + event_box.set_tooltip_markup(tooltip.text); + } } else if (name == "IconThemePath") { icon_theme_path = get_variant(value); if (!icon_theme_path.empty()) { @@ -172,7 +189,6 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { } this->updateImage(); - // this->event_box.set_tooltip_text(this->title); } catch (const Glib::Error& err) { spdlog::warn("Failed to update properties: {}", err.what()); } catch (const std::exception& err) { From 1418f96e4653aad2e8f0be25453ac022632cbe32 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 30 Jun 2021 22:17:27 -0700 Subject: [PATCH 43/51] feat(tray): fallback to Title for items without ToolTip --- src/modules/sni/item.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index f092183..d4c4872 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -119,6 +119,9 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { id = get_variant(value); } else if (name == "Title") { title = get_variant(value); + if (tooltip.text.empty()) { + event_box.set_tooltip_markup(title); + } } else if (name == "Status") { status = get_variant(value); } else if (name == "IconName") { From 245f7f4b11731c158f15703750b85dd6c87d660f Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 26 Jun 2021 16:33:25 -0700 Subject: [PATCH 44/51] feat(tray): handle scroll events --- include/modules/sni/item.hpp | 6 ++++ man/waybar-tray.5.scd | 4 +++ src/modules/sni/item.cpp | 54 +++++++++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index fc4596e..e51d3c7 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -70,6 +70,12 @@ class Item : public sigc::trackable { static void onMenuDestroyed(Item* self, GObject* old_menu_pointer); void makeMenu(); bool handleClick(GdkEventButton* const& /*ev*/); + bool handleScroll(GdkEventScroll* const&); + + // smooth scrolling threshold + gdouble scroll_threshold_ = 0; + gdouble distance_scrolled_x_ = 0; + gdouble distance_scrolled_y_ = 0; Glib::RefPtr proxy_; Glib::RefPtr cancellable_; diff --git a/man/waybar-tray.5.scd b/man/waybar-tray.5.scd index cd0e93f..5446a56 100644 --- a/man/waybar-tray.5.scd +++ b/man/waybar-tray.5.scd @@ -16,6 +16,10 @@ Addressed by *tray* typeof: integer ++ Defines the size of the tray icons. +*smooth-scrolling-threshold*: ++ + typeof: double ++ + Threshold to be used when scrolling. + *spacing*: ++ typeof: integer ++ Defines the spacing between the tray icons. diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index d4c4872..9a77d24 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -48,9 +48,13 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf if (config["icon-size"].isUInt()) { icon_size = config["icon-size"].asUInt(); } + if (config["smooth-scrolling-threshold"].isNumeric()) { + scroll_threshold_ = config["smooth-scrolling-threshold"].asDouble(); + } event_box.add(image); - event_box.add_events(Gdk::BUTTON_PRESS_MASK); + event_box.add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick)); + event_box.signal_scroll_event().connect(sigc::mem_fun(*this, &Item::handleScroll)); cancellable_ = Gio::Cancellable::create(); @@ -403,4 +407,52 @@ bool Item::handleClick(GdkEventButton* const& ev) { return false; } +bool Item::handleScroll(GdkEventScroll* const& ev) { + int dx = 0, dy = 0; + switch (ev->direction) { + case GDK_SCROLL_UP: + dy = -1; + break; + case GDK_SCROLL_DOWN: + dy = 1; + break; + case GDK_SCROLL_LEFT: + dx = -1; + break; + case GDK_SCROLL_RIGHT: + dx = 1; + break; + case GDK_SCROLL_SMOOTH: + distance_scrolled_x_ += ev->delta_x; + distance_scrolled_y_ += ev->delta_y; + // check against the configured threshold and ensure that the absolute value >= 1 + if (distance_scrolled_x_ > scroll_threshold_) { + dx = (int)lround(std::max(distance_scrolled_x_, 1.0)); + distance_scrolled_x_ = 0; + } else if (distance_scrolled_x_ < -scroll_threshold_) { + dx = (int)lround(std::min(distance_scrolled_x_, -1.0)); + distance_scrolled_x_ = 0; + } + if (distance_scrolled_y_ > scroll_threshold_) { + dy = (int)lround(std::max(distance_scrolled_y_, 1.0)); + distance_scrolled_y_ = 0; + } else if (distance_scrolled_y_ < -scroll_threshold_) { + dy = (int)lround(std::min(distance_scrolled_y_, -1.0)); + distance_scrolled_y_ = 0; + } + break; + } + if (dx != 0) { + auto parameters = Glib::VariantContainerBase::create_tuple( + {Glib::Variant::create(dx), Glib::Variant::create("horizontal")}); + proxy_->call("Scroll", parameters); + } + if (dy != 0) { + auto parameters = Glib::VariantContainerBase::create_tuple( + {Glib::Variant::create(dy), Glib::Variant::create("vertical")}); + proxy_->call("Scroll", parameters); + } + return true; +} + } // namespace waybar::modules::SNI From a5fe6f40b8d1439b8c9794e30ba97d92fabc4715 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 2 Jul 2021 19:44:10 -0700 Subject: [PATCH 45/51] feat(tray): handle Status property On the `Passive` value of `Status` tray items would be hidden unless `show-passive-items` is set to true. On the `NeedsAttention` value of `Status` tray items will have a `.needs-attention` CSS class. --- include/modules/sni/item.hpp | 4 +++- man/waybar-tray.5.scd | 8 ++++++++ resources/style.css | 9 +++++++++ src/modules/sni/item.cpp | 24 ++++++++++++++++++++++-- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index e51d3c7..1f1503e 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -35,7 +35,6 @@ class Item : public sigc::trackable { Gtk::EventBox event_box; std::string category; std::string id; - std::string status; std::string title; std::string icon_name; @@ -59,6 +58,7 @@ class Item : public sigc::trackable { private: void proxyReady(Glib::RefPtr& result); void setProperty(const Glib::ustring& name, Glib::VariantBase& value); + void setStatus(const Glib::ustring& value); void getUpdatedProperties(); void processUpdatedProperties(Glib::RefPtr& result); void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, @@ -76,6 +76,8 @@ class Item : public sigc::trackable { gdouble scroll_threshold_ = 0; gdouble distance_scrolled_x_ = 0; gdouble distance_scrolled_y_ = 0; + // visibility of items with Status == Passive + bool show_passive_ = false; Glib::RefPtr proxy_; Glib::RefPtr cancellable_; diff --git a/man/waybar-tray.5.scd b/man/waybar-tray.5.scd index 5446a56..c664594 100644 --- a/man/waybar-tray.5.scd +++ b/man/waybar-tray.5.scd @@ -16,6 +16,11 @@ Addressed by *tray* typeof: integer ++ Defines the size of the tray icons. +*show-passive-items*: ++ + typeof: bool ++ + default: false ++ + Defines visibility of the tray icons with *Passive* status. + *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. @@ -41,3 +46,6 @@ Addressed by *tray* # STYLE - *#tray* +- *#tray > .passive* +- *#tray > .active* +- *#tray > .needs-attention* diff --git a/resources/style.css b/resources/style.css index 63ea871..32dce42 100644 --- a/resources/style.css +++ b/resources/style.css @@ -195,6 +195,15 @@ label:focus { background-color: #2980b9; } +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #eb4d4b; +} + #idle_inhibitor { background-color: #2d3436; } diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 9a77d24..267cf63 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -51,10 +51,15 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf if (config["smooth-scrolling-threshold"].isNumeric()) { scroll_threshold_ = config["smooth-scrolling-threshold"].asDouble(); } + if (config["show-passive-items"].isBool()) { + show_passive_ = config["show-passive-items"].asBool(); + } event_box.add(image); event_box.add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick)); event_box.signal_scroll_event().connect(sigc::mem_fun(*this, &Item::handleScroll)); + // initial visibility + event_box.set_visible(show_passive_); cancellable_ = Gio::Cancellable::create(); @@ -81,7 +86,7 @@ void Item::proxyReady(Glib::RefPtr& result) { this->proxy_->signal_signal().connect(sigc::mem_fun(*this, &Item::onSignal)); - if (this->id.empty() || this->category.empty() || this->status.empty()) { + if (this->id.empty() || this->category.empty()) { spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path); return; } @@ -127,7 +132,7 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { event_box.set_tooltip_markup(title); } } else if (name == "Status") { - status = get_variant(value); + setStatus(get_variant(value)); } else if (name == "IconName") { icon_name = get_variant(value); } else if (name == "IconPixmap") { @@ -173,6 +178,21 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } } +void Item::setStatus(const Glib::ustring& value) { + Glib::ustring lower = value.lowercase(); + event_box.set_visible(show_passive_ || lower.compare("passive") != 0); + + auto style = event_box.get_style_context(); + for (const auto& class_name : style->list_classes()) { + style->remove_class(class_name); + } + if (lower.compare("needsattention") == 0) { + // convert status to dash-case for CSS + lower = "needs-attention"; + } + style->add_class(lower); +} + void Item::getUpdatedProperties() { auto params = Glib::VariantContainerBase::create_tuple( {Glib::Variant::create(SNI_INTERFACE_NAME)}); From cf832798fb4ac250fb1ff0435e8f8f3c5d384b30 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Jul 2021 14:46:03 +0200 Subject: [PATCH 46/51] Update debian --- Dockerfiles/debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 32d7a6f..026d8fd 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry0 && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 && \ apt-get clean From 77a2eff2ce828eb14105c076f84333531b953ec9 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Jul 2021 14:49:03 +0200 Subject: [PATCH 47/51] Update opensuse --- Dockerfiles/opensuse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index a88aa7d..d7cdcf6 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -6,4 +6,4 @@ RUN zypper -n up && \ zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \ zypper -n refresh && \ zypper -n install -t pattern devel_C_C++ && \ - zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc libxkbcommon + zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc From 2009ceb35093fcf24c23d1d20e024fc3bfc58c35 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Jul 2021 15:01:29 +0200 Subject: [PATCH 48/51] Update opensuse --- Dockerfiles/opensuse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index d7cdcf6..49dea27 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -6,4 +6,4 @@ RUN zypper -n up && \ zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \ zypper -n refresh && \ zypper -n install -t pattern devel_C_C++ && \ - zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel scdoc + zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc From 88a5f713ed2c98034dbdd547d184e56a3071c5c8 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Fri, 23 Jul 2021 09:45:07 -0400 Subject: [PATCH 49/51] Prefer keyboard-state over keyboard_state --- man/waybar-keyboard-state.5.scd | 12 ++++++------ resources/config | 4 ++-- resources/style.css | 6 +++--- src/factory.cpp | 2 +- src/modules/keyboard_state.cpp | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/man/waybar-keyboard-state.5.scd b/man/waybar-keyboard-state.5.scd index 8e9ca5e..1d7c3a8 100644 --- a/man/waybar-keyboard-state.5.scd +++ b/man/waybar-keyboard-state.5.scd @@ -2,11 +2,11 @@ waybar-keyboard-state(5) # NAME -waybar - keyboard_state module +waybar - keyboard-state module # DESCRIPTION -The *keyboard_state* module displays the state of number lock, caps lock, and scroll lock. +The *keyboard-state* module displays the state of number lock, caps lock, and scroll lock. # CONFIGURATION @@ -61,7 +61,7 @@ The following *format-icons* can be set. # EXAMPLE: ``` -"keyboard_state": { +"keyboard-state": { "numlock": true, "capslock": true, "format": "{name} {icon}", @@ -74,7 +74,7 @@ The following *format-icons* can be set. # STYLE -- *#keyboard_state* -- *#keyboard_state label* -- *#keyboard_state label.locked* +- *#keyboard-state* +- *#keyboard-state label* +- *#keyboard-state label.locked* diff --git a/resources/config b/resources/config index 465f694..87f24c0 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": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "keyboard_state", "sway/language", "battery", "battery#bat2", "clock", "tray"], + "modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "keyboard-state", "sway/language", "battery", "battery#bat2", "clock", "tray"], // Modules configuration // "sway/workspaces": { // "disable-scroll": true, @@ -23,7 +23,7 @@ // "default": "" // } // }, - "keyboard_state": { + "keyboard-state": { "numlock": true, "capslock": true, "format": "{name} {icon}", diff --git a/resources/style.css b/resources/style.css index c1ac5ad..b585e5f 100644 --- a/resources/style.css +++ b/resources/style.css @@ -229,7 +229,7 @@ label:focus { min-width: 16px; } -#keyboard_state { +#keyboard-state { background: #97e1ad; color: #000000; padding: 0 0px; @@ -237,10 +237,10 @@ label:focus { min-width: 16px; } -#keyboard_state > label { +#keyboard-state > label { padding: 0 5px; } -#keyboard_state > label.locked { +#keyboard-state > label.locked { background: rgba(0, 0, 0, 0.2); } diff --git a/src/factory.cpp b/src/factory.cpp index 6a0147d..9836354 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -71,7 +71,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { } #endif #ifdef HAVE_LIBEVDEV - if (ref == "keyboard_state") { + if (ref == "keyboard-state") { return new waybar::modules::KeyboardState(id, bar_, config_[name]); } #endif diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index ca72144..2b6eb2d 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -9,7 +9,7 @@ extern "C" { } waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config) - : AModule(config, "keyboard_state", id, false, !config["disable-scroll"].asBool()), + : AModule(config, "keyboard-state", id, false, !config["disable-scroll"].asBool()), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), numlock_label_(""), capslock_label_(""), @@ -31,7 +31,7 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& : "unlocked"), fd_(0), dev_(nullptr) { - box_.set_name("keyboard_state"); + box_.set_name("keyboard-state"); if (config_["numlock"].asBool()) { box_.pack_end(numlock_label_, false, false, 0); } From 68e4457f3a226c985bb48bf470f1a992e91cf688 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 24 Jul 2021 17:24:37 +0300 Subject: [PATCH 50/51] Add tooltip-formay --- include/modules/sway/language.hpp | 1 + man/waybar-sway-language.5.scd | 5 +++++ src/modules/sway/language.cpp | 15 ++++++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index fb4438b..b310b7f 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -48,6 +48,7 @@ class Language : public ALabel, public sigc::trackable { const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY; Layout layout_; + std::string tooltip_format_ = ""; std::map layouts_map_; XKBContext xkb_context_; bool is_variant_displayed; diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index f7f8830..60a18fc 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -17,6 +17,11 @@ Addressed by *sway/language* default: {} ++ The format, how layout should be displayed. +*format-tooltip*: ++ + typeof: string ++ + default: {} ++ + The format, how layout should be displayed in tooltip. + *tooltip*: ++ typeof: bool ++ default: true ++ diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 86a8e1e..5f3ce06 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -1,6 +1,7 @@ #include "modules/sway/language.hpp" #include +#include #include #include @@ -19,6 +20,9 @@ const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name Language::Language(const std::string& id, const Json::Value& config) : ALabel(config, "language", id, "{}", 0, true) { is_variant_displayed = format_.find("{variant}") != std::string::npos; + if (config.isMember("tooltip-format")) { + tooltip_format_ = config["tooltip-format"].asString(); + } ipc_.subscribe(R"(["input"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd)); @@ -90,7 +94,16 @@ auto Language::update() -> void { fmt::arg("variant", layout_.variant))); label_.set_markup(display_layout); if (tooltipEnabled()) { - label_.set_tooltip_markup(display_layout); + if (tooltip_format_ != "") { + auto tooltip_display_layout = trim(fmt::format(tooltip_format_, + fmt::arg("short", layout_.short_name), + fmt::arg("long", layout_.full_name), + fmt::arg("variant", layout_.variant))); + label_.set_tooltip_markup(tooltip_display_layout); + + } else { + label_.set_tooltip_markup(display_layout); + } } event_box_.show(); From af2113931a2b1fc8d331581bbaec2f747d158f04 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 24 Jul 2021 17:26:49 +0300 Subject: [PATCH 51/51] fix typo --- man/waybar-sway-language.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index 60a18fc..76bc720 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -17,7 +17,7 @@ Addressed by *sway/language* default: {} ++ The format, how layout should be displayed. -*format-tooltip*: ++ +*tooltip-format*: ++ typeof: string ++ default: {} ++ The format, how layout should be displayed in tooltip.