From c420b40668de351226c03b1b3822aae529dc4d21 Mon Sep 17 00:00:00 2001 From: Yaroslav Chvanov Date: Sat, 4 Nov 2023 18:17:45 +0300 Subject: [PATCH 001/407] refactor(backlight): use concrete types for some helper functions This fixes linking of the best_device() function with 'mold' linker. --- include/util/backlight_backend.hpp | 14 +--- src/util/backlight_backend.cpp | 122 ++++++++++++++--------------- 2 files changed, 63 insertions(+), 73 deletions(-) diff --git a/include/util/backlight_backend.hpp b/include/util/backlight_backend.hpp index 8dcb8958f..20925b524 100644 --- a/include/util/backlight_backend.hpp +++ b/include/util/backlight_backend.hpp @@ -20,7 +20,7 @@ std::scoped_lock lock((backend).udev_thread_mutex_); \ __devices = (backend).devices_; \ } \ - auto varname = (backend).best_device(__devices.cbegin(), __devices.cend(), preferred_device); + auto varname = (backend).best_device(__devices, preferred_device); namespace waybar::util { @@ -61,16 +61,10 @@ class BacklightBackend { void set_scaled_brightness(std::string preferred_device, int brightness); int get_scaled_brightness(std::string preferred_device); - template - static void upsert_device(ForwardIt first, ForwardIt last, Inserter inserter, udev_device *dev); - - template - static void enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, udev *udev); - bool is_login_proxy_initialized() const { return static_cast(login_proxy_); } - template - static const BacklightDevice *best_device(ForwardIt first, ForwardIt last, std::string_view); + static const BacklightDevice *best_device(const std::vector &devices, + std::string_view); std::vector devices_; std::mutex udev_thread_mutex_; @@ -90,4 +84,4 @@ class BacklightBackend { static constexpr int EPOLL_MAX_EVENTS = 16; }; -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index 1512103cb..60d5ca3a6 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -73,6 +73,54 @@ void check_nn(const void *ptr, const char *message = "ptr was null") { namespace waybar::util { +static void upsert_device(std::vector &devices, udev_device *dev) { + const char *name = udev_device_get_sysname(dev); + check_nn(name); + + const char *actual_brightness_attr = + strncmp(name, "amdgpu_bl", 9) == 0 || strcmp(name, "apple-panel-bl") == 0 + ? "brightness" + : "actual_brightness"; + + const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr); + const char *max = udev_device_get_sysattr_value(dev, "max_brightness"); + const char *power = udev_device_get_sysattr_value(dev, "bl_power"); + + auto found = std::find_if(devices.begin(), devices.end(), [name](const BacklightDevice &device) { + return device.name() == name; + }); + if (found != devices.end()) { + if (actual != nullptr) { + found->set_actual(std::stoi(actual)); + } + if (max != nullptr) { + found->set_max(std::stoi(max)); + } + if (power != nullptr) { + found->set_powered(std::stoi(power) == 0); + } + } else { + const int actual_int = actual == nullptr ? 0 : std::stoi(actual); + const int max_int = max == nullptr ? 0 : std::stoi(max); + const bool power_bool = power == nullptr ? true : std::stoi(power) == 0; + devices.emplace_back(name, actual_int, max_int, power_bool); + } +} + +static void enumerate_devices(std::vector &devices, udev *udev) { + std::unique_ptr enumerate{udev_enumerate_new(udev)}; + udev_enumerate_add_match_subsystem(enumerate.get(), "backlight"); + udev_enumerate_scan_devices(enumerate.get()); + udev_list_entry *enum_devices = udev_enumerate_get_list_entry(enumerate.get()); + udev_list_entry *dev_list_entry; + udev_list_entry_foreach(dev_list_entry, enum_devices) { + const char *path = udev_list_entry_get_name(dev_list_entry); + std::unique_ptr dev{udev_device_new_from_syspath(udev, path)}; + check_nn(dev.get(), "dev new failed"); + upsert_device(devices, dev.get()); + } +} + BacklightDevice::BacklightDevice(std::string name, int actual, int max, bool powered) : name_(name), actual_(actual), max_(max), powered_(powered) {} @@ -95,8 +143,7 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, : on_updated_cb_(on_updated_cb), polling_interval_(interval), previous_best_({}) { std::unique_ptr udev_check{udev_new()}; check_nn(udev_check.get(), "Udev check new failed"); - enumerate_devices(devices_.begin(), devices_.end(), std::back_inserter(devices_), - udev_check.get()); + enumerate_devices(devices_, udev_check.get()); if (devices_.empty()) { throw std::runtime_error("No backlight found"); } @@ -145,12 +192,12 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, check_eq(event.data.fd, udev_fd, "unexpected udev fd"); std::unique_ptr dev{udev_monitor_receive_device(mon.get())}; check_nn(dev.get(), "epoll dev was null"); - upsert_device(devices.begin(), devices.end(), std::back_inserter(devices), dev.get()); + upsert_device(devices, dev.get()); } // Refresh state if timed out if (event_count == 0) { - enumerate_devices(devices.begin(), devices.end(), std::back_inserter(devices), udev.get()); + enumerate_devices(devices, udev.get()); } { std::scoped_lock lock(udev_thread_mutex_); @@ -161,19 +208,20 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, }; } -template -const BacklightDevice *BacklightBackend::best_device(ForwardIt first, ForwardIt last, +const BacklightDevice *BacklightBackend::best_device(const std::vector &devices, std::string_view preferred_device) { const auto found = std::find_if( - first, last, [preferred_device](const auto &dev) { return dev.name() == preferred_device; }); - if (found != last) { + devices.begin(), devices.end(), + [preferred_device](const BacklightDevice &dev) { return dev.name() == preferred_device; }); + if (found != devices.end()) { return &(*found); } const auto max = std::max_element( - first, last, [](const auto &l, const auto &r) { return l.get_max() < r.get_max(); }); + devices.begin(), devices.end(), + [](const BacklightDevice &l, const BacklightDevice &r) { return l.get_max() < r.get_max(); }); - return max == last ? nullptr : &(*max); + return max == devices.end() ? nullptr : &(*max); } const BacklightDevice *BacklightBackend::get_previous_best_device() { @@ -233,56 +281,4 @@ int BacklightBackend::get_scaled_brightness(std::string preferred_device) { return 0; } -template -void BacklightBackend::upsert_device(ForwardIt first, ForwardIt last, Inserter inserter, - udev_device *dev) { - const char *name = udev_device_get_sysname(dev); - check_nn(name); - - const char *actual_brightness_attr = - strncmp(name, "amdgpu_bl", 9) == 0 || strcmp(name, "apple-panel-bl") == 0 - ? "brightness" - : "actual_brightness"; - - const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr); - const char *max = udev_device_get_sysattr_value(dev, "max_brightness"); - const char *power = udev_device_get_sysattr_value(dev, "bl_power"); - - auto found = - std::find_if(first, last, [name](const auto &device) { return device.name() == name; }); - if (found != last) { - if (actual != nullptr) { - found->set_actual(std::stoi(actual)); - } - if (max != nullptr) { - found->set_max(std::stoi(max)); - } - if (power != nullptr) { - found->set_powered(std::stoi(power) == 0); - } - } else { - const int actual_int = actual == nullptr ? 0 : std::stoi(actual); - const int max_int = max == nullptr ? 0 : std::stoi(max); - const bool power_bool = power == nullptr ? true : std::stoi(power) == 0; - *inserter = BacklightDevice{name, actual_int, max_int, power_bool}; - ++inserter; - } -} - -template -void BacklightBackend::enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, - udev *udev) { - std::unique_ptr enumerate{udev_enumerate_new(udev)}; - udev_enumerate_add_match_subsystem(enumerate.get(), "backlight"); - udev_enumerate_scan_devices(enumerate.get()); - udev_list_entry *enum_devices = udev_enumerate_get_list_entry(enumerate.get()); - udev_list_entry *dev_list_entry; - udev_list_entry_foreach(dev_list_entry, enum_devices) { - const char *path = udev_list_entry_get_name(dev_list_entry); - std::unique_ptr dev{udev_device_new_from_syspath(udev, path)}; - check_nn(dev.get(), "dev new failed"); - upsert_device(first, last, inserter, dev.get()); - } -} - -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util From 3390c16f528223e3201742868fd1069beec79ea9 Mon Sep 17 00:00:00 2001 From: ArneshRC <84434238+ArneshRC@users.noreply.github.com> Date: Sun, 7 Jan 2024 17:09:31 +0530 Subject: [PATCH 002/407] added support for battery state-based classes on the entire waybar --- include/modules/battery.hpp | 5 ++++- src/factory.cpp | 2 +- src/modules/battery.cpp | 39 +++++++++++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 017b0e48d..bbdd0eedc 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -16,6 +16,7 @@ #include #include "ALabel.hpp" +#include "bar.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { @@ -28,7 +29,7 @@ namespace fs = std::filesystem; class Battery : public ALabel { public: - Battery(const std::string&, const Json::Value&); + Battery(const std::string&, const waybar::Bar&, const Json::Value&); virtual ~Battery(); auto update() -> void override; @@ -40,6 +41,7 @@ class Battery : public ALabel { const std::string getAdapterStatus(uint8_t capacity) const; const std::tuple getInfos(); const std::string formatTimeRemaining(float hoursRemaining); + void setBarClass(std::string&); int global_watch; std::map batteries_; @@ -49,6 +51,7 @@ class Battery : public ALabel { std::mutex battery_list_mutex_; std::string old_status_; bool warnFirstTime_{true}; + const Bar& bar_; util::SleeperThread thread_; util::SleeperThread thread_battery_update_; diff --git a/src/factory.cpp b/src/factory.cpp index f3d7ebe64..1828fb46d 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -18,7 +18,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : ""; #if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM)) if (ref == "battery") { - return new waybar::modules::Battery(id, config_[name]); + return new waybar::modules::Battery(id, bar_, config_[name]); } #endif #ifdef HAVE_GAMEMODE diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 70268c8af..6c39eb0f8 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -1,12 +1,13 @@ #include "modules/battery.hpp" +#include #if defined(__FreeBSD__) #include #endif #include #include -waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config) - : ALabel(config, "battery", id, "{capacity}%", 60) { +waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const Json::Value& config) + : ALabel(config, "battery", id, "{capacity}%", 60), bar_(bar) { #if defined(__linux__) battery_watch_fd_ = inotify_init1(IN_CLOEXEC); if (battery_watch_fd_ == -1) { @@ -641,6 +642,7 @@ auto waybar::modules::Battery::update() -> void { [](char ch) { return ch == ' ' ? '-' : std::tolower(ch); }); auto format = format_; auto state = getState(capacity, true); + setBarClass(state); auto time_remaining_formatted = formatTimeRemaining(time_remaining); if (tooltipEnabled()) { std::string tooltip_text_default; @@ -689,3 +691,36 @@ auto waybar::modules::Battery::update() -> void { // Call parent update ALabel::update(); } + +void waybar::modules::Battery::setBarClass(std::string& state) { + auto classes = bar_.window.get_style_context()->list_classes(); + auto old_class_it = std::find_if(classes.begin(), classes.end(), + [](auto classname) { + return classname.rfind("battery-", 0) == 0; + }); + + // If the bar doesn't have any `battery-` class + if(old_class_it == classes.end()) { + if(!state.empty()) { + bar_.window.get_style_context()->add_class("battery-" + state); + } + return; + } + + auto old_class = *old_class_it; + + // If the bar has a `battery-` class, + // but `state` is empty + if(state.empty()) { + bar_.window.get_style_context()->remove_class(old_class); + return; + } + auto new_class = "battery-" + state; + + // If the bar has a `battery-` class, + // and `state` is NOT empty + if(old_class != new_class) { + bar_.window.get_style_context()->remove_class(old_class); + bar_.window.get_style_context()->add_class("battery-" + state); + } +} From 8f5d0098d6dd18b4967372f9aeac676d2dbbf063 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 10 Sep 2023 20:36:27 +0200 Subject: [PATCH 003/407] Fixed json parsing with hexadecimal characters * replace \x with \u00 to follow JSON spec * fixes #2475 and #2495 * added unit tests for json parsing --- include/util/json.hpp | 39 +++++++++++++++++++++++-------------- test/JsonParser.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++ test/meson.build | 1 + 3 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 test/JsonParser.cpp diff --git a/include/util/json.hpp b/include/util/json.hpp index 7cd43552b..f0736f9ba 100644 --- a/include/util/json.hpp +++ b/include/util/json.hpp @@ -3,6 +3,12 @@ #include #include +#include +#include +#include +#include +#include + #if (FMT_VERSION >= 90000) template <> @@ -12,25 +18,30 @@ struct fmt::formatter : ostream_formatter {}; namespace waybar::util { -struct JsonParser { - JsonParser() {} +class JsonParser { + public: + JsonParser() = default; + + Json::Value parse(const std::string& jsonStr) { + Json::Value root; - const Json::Value parse(const std::string& data) const { - Json::Value root(Json::objectValue); - if (data.empty()) { - return root; + // replace all occurrences of "\x" with "\u00", because JSON doesn't allow "\x" escape sequences + std::string modifiedJsonStr = replaceHexadecimalEscape(jsonStr); + + std::istringstream jsonStream(modifiedJsonStr); + std::string errs; + if (!Json::parseFromStream(m_readerBuilder, jsonStream, &root, &errs)) { + throw std::runtime_error("Error parsing JSON: " + errs); } - std::unique_ptr const reader(builder_.newCharReader()); - std::string err; - bool res = reader->parse(data.c_str(), data.c_str() + data.size(), &root, &err); - if (!res) throw std::runtime_error(err); return root; } - ~JsonParser() = default; - private: - Json::CharReaderBuilder builder_; -}; + Json::CharReaderBuilder m_readerBuilder; + static std::string replaceHexadecimalEscape(const std::string& str) { + static std::regex re("\\\\x"); + return std::regex_replace(str, re, "\\u00"); + } +}; } // namespace waybar::util diff --git a/test/JsonParser.cpp b/test/JsonParser.cpp new file mode 100644 index 000000000..99a8649eb --- /dev/null +++ b/test/JsonParser.cpp @@ -0,0 +1,45 @@ +#include "util/json.hpp" + +#if __has_include() +#include +#else +#include +#endif + +TEST_CASE("Simple json", "[json]") { + SECTION("Parse simple json") { + std::string stringToTest = R"({"number": 5, "string": "test"})"; + waybar::util::JsonParser parser; + Json::Value jsonValue = parser.parse(stringToTest); + REQUIRE(jsonValue["number"].asInt() == 5); + REQUIRE(jsonValue["string"].asString() == "test"); + } +} + +TEST_CASE("Json with unicode", "[json]") { + SECTION("Parse json with unicode") { + std::string stringToTest = R"({"test": "\xab"})"; + waybar::util::JsonParser parser; + Json::Value jsonValue = parser.parse(stringToTest); + // compare with "\u00ab" because "\xab" is replaced with "\u00ab" in the parser + REQUIRE(jsonValue["test"].asString() == "\u00ab"); + } +} + +TEST_CASE("Json with emoji", "[json]") { + SECTION("Parse json with emoji") { + std::string stringToTest = R"({"test": "😊"})"; + waybar::util::JsonParser parser; + Json::Value jsonValue = parser.parse(stringToTest); + REQUIRE(jsonValue["test"].asString() == "😊"); + } +} + +TEST_CASE("Json with chinese characters", "[json]") { + SECTION("Parse json with chinese characters") { + std::string stringToTest = R"({"test": "你好"})"; + waybar::util::JsonParser parser; + Json::Value jsonValue = parser.parse(stringToTest); + REQUIRE(jsonValue["test"].asString() == "你好"); + } +} \ No newline at end of file diff --git a/test/meson.build b/test/meson.build index 02cbb2a48..7a55a4738 100644 --- a/test/meson.build +++ b/test/meson.build @@ -8,6 +8,7 @@ test_dep = [ ] test_src = files( 'main.cpp', + 'JsonParser.cpp', 'SafeSignal.cpp', 'config.cpp', '../src/config.cpp', From 95ffc291f61ac06474dc0601448d32573adb0f09 Mon Sep 17 00:00:00 2001 From: Joshua Manchester Date: Sun, 14 Jan 2024 15:06:06 +0000 Subject: [PATCH 004/407] fix: hide tray event box instead of box --- src/modules/sni/tray.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index 09d53e7f7..09a7ff30c 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -38,7 +38,7 @@ void Tray::onRemove(std::unique_ptr& item) { auto Tray::update() -> void { // Show tray only when items are available - box_.set_visible(!box_.get_children().empty()); + event_box_.set_visible(!box_.get_children().empty()); // Call parent update AModule::update(); } From d343f616fc0964606e533a2a382c88ef140483d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABlys=20Bras=20de=20fer?= Date: Tue, 16 Jan 2024 21:35:42 +0100 Subject: [PATCH 005/407] clock: handle timezone changes (again again) --- src/modules/clock.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index d78d4c26f..5bfd0c497 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -28,7 +28,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) if (!zone_name.isString()) continue; if (zone_name.asString().empty()) // local time should be shown - tzList_.push_back(current_zone()); + tzList_.push_back(nullptr); else try { tzList_.push_back(locate_zone(zone_name.asString())); @@ -39,7 +39,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } else if (config_["timezone"].isString()) { if (config_["timezone"].asString().empty()) // local time should be shown - tzList_.push_back(current_zone()); + tzList_.push_back(nullptr); else try { tzList_.push_back(locate_zone(config_["timezone"].asString())); @@ -47,7 +47,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) spdlog::warn("Timezone: {0}. {1}", config_["timezone"].asString(), e.what()); } } - if (!tzList_.size()) tzList_.push_back(current_zone()); + if (!tzList_.size()) tzList_.push_back(nullptr); // Calendar properties if (cldInTooltip_) { @@ -84,7 +84,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) fmtMap_.insert({3, config_[kCldPlaceholder]["format"]["today"].asString()}); cldBaseDay_ = year_month_day{ - floor(zoned_time{current_zone(), system_clock::now()}.get_local_time())} + floor(zoned_time{nullptr, system_clock::now()}.get_local_time())} .day(); } else fmtMap_.insert({3, "{}"}); @@ -127,7 +127,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } auto waybar::modules::Clock::update() -> void { - auto tz{tzList_[tzCurrIdx_]}; + auto tz{tzList_[tzCurrIdx_] ?: current_zone()}; const zoned_time now{tz, floor(system_clock::now())}; label_.set_markup(fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(now))); From 4f0fbaba8e918394ee8cf0e9fa390b478380ad14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABlys=20Bras=20de=20fer?= Date: Wed, 17 Jan 2024 13:30:32 +0100 Subject: [PATCH 006/407] clock: fix construction with calendar.format.today --- src/modules/clock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 5bfd0c497..495dfab37 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -84,7 +84,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) fmtMap_.insert({3, config_[kCldPlaceholder]["format"]["today"].asString()}); cldBaseDay_ = year_month_day{ - floor(zoned_time{nullptr, system_clock::now()}.get_local_time())} + floor(zoned_time{current_zone(), system_clock::now()}.get_local_time())} .day(); } else fmtMap_.insert({3, "{}"}); From 74e863ed73a650b9f89417d7731ca8269ed88cd1 Mon Sep 17 00:00:00 2001 From: ArneshRC <84434238+ArneshRC@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:45:49 +0530 Subject: [PATCH 007/407] updated man waybar-battery --- man/waybar-battery.5.scd | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 7827f4a8f..52a6a2d1b 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -167,3 +167,10 @@ The *battery* module allows one to define custom formats based on up to two fact - ** can be defined in the *config*. For more information see *states*. - *#battery..* - Combination of both ** and **. + +The following classes are applied to the entire Waybar rather than just the +battery widget: + +- *window#waybar.battery-* + - ** can be defined in the *config*, as previously mentioned. + From dacffdb095eb743d2162c64c77b2f6bbf46ab4f7 Mon Sep 17 00:00:00 2001 From: ArneshRC <84434238+ArneshRC@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:11:30 +0530 Subject: [PATCH 008/407] removed duplicate code --- src/modules/battery.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 6c39eb0f8..f9f9cea41 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -694,33 +694,35 @@ auto waybar::modules::Battery::update() -> void { void waybar::modules::Battery::setBarClass(std::string& state) { auto classes = bar_.window.get_style_context()->list_classes(); + const std::string prefix = "battery-"; + auto old_class_it = std::find_if(classes.begin(), classes.end(), - [](auto classname) { - return classname.rfind("battery-", 0) == 0; + [&prefix](auto classname) { + return classname.rfind(prefix, 0) == 0; }); + auto old_class = *old_class_it; + auto new_class = prefix + state; + // If the bar doesn't have any `battery-` class if(old_class_it == classes.end()) { if(!state.empty()) { - bar_.window.get_style_context()->add_class("battery-" + state); + bar_.window.get_style_context()->add_class(new_class); } return; } - auto old_class = *old_class_it; - // If the bar has a `battery-` class, // but `state` is empty if(state.empty()) { bar_.window.get_style_context()->remove_class(old_class); return; } - auto new_class = "battery-" + state; // If the bar has a `battery-` class, // and `state` is NOT empty if(old_class != new_class) { bar_.window.get_style_context()->remove_class(old_class); - bar_.window.get_style_context()->add_class("battery-" + state); + bar_.window.get_style_context()->add_class(new_class); } } From d14a4a2b1aa6b4a9af15b345f1435e68103c5d26 Mon Sep 17 00:00:00 2001 From: ArneshRC <84434238+ArneshRC@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:00:13 +0530 Subject: [PATCH 009/407] fixed early dereference error --- src/modules/battery.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index f9f9cea41..2495b33a0 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -701,7 +701,6 @@ void waybar::modules::Battery::setBarClass(std::string& state) { return classname.rfind(prefix, 0) == 0; }); - auto old_class = *old_class_it; auto new_class = prefix + state; // If the bar doesn't have any `battery-` class @@ -712,6 +711,8 @@ void waybar::modules::Battery::setBarClass(std::string& state) { return; } + auto old_class = *old_class_it; + // If the bar has a `battery-` class, // but `state` is empty if(state.empty()) { From d7ed4f1fa8f749ae257d769013269d262ffb90c4 Mon Sep 17 00:00:00 2001 From: dpayne Date: Sun, 21 Jan 2024 18:23:46 -0800 Subject: [PATCH 010/407] Adding css reloader --- include/client.hpp | 3 + include/util/css_reload_helper.hpp | 49 +++++++ meson.build | 3 +- src/client.cpp | 7 +- src/util/css_reload_helper.cpp | 214 +++++++++++++++++++++++++++++ test/css_reload_helper.cpp | 188 +++++++++++++++++++++++++ test/meson.build | 2 + 7 files changed, 463 insertions(+), 3 deletions(-) create mode 100644 include/util/css_reload_helper.hpp create mode 100644 src/util/css_reload_helper.cpp create mode 100644 test/css_reload_helper.cpp diff --git a/include/client.hpp b/include/client.hpp index 9ec29ef3c..641ee6a74 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -7,6 +7,7 @@ #include "bar.hpp" #include "config.hpp" +#include "util/css_reload_helper.hpp" #include "util/portal.hpp" struct zwlr_layer_shell_v1; @@ -55,6 +56,8 @@ class Client { Glib::RefPtr css_provider_; std::unique_ptr portal; std::list outputs_; + std::unique_ptr m_cssReloadHelper; + std::string m_cssFile; }; } // namespace waybar diff --git a/include/util/css_reload_helper.hpp b/include/util/css_reload_helper.hpp new file mode 100644 index 000000000..62507e42e --- /dev/null +++ b/include/util/css_reload_helper.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +struct pollfd; + +namespace waybar { +class CssReloadHelper { + public: + CssReloadHelper(std::string cssFile, std::function callback); + + ~CssReloadHelper(); + + virtual void monitorChanges(); + + void stop(); + + protected: + std::vector parseImports(const std::string& cssFile); + + void parseImports(const std::string& cssFile, + std::unordered_map& imports); + + + void watchFiles(const std::vector& files); + + bool handleInotifyEvents(int fd); + + bool watch(int inotifyFd, pollfd * pollFd); + + virtual std::string getFileContents(const std::string& filename); + + virtual std::string findPath(const std::string& filename); + + private: + std::string m_cssFile; + std::function m_callback; + std::atomic m_running = false; + std::thread m_thread; + std::mutex m_mutex; + std::condition_variable m_cv; +}; +} // namespace waybar diff --git a/meson.build b/meson.build index c1ae48b54..d75497316 100644 --- a/meson.build +++ b/meson.build @@ -196,7 +196,8 @@ src_files = files( 'src/util/sanitize_str.cpp', 'src/util/rewrite_string.cpp', 'src/util/gtk_icon.cpp', - 'src/util/regex_collection.cpp' + 'src/util/regex_collection.cpp', + 'src/util/css_reload_helper.cpp' ) inc_dirs = ['include'] diff --git a/src/client.cpp b/src/client.cpp index 066247e76..ff1be5d83 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -262,15 +262,18 @@ int waybar::Client::main(int argc, char *argv[]) { if (!portal) { portal = std::make_unique(); } - auto css_file = getStyle(style_opt); - setupCss(css_file); + m_cssFile = getStyle(style_opt); + setupCss(m_cssFile); + m_cssReloadHelper = std::make_unique(m_cssFile, [&]() { setupCss(m_cssFile); }); portal->signal_appearance_changed().connect([&](waybar::Appearance appearance) { auto css_file = getStyle(style_opt, appearance); setupCss(css_file); }); + m_cssReloadHelper->monitorChanges(); bindInterfaces(); gtk_app->hold(); gtk_app->run(); + m_cssReloadHelper.reset(); // stop watching css file bars.clear(); return 0; } diff --git a/src/util/css_reload_helper.cpp b/src/util/css_reload_helper.cpp new file mode 100644 index 000000000..4038f1f34 --- /dev/null +++ b/src/util/css_reload_helper.cpp @@ -0,0 +1,214 @@ +#include "util/css_reload_helper.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +#include "config.hpp" +namespace { +const std::regex IMPORT_REGEX(R"(@import\s+(?:url\()?(?:"|')([^"')]+)(?:"|')\)?;)"); +} + +waybar::CssReloadHelper::CssReloadHelper(std::string cssFile, std::function callback) + : m_cssFile(std::move(cssFile)), m_callback(std::move(callback)) {} + +waybar::CssReloadHelper::~CssReloadHelper() { stop(); } + +std::string waybar::CssReloadHelper::getFileContents(const std::string& filename) { + if (filename.empty()) { + return {}; + } + + std::ifstream file(filename); + if (!file.is_open()) { + return {}; + } + + return std::string((std::istreambuf_iterator(file)), std::istreambuf_iterator()); +} + +std::string waybar::CssReloadHelper::findPath(const std::string& filename) { + // try path and fallback to looking relative to the config + if (std::filesystem::exists(filename)) { + return filename; + } + + return Config::findConfigPath({filename}).value_or(""); +} + +void waybar::CssReloadHelper::monitorChanges() { + m_thread = std::thread([this] { + m_running = true; + while (m_running) { + auto files = parseImports(m_cssFile); + watchFiles(files); + } + }); +} + +std::vector waybar::CssReloadHelper::parseImports(const std::string& cssFile) { + std::unordered_map imports; + + auto cssFullPath = findPath(cssFile); + if (cssFullPath.empty()) { + spdlog::error("Failed to find css file: {}", cssFile); + return {}; + } + + spdlog::debug("Parsing imports for file: {}", cssFullPath); + imports[cssFullPath] = false; + + auto previousSize = 0UL; + auto maxIterations = 100U; + do { + previousSize = imports.size(); + for (const auto& [file, parsed] : imports) { + if (!parsed) { + parseImports(file, imports); + } + } + + } while (imports.size() > previousSize && maxIterations-- > 0); + + std::vector result; + for (const auto& [file, parsed] : imports) { + if (parsed) { + spdlog::debug("Adding file to watch list: {}", file); + result.push_back(file); + } + } + + return result; +} + +void waybar::CssReloadHelper::parseImports(const std::string& cssFile, + std::unordered_map& imports) { + // if the file has already been parsed, skip + if (imports.find(cssFile) != imports.end() && imports[cssFile]) { + return; + } + + auto contents = getFileContents(cssFile); + std::smatch matches; + while (std::regex_search(contents, matches, IMPORT_REGEX)) { + auto importFile = findPath({matches[1].str()}); + if (!importFile.empty() && imports.find(importFile) == imports.end()) { + imports[importFile] = false; + } + + contents = matches.suffix().str(); + } + + imports[cssFile] = true; +} + +void waybar::CssReloadHelper::stop() { + if (!m_running) { + return; + } + + m_running = false; + m_cv.notify_all(); + if (m_thread.joinable()) { + m_thread.join(); + } +} + +void waybar::CssReloadHelper::watchFiles(const std::vector& files) { + auto inotifyFd = inotify_init1(IN_NONBLOCK); + if (inotifyFd < 0) { + spdlog::error("Failed to initialize inotify: {}", strerror(errno)); + return; + } + + std::vector watchFds; + for (const auto& file : files) { + auto watchFd = inotify_add_watch(inotifyFd, file.c_str(), IN_MODIFY | IN_MOVED_TO); + if (watchFd < 0) { + spdlog::error("Failed to add watch for file: {}", file); + } else { + spdlog::debug("Added watch for file: {}", file); + } + watchFds.push_back(watchFd); + } + + auto pollFd = pollfd{inotifyFd, POLLIN, 0}; + + while (true) { + if (watch(inotifyFd, &pollFd)) { + break; + } + } + + for (const auto& watchFd : watchFds) { + inotify_rm_watch(inotifyFd, watchFd); + } + + close(inotifyFd); +} + +bool waybar::CssReloadHelper::watch(int inotifyFd, pollfd* pollFd) { + auto pollResult = poll(pollFd, 1, 10); + if (pollResult < 0) { + spdlog::error("Failed to poll inotify: {}", strerror(errno)); + return true; + } + + if (pollResult == 0) { + // check if we should stop + if (!m_running) { + return true; + } + + std::unique_lock lock(m_mutex); + // a condition variable is used to allow the thread to be stopped immediately while still not + // spamming poll + m_cv.wait_for(lock, std::chrono::milliseconds(250), [this] { return !m_running; }); + + // timeout + return false; + } + + if (static_cast(pollFd->revents & POLLIN)) { + if (handleInotifyEvents(inotifyFd)) { + // after the callback is fired we need to re-parse the imports and setup the watches + // again in case the import list has changed + return true; + } + } + + return false; +} + +bool waybar::CssReloadHelper::handleInotifyEvents(int inotify_fd) { + // inotify event + auto buffer = std::array{}; + auto readResult = read(inotify_fd, buffer.data(), buffer.size()); + if (readResult < 0) { + spdlog::error("Failed to read inotify event: {}", strerror(errno)); + return false; + } + + auto offset = 0; + auto shouldFireCallback = false; + + // read all events on the fd + while (offset < readResult) { + auto* event = reinterpret_cast(buffer.data() + offset); + offset += sizeof(inotify_event) + event->len; + shouldFireCallback = true; + } + + // we only need to fire the callback once + if (shouldFireCallback) { + m_callback(); + return true; + } + + return false; +} diff --git a/test/css_reload_helper.cpp b/test/css_reload_helper.cpp new file mode 100644 index 000000000..3ee1fb6e6 --- /dev/null +++ b/test/css_reload_helper.cpp @@ -0,0 +1,188 @@ +#include "util/css_reload_helper.hpp" +#include +#include + +#if __has_include() +#include +#include +#else +#include +#endif + + +class CssReloadHelperTest : public waybar::CssReloadHelper +{ +public: + CssReloadHelperTest() + : CssReloadHelper("/tmp/waybar_test.css", [this]() {callback();}) + { + } + + void callback() + { + m_callbackCounter++; + } + +protected: + + std::string getFileContents(const std::string& filename) override + { + return m_fileContents[filename]; + } + + std::string findPath(const std::string& filename) override + { + return filename; + } + + void setFileContents(const std::string& filename, const std::string& contents) + { + m_fileContents[filename] = contents; + } + + int getCallbackCounter() const + { + return m_callbackCounter; + } + +private: + int m_callbackCounter{}; + std::map m_fileContents; +}; + +TEST_CASE_METHOD(CssReloadHelperTest, "parse_imports", "[util][css_reload_helper]") +{ + SECTION("no imports") + { + setFileContents("/tmp/waybar_test.css", "body { color: red; }"); + auto files = parseImports("/tmp/waybar_test.css"); + REQUIRE(files.size() == 1); + CHECK(files[0] == "/tmp/waybar_test.css"); + } + + SECTION("single import") + { + setFileContents("/tmp/waybar_test.css", "@import 'test.css';"); + setFileContents("test.css", "body { color: red; }"); + auto files = parseImports("/tmp/waybar_test.css"); + std::sort(files.begin(), files.end()); + REQUIRE(files.size() == 2); + CHECK(files[0] == "/tmp/waybar_test.css"); + CHECK(files[1] == "test.css"); + } + + SECTION("multiple imports") + { + setFileContents("/tmp/waybar_test.css", "@import 'test.css'; @import 'test2.css';"); + setFileContents("test.css", "body { color: red; }"); + setFileContents("test2.css", "body { color: blue; }"); + auto files = parseImports("/tmp/waybar_test.css"); + std::sort(files.begin(), files.end()); + REQUIRE(files.size() == 3); + CHECK(files[0] == "/tmp/waybar_test.css"); + CHECK(files[1] == "test.css"); + CHECK(files[2] == "test2.css"); + } + + SECTION("nested imports") + { + setFileContents("/tmp/waybar_test.css", "@import 'test.css';"); + setFileContents("test.css", "@import 'test2.css';"); + setFileContents("test2.css", "body { color: red; }"); + auto files = parseImports("/tmp/waybar_test.css"); + std::sort(files.begin(), files.end()); + REQUIRE(files.size() == 3); + CHECK(files[0] == "/tmp/waybar_test.css"); + CHECK(files[1] == "test.css"); + CHECK(files[2] == "test2.css"); + } + + SECTION("circular imports") + { + setFileContents("/tmp/waybar_test.css", "@import 'test.css';"); + setFileContents("test.css", "@import 'test2.css';"); + setFileContents("test2.css", "@import 'test.css';"); + auto files = parseImports("/tmp/waybar_test.css"); + std::sort(files.begin(), files.end()); + REQUIRE(files.size() == 3); + CHECK(files[0] == "/tmp/waybar_test.css"); + CHECK(files[1] == "test.css"); + CHECK(files[2] == "test2.css"); + } + + SECTION("empty") + { + setFileContents("/tmp/waybar_test.css", ""); + auto files = parseImports("/tmp/waybar_test.css"); + REQUIRE(files.size() == 1); + CHECK(files[0] == "/tmp/waybar_test.css"); + } + + SECTION("empty name") + { + auto files = parseImports(""); + REQUIRE(files.empty()); + } +} + +TEST_CASE("file_watcher", "[util][css_reload_helper]") +{ + SECTION("file does not exist") + { + std::atomic count; + std::string f1 = std::tmpnam(nullptr); + waybar::CssReloadHelper helper(f1, [&count](){++count;}); + helper.monitorChanges(); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + CHECK(count == 0); + helper.stop(); + std::remove(f1.c_str()); + } + + SECTION("file exists") + { + std::atomic count; + std::string f1 = std::tmpnam(nullptr); + std::ofstream(f1) << "body { color: red; }"; + waybar::CssReloadHelper helper(f1, [&count](){++count;}); + helper.monitorChanges(); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + CHECK(count == 0); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::ofstream(f1) << "body { color: blue; }"; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + CHECK(count == 1); + helper.stop(); + std::remove(f1.c_str()); + } + + SECTION("multiple files") + { + std::atomic count; + std::string f1 = std::tmpnam(nullptr); + std::string f2 = std::tmpnam(nullptr); + std::ofstream(f1) << ("@import '" + f2 + " ';"); + std::ofstream(f2) << "body { color: red; }"; + waybar::CssReloadHelper helper(f1, [&count](){++count;}); + helper.monitorChanges(); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + CHECK(count == 0); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::ofstream(f2) << "body { color: blue; }"; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + CHECK(count == 1); + helper.stop(); + std::remove(f1.c_str()); + std::remove(f2.c_str()); + } +} diff --git a/test/meson.build b/test/meson.build index 02cbb2a48..358d6c96b 100644 --- a/test/meson.build +++ b/test/meson.build @@ -10,7 +10,9 @@ test_src = files( 'main.cpp', 'SafeSignal.cpp', 'config.cpp', + 'css_reload_helper.cpp', '../src/config.cpp', + '../src/util/css_reload_helper.cpp', ) if tz_dep.found() From 53233e47a313773dab278879ce6cf9866ea9ed22 Mon Sep 17 00:00:00 2001 From: dpayne Date: Sun, 21 Jan 2024 19:08:23 -0800 Subject: [PATCH 011/407] Fix use after free on task close --- src/modules/wlr/taskbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 9a8b89e7b..0eaf264af 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -527,11 +527,11 @@ void Task::handle_closed() { spdlog::debug("{} closed", repr()); zwlr_foreign_toplevel_handle_v1_destroy(handle_); handle_ = nullptr; - tbar_->remove_task(id_); if (button_visible_) { tbar_->remove_button(button); button_visible_ = false; } + tbar_->remove_task(id_); } bool Task::handle_clicked(GdkEventButton *bt) { From f7eca99496e4ad8c635db5aa2c38967994eabad0 Mon Sep 17 00:00:00 2001 From: dpayne Date: Sun, 21 Jan 2024 20:49:13 -0800 Subject: [PATCH 012/407] Using Gio::FileMonitor for watching style changes --- include/util/css_reload_helper.hpp | 24 ++--- src/util/css_reload_helper.cpp | 162 ++++++++--------------------- test/css_reload_helper.cpp | 62 ----------- 3 files changed, 58 insertions(+), 190 deletions(-) diff --git a/include/util/css_reload_helper.hpp b/include/util/css_reload_helper.hpp index 62507e42e..4da64ec64 100644 --- a/include/util/css_reload_helper.hpp +++ b/include/util/css_reload_helper.hpp @@ -1,13 +1,13 @@ #pragma once -#include -#include #include -#include #include -#include #include +#include "glibmm/refptr.h" +#include "giomm/file.h" +#include "giomm/filemonitor.h" + struct pollfd; namespace waybar { @@ -15,12 +15,8 @@ class CssReloadHelper { public: CssReloadHelper(std::string cssFile, std::function callback); - ~CssReloadHelper(); - virtual void monitorChanges(); - void stop(); - protected: std::vector parseImports(const std::string& cssFile); @@ -38,12 +34,16 @@ class CssReloadHelper { virtual std::string findPath(const std::string& filename); + void handleFileChange( + Glib::RefPtr const& file, + Glib::RefPtr const& other_type, + Gio::FileMonitorEvent event_type); + private: std::string m_cssFile; + std::function m_callback; - std::atomic m_running = false; - std::thread m_thread; - std::mutex m_mutex; - std::condition_variable m_cv; + + std::vector>> m_fileMonitors; }; } // namespace waybar diff --git a/src/util/css_reload_helper.cpp b/src/util/css_reload_helper.cpp index 4038f1f34..9bbd0f93b 100644 --- a/src/util/css_reload_helper.cpp +++ b/src/util/css_reload_helper.cpp @@ -8,8 +8,11 @@ #include #include #include +#include "glibmm/refptr.h" +#include "giomm/file.h" #include "config.hpp" + namespace { const std::regex IMPORT_REGEX(R"(@import\s+(?:url\()?(?:"|')([^"')]+)(?:"|')\)?;)"); } @@ -17,8 +20,6 @@ const std::regex IMPORT_REGEX(R"(@import\s+(?:url\()?(?:"|')([^"')]+)(?:"|')\)?; waybar::CssReloadHelper::CssReloadHelper(std::string cssFile, std::function callback) : m_cssFile(std::move(cssFile)), m_callback(std::move(callback)) {} -waybar::CssReloadHelper::~CssReloadHelper() { stop(); } - std::string waybar::CssReloadHelper::getFileContents(const std::string& filename) { if (filename.empty()) { return {}; @@ -34,21 +35,56 @@ std::string waybar::CssReloadHelper::getFileContents(const std::string& filename std::string waybar::CssReloadHelper::findPath(const std::string& filename) { // try path and fallback to looking relative to the config + std::string result; if (std::filesystem::exists(filename)) { - return filename; + result = filename; + } else { + result = Config::findConfigPath({filename}).value_or(""); + } + + // File monitor does not work with symlinks, so resolve them + if (std::filesystem::is_symlink(result)) { + result = std::filesystem::read_symlink(result); } - return Config::findConfigPath({filename}).value_or(""); + return result; } void waybar::CssReloadHelper::monitorChanges() { - m_thread = std::thread([this] { - m_running = true; - while (m_running) { - auto files = parseImports(m_cssFile); - watchFiles(files); + auto files = parseImports(m_cssFile); + for (const auto& file : files) { + auto gioFile = Gio::File::create_for_path(file); + if (!gioFile) { + spdlog::error("Failed to create file for path: {}", file); + continue; + } + + auto fileMonitor = gioFile->monitor_file(); + if (!fileMonitor) { + spdlog::error("Failed to create file monitor for path: {}", file); + continue; } - }); + + auto connection = fileMonitor->signal_changed().connect( + sigc::mem_fun(*this, &CssReloadHelper::handleFileChange)); + + if (!connection.connected()) { + spdlog::error("Failed to connect to file monitor for path: {}", file); + continue; + } + m_fileMonitors.emplace_back(std::move(fileMonitor)); + } +} + +void waybar::CssReloadHelper::handleFileChange(Glib::RefPtr const& file, + Glib::RefPtr const& other_type, + Gio::FileMonitorEvent event_type) { + // Multiple events are fired on file changed (attributes, write, changes done hint, etc.), only + // fire for one + if (event_type == Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { + spdlog::debug("Reloading style, file changed: {}", file->get_path()); + m_callback(); + } } std::vector waybar::CssReloadHelper::parseImports(const std::string& cssFile) { @@ -106,109 +142,3 @@ void waybar::CssReloadHelper::parseImports(const std::string& cssFile, imports[cssFile] = true; } - -void waybar::CssReloadHelper::stop() { - if (!m_running) { - return; - } - - m_running = false; - m_cv.notify_all(); - if (m_thread.joinable()) { - m_thread.join(); - } -} - -void waybar::CssReloadHelper::watchFiles(const std::vector& files) { - auto inotifyFd = inotify_init1(IN_NONBLOCK); - if (inotifyFd < 0) { - spdlog::error("Failed to initialize inotify: {}", strerror(errno)); - return; - } - - std::vector watchFds; - for (const auto& file : files) { - auto watchFd = inotify_add_watch(inotifyFd, file.c_str(), IN_MODIFY | IN_MOVED_TO); - if (watchFd < 0) { - spdlog::error("Failed to add watch for file: {}", file); - } else { - spdlog::debug("Added watch for file: {}", file); - } - watchFds.push_back(watchFd); - } - - auto pollFd = pollfd{inotifyFd, POLLIN, 0}; - - while (true) { - if (watch(inotifyFd, &pollFd)) { - break; - } - } - - for (const auto& watchFd : watchFds) { - inotify_rm_watch(inotifyFd, watchFd); - } - - close(inotifyFd); -} - -bool waybar::CssReloadHelper::watch(int inotifyFd, pollfd* pollFd) { - auto pollResult = poll(pollFd, 1, 10); - if (pollResult < 0) { - spdlog::error("Failed to poll inotify: {}", strerror(errno)); - return true; - } - - if (pollResult == 0) { - // check if we should stop - if (!m_running) { - return true; - } - - std::unique_lock lock(m_mutex); - // a condition variable is used to allow the thread to be stopped immediately while still not - // spamming poll - m_cv.wait_for(lock, std::chrono::milliseconds(250), [this] { return !m_running; }); - - // timeout - return false; - } - - if (static_cast(pollFd->revents & POLLIN)) { - if (handleInotifyEvents(inotifyFd)) { - // after the callback is fired we need to re-parse the imports and setup the watches - // again in case the import list has changed - return true; - } - } - - return false; -} - -bool waybar::CssReloadHelper::handleInotifyEvents(int inotify_fd) { - // inotify event - auto buffer = std::array{}; - auto readResult = read(inotify_fd, buffer.data(), buffer.size()); - if (readResult < 0) { - spdlog::error("Failed to read inotify event: {}", strerror(errno)); - return false; - } - - auto offset = 0; - auto shouldFireCallback = false; - - // read all events on the fd - while (offset < readResult) { - auto* event = reinterpret_cast(buffer.data() + offset); - offset += sizeof(inotify_event) + event->len; - shouldFireCallback = true; - } - - // we only need to fire the callback once - if (shouldFireCallback) { - m_callback(); - return true; - } - - return false; -} diff --git a/test/css_reload_helper.cpp b/test/css_reload_helper.cpp index 3ee1fb6e6..ac7b3eb79 100644 --- a/test/css_reload_helper.cpp +++ b/test/css_reload_helper.cpp @@ -124,65 +124,3 @@ TEST_CASE_METHOD(CssReloadHelperTest, "parse_imports", "[util][css_reload_helper REQUIRE(files.empty()); } } - -TEST_CASE("file_watcher", "[util][css_reload_helper]") -{ - SECTION("file does not exist") - { - std::atomic count; - std::string f1 = std::tmpnam(nullptr); - waybar::CssReloadHelper helper(f1, [&count](){++count;}); - helper.monitorChanges(); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - CHECK(count == 0); - helper.stop(); - std::remove(f1.c_str()); - } - - SECTION("file exists") - { - std::atomic count; - std::string f1 = std::tmpnam(nullptr); - std::ofstream(f1) << "body { color: red; }"; - waybar::CssReloadHelper helper(f1, [&count](){++count;}); - helper.monitorChanges(); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - CHECK(count == 0); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - std::ofstream(f1) << "body { color: blue; }"; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - CHECK(count == 1); - helper.stop(); - std::remove(f1.c_str()); - } - - SECTION("multiple files") - { - std::atomic count; - std::string f1 = std::tmpnam(nullptr); - std::string f2 = std::tmpnam(nullptr); - std::ofstream(f1) << ("@import '" + f2 + " ';"); - std::ofstream(f2) << "body { color: red; }"; - waybar::CssReloadHelper helper(f1, [&count](){++count;}); - helper.monitorChanges(); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - CHECK(count == 0); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - std::ofstream(f2) << "body { color: blue; }"; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - CHECK(count == 1); - helper.stop(); - std::remove(f1.c_str()); - std::remove(f2.c_str()); - } -} From 20fa578b12212591299d2010f1b6f8d5c117dcde Mon Sep 17 00:00:00 2001 From: dpayne Date: Sun, 21 Jan 2024 21:02:01 -0800 Subject: [PATCH 013/407] Adding config option to enable reloading style on file change --- src/client.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index ff1be5d83..bd0ee41a1 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -269,11 +269,14 @@ int waybar::Client::main(int argc, char *argv[]) { auto css_file = getStyle(style_opt, appearance); setupCss(css_file); }); - m_cssReloadHelper->monitorChanges(); + + if (config.getConfig()["reload_style_on_change"].asBool()) { + m_cssReloadHelper->monitorChanges(); + } bindInterfaces(); gtk_app->hold(); gtk_app->run(); - m_cssReloadHelper.reset(); // stop watching css file + m_cssReloadHelper.reset(); // stop watching css file bars.clear(); return 0; } From 82b632e4ec1489f9b4446b8c8df6f0a8c59efe3f Mon Sep 17 00:00:00 2001 From: SquishyPandaDev <55671441+SquishyPandaDev@users.noreply.github.com> Date: Mon, 22 Jan 2024 03:22:19 -0500 Subject: [PATCH 014/407] Fix module UPower display device poniter Force displayDevice to be a nullptr on class setup --- include/modules/upower/upower.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index eda8ab050..d763259b6 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -66,7 +66,7 @@ class UPower : public AModule { Devices devices; std::mutex m_Mutex; UpClient *client; - UpDevice *displayDevice; + UpDevice *displayDevice = nullptr; guint login1_id; GDBusConnection *login1_connection; std::unique_ptr upower_tooltip; From 0af8f5c6910f43cb1bf43bc876a42d16938a6f1e Mon Sep 17 00:00:00 2001 From: Paul Rey Date: Tue, 23 Jan 2024 18:15:47 +0100 Subject: [PATCH 015/407] Add "empty" class for Image module when path/exec is returning an empty value. --- man/waybar-image.5.scd | 1 + src/modules/image.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/man/waybar-image.5.scd b/man/waybar-image.5.scd index 1671e711f..e3a69e38d 100644 --- a/man/waybar-image.5.scd +++ b/man/waybar-image.5.scd @@ -87,3 +87,4 @@ $path\\n$tooltip # STYLE - *#image* +- *#image.empty* diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 08b03b92c..9d59b4a37 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -63,9 +63,11 @@ auto waybar::modules::Image::update() -> void { } image_.set(pixbuf); image_.show(); + image_.get_style_context()->remove_class("empty"); } else { image_.clear(); image_.hide(); + image_.get_style_context()->add_class("empty"); } AModule::update(); From 002ff002fe587b72136d4582cec3e9aaf85c51e9 Mon Sep 17 00:00:00 2001 From: Paul Rey Date: Wed, 24 Jan 2024 11:10:16 +0100 Subject: [PATCH 016/407] Move `.empty` CSS class from Img to Box in the Image module Since the GTK Image is hidden, the CSS class wasn't used for every cases. --- src/modules/image.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 9d59b4a37..3c90b5575 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -63,11 +63,11 @@ auto waybar::modules::Image::update() -> void { } image_.set(pixbuf); image_.show(); - image_.get_style_context()->remove_class("empty"); + box_.get_style_context()->remove_class("empty"); } else { image_.clear(); image_.hide(); - image_.get_style_context()->add_class("empty"); + box_.get_style_context()->add_class("empty"); } AModule::update(); From 7e76369ec8d59582d191ba58892b55b0404e5924 Mon Sep 17 00:00:00 2001 From: Siddhartha Singh Date: Thu, 25 Jan 2024 14:00:21 +0530 Subject: [PATCH 017/407] Using steps() in animation --- resources/style.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/style.css b/resources/style.css index e6017fdb0..3d44829f3 100644 --- a/resources/style.css +++ b/resources/style.css @@ -128,12 +128,13 @@ button:hover { } } +/* Using steps() instead of linear as a timing function to limit cpu usage */ #battery.critical:not(.charging) { background-color: #f53c3c; color: #ffffff; animation-name: blink; animation-duration: 0.5s; - animation-timing-function: linear; + animation-timing-function: steps(12); animation-iteration-count: infinite; animation-direction: alternate; } From 94633c346ab2ed5360230667047b8b02e9b1585a Mon Sep 17 00:00:00 2001 From: Paul Rey Date: Thu, 25 Jan 2024 11:02:44 +0100 Subject: [PATCH 018/407] retrigger checks From 167f04a4dfab2b1d7834c7348185ce2b2ccb0dc8 Mon Sep 17 00:00:00 2001 From: Jay-716 <13422525511@163.com> Date: Thu, 25 Jan 2024 22:51:07 +0800 Subject: [PATCH 019/407] pulseaudio: reconnect context when pulseaudio server restarts When pulseaudio server restarts, the context is not reconnect automatically. So the pulseaudio module will stop updating. --- src/util/audio_backend.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 7eef14483..b7319d583 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -80,7 +80,22 @@ void AudioBackend::contextStateCb(pa_context *c, void *data) { nullptr, nullptr); break; case PA_CONTEXT_FAILED: - backend->mainloop_api_->quit(backend->mainloop_api_, 1); + // When pulseaudio server restarts, the connection is "failed". Try to reconnect. + // pa_threaded_mainloop_lock is already acquired in callback threads. + // So there is no need to lock it again. + if (backend->context_ != nullptr) { + pa_context_disconnect(backend->context_); + } + backend->context_ = pa_context_new(backend->mainloop_api_, "waybar"); + if (backend->context_ == nullptr) { + throw std::runtime_error("pa_context_new() failed."); + } + pa_context_set_state_callback(backend->context_, contextStateCb, data); + if (pa_context_connect(backend->context_, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) { + auto err = + fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(backend->context_))); + throw std::runtime_error(err); + } break; case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: From 14d168c254828cdb9b29b92fddc0f2634d656d0b Mon Sep 17 00:00:00 2001 From: Jay-716 <13422525511@163.com> Date: Sat, 27 Jan 2024 23:44:32 +0800 Subject: [PATCH 020/407] pulseaudio: extract context connecting into `connectContext()` --- include/util/audio_backend.hpp | 1 + src/util/audio_backend.cpp | 35 +++++++++++++++------------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/include/util/audio_backend.hpp b/include/util/audio_backend.hpp index 8d9b6f2f0..2f53103e5 100644 --- a/include/util/audio_backend.hpp +++ b/include/util/audio_backend.hpp @@ -22,6 +22,7 @@ class AudioBackend { static void sourceInfoCb(pa_context*, const pa_source_info* i, int, void* data); static void serverInfoCb(pa_context*, const pa_server_info*, void*); static void volumeModifyCb(pa_context*, int, void*); + void connectContext(); pa_threaded_mainloop* mainloop_; pa_mainloop_api* mainloop_api_; diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index b7319d583..f4dd72c4a 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -28,16 +28,7 @@ AudioBackend::AudioBackend(std::function on_updated_cb, private_construc } pa_threaded_mainloop_lock(mainloop_); mainloop_api_ = pa_threaded_mainloop_get_api(mainloop_); - context_ = pa_context_new(mainloop_api_, "waybar"); - if (context_ == nullptr) { - throw std::runtime_error("pa_context_new() failed."); - } - if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) { - auto err = - fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(context_))); - throw std::runtime_error(err); - } - pa_context_set_state_callback(context_, contextStateCb, this); + connectContext(); if (pa_threaded_mainloop_start(mainloop_) < 0) { throw std::runtime_error("pa_mainloop_run() failed."); } @@ -61,6 +52,19 @@ std::shared_ptr AudioBackend::getInstance(std::function on return std::make_shared(on_updated_cb, tag); } +void AudioBackend::connectContext() { + context_ = pa_context_new(mainloop_api_, "waybar"); + if (context_ == nullptr) { + throw std::runtime_error("pa_context_new() failed."); + } + pa_context_set_state_callback(context_, contextStateCb, this); + if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) { + auto err = + fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(context_))); + throw std::runtime_error(err); + } +} + void AudioBackend::contextStateCb(pa_context *c, void *data) { auto backend = static_cast(data); switch (pa_context_get_state(c)) { @@ -86,16 +90,7 @@ void AudioBackend::contextStateCb(pa_context *c, void *data) { if (backend->context_ != nullptr) { pa_context_disconnect(backend->context_); } - backend->context_ = pa_context_new(backend->mainloop_api_, "waybar"); - if (backend->context_ == nullptr) { - throw std::runtime_error("pa_context_new() failed."); - } - pa_context_set_state_callback(backend->context_, contextStateCb, data); - if (pa_context_connect(backend->context_, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) { - auto err = - fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(backend->context_))); - throw std::runtime_error(err); - } + backend->connectContext(); break; case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: From 9556b0fe893763d15dc675f017c895742e6cddbb Mon Sep 17 00:00:00 2001 From: dpayne Date: Sun, 28 Jan 2024 14:25:00 -0800 Subject: [PATCH 021/407] Adding a man page entry for the realod_style_on_change option --- man/waybar.5.scd.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index e076b000e..17324d693 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -130,6 +130,11 @@ Also, a minimal example configuration can be found at the bottom of this man pag Each file can contain a single object with any of the bar configuration options. In case of duplicate options, the first defined value takes precedence, i.e. including file -> first included file -> etc. Nested includes are permitted, but make sure to avoid circular imports. For a multi-bar config, the include directive affects only current bar configuration object. +*reload_style_on_change* ++ + typeof: bool ++ + default: *false* ++ + Option to enable reloading the css style if a modification is detected on the style sheet file or any imported css files. + # MODULE FORMAT You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat). From 10cb4180f67a1a3d43cf00574ebcade25205047c Mon Sep 17 00:00:00 2001 From: dpayne Date: Sun, 28 Jan 2024 14:44:25 -0800 Subject: [PATCH 022/407] * Fixing clang tidy comments * Fixing missing includes * Fixing formatting --- include/util/css_reload_helper.hpp | 16 ++++---- src/util/css_reload_helper.cpp | 6 +-- test/css_reload_helper.cpp | 63 +++++++++--------------------- 3 files changed, 29 insertions(+), 56 deletions(-) diff --git a/include/util/css_reload_helper.hpp b/include/util/css_reload_helper.hpp index 4da64ec64..4826fc312 100644 --- a/include/util/css_reload_helper.hpp +++ b/include/util/css_reload_helper.hpp @@ -2,11 +2,12 @@ #include #include +#include #include -#include "glibmm/refptr.h" #include "giomm/file.h" #include "giomm/filemonitor.h" +#include "glibmm/refptr.h" struct pollfd; @@ -20,24 +21,21 @@ class CssReloadHelper { protected: std::vector parseImports(const std::string& cssFile); - void parseImports(const std::string& cssFile, - std::unordered_map& imports); - + void parseImports(const std::string& cssFile, std::unordered_map& imports); void watchFiles(const std::vector& files); bool handleInotifyEvents(int fd); - bool watch(int inotifyFd, pollfd * pollFd); + bool watch(int inotifyFd, pollfd* pollFd); virtual std::string getFileContents(const std::string& filename); virtual std::string findPath(const std::string& filename); - void handleFileChange( - Glib::RefPtr const& file, - Glib::RefPtr const& other_type, - Gio::FileMonitorEvent event_type); + void handleFileChange(Glib::RefPtr const& file, + Glib::RefPtr const& other_type, + Gio::FileMonitorEvent event_type); private: std::string m_cssFile; diff --git a/src/util/css_reload_helper.cpp b/src/util/css_reload_helper.cpp index 9bbd0f93b..45fd801a7 100644 --- a/src/util/css_reload_helper.cpp +++ b/src/util/css_reload_helper.cpp @@ -8,10 +8,10 @@ #include #include #include -#include "glibmm/refptr.h" -#include "giomm/file.h" #include "config.hpp" +#include "giomm/file.h" +#include "glibmm/refptr.h" namespace { const std::regex IMPORT_REGEX(R"(@import\s+(?:url\()?(?:"|')([^"')]+)(?:"|')\)?;)"); @@ -30,7 +30,7 @@ std::string waybar::CssReloadHelper::getFileContents(const std::string& filename return {}; } - return std::string((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + return {(std::istreambuf_iterator(file)), std::istreambuf_iterator()}; } std::string waybar::CssReloadHelper::findPath(const std::string& filename) { diff --git a/test/css_reload_helper.cpp b/test/css_reload_helper.cpp index ac7b3eb79..01850bc32 100644 --- a/test/css_reload_helper.cpp +++ b/test/css_reload_helper.cpp @@ -1,6 +1,6 @@ #include "util/css_reload_helper.hpp" + #include -#include #if __has_include() #include @@ -9,59 +9,39 @@ #include #endif +class CssReloadHelperTest : public waybar::CssReloadHelper { + public: + CssReloadHelperTest() : CssReloadHelper("/tmp/waybar_test.css", [this]() { callback(); }) {} -class CssReloadHelperTest : public waybar::CssReloadHelper -{ -public: - CssReloadHelperTest() - : CssReloadHelper("/tmp/waybar_test.css", [this]() {callback();}) - { - } - - void callback() - { - m_callbackCounter++; - } + void callback() { m_callbackCounter++; } -protected: - - std::string getFileContents(const std::string& filename) override - { + protected: + std::string getFileContents(const std::string& filename) override { return m_fileContents[filename]; } - std::string findPath(const std::string& filename) override - { - return filename; - } + std::string findPath(const std::string& filename) override { return filename; } - void setFileContents(const std::string& filename, const std::string& contents) - { + void setFileContents(const std::string& filename, const std::string& contents) { m_fileContents[filename] = contents; } - int getCallbackCounter() const - { - return m_callbackCounter; - } + int getCallbackCounter() const { return m_callbackCounter; } -private: + private: int m_callbackCounter{}; std::map m_fileContents; }; -TEST_CASE_METHOD(CssReloadHelperTest, "parse_imports", "[util][css_reload_helper]") -{ - SECTION("no imports") - { +TEST_CASE_METHOD(CssReloadHelperTest, "parse_imports", "[util][css_reload_helper]") { + SECTION("no imports") { setFileContents("/tmp/waybar_test.css", "body { color: red; }"); auto files = parseImports("/tmp/waybar_test.css"); REQUIRE(files.size() == 1); CHECK(files[0] == "/tmp/waybar_test.css"); } - SECTION("single import") - { + SECTION("single import") { setFileContents("/tmp/waybar_test.css", "@import 'test.css';"); setFileContents("test.css", "body { color: red; }"); auto files = parseImports("/tmp/waybar_test.css"); @@ -71,8 +51,7 @@ TEST_CASE_METHOD(CssReloadHelperTest, "parse_imports", "[util][css_reload_helper CHECK(files[1] == "test.css"); } - SECTION("multiple imports") - { + SECTION("multiple imports") { setFileContents("/tmp/waybar_test.css", "@import 'test.css'; @import 'test2.css';"); setFileContents("test.css", "body { color: red; }"); setFileContents("test2.css", "body { color: blue; }"); @@ -84,8 +63,7 @@ TEST_CASE_METHOD(CssReloadHelperTest, "parse_imports", "[util][css_reload_helper CHECK(files[2] == "test2.css"); } - SECTION("nested imports") - { + SECTION("nested imports") { setFileContents("/tmp/waybar_test.css", "@import 'test.css';"); setFileContents("test.css", "@import 'test2.css';"); setFileContents("test2.css", "body { color: red; }"); @@ -97,8 +75,7 @@ TEST_CASE_METHOD(CssReloadHelperTest, "parse_imports", "[util][css_reload_helper CHECK(files[2] == "test2.css"); } - SECTION("circular imports") - { + SECTION("circular imports") { setFileContents("/tmp/waybar_test.css", "@import 'test.css';"); setFileContents("test.css", "@import 'test2.css';"); setFileContents("test2.css", "@import 'test.css';"); @@ -110,16 +87,14 @@ TEST_CASE_METHOD(CssReloadHelperTest, "parse_imports", "[util][css_reload_helper CHECK(files[2] == "test2.css"); } - SECTION("empty") - { + SECTION("empty") { setFileContents("/tmp/waybar_test.css", ""); auto files = parseImports("/tmp/waybar_test.css"); REQUIRE(files.size() == 1); CHECK(files[0] == "/tmp/waybar_test.css"); } - SECTION("empty name") - { + SECTION("empty name") { auto files = parseImports(""); REQUIRE(files.empty()); } From fb6658e8fdae97ef1e1d6042b7fd15287ed046bc Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 30 Jan 2024 09:07:37 +0100 Subject: [PATCH 023/407] chore: lint --- include/modules/systemd_failed_units.hpp | 5 +++-- src/modules/battery.cpp | 18 ++++++++--------- src/modules/systemd_failed_units.cpp | 25 +++++++++++------------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/include/modules/systemd_failed_units.hpp b/include/modules/systemd_failed_units.hpp index 7e0b1a910..d305264df 100644 --- a/include/modules/systemd_failed_units.hpp +++ b/include/modules/systemd_failed_units.hpp @@ -1,15 +1,16 @@ #pragma once -#include #include +#include + #include "ALabel.hpp" namespace waybar::modules { class SystemdFailedUnits : public ALabel { public: - SystemdFailedUnits(const std::string&, const Json::Value&); + SystemdFailedUnits(const std::string &, const Json::Value &); virtual ~SystemdFailedUnits(); auto update() -> void override; diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 2495b33a0..9003db6e9 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -1,4 +1,5 @@ #include "modules/battery.hpp" + #include #if defined(__FreeBSD__) #include @@ -696,17 +697,16 @@ void waybar::modules::Battery::setBarClass(std::string& state) { auto classes = bar_.window.get_style_context()->list_classes(); const std::string prefix = "battery-"; - auto old_class_it = std::find_if(classes.begin(), classes.end(), - [&prefix](auto classname) { - return classname.rfind(prefix, 0) == 0; - }); + auto old_class_it = std::find_if(classes.begin(), classes.end(), [&prefix](auto classname) { + return classname.rfind(prefix, 0) == 0; + }); auto new_class = prefix + state; // If the bar doesn't have any `battery-` class - if(old_class_it == classes.end()) { - if(!state.empty()) { - bar_.window.get_style_context()->add_class(new_class); + if (old_class_it == classes.end()) { + if (!state.empty()) { + bar_.window.get_style_context()->add_class(new_class); } return; } @@ -715,14 +715,14 @@ void waybar::modules::Battery::setBarClass(std::string& state) { // If the bar has a `battery-` class, // but `state` is empty - if(state.empty()) { + if (state.empty()) { bar_.window.get_style_context()->remove_class(old_class); return; } // If the bar has a `battery-` class, // and `state` is NOT empty - if(old_class != new_class) { + if (old_class != new_class) { bar_.window.get_style_context()->remove_class(old_class); bar_.window.get_style_context()->add_class(new_class); } diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp index 382eea4a5..56e624cf2 100644 --- a/src/modules/systemd_failed_units.cpp +++ b/src/modules/systemd_failed_units.cpp @@ -1,10 +1,11 @@ #include "modules/systemd_failed_units.hpp" -#include #include #include #include +#include + static const unsigned UPDATE_DEBOUNCE_TIME_MS = 1000; namespace waybar::modules { @@ -55,23 +56,21 @@ SystemdFailedUnits::~SystemdFailedUnits() { if (user_proxy) user_proxy.reset(); } -auto SystemdFailedUnits::notify_cb( - const Glib::ustring &sender_name, - const Glib::ustring &signal_name, - const Glib::VariantContainerBase &arguments) -> void { +auto SystemdFailedUnits::notify_cb(const Glib::ustring& sender_name, + const Glib::ustring& signal_name, + const Glib::VariantContainerBase& arguments) -> void { if (signal_name == "PropertiesChanged" && !update_pending) { update_pending = true; /* The fail count may fluctuate due to restarting. */ - Glib::signal_timeout().connect_once( - sigc::mem_fun(*this, &SystemdFailedUnits::updateData), - UPDATE_DEBOUNCE_TIME_MS); + Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &SystemdFailedUnits::updateData), + UPDATE_DEBOUNCE_TIME_MS); } } void SystemdFailedUnits::updateData() { update_pending = false; - auto load = [](const char* kind, Glib::RefPtr &proxy) -> uint32_t { + auto load = [](const char* kind, Glib::RefPtr& proxy) -> uint32_t { try { auto parameters = Glib::VariantContainerBase( g_variant_new("(ss)", "org.freedesktop.systemd1.Manager", "NFailedUnits")); @@ -123,11 +122,9 @@ auto SystemdFailedUnits::update() -> void { last_status = status; label_.set_markup(fmt::format( - fmt::runtime(nr_failed == 0 ? format_ok : format_), - fmt::arg("nr_failed", nr_failed), - fmt::arg("nr_failed_system", nr_failed_system), - fmt::arg("nr_failed_user", nr_failed_user))); + fmt::runtime(nr_failed == 0 ? format_ok : format_), fmt::arg("nr_failed", nr_failed), + fmt::arg("nr_failed_system", nr_failed_system), fmt::arg("nr_failed_user", nr_failed_user))); ALabel::update(); } -} // namespace waybar::modules::systemd_failed_units +} // namespace waybar::modules From 6dcae2cadeccc74e8c575be6caed8cde29682f1e Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 31 Jan 2024 22:57:20 +0100 Subject: [PATCH 024/407] fix: reload style --- src/client.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/client.cpp b/src/client.cpp index bd0ee41a1..e32f8140c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -270,9 +270,18 @@ int waybar::Client::main(int argc, char *argv[]) { setupCss(css_file); }); - if (config.getConfig()["reload_style_on_change"].asBool()) { + auto config = config.getConfig(); + if (config.isObject() && config["reload_style_on_change"].asBool()) { m_cssReloadHelper->monitorChanges(); + } else if (config.isArray()) { + for (const auto &conf : config) { + if (conf["reload_style_on_change"].asBool()) { + m_cssReloadHelper->monitorChanges(); + break; + } + } } + bindInterfaces(); gtk_app->hold(); gtk_app->run(); From f1016322b988967f059304b1beee69860baf8b12 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 31 Jan 2024 22:59:09 +0100 Subject: [PATCH 025/407] fix: tpyo --- src/client.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index e32f8140c..73c06fb87 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -270,11 +270,11 @@ int waybar::Client::main(int argc, char *argv[]) { setupCss(css_file); }); - auto config = config.getConfig(); - if (config.isObject() && config["reload_style_on_change"].asBool()) { + auto m_config = config.getConfig(); + if (m_config.isObject() && m_config["reload_style_on_change"].asBool()) { m_cssReloadHelper->monitorChanges(); - } else if (config.isArray()) { - for (const auto &conf : config) { + } else if (m_config.isArray()) { + for (const auto &conf : m_config) { if (conf["reload_style_on_change"].asBool()) { m_cssReloadHelper->monitorChanges(); break; From 029b380c15bda9215ea5531b76adebf7e25032ea Mon Sep 17 00:00:00 2001 From: Jannik Date: Fri, 2 Feb 2024 20:54:16 +0100 Subject: [PATCH 026/407] Fix: drawer not appearing on configured side --- src/group.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/group.cpp b/src/group.cpp index cad36e51f..f90610432 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -73,7 +73,13 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& revealer.get_style_context()->add_class("drawer"); revealer.add(revealer_box); - box.pack_start(revealer); + + if (left_to_right) { + box.pack_end(revealer); + } + else { + box.pack_start(revealer); + } addHoverHandlerTo(revealer); } From c641d52e0619cfe4782c56ca22195fd6f40f6a25 Mon Sep 17 00:00:00 2001 From: Jo De Boeck Date: Thu, 28 Dec 2023 00:06:09 +0200 Subject: [PATCH 027/407] Implement windows formating in sway/workspaces This implementation mimics to some extend the implementation of hyprland Signed-off-by: Jo De Boeck --- include/modules/sway/workspaces.hpp | 7 ++ man/waybar-sway-workspaces.5.scd | 32 +++++++++ src/modules/sway/workspaces.cpp | 105 +++++++++++++++++++++++----- 3 files changed, 128 insertions(+), 16 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 0efffe643..4258252a2 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -12,6 +12,7 @@ #include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" +#include "util/regex_collection.hpp" namespace waybar::modules::sway { @@ -27,10 +28,13 @@ class Workspaces : public AModule, public sigc::trackable { R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")"; static int convertWorkspaceNameToNum(std::string name); + static int windowRewritePriorityFunction(std::string const& window_rule); void onCmd(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&); bool filterButtons(); + static bool hasFlag(const Json::Value&, const std::string&); + void updateWindows(const Json::Value&, std::string&); Gtk::Button& addButton(const Json::Value&); void onButtonReady(const Json::Value&, Gtk::Button&); std::string getIcon(const std::string&, const Json::Value&); @@ -44,6 +48,9 @@ class Workspaces : public AModule, public sigc::trackable { std::vector high_priority_named_; std::vector workspaces_order_; Gtk::Box box_; + std::string m_formatWindowSeperator; + std::string m_windowRewriteDefault; + util::RegexCollection m_windowRewriteRules; util::JsonParser parser_; std::unordered_map buttons_; std::mutex mutex_; diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index cdb653f9d..3343b8d5a 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -82,6 +82,23 @@ warp-on-scroll: ++ default: true ++ If set to false, you can scroll to cycle through workspaces without mouse warping being enabled. If set to true this behaviour is disabled. +*window-rewrite*: ++ + typeof: object ++ + Regex rules to map window class to an icon or preferred method of representation for a workspace's window. + Keys are the rules, while the values are the methods of representation. + Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching. + +*window-rewrite-default*: + typeof: string ++ + default: "?" ++ + The default method of representation for a workspace's window. This will be used for windows whose classes do not match any of the rules in *window-rewrite*. + +*format-window-separator*: ++ + typeof: string ++ + default: " " ++ + The separator to be used between windows in a workspace. + + # FORMAT REPLACEMENTS *{value}*: Name of the workspace, as defined by sway. @@ -94,6 +111,8 @@ warp-on-scroll: ++ *{output}*: Output where the workspace is located. +*{windows}*: Result from window-rewrite + # ICONS Additional to workspace name matching, the following *format-icons* can be set. @@ -143,6 +162,19 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge } ``` +``` +"sway/workspaces": { + "format": "{name} {windows}", + "format-window-separator": " | ", + "window-rewrite-default": "{name}", + "window-format": "{name}", + "window-rewrite": { + "class": "", + "class": "k", + } +} +``` + # Style - *#workspaces button* diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 1a47e4785..327ba9098 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -24,6 +24,24 @@ int Workspaces::convertWorkspaceNameToNum(std::string name) { return -1; } +int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) { + // Rules that match against title are prioritized + // Rules that don't specify if they're matching against either title or class are deprioritized + bool const hasTitle = window_rule.find("title") != std::string::npos; + bool const hasClass = window_rule.find("class") != std::string::npos; + + if (hasTitle && hasClass) { + return 3; + } + if (hasTitle) { + return 2; + } + if (hasClass) { + return 1; + } + return 0; +} + Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), bar_(bar), @@ -38,10 +56,25 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value box_.get_style_context()->add_class(id); } event_box_.add(box_); + if (config_["format-window-separator"].isString()) { + m_formatWindowSeperator = config_["format-window-separator"].asString(); + } else { + m_formatWindowSeperator = " "; + } + const Json::Value &windowRewrite = config["window-rewrite"]; + + const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"]; + m_windowRewriteDefault = + windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; + + m_windowRewriteRules = waybar::util::RegexCollection( + windowRewrite, m_windowRewriteDefault, + [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); ipc_.subscribe(R"(["workspace"])"); + ipc_.subscribe(R"(["window"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd)); - ipc_.sendCmd(IPC_GET_WORKSPACES); + ipc_.sendCmd(IPC_GET_TREE); if (config["enable-bar-scroll"].asBool()) { auto &window = const_cast(bar_).window; window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); @@ -59,26 +92,31 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value void Workspaces::onEvent(const struct Ipc::ipc_response &res) { try { - ipc_.sendCmd(IPC_GET_WORKSPACES); + ipc_.sendCmd(IPC_GET_TREE); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } } void Workspaces::onCmd(const struct Ipc::ipc_response &res) { - if (res.type == IPC_GET_WORKSPACES) { + if (res.type == IPC_GET_TREE) { try { { std::lock_guard lock(mutex_); auto payload = parser_.parse(res.payload); workspaces_.clear(); - std::copy_if(payload.begin(), payload.end(), std::back_inserter(workspaces_), + std::vector outputs; + std::copy_if(payload["nodes"].begin(), payload["nodes"].end(), std::back_inserter(outputs), [&](const auto &workspace) { return !config_["all-outputs"].asBool() - ? workspace["output"].asString() == bar_.output->name + ? workspace["name"].asString() == bar_.output->name : true; }); + for (auto &output : outputs) { + std::copy(output["nodes"].begin(), output["nodes"].end(), + std::back_inserter(workspaces_)); + } if (config_["persistent_workspaces"].isObject()) { spdlog::warn( "persistent_workspaces is deprecated. Please change config to use " @@ -203,6 +241,35 @@ bool Workspaces::filterButtons() { return needReorder; } +bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { + if (node[flag].asBool()) { + return true; + } + + if (std::ranges::any_of(node["nodes"], [&](auto const &e) { return hasFlag(e, flag); })) { + return true; + } + return false; +} + +void Workspaces::updateWindows(const Json::Value &node, std::string &windows) { + auto format = config_["window-format"].asString(); + if (node["type"].asString() == "con" && node["name"].isString()) { + std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1); + std::string windowClass = node["app_id"].asString(); + std::string windowReprKey = fmt::format("class<{}> title<{}>", windowClass, title); + std::string window = m_windowRewriteRules.get(windowReprKey); + // allow result to have formatting + window = + fmt::format(fmt::runtime(window), fmt::arg("name", title), fmt::arg("class", windowClass)); + windows.append(window); + windows.append(m_formatWindowSeperator); + } + for (const Json::Value &child : node["nodes"]) { + updateWindows(child, windows); + } +} + auto Workspaces::update() -> void { std::lock_guard lock(mutex_); bool needReorder = filterButtons(); @@ -212,22 +279,25 @@ auto Workspaces::update() -> void { needReorder = true; } auto &button = bit == buttons_.end() ? addButton(*it) : bit->second; - if ((*it)["focused"].asBool()) { + if (needReorder) { + box_.reorder_child(button, it - workspaces_.begin()); + } + if (hasFlag((*it), "focused")) { button.get_style_context()->add_class("focused"); } else { button.get_style_context()->remove_class("focused"); } - if ((*it)["visible"].asBool()) { + if (hasFlag((*it), "visible")) { button.get_style_context()->add_class("visible"); } else { button.get_style_context()->remove_class("visible"); } - if ((*it)["urgent"].asBool()) { + if (hasFlag((*it), "urgent")) { button.get_style_context()->add_class("urgent"); } else { button.get_style_context()->remove_class("urgent"); } - if ((*it)["target_output"].isString()) { + if (hasFlag((*it), "target_output")) { button.get_style_context()->add_class("persistent"); } else { button.get_style_context()->remove_class("persistent"); @@ -241,16 +311,19 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("current_output"); } - if (needReorder) { - box_.reorder_child(button, it - workspaces_.begin()); - } std::string output = (*it)["name"].asString(); + std::string windows = ""; + if (config_["window-format"].isString()) { + updateWindows((*it), windows); + } if (config_["format"].isString()) { auto format = config_["format"].asString(); - output = fmt::format(fmt::runtime(format), fmt::arg("icon", getIcon(output, *it)), - fmt::arg("value", output), fmt::arg("name", trimWorkspaceName(output)), - fmt::arg("index", (*it)["num"].asString()), - fmt::arg("output", (*it)["output"].asString())); + output = fmt::format( + fmt::runtime(format), fmt::arg("icon", getIcon(output, *it)), fmt::arg("value", output), + fmt::arg("name", trimWorkspaceName(output)), fmt::arg("index", (*it)["num"].asString()), + fmt::arg("windows", + windows.substr(0, windows.length() - m_formatWindowSeperator.length())), + fmt::arg("output", (*it)["output"].asString())); } if (!config_["disable-markup"].asBool()) { static_cast(button.get_children()[0])->set_markup(output); From 718dd4afae5f9e04ee22873797b995e4c7e17a33 Mon Sep 17 00:00:00 2001 From: Josh Jones Date: Sun, 4 Feb 2024 15:41:39 +0100 Subject: [PATCH 028/407] add ordinal date toolbar format specifier to clock module --- include/modules/clock.hpp | 6 ++++++ src/modules/clock.cpp | 44 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index d6aabaa0c..8b597c4e6 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -8,6 +8,7 @@ namespace waybar::modules { const std::string kCldPlaceholder{"calendar"}; const std::string kTZPlaceholder{"tz_list"}; +const std::string kOrdPlaceholder{"ordinal_date"}; enum class CldMode { MONTH, YEAR }; enum class WS { LEFT, RIGHT, HIDDEN }; @@ -57,6 +58,11 @@ class Clock final : public ALabel { std::string tzText_{""}; // time zones text to print util::SleeperThread thread_; + // ordinal date in tooltip + const bool ordInTooltip_; + std::string ordText_{""}; + auto get_ordinal_date(const year_month_day& today) -> std::string; + auto getTZtext(sys_seconds now) -> std::string; auto first_day_of_week() -> weekday; // Module actions diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 495dfab37..6b1975ba5 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -2,8 +2,10 @@ #include +#include #include #include +#include #include "util/ustring_clen.hpp" @@ -20,7 +22,8 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""}, cldInTooltip_{tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, tzInTooltip_{tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, - tzCurrIdx_{0} { + tzCurrIdx_{0}, + ordInTooltip_{tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { tlpText_ = tlpFmt_; if (config_["timezones"].isArray() && !config_["timezones"].empty()) { @@ -126,6 +129,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) }; } + auto waybar::modules::Clock::update() -> void { auto tz{tzList_[tzCurrIdx_] ?: current_zone()}; const zoned_time now{tz, floor(system_clock::now())}; @@ -140,11 +144,13 @@ auto waybar::modules::Clock::update() -> void { if (tzInTooltip_) tzText_ = getTZtext(now.get_sys_time()); if (cldInTooltip_) cldText_ = get_calendar(today, shiftedDay, tz); - if (tzInTooltip_ || cldInTooltip_) { + if (ordInTooltip_) ordText_ = get_ordinal_date(shiftedDay); + if (tzInTooltip_ || cldInTooltip_ || ordInTooltip_) { // std::vformat doesn't support named arguments. tlpText_ = std::regex_replace(tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_); tlpText_ = std::regex_replace(tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); + tlpText_ = std::regex_replace(tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); } tlpText_ = fmt_lib::vformat(locale_, tlpText_, fmt_lib::make_format_args(shiftedNow)); @@ -437,3 +443,37 @@ auto waybar::modules::Clock::first_day_of_week() -> weekday { #endif return Sunday; } + +auto waybar::modules::Clock::get_ordinal_date(const year_month_day& today) -> std::string { + auto day = static_cast(today.day()); + switch (day) { + case 11: + return "11th"; + case 12: + return "12th"; + case 13: + return "13th"; + } + + std::stringstream res; + res << day; + if (day >= 11 && day <= 13) { + res << "th"; + return res.str(); + } + + switch (day % 10) { + case 1: + res << "st"; + break; + case 2: + res << "nd"; + break; + case 3: + res << "rd"; + break; + default: + res << "th"; + } + return res.str(); +} \ No newline at end of file From e02cb9cfb91fc607862967cac0d982f51cf2afb4 Mon Sep 17 00:00:00 2001 From: Josh Jones Date: Sun, 4 Feb 2024 15:49:14 +0100 Subject: [PATCH 029/407] add ordinal format specifier to man clock --- man/waybar-clock.5.scd | 1 + 1 file changed, 1 insertion(+) diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index fc0793387..e8ef7bed9 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -157,6 +157,7 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe - *{calendar}*: Current month calendar - *{tz_list}*: List of time in the rest timezones, if more than one timezone is set in the config +- *{ordinal_date}*: The current day in (English) ordinal form, e.g. 21st # EXAMPLES From 1fa1045af97879093d19c96248ea3bd14cb4865c Mon Sep 17 00:00:00 2001 From: Josh Jones Date: Sun, 4 Feb 2024 16:11:39 +0100 Subject: [PATCH 030/407] remove duplicated segment --- src/modules/clock.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 6b1975ba5..e83cbef03 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -446,15 +446,6 @@ auto waybar::modules::Clock::first_day_of_week() -> weekday { auto waybar::modules::Clock::get_ordinal_date(const year_month_day& today) -> std::string { auto day = static_cast(today.day()); - switch (day) { - case 11: - return "11th"; - case 12: - return "12th"; - case 13: - return "13th"; - } - std::stringstream res; res << day; if (day >= 11 && day <= 13) { From 08b32cb901cb992e1a79faaa5e8140fc81bfedc6 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 4 Feb 2024 16:17:06 +0100 Subject: [PATCH 031/407] Removing unnecessary parts of transition_type handling --- src/group.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/group.cpp b/src/group.cpp index f90610432..9deb4f3cc 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -9,19 +9,18 @@ namespace waybar { -const Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical, bool left_to_right) { +const Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) { + /* The transition direction of a drawer is not actually determined by the transition type, + * but rather by the order of 'box' and 'revealer_box': + * 'REVEALER_TRANSITION_TYPE_SLIDE_LEFT' and 'REVEALER_TRANSITION_TYPE_SLIDE_RIGHT' + * will result in the same thing. + * However: we still need to differentiate between vertical and horizontal transition types. + */ + if (is_vertical) { - if (left_to_right) { - return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_DOWN; - } else { - return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_UP; - } + return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_UP; } else { - if (left_to_right) { - return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_RIGHT; - } else { - return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_LEFT; - } + return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_LEFT; } } @@ -64,7 +63,7 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& ? drawer_config["transition-left-to-right"].asBool() : true); - auto transition_type = getPreferredTransitionType(vertical, left_to_right); + auto transition_type = getPreferredTransitionType(vertical); revealer.set_transition_type(transition_type); revealer.set_transition_duration(transition_duration); From 89b3203bfa490ac166382a54ccc126007b87ae8a Mon Sep 17 00:00:00 2001 From: Merlin Sievers Date: Mon, 5 Feb 2024 14:44:59 +0100 Subject: [PATCH 032/407] Add justify config option for Labels This is especially useful for centering labels on vertical bars. --- src/ALabel.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index c87e3228d..4163385f9 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -49,6 +49,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st label_.set_xalign(align); } } + + if (config_["justify"].isString()) { + auto justify_str = config_["justify"].asString(); + if (justify_str == "left") { + label_.set_justify(Gtk::Justification::JUSTIFY_LEFT); + } else if (justify_str == "right") { + label_.set_justify(Gtk::Justification::JUSTIFY_RIGHT); + } else if (justify_str == "center") { + label_.set_justify(Gtk::Justification::JUSTIFY_CENTER); + } + } } auto ALabel::update() -> void { AModule::update(); } From 92875711c6ad517dfad60437fa824b388fe3189b Mon Sep 17 00:00:00 2001 From: Imran Haider Date: Mon, 5 Feb 2024 21:31:02 -0500 Subject: [PATCH 033/407] Search for the first hwmon* directory Background and Motivation ------------------------- When the `hwmon-path-abs` and the `input-filename` fields are used for the temperature module, we evaluated the following path: ``` [hwmon-path-abs] / [gap] / [input-filename] ``` where `gap` is the first file or directory in the `hwmon-path-abs` directory. This usually works but it doesn't seem to work for NVME or WiFi temperature sensors. For those cases, there are a bunch of other files in the `hwmon-path-abs` directory. In the bad case, the first selected file is not the one with the prefix `hwmon` and we end up checking the wrong location for the `input-filename`. Change description ------------------ We are simply going through the `hwmon-path-abs` directory and searching for the first file/directory that begins with `hwmon`. Test case --------- I tested this on a AMD based Framework 13 laptop. --- src/modules/temperature.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 5ef2f4c98..054c9bd26 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -24,11 +24,15 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val } } } else if (config_["hwmon-path-abs"].isString() && config_["input-filename"].isString()) { - file_path_ = (*std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())) - .path() - .string() + - "/" + config_["input-filename"].asString(); - } else { + for (const auto& hwmon : std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())) { + if (hwmon.path().filename().string().starts_with("hwmon")) { + file_path_ = hwmon.path().string() + "/" + config_["input-filename"].asString(); + break; + } + } + } + + if (file_path_.empty()) { auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); } From 3bfcd5e0868dd3b54d0044e7923a037304675ccf Mon Sep 17 00:00:00 2001 From: Jannik Date: Wed, 7 Feb 2024 16:33:19 +0100 Subject: [PATCH 034/407] Add 'active' css class to special workspaces --- include/modules/hyprland/workspaces.hpp | 2 ++ src/modules/hyprland/workspaces.cpp | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index d2006fcc4..9f49dd763 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -144,6 +144,7 @@ class Workspaces : public AModule, public EventHandler { // workspace events void onWorkspaceActivated(std::string const& payload); + void onSpecialWorkspaceActivated(std::string const& payload); void onWorkspaceDestroyed(std::string const& payload); void onWorkspaceCreated(std::string const& workspaceName, Json::Value const& clientsData = Json::Value::nullRef); @@ -199,6 +200,7 @@ class Workspaces : public AModule, public EventHandler { bool m_withIcon; uint64_t m_monitorId; std::string m_activeWorkspaceName; + std::string m_activeSpecialWorkspaceName; std::vector> m_workspaces; std::vector> m_workspacesToCreate; std::vector m_workspacesToRemove; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index b05ce1341..a12366e45 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -136,6 +136,7 @@ void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_paylod auto Workspaces::registerIpc() -> void { gIPC->registerForIPC("workspace", this); + gIPC->registerForIPC("activespecial", this); gIPC->registerForIPC("createworkspace", this); gIPC->registerForIPC("destroyworkspace", this); gIPC->registerForIPC("focusedmon", this); @@ -187,7 +188,8 @@ void Workspaces::doUpdate() { for (auto &workspace : m_workspaces) { // active - workspace->setActive(workspace->name() == m_activeWorkspaceName); + workspace->setActive( workspace->name() == m_activeWorkspaceName || + workspace->name() == m_activeSpecialWorkspaceName ); // disable urgency if workspace is active if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { workspace->setUrgent(false); @@ -266,6 +268,8 @@ void Workspaces::onEvent(const std::string &ev) { if (eventName == "workspace") { onWorkspaceActivated(payload); + } else if (eventName == "activespecial") { + onSpecialWorkspaceActivated(payload); } else if (eventName == "destroyworkspace") { onWorkspaceDestroyed(payload); } else if (eventName == "createworkspace") { @@ -295,6 +299,13 @@ void Workspaces::onWorkspaceActivated(std::string const &payload) { m_activeWorkspaceName = payload; } +void Workspaces::onSpecialWorkspaceActivated(std::string const &payload) { + std::string name(begin(payload), begin(payload) + payload.find_first_of(',')); + m_activeSpecialWorkspaceName = ( + ( name == "special" || name == "" ) ? name : name.substr(8, name.length() - 8) + ); +} + void Workspaces::onWorkspaceDestroyed(std::string const &payload) { if (!isDoubleSpecial(payload)) { m_workspacesToRemove.push_back(payload); From c30541b954bf3739ab694ec24d59bba61675de3e Mon Sep 17 00:00:00 2001 From: Jannik Date: Wed, 7 Feb 2024 16:56:37 +0100 Subject: [PATCH 035/407] remove whitespaces --- src/modules/hyprland/workspaces.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index a12366e45..44c840fa8 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -188,8 +188,8 @@ void Workspaces::doUpdate() { for (auto &workspace : m_workspaces) { // active - workspace->setActive( workspace->name() == m_activeWorkspaceName || - workspace->name() == m_activeSpecialWorkspaceName ); + workspace->setActive(workspace->name() == m_activeWorkspaceName || + workspace->name() == m_activeSpecialWorkspaceName); // disable urgency if workspace is active if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { workspace->setUrgent(false); From 61be2267abdf6f7014319d75376055350cd3dbd7 Mon Sep 17 00:00:00 2001 From: Jannik Date: Wed, 7 Feb 2024 21:10:17 +0100 Subject: [PATCH 036/407] add 'visible' css class to special workspaces --- src/modules/hyprland/workspaces.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 44c840fa8..531ac3044 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -184,6 +184,12 @@ void Workspaces::doUpdate() { if (ws.isObject() && (ws["name"].isString())) { visibleWorkspaces.push_back(ws["name"].asString()); } + auto sws = monitor["specialWorkspace"]; + auto name = sws["name"].asString(); + if (sws.isObject() && (sws["name"].isString()) && !name.empty()) { + visibleWorkspaces.push_back(name == "special" ? "special" + : name.substr(8, name.length() - 8)); + } } for (auto &workspace : m_workspaces) { From 692f8f4ea4a5aa18669b2e6814b2d7d8de65a8da Mon Sep 17 00:00:00 2001 From: Jannik Date: Wed, 7 Feb 2024 22:42:05 +0100 Subject: [PATCH 037/407] add/remove 'active' on 'focusedmon' IPC event --- src/modules/hyprland/workspaces.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 531ac3044..e4c02de10 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -365,6 +365,13 @@ void Workspaces::onWorkspaceRenamed(std::string const &payload) { void Workspaces::onMonitorFocused(std::string const &payload) { m_activeWorkspaceName = payload.substr(payload.find(',') + 1); + + for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { + if (monitor["name"].asString() == payload.substr(0, payload.find(','))) { + auto name = monitor["specialWorkspace"]["name"].asString(); + m_activeSpecialWorkspaceName = !name.starts_with("special:") ? name : name.substr(8); + } + } } void Workspaces::onWindowOpened(std::string const &payload) { From 90c2415b6410908fef5f4655d702b29acaeb3329 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Thu, 8 Feb 2024 15:04:21 +0000 Subject: [PATCH 038/407] Battery estimate is no longer accessible in the "Full" state on default config --- resources/config | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/config b/resources/config index daad8ab18..adf03a1f3 100644 --- a/resources/config +++ b/resources/config @@ -113,6 +113,7 @@ "critical": 15 }, "format": "{capacity}% {icon}", + "format-full": "{capacity}% {icon}", "format-charging": "{capacity}% ", "format-plugged": "{capacity}% ", "format-alt": "{time} {icon}", From d4331ce7fe4268acdb2d74a6f6d39ce98059a1f7 Mon Sep 17 00:00:00 2001 From: Jannik Date: Fri, 9 Feb 2024 13:49:39 +0100 Subject: [PATCH 039/407] improve handling of special workspace name --- src/modules/hyprland/workspaces.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index e4c02de10..78d59cf7f 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -187,8 +187,7 @@ void Workspaces::doUpdate() { auto sws = monitor["specialWorkspace"]; auto name = sws["name"].asString(); if (sws.isObject() && (sws["name"].isString()) && !name.empty()) { - visibleWorkspaces.push_back(name == "special" ? "special" - : name.substr(8, name.length() - 8)); + visibleWorkspaces.push_back(name.starts_with("special:") ? name : name.substr(8)); } } @@ -307,9 +306,7 @@ void Workspaces::onWorkspaceActivated(std::string const &payload) { void Workspaces::onSpecialWorkspaceActivated(std::string const &payload) { std::string name(begin(payload), begin(payload) + payload.find_first_of(',')); - m_activeSpecialWorkspaceName = ( - ( name == "special" || name == "" ) ? name : name.substr(8, name.length() - 8) - ); + m_activeSpecialWorkspaceName = (!name.starts_with("special:") ? name : name.substr(8)); } void Workspaces::onWorkspaceDestroyed(std::string const &payload) { From 240b49f9d211486e68bea648885f776a8f712ab5 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sat, 10 Feb 2024 16:59:53 +0100 Subject: [PATCH 040/407] Add 'empty' css class to special workspaces --- src/modules/hyprland/workspaces.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 78d59cf7f..d824b941a 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -486,7 +486,10 @@ void Workspaces::updateWindowCount() { for (auto &workspace : m_workspaces) { auto workspaceJson = std::find_if( workspacesJson.begin(), workspacesJson.end(), - [&](Json::Value const &x) { return x["name"].asString() == workspace->name(); }); + [&](Json::Value const &x) { + return x["name"].asString() == workspace->name() || + (workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name()); + }); uint32_t count = 0; if (workspaceJson != workspacesJson.end()) { try { From a2925fa5da2b4af829510e0e305394ac9a6e455e Mon Sep 17 00:00:00 2001 From: Jannik Date: Sat, 10 Feb 2024 17:26:44 +0100 Subject: [PATCH 041/407] fix 'visible' class for special workspaces --- src/modules/hyprland/workspaces.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index d824b941a..0f0afb108 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -187,7 +187,7 @@ void Workspaces::doUpdate() { auto sws = monitor["specialWorkspace"]; auto name = sws["name"].asString(); if (sws.isObject() && (sws["name"].isString()) && !name.empty()) { - visibleWorkspaces.push_back(name.starts_with("special:") ? name : name.substr(8)); + visibleWorkspaces.push_back(!name.starts_with("special:") ? name : name.substr(8)); } } From acf661109851cacb6f6807c419593899f2eeaef0 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sat, 10 Feb 2024 17:35:46 +0100 Subject: [PATCH 042/407] clang-format --- src/modules/hyprland/workspaces.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 0f0afb108..8d4c416f7 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -484,9 +484,8 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { void Workspaces::updateWindowCount() { const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { - auto workspaceJson = std::find_if( - workspacesJson.begin(), workspacesJson.end(), - [&](Json::Value const &x) { + auto workspaceJson = + std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) { return x["name"].asString() == workspace->name() || (workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name()); }); From dd8d6fbe6c43ac7bdb98dd6f3b80b121631c273d Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Mon, 12 Feb 2024 20:09:22 +0100 Subject: [PATCH 043/407] Fix build warnings --- include/modules/systemd_failed_units.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/modules/systemd_failed_units.hpp b/include/modules/systemd_failed_units.hpp index d305264df..9c3fbcee1 100644 --- a/include/modules/systemd_failed_units.hpp +++ b/include/modules/systemd_failed_units.hpp @@ -19,8 +19,8 @@ class SystemdFailedUnits : public ALabel { std::string format_ok; bool update_pending; - std::string last_status; uint32_t nr_failed_system, nr_failed_user; + std::string last_status; Glib::RefPtr system_proxy, user_proxy; void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name, From a0bac34329c88ba84891147d438e596450bbbe01 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Tue, 13 Feb 2024 10:49:57 +0100 Subject: [PATCH 044/407] Add style class for CPU state Fixes: https://github.com/Alexays/Waybar/issues/2911 --- include/modules/cpu.hpp | 1 + man/waybar-cpu.5.scd | 2 ++ src/modules/cpu.cpp | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 7f78c1650..449eb1b30 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -22,6 +22,7 @@ class Cpu : public ALabel { private: std::vector> prev_times_; + std::string prev_state_; util::SleeperThread thread_; }; diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 484795689..64b2bde1d 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -121,3 +121,5 @@ CPU usage per core rendered as icons: # STYLE - *#cpu* +- *#cpu.* + - ** can be defined in the *config*. For more information see *states*. diff --git a/src/modules/cpu.cpp b/src/modules/cpu.cpp index 0703eaf7c..4fdb6590e 100644 --- a/src/modules/cpu.cpp +++ b/src/modules/cpu.cpp @@ -36,6 +36,12 @@ auto waybar::modules::Cpu::update() -> void { format = config_["format-" + state].asString(); } + if (!prev_state_.empty()) { + label_.get_style_context()->remove_class(prev_state_); + } + label_.get_style_context()->add_class(state); + prev_state_ = state; + if (format.empty()) { event_box_.hide(); } else { From 77c7b91b40e2fbc1f31b4ca479c5aba9ed4f4f54 Mon Sep 17 00:00:00 2001 From: alttabber Date: Tue, 13 Feb 2024 11:42:09 +0100 Subject: [PATCH 045/407] Add style classes for hyprland/submap --- man/waybar-hyprland-submap.5.scd | 1 + src/modules/hyprland/submap.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index 3f8d62805..0dc0b11af 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -80,3 +80,4 @@ Addressed by *hyprland/submap* # STYLE - *#submap* +- *#submap.* diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index d1d9a116d..ce27fc9a0 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -54,8 +54,16 @@ void Submap::onEvent(const std::string& ev) { auto submapName = ev.substr(ev.find_last_of('>') + 1); submapName = waybar::util::sanitize_string(submapName); + if (!submap_.empty()){ + label_.get_style_context()->remove_class(submap_); + } + submap_ = submapName; + label_.get_style_context()->add_class(submap_); + + + spdlog::debug("hyprland submap onevent with {}", submap_); dp.emit(); From 9ea470410f0e3d351ee0e5410e68ff21d64b6f53 Mon Sep 17 00:00:00 2001 From: alttabber Date: Tue, 13 Feb 2024 12:53:45 +0100 Subject: [PATCH 046/407] Add always on option for hyprland/submap --- include/modules/hyprland/submap.hpp | 3 +++ man/waybar-hyprland-submap.5.scd | 10 ++++++++++ src/modules/hyprland/submap.cpp | 29 ++++++++++++++++++++++++++--- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/include/modules/hyprland/submap.hpp b/include/modules/hyprland/submap.hpp index 4ff232fff..b70d236a2 100644 --- a/include/modules/hyprland/submap.hpp +++ b/include/modules/hyprland/submap.hpp @@ -19,12 +19,15 @@ class Submap : public waybar::ALabel, public EventHandler { auto update() -> void override; private: + auto parseConfig(const Json::Value&) -> void; void onEvent(const std::string&) override; std::mutex mutex_; const Bar& bar_; util::JsonParser parser_; std::string submap_; + bool always_on_ = false; + std::string default_submap_ = "Default"; }; } // namespace waybar::modules::hyprland diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index 0dc0b11af..fdcb33a89 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -66,6 +66,16 @@ Addressed by *hyprland/submap* default: true ++ Option to disable tooltip on hover. +*always-on*: ++ + typeof: bool ++ + default: false ++ + Option to display the widget even when there's no active submap. + +*default-submap* ++ + typeof: string ++ + default: Default ++ + Option to set the submap name to display when not in an active submap. + # EXAMPLES diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index ce27fc9a0..b9ae9f211 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -10,6 +10,8 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "submap", id, "{}", 0, true), bar_(bar) { modulesReady = true; + parseConfig(config); + if (!gIPC.get()) { gIPC = std::make_unique(); } @@ -17,6 +19,13 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) label_.hide(); ALabel::update(); + // Displays widget immediately if always_on_ assuming default submap + // Needs an actual way to retrive current submap on startup + if (always_on_) { + submap_ = default_submap_; + label_.get_style_context()->add_class(submap_); + } + // register for hyprland ipc gIPC->registerForIPC("submap", this); dp.emit(); @@ -28,6 +37,18 @@ Submap::~Submap() { std::lock_guard lg(mutex_); } +auto Submap::parseConfig(const Json::Value& config) -> void { + auto const alwaysOn = config["always-on"]; + if (alwaysOn.isBool()) { + always_on_ = alwaysOn.asBool(); + } + + auto const defaultSubmap = config["default-submap"]; + if (defaultSubmap.isString()) { + default_submap_ = defaultSubmap.asString(); + } +} + auto Submap::update() -> void { std::lock_guard lg(mutex_); @@ -54,15 +75,17 @@ void Submap::onEvent(const std::string& ev) { auto submapName = ev.substr(ev.find_last_of('>') + 1); submapName = waybar::util::sanitize_string(submapName); - if (!submap_.empty()){ + if (!submap_.empty()) { label_.get_style_context()->remove_class(submap_); } submap_ = submapName; - label_.get_style_context()->add_class(submap_); - + if (submap_.empty() && always_on_) { + submap_ = default_submap_; + } + label_.get_style_context()->add_class(submap_); spdlog::debug("hyprland submap onevent with {}", submap_); From 2f555a693617868669c4370b192fa12652bc3776 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 14 Feb 2024 19:14:39 -0800 Subject: [PATCH 047/407] refactor(bar): use Gtk enums for position and orientation Ensure that the position and the corresponding CSS class on window are always set. --- include/bar.hpp | 5 +- src/bar.cpp | 159 +++++++++++++++++--------- src/modules/dwl/tags.cpp | 2 +- src/modules/hyprland/workspaces.cpp | 4 +- src/modules/keyboard_state.cpp | 2 +- src/modules/river/tags.cpp | 2 +- src/modules/sni/tray.cpp | 2 +- src/modules/sway/workspaces.cpp | 2 +- src/modules/wlr/taskbar.cpp | 4 +- src/modules/wlr/workspace_manager.cpp | 4 +- 10 files changed, 114 insertions(+), 72 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index d2cbffa02..0cacc3d71 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -62,7 +62,7 @@ class BarSurface { virtual void setLayer(bar_layer layer) = 0; virtual void setMargins(const struct bar_margins &margins) = 0; virtual void setPassThrough(bool enable) = 0; - virtual void setPosition(const std::string_view &position) = 0; + virtual void setPosition(Gtk::PositionType position) = 0; virtual void setSize(uint32_t width, uint32_t height) = 0; virtual void commit(){}; @@ -89,8 +89,9 @@ class Bar { Json::Value config; struct wl_surface *surface; bool visible = true; - bool vertical = false; Gtk::Window window; + Gtk::Orientation orientation = Gtk::ORIENTATION_HORIZONTAL; + Gtk::PositionType position = Gtk::POS_TOP; int x_global; int y_global; diff --git a/src/bar.cpp b/src/bar.cpp index 1ffe2ef6d..0857724e2 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -93,6 +93,32 @@ void from_json(const Json::Value& j, bar_mode& m) { } } +/* Deserializer for enum Gtk::PositionType */ +void from_json(const Json::Value& j, Gtk::PositionType& pos) { + if (j == "left") { + pos = Gtk::POS_LEFT; + } else if (j == "right") { + pos = Gtk::POS_RIGHT; + } else if (j == "top") { + pos = Gtk::POS_TOP; + } else if (j == "bottom") { + pos = Gtk::POS_BOTTOM; + } +} + +Glib::ustring to_string(Gtk::PositionType pos) { + switch (pos) { + case Gtk::POS_LEFT: + return "left"; + case Gtk::POS_RIGHT: + return "right"; + case Gtk::POS_TOP: + return "top"; + case Gtk::POS_BOTTOM: + return "bottom"; + } +} + /* Deserializer for JSON Object -> map * Assumes that all the values in the object are deserializable to the same type. */ @@ -158,18 +184,26 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { } } - void setPosition(const std::string_view& position) override { + void setPosition(Gtk::PositionType position) override { auto unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM; - vertical_ = false; - if (position == "bottom") { - unanchored = GTK_LAYER_SHELL_EDGE_TOP; - } else if (position == "left") { - unanchored = GTK_LAYER_SHELL_EDGE_RIGHT; - vertical_ = true; - } else if (position == "right") { - vertical_ = true; - unanchored = GTK_LAYER_SHELL_EDGE_LEFT; - } + orientation_ = Gtk::ORIENTATION_HORIZONTAL; + switch (position) { + case Gtk::POS_LEFT: + unanchored = GTK_LAYER_SHELL_EDGE_RIGHT; + orientation_ = Gtk::ORIENTATION_VERTICAL; + break; + case Gtk::POS_RIGHT: + unanchored = GTK_LAYER_SHELL_EDGE_LEFT; + orientation_ = Gtk::ORIENTATION_VERTICAL; + break; + case Gtk::POS_TOP: + unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM; + break; + case Gtk::POS_BOTTOM: + unanchored = GTK_LAYER_SHELL_EDGE_TOP; + break; + }; + for (auto edge : {GTK_LAYER_SHELL_EDGE_LEFT, GTK_LAYER_SHELL_EDGE_RIGHT, GTK_LAYER_SHELL_EDGE_TOP, GTK_LAYER_SHELL_EDGE_BOTTOM}) { gtk_layer_set_anchor(window_.gobj(), edge, unanchored != edge); @@ -178,10 +212,10 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { // Disable anchoring for other edges too if the width // or the height has been set to a value other than 'auto' // otherwise the bar will use all space - if (vertical_ && height_ > 1) { + if (orientation_ == Gtk::ORIENTATION_VERTICAL && height_ > 1) { gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, false); gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_TOP, false); - } else if (!vertical_ && width_ > 1) { + } else if (orientation_ == Gtk::ORIENTATION_HORIZONTAL && width_ > 1) { gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_LEFT, false); gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, false); } @@ -195,11 +229,11 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { private: Gtk::Window& window_; + Gtk::Orientation orientation_ = Gtk::ORIENTATION_HORIZONTAL; std::string output_name_; uint32_t width_; uint32_t height_; bool passthrough_ = false; - bool vertical_ = false; void onMap(GdkEventAny* ev) { setPassThrough(passthrough_); } @@ -212,7 +246,7 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { * Note: forced resizing to a window smaller than required by GTK would not work with * gtk-layer-shell. */ - if (vertical_) { + if (orientation_ == Gtk::ORIENTATION_VERTICAL) { if (width_ > 1 && ev->width > static_cast(width_)) { spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); } @@ -304,15 +338,21 @@ struct RawSurfaceImpl : public BarSurface, public sigc::trackable { } } - void setPosition(const std::string_view& position) override { - anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; - if (position == "bottom") { - anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - } else if (position == "left") { - anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; - } else if (position == "right") { - anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - } + void setPosition(Gtk::PositionType position) override { + switch (position) { + case Gtk::POS_LEFT: + anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; + break; + case Gtk::POS_RIGHT: + anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + break; + case Gtk::POS_TOP: + anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; + break; + case Gtk::POS_BOTTOM: + anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + break; + }; // updating already mapped window if (layer_surface_) { @@ -493,17 +533,18 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) window.set_decorated(false); window.get_style_context()->add_class(output->name); window.get_style_context()->add_class(config["name"].asString()); - window.get_style_context()->add_class(config["position"].asString()); - auto position = config["position"].asString(); + from_json(config["position"], position); + orientation = (position == Gtk::POS_LEFT || position == Gtk::POS_RIGHT) + ? Gtk::ORIENTATION_VERTICAL + : Gtk::ORIENTATION_HORIZONTAL; - if (position == "right" || position == "left") { - left_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); - center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); - right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); - box_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0); - vertical = true; - } + window.get_style_context()->add_class(to_string(position)); + + left_ = Gtk::Box(orientation, 0); + center_ = Gtk::Box(orientation, 0); + right_ = Gtk::Box(orientation, 0); + box_ = Gtk::Box(orientation, 0); left_.get_style_context()->add_class("modules-left"); center_.get_style_context()->add_class("modules-center"); @@ -829,34 +870,38 @@ void waybar::Bar::onConfigure(GdkEventConfigure* ev) { void waybar::Bar::configureGlobalOffset(int width, int height) { auto monitor_geometry = *output->monitor->property_geometry().get_value().gobj(); - auto position = config["position"].asString(); int x; int y; - if (position == "bottom") { - if (width + margins_.left + margins_.right >= monitor_geometry.width) + switch (position) { + case Gtk::POS_BOTTOM: + if (width + margins_.left + margins_.right >= monitor_geometry.width) + x = margins_.left; + else + x = (monitor_geometry.width - width) / 2; + y = monitor_geometry.height - height - margins_.bottom; + break; + case Gtk::POS_LEFT: x = margins_.left; - else - x = (monitor_geometry.width - width) / 2; - y = monitor_geometry.height - height - margins_.bottom; - } else if (position == "left") { - x = margins_.left; - if (height + margins_.top + margins_.bottom >= monitor_geometry.height) - y = margins_.top; - else - y = (monitor_geometry.height - height) / 2; - } else if (position == "right") { - x = monitor_geometry.width - width - margins_.right; - if (height + margins_.top + margins_.bottom >= monitor_geometry.height) + if (height + margins_.top + margins_.bottom >= monitor_geometry.height) + y = margins_.top; + else + y = (monitor_geometry.height - height) / 2; + break; + case Gtk::POS_RIGHT: + x = monitor_geometry.width - width - margins_.right; + if (height + margins_.top + margins_.bottom >= monitor_geometry.height) + y = margins_.top; + else + y = (monitor_geometry.height - height) / 2; + break; + case Gtk::POS_TOP: + // position is top + if (width + margins_.left + margins_.right >= monitor_geometry.width) + x = margins_.left; + else + x = (monitor_geometry.width - width) / 2; y = margins_.top; - else - y = (monitor_geometry.height - height) / 2; - } else { - // position is top - if (width + margins_.left + margins_.right >= monitor_geometry.width) - x = margins_.left; - else - x = (monitor_geometry.width - width) / 2; - y = margins_.top; + break; } x_global = x + monitor_geometry.x; diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index 7faa5c52a..afe658e15 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -93,7 +93,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con status_manager_{nullptr}, seat_{nullptr}, bar_(bar), - box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, + box_{bar.orientation, 0}, output_status_{nullptr} { struct wl_display *display = Client::inst()->wl_display; struct wl_registry *registry = wl_display_get_registry(display); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index b05ce1341..4200f85ee 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -34,9 +34,7 @@ int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) { } Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) - : AModule(config, "workspaces", id, false, false), - m_bar(bar), - m_box(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { + : AModule(config, "workspaces", id, false, false), m_bar(bar), m_box(bar.orientation, 0) { modulesReady = true; parseConfig(config); diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 5e5d4acd0..edab0827b 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -81,7 +81,7 @@ auto supportsLockStates(const libevdev* dev) -> bool { 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()), - box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), + box_(bar.orientation, 0), numlock_label_(""), capslock_label_(""), numlock_format_(config_["format"].isString() ? config_["format"].asString() diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index baa6b7ece..cad8c762e 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -87,7 +87,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con control_{nullptr}, seat_{nullptr}, bar_(bar), - box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, + box_{bar.orientation, 0}, output_status_{nullptr} { struct wl_display *display = Client::inst()->wl_display; struct wl_registry *registry = wl_display_get_registry(display); diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index 09a7ff30c..8a6a5b829 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -6,7 +6,7 @@ namespace waybar::modules::SNI { Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) : AModule(config, "tray", id), - box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), + box_(bar.orientation, 0), watcher_(SNI::Watcher::getInstance()), host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1), std::bind(&Tray::onRemove, this, std::placeholders::_1)) { diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 1a47e4785..37cc74854 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -27,7 +27,7 @@ int Workspaces::convertWorkspaceNameToNum(std::string name) { Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), bar_(bar), - box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { + box_(bar.orientation, 0) { if (config["format-icons"]["high-priority-named"].isArray()) { for (auto &it : config["format-icons"]["high-priority-named"]) { high_priority_named_.push_back(it.asString()); diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 0eaf264af..57f72978f 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -277,7 +277,7 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, handle_{tl_handle}, seat_{seat}, id_{global_id++}, - content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} { + content_{bar.orientation, 0} { zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this); button.set_relief(Gtk::RELIEF_NONE); @@ -730,7 +730,7 @@ static const wl_registry_listener registry_listener_impl = {.global = handle_glo Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Value &config) : waybar::AModule(config, "taskbar", id, false, false), bar_(bar), - box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, + box_{bar.orientation, 0}, manager_{nullptr}, seat_{nullptr} { box_.set_name("taskbar"); diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index ce14b3b5e..17e5bc9cc 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -21,9 +21,7 @@ std::map Workspace::icons_map_; WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config) - : waybar::AModule(config, "workspaces", id, false, false), - bar_(bar), - box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { + : waybar::AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.orientation, 0) { auto config_sort_by_name = config_["sort-by-name"]; if (config_sort_by_name.isBool()) { sort_by_name_ = config_sort_by_name.asBool(); From d590d508ca684c5f6d6e0a6a0ac3565de2716737 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 14 Feb 2024 22:04:42 -0800 Subject: [PATCH 048/407] feat: add `module` class to the root elements of the modules Previously, the only way to select all the module labels was with the following kind of selector: ```css .modules-left > widget > label, .modules-center > widget > label, .modules-right > widget > label { /* ... */ } ``` (and a matching block for the `box` containers). Now, this can be expressed as ```css label.module, box.module { /* ... */ } ``` --- include/AModule.hpp | 2 ++ src/ALabel.cpp | 1 + src/ASlider.cpp | 1 + src/modules/custom.cpp | 12 +++++++----- src/modules/dwl/tags.cpp | 1 + src/modules/hyprland/workspaces.cpp | 1 + src/modules/image.cpp | 1 + src/modules/keyboard_state.cpp | 1 + src/modules/river/tags.cpp | 1 + src/modules/sni/tray.cpp | 1 + src/modules/sway/workspaces.cpp | 1 + src/modules/wlr/taskbar.cpp | 1 + src/modules/wlr/workspace_manager.cpp | 1 + 13 files changed, 20 insertions(+), 5 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index df0165cf0..c15efb006 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -11,6 +11,8 @@ namespace waybar { class AModule : public IModule { public: + static constexpr const char *MODULE_CLASS = "module"; + virtual ~AModule(); auto update() -> void override; virtual auto refresh(int) -> void{}; diff --git a/src/ALabel.cpp b/src/ALabel.cpp index c87e3228d..7840819d9 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -20,6 +20,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st if (!id.empty()) { label_.get_style_context()->add_class(id); } + label_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(label_); if (config_["max-length"].isUInt()) { label_.set_max_width_chars(config_["max-length"].asInt()); diff --git a/src/ASlider.cpp b/src/ASlider.cpp index a5e3889c4..b434be301 100644 --- a/src/ASlider.cpp +++ b/src/ASlider.cpp @@ -13,6 +13,7 @@ ASlider::ASlider(const Json::Value& config, const std::string& name, const std:: if (!id.empty()) { scale_.get_style_context()->add_class(id); } + scale_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(scale_); scale_.signal_value_changed().connect(sigc::mem_fun(*this, &ASlider::onValueChanged)); diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index d628b85d7..7f66213df 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -176,16 +176,18 @@ auto waybar::modules::Custom::update() -> void { } } } - auto classes = label_.get_style_context()->list_classes(); + auto style = label_.get_style_context(); + auto classes = style->list_classes(); for (auto const& c : classes) { if (c == id_) continue; - label_.get_style_context()->remove_class(c); + style->remove_class(c); } for (auto const& c : class_) { - label_.get_style_context()->add_class(c); + style->add_class(c); } - label_.get_style_context()->add_class("flat"); - label_.get_style_context()->add_class("text-button"); + style->add_class("flat"); + style->add_class("text-button"); + style->add_class(MODULE_CLASS); event_box_.show(); } } diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index afe658e15..f36ece1d0 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -113,6 +113,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con if (!id.empty()) { box_.get_style_context()->add_class(id); } + box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); // Default to 9 tags, cap at 32 diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 4200f85ee..88b5e135d 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -42,6 +42,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value if (!id.empty()) { m_box.get_style_context()->add_class(id); } + m_box.get_style_context()->add_class(MODULE_CLASS); event_box_.add(m_box); if (!gIPC) { diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 3c90b5575..8274d323e 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -7,6 +7,7 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config) if (!id.empty()) { box_.get_style_context()->add_class(id); } + box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); dp.emit(); diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index edab0827b..18ce0a7c4 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -132,6 +132,7 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& if (!id.empty()) { box_.get_style_context()->add_class(id); } + box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); if (config_["device-path"].isString()) { diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index cad8c762e..9e7cd5aa4 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -111,6 +111,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con if (!id.empty()) { box_.get_style_context()->add_class(id); } + box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); // Default to 9 tags, cap at 32 diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index 8a6a5b829..a2c56808b 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -15,6 +15,7 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) if (!id.empty()) { box_.get_style_context()->add_class(id); } + box_.get_style_context()->add_class(MODULE_CLASS); if (config_["spacing"].isUInt()) { box_.set_spacing(config_["spacing"].asUInt()); } diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 37cc74854..c8ec4387a 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -37,6 +37,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value if (!id.empty()) { box_.get_style_context()->add_class(id); } + box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); ipc_.subscribe(R"(["workspace"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 57f72978f..2709584b3 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -737,6 +737,7 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu if (!id.empty()) { box_.get_style_context()->add_class(id); } + box_.get_style_context()->add_class(MODULE_CLASS); box_.get_style_context()->add_class("empty"); event_box_.add(box_); diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 17e5bc9cc..f556a161e 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -52,6 +52,7 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar if (!id.empty()) { box_.get_style_context()->add_class(id); } + box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); add_registry_listener(this); From 9c3881f6f8311099bda8ae99486934caad186308 Mon Sep 17 00:00:00 2001 From: Josh Jones Date: Fri, 16 Feb 2024 01:24:22 +0100 Subject: [PATCH 049/407] add check for tooltip-format for custom modules --- src/modules/custom.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index d628b85d7..6c493a58c 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -170,6 +170,12 @@ auto waybar::modules::Custom::update() -> void { if (label_.get_tooltip_markup() != str) { label_.set_tooltip_markup(str); } + } else if (config_["tooltip-format"].isString()) { + auto tooltip = config_["tooltip-format"].asString(); + tooltip = fmt::format(fmt::runtime(tooltip), text_, fmt::arg("alt", alt_), + fmt::arg("icon", getIcon(percentage_, alt_)), + fmt::arg("percentage", percentage_)); + label_.set_tooltip_markup(tooltip); } else { if (label_.get_tooltip_markup() != tooltip_) { label_.set_tooltip_markup(tooltip_); From 7f3e3963831b0481718e29bb21907fac6e1d9998 Mon Sep 17 00:00:00 2001 From: Josh Jones Date: Fri, 16 Feb 2024 15:26:36 +0100 Subject: [PATCH 050/407] add tooltip-format to custom module man page --- man/waybar-custom.5.scd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 67e4c89c1..4c2190311 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -107,6 +107,11 @@ Addressed by *custom/* default: true ++ Option to disable tooltip on hover. +*tooltip-format*: ++ + typeof: string ++ + The tooltip format. If specified, overrides any tooltip output from the script in *exec*. ++ + Uses the same format replacements as *format*. + *escape*: ++ typeof: bool ++ default: false ++ From d7d4dca6ba9390ca4f1f1c02dfec348819841894 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 17 Feb 2024 18:20:03 +0300 Subject: [PATCH 051/407] libcava bump 0.10.1 Signed-off-by: Viktar Lukashonak --- meson.build | 2 +- src/group.cpp | 3 +-- src/modules/cava.cpp | 4 ++-- src/modules/hyprland/submap.cpp | 4 +--- subprojects/cava.wrap | 8 ++++---- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/meson.build b/meson.build index d75497316..b6a402efe 100644 --- a/meson.build +++ b/meson.build @@ -390,7 +390,7 @@ if get_option('experimental') endif cava = dependency('cava', - version : '>=0.9.1', + version : '>=0.10.1', required: get_option('cava'), fallback : ['cava', 'cava_dep'], not_found_message: 'cava is not found. Building waybar without cava') diff --git a/src/group.cpp b/src/group.cpp index 9deb4f3cc..262cae656 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -75,8 +75,7 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& if (left_to_right) { box.pack_end(revealer); - } - else { + } else { box.pack_start(revealer); } diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index c0ce0076a..072275462 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -53,8 +53,8 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) if (config_["method"].isString()) prm_.input = cava::input_method_by_name(config_["method"].asString().c_str()); if (config_["source"].isString()) prm_.audio_source = config_["source"].asString().data(); - if (config_["sample_rate"].isNumeric()) prm_.fifoSample = config_["sample_rate"].asLargestInt(); - if (config_["sample_bits"].isInt()) prm_.fifoSampleBits = config_["sample_bits"].asInt(); + if (config_["sample_rate"].isNumeric()) prm_.samplerate = config_["sample_rate"].asLargestInt(); + if (config_["sample_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt(); if (config_["stereo"].isBool()) prm_.stereo = config_["stereo"].asBool(); if (config_["reverse"].isBool()) prm_.reverse = config_["reverse"].asBool(); if (config_["bar_delimiter"].isInt()) prm_.bar_delim = config_["bar_delimiter"].asInt(); diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index ce27fc9a0..9f2a98297 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -54,7 +54,7 @@ void Submap::onEvent(const std::string& ev) { auto submapName = ev.substr(ev.find_last_of('>') + 1); submapName = waybar::util::sanitize_string(submapName); - if (!submap_.empty()){ + if (!submap_.empty()) { label_.get_style_context()->remove_class(submap_); } @@ -62,8 +62,6 @@ void Submap::onEvent(const std::string& ev) { label_.get_style_context()->add_class(submap_); - - spdlog::debug("hyprland submap onevent with {}", submap_); dp.emit(); diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap index 73fc95126..19383d119 100644 --- a/subprojects/cava.wrap +++ b/subprojects/cava.wrap @@ -1,7 +1,7 @@ [wrap-file] -directory = cava-0.9.1 -source_url = https://github.com/LukashonakV/cava/archive/0.9.1.tar.gz -source_filename = cava-0.9.1.tar.gz -source_hash = 4df540b7f4892f72e48772929a15bc9ad61e2bce7a084be2df01c72ca5c02333 +directory = cava-0.10.1 +source_url = https://github.com/LukashonakV/cava/archive/0.10.1.tar.gz +source_filename = cava-0.10.1.tar.gz +source_hash = ae8c7339908d6febeac5ab8df4576c03c9fdbca6c8e8975daf9ce68b57038bb5 [provide] cava = cava_dep From 104accdc34ef961149696c688be714a00f7d2fd0 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 16 Feb 2024 15:29:32 -0800 Subject: [PATCH 052/407] build: drop std::filesystem checks The `` and `-lc++experimental` aren't needed since LLVM 9.0. And since we now require C++20, checking for the `` support shouldn't be necessary either. --- include/factory.hpp | 2 +- include/modules/battery.hpp | 11 ++--------- meson.build | 11 ----------- src/factory.cpp | 2 +- 4 files changed, 4 insertions(+), 22 deletions(-) diff --git a/include/factory.hpp b/include/factory.hpp index 9ce680d72..2eba6c841 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -33,7 +33,7 @@ #include "modules/hyprland/window.hpp" #include "modules/hyprland/workspaces.hpp" #endif -#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM)) +#if defined(__FreeBSD__) || defined(__linux__) #include "modules/battery.hpp" #endif #if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index bbdd0eedc..7955e598f 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -1,11 +1,8 @@ #pragma once -#ifdef FILESYSTEM_EXPERIMENTAL -#include -#else -#include -#endif #include + +#include #if defined(__linux__) #include #endif @@ -21,11 +18,7 @@ namespace waybar::modules { -#ifdef FILESYSTEM_EXPERIMENTAL -namespace fs = std::experimental::filesystem; -#else namespace fs = std::filesystem; -#endif class Battery : public ALabel { public: diff --git a/meson.build b/meson.build index d75497316..f2c74947d 100644 --- a/meson.build +++ b/meson.build @@ -22,8 +22,6 @@ endif if compiler.has_link_argument('-lc++fs') cpp_link_args += ['-lc++fs'] -elif compiler.has_link_argument('-lc++experimental') - cpp_link_args += ['-lc++experimental'] elif compiler.has_link_argument('-lstdc++fs') cpp_link_args += ['-lstdc++fs'] endif @@ -44,15 +42,6 @@ else endif endif -if not compiler.has_header('filesystem') - if compiler.has_header('experimental/filesystem') - add_project_arguments('-DFILESYSTEM_EXPERIMENTAL', language: 'cpp') - else - add_project_arguments('-DNO_FILESYSTEM', language: 'cpp') - warning('No filesystem header found, some modules may not work') - endif -endif - code = ''' #include #include diff --git a/src/factory.cpp b/src/factory.cpp index 2ad5b6fa6..c39f8ca00 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -16,7 +16,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, auto hash_pos = name.find('#'); auto ref = name.substr(0, hash_pos); auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : ""; -#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM)) +#if defined(__FreeBSD__) || defined(__linux__) if (ref == "battery") { return new waybar::modules::Battery(id, bar_, config_[name]); } From 72406fa3f220ae70985a8e35df8e53ee969091f5 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 16 Feb 2024 23:30:00 -0800 Subject: [PATCH 053/407] build: require gio-unix-2.0 unconditionally We already use it without checking (`` in wlr/taskbar), it's a transitive dependency of GTK and it's always available on Unix platforms. --- include/factory.hpp | 2 +- meson.build | 23 ++++++++--------------- src/factory.cpp | 2 +- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/include/factory.hpp b/include/factory.hpp index 2eba6c841..ddf545da3 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -80,8 +80,8 @@ #ifdef HAVE_LIBSNDIO #include "modules/sndio.hpp" #endif -#ifdef HAVE_GIO_UNIX #include "modules/bluetooth.hpp" +#ifdef HAVE_LOGIND_INHIBITOR #include "modules/inhibitor.hpp" #endif #ifdef HAVE_LIBJACK diff --git a/meson.build b/meson.build index f2c74947d..cb2b4a333 100644 --- a/meson.build +++ b/meson.build @@ -75,10 +75,7 @@ wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0']) dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk')) -giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or - get_option('logind').enabled() or - get_option('upower_glib').enabled() or - get_option('mpris').enabled())) +giounix = dependency('gio-unix-2.0') jsoncpp = dependency('jsoncpp', version : ['>=1.9.2'], fallback : ['jsoncpp', 'jsoncpp_dep']) sigcpp = dependency('sigc++-2.0') libinotify = dependency('libinotify', required: false) @@ -165,6 +162,7 @@ src_files = files( 'src/ALabel.cpp', 'src/AIconLabel.cpp', 'src/AAppIconLabel.cpp', + 'src/modules/bluetooth.cpp', 'src/modules/custom.cpp', 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', @@ -272,12 +270,13 @@ if libnl.found() and libnlgen.found() src_files += 'src/modules/network.cpp' endif -if (giounix.found() and not get_option('logind').disabled()) - add_project_arguments('-DHAVE_GAMEMODE', language: 'cpp') +if not get_option('logind').disabled() + add_project_arguments('-DHAVE_GAMEMODE', '-DHAVE_LOGIND_INHIBITOR', language: 'cpp') src_files += 'src/modules/gamemode.cpp' + src_files += 'src/modules/inhibitor.cpp' endif -if (upower_glib.found() and giounix.found() and not get_option('logind').disabled()) +if (upower_glib.found() and not get_option('logind').disabled()) add_project_arguments('-DHAVE_UPOWER', language: 'cpp') src_files += 'src/modules/upower/upower.cpp' src_files += 'src/modules/upower/upower_tooltip.cpp' @@ -291,7 +290,7 @@ if (pipewire.found()) src_files += 'src/util/pipewire_backend.cpp' endif -if (playerctl.found() and giounix.found() and not get_option('logind').disabled()) +if (playerctl.found() and not get_option('logind').disabled()) add_project_arguments('-DHAVE_MPRIS', language: 'cpp') src_files += 'src/modules/mpris/mpris.cpp' endif @@ -351,12 +350,6 @@ if libsndio.found() src_files += 'src/modules/sndio.cpp' endif -if (giounix.found() and not get_option('logind').disabled()) - add_project_arguments('-DHAVE_GIO_UNIX', language: 'cpp') - src_files += 'src/modules/inhibitor.cpp' - src_files += 'src/modules/bluetooth.cpp' -endif - if get_option('rfkill').enabled() and is_linux add_project_arguments('-DWANT_RFKILL', language: 'cpp') src_files += files( @@ -500,7 +493,7 @@ if scdoc.found() 'waybar-dwl-tags.5.scd', ] - if (giounix.found() and not get_option('logind').disabled()) + if not get_option('logind').disabled() man_files += 'waybar-inhibitor.5.scd' endif diff --git a/src/factory.cpp b/src/factory.cpp index c39f8ca00..2692bd859 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -178,10 +178,10 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, return new waybar::modules::Sndio(id, config_[name]); } #endif -#ifdef HAVE_GIO_UNIX if (ref == "bluetooth") { return new waybar::modules::Bluetooth(id, config_[name]); } +#ifdef HAVE_LOGIND_INHIBITOR if (ref == "inhibitor") { return new waybar::modules::Inhibitor(id, bar_, config_[name]); } From c2f37705ad809dffefa90b04b6d4f0b54b6b4202 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 16 Feb 2024 23:59:38 -0800 Subject: [PATCH 054/407] build: address meson deprecation warnings: - `ExternalProgram.path` - `dependency.get_pkgconfig_variable` - `meson.build_root` - `meson.source_root` --- meson.build | 20 ++++++++++---------- protocol/meson.build | 4 ++-- test/meson.build | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/meson.build b/meson.build index cb2b4a333..2b4f94a20 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project( 'waybar', 'cpp', 'c', version: '0.9.24', license: 'MIT', - meson_version: '>= 0.50.0', + meson_version: '>= 0.56.0', default_options : [ 'cpp_std=c++20', 'buildtype=release', @@ -31,10 +31,10 @@ git = find_program('git', native: true, required: false) if not git.found() add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'cpp') else - git_path = run_command([git.path(), 'rev-parse', '--show-toplevel']).stdout().strip() - if meson.source_root() == git_path - git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip() - git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() + git_path = run_command(git, 'rev-parse', '--show-toplevel', check: false).stdout().strip() + if meson.project_source_root() == git_path + git_commit_hash = run_command(git, 'describe', '--always', '--tags', check: false).stdout().strip() + git_branch = run_command(git, 'rev-parse', '--abbrev-ref', 'HEAD', check: false).stdout().strip() version = '"@0@ (branch \'@1@\')"'.format(git_commit_hash, git_branch) add_project_arguments('-DVERSION=@0@'.format(version), language: 'cpp') else @@ -146,7 +146,7 @@ conf_data.set('prefix', prefix) add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'cpp') if systemd.found() - user_units_dir = systemd.get_pkgconfig_variable('systemduserunitdir') + user_units_dir = systemd.get_variable(pkgconfig: 'systemduserunitdir') configure_file( configuration: conf_data, @@ -435,7 +435,7 @@ install_data( scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() - scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) + scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true) sh = find_program('sh', native: true) main_manpage = configure_file( @@ -446,7 +446,7 @@ if scdoc.found() } ) - main_manpage_path = join_paths(meson.build_root(), '@0@'.format(main_manpage)) + main_manpage_path = join_paths(meson.project_build_root(), '@0@'.format(main_manpage)) mandir = get_option('mandir') man_files = [ @@ -511,7 +511,7 @@ if scdoc.found() input: join_paths('man', path), output: output, command: [ - sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output) + sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output) ], install: true, install_dir: '@0@/man@1@'.format(mandir, section) @@ -537,7 +537,7 @@ if clangtidy.found() command: [ clangtidy, '-checks=*,-fuchsia-default-arguments', - '-p', meson.build_root() + '-p', meson.project_build_root() ] + src_files) endif diff --git a/protocol/meson.build b/protocol/meson.build index e1e745a98..0935b6c69 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,4 +1,4 @@ -wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') +wl_protocol_dir = wayland_protos.get_variable(pkgconfig: 'pkgdatadir') wayland_scanner = find_program('wayland-scanner') @@ -44,7 +44,7 @@ endforeach gdbus_codegen = find_program('gdbus-codegen') -r = run_command(gdbus_codegen, '--body', '--output', '/dev/null') +r = run_command(gdbus_codegen, '--body', '--output', '/dev/null', check: false) if r.returncode() != 0 gdbus_code_dsnw = custom_target( 'dbus-status-notifier-watcher.[ch]', diff --git a/test/meson.build b/test/meson.build index 4c71d326b..7c9226712 100644 --- a/test/meson.build +++ b/test/meson.build @@ -31,5 +31,5 @@ waybar_test = executable( test( 'waybar', waybar_test, - workdir: meson.source_root(), + workdir: meson.project_source_root(), ) From d9f9fb51ff5a7c16cf76da46543da1d387f042f5 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Feb 2024 00:53:45 -0800 Subject: [PATCH 055/407] build: use `/` instead of `join_paths` --- meson.build | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index 2b4f94a20..3f1b5a91d 100644 --- a/meson.build +++ b/meson.build @@ -143,7 +143,7 @@ sysconfdir = get_option('sysconfdir') conf_data = configuration_data() conf_data.set('prefix', prefix) -add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'cpp') +add_project_arguments('-DSYSCONFDIR="@0@"'.format(prefix / sysconfdir), language : 'cpp') if systemd.found() user_units_dir = systemd.get_variable(pkgconfig: 'systemduserunitdir') @@ -429,7 +429,7 @@ executable( install_data( './resources/config', './resources/style.css', - install_dir: sysconfdir + '/xdg/waybar' + install_dir: sysconfdir / 'xdg/waybar' ) scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) @@ -442,11 +442,11 @@ if scdoc.found() input: 'man/waybar.5.scd.in', output: 'waybar.5.scd', configuration: { - 'sysconfdir': join_paths(prefix, sysconfdir) + 'sysconfdir': prefix / sysconfdir } ) - main_manpage_path = join_paths(meson.project_build_root(), '@0@'.format(main_manpage)) + main_manpage_path = meson.project_build_root() / '@0@'.format(main_manpage) mandir = get_option('mandir') man_files = [ @@ -508,7 +508,7 @@ if scdoc.found() custom_target( output, # drops the 'man' if `path` is an absolute path - input: join_paths('man', path), + input: 'man' / path, output: output, command: [ sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output) From 63935ba0fb519cf5f4fa89054ce3e4a4422b0e92 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Feb 2024 00:58:39 -0800 Subject: [PATCH 056/407] build: don't use sh for scdoc --- meson.build | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index 3f1b5a91d..08de3df4e 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project( 'waybar', 'cpp', 'c', version: '0.9.24', license: 'MIT', - meson_version: '>= 0.56.0', + meson_version: '>= 0.59.0', default_options : [ 'cpp_std=c++20', 'buildtype=release', @@ -435,9 +435,6 @@ install_data( scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() - scdoc_prog = find_program(scdoc.get_variable(pkgconfig: 'scdoc'), native: true) - sh = find_program('sh', native: true) - main_manpage = configure_file( input: 'man/waybar.5.scd.in', output: 'waybar.5.scd', @@ -510,9 +507,9 @@ if scdoc.found() # drops the 'man' if `path` is an absolute path input: 'man' / path, output: output, - command: [ - sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output) - ], + command: scdoc.get_variable('scdoc'), + feed: true, + capture: true, install: true, install_dir: '@0@/man@1@'.format(mandir, section) ) From 4b344861433ecd05665b05c84eaf8068b66ff3c9 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Feb 2024 02:33:28 -0800 Subject: [PATCH 057/407] man: fix missing code block fence in hyprland-workspaces --- man/waybar-hyprland-workspaces.5.scd | 1 + 1 file changed, 1 insertion(+) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index fb7b6e4d0..12c1fe391 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -139,6 +139,7 @@ Additional to workspace name matching, the following *format-icons* can be set. } ``` +``` "hyprland/workspaces": { // Formatting omitted for brevity "ignore-workspaces": [ From 4f5dd535715aa1cb05d6ba7067aedd22d7627850 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 14 Jan 2024 10:25:37 -0800 Subject: [PATCH 058/407] chore: update gtk-layer-shell subproject to 0.8.2 --- meson.build | 3 ++- subprojects/gtk-layer-shell.wrap | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index d75497316..69eddb556 100644 --- a/meson.build +++ b/meson.build @@ -122,7 +122,8 @@ endif gtk_layer_shell = dependency('gtk-layer-shell-0', required: get_option('gtk-layer-shell'), - fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep']) + default_options: ['introspection=false', 'vapi=false'], + fallback: ['gtk-layer-shell', 'gtk_layer_shell']) systemd = dependency('systemd', required: get_option('systemd')) cpp_lib_chrono = compiler.compute_int('__cpp_lib_chrono', prefix : '#include ') diff --git a/subprojects/gtk-layer-shell.wrap b/subprojects/gtk-layer-shell.wrap index 555fbcb6b..cb7303455 100644 --- a/subprojects/gtk-layer-shell.wrap +++ b/subprojects/gtk-layer-shell.wrap @@ -1,5 +1,5 @@ [wrap-file] -directory = gtk-layer-shell-0.4.0 -source_filename = gtk-layer-shell-0.4.0.tar.gz -source_hash = 52fd74d3161fefa5528585ca5a523c3150934961f2284ad010ae54336dad097e -source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.4.0/gtk-layer-shell-0.4.0.tar.gz +directory = gtk-layer-shell-0.8.2 +source_filename = gtk-layer-shell-0.8.2.tar.gz +source_hash = 254dd246303127c5d5236ea640f01a82e35d2d652a48d139dd669c832a0f0dce +source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.8.2/gtk-layer-shell-0.8.2.tar.gz From 9a21884272b41709db6b3b1c12482c59788fb620 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 14 Jan 2024 10:37:49 -0800 Subject: [PATCH 059/407] feat!: drop RawSurfaceImpl with direct use of wlr-layer-shell BREAKING CHANGE: gtk-layer-shell is now required and unconditionally used. The corresponding config option is removed. As a part of preparation for future versions of GTK, remove an ability to use wlr-layer-shell directly. The APIs it required were dropped in GTK4, and with the menus/tooltips positioning issue being practically unsolvable it doesn't make sense to keep maintaining the code. --- include/client.hpp | 2 - man/waybar.5.scd.in | 6 - meson.build | 7 +- meson_options.txt | 1 - protocol/meson.build | 1 - protocol/wlr-layer-shell-unstable-v1.xml | 311 ----------------------- src/bar.cpp | 276 +------------------- src/client.cpp | 18 +- 8 files changed, 13 insertions(+), 609 deletions(-) delete mode 100644 protocol/wlr-layer-shell-unstable-v1.xml diff --git a/include/client.hpp b/include/client.hpp index 641ee6a74..0e68f0025 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -10,7 +10,6 @@ #include "util/css_reload_helper.hpp" #include "util/portal.hpp" -struct zwlr_layer_shell_v1; struct zwp_idle_inhibitor_v1; struct zwp_idle_inhibit_manager_v1; @@ -26,7 +25,6 @@ class Client { Glib::RefPtr gdk_display; struct wl_display *wl_display = nullptr; struct wl_registry *registry = nullptr; - struct zwlr_layer_shell_v1 *layer_shell = nullptr; struct zxdg_output_manager_v1 *xdg_output_manager = nullptr; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr; std::vector> bars; diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 17324d693..628bbf610 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -108,12 +108,6 @@ Also, a minimal example configuration can be found at the bottom of this man pag Option to pass any pointer events to the window under the bar. Intended to be used with either *top* or *overlay* layers and without exclusive zone. -*gtk-layer-shell* ++ - typeof: bool ++ - default: true ++ - Option to disable the use of gtk-layer-shell for popups. - Only functional if compiled with gtk-layer-shell support. - *ipc* ++ typeof: bool ++ default: false ++ diff --git a/meson.build b/meson.build index 69eddb556..648de3fd6 100644 --- a/meson.build +++ b/meson.build @@ -120,8 +120,7 @@ if libsndio.found() endif endif -gtk_layer_shell = dependency('gtk-layer-shell-0', - required: get_option('gtk-layer-shell'), +gtk_layer_shell = dependency('gtk-layer-shell-0', version: ['>=0.5.0'], default_options: ['introspection=false', 'vapi=false'], fallback: ['gtk-layer-shell', 'gtk_layer_shell']) systemd = dependency('systemd', required: get_option('systemd')) @@ -354,10 +353,6 @@ if libmpdclient.found() src_files += 'src/modules/mpd/state.cpp' endif -if gtk_layer_shell.found() - add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp') -endif - if libsndio.found() add_project_arguments('-DHAVE_LIBSNDIO', language: 'cpp') src_files += 'src/modules/sndio.cpp' diff --git a/meson_options.txt b/meson_options.txt index 827f9ac12..fef508397 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,7 +11,6 @@ option('systemd', type: 'feature', value: 'auto', description: 'Install systemd option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon') -option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support') option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL') option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio') option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind') diff --git a/protocol/meson.build b/protocol/meson.build index e1e745a98..6a86fa672 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -25,7 +25,6 @@ client_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], - ['wlr-layer-shell-unstable-v1.xml'], ['wlr-foreign-toplevel-management-unstable-v1.xml'], ['ext-workspace-unstable-v1.xml'], ['river-status-unstable-v1.xml'], diff --git a/protocol/wlr-layer-shell-unstable-v1.xml b/protocol/wlr-layer-shell-unstable-v1.xml deleted file mode 100644 index f9a4fe050..000000000 --- a/protocol/wlr-layer-shell-unstable-v1.xml +++ /dev/null @@ -1,311 +0,0 @@ - - - - Copyright © 2017 Drew DeVault - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - - Clients can use this interface to assign the surface_layer role to - wl_surfaces. Such surfaces are assigned to a "layer" of the output and - rendered with a defined z-depth respective to each other. They may also be - anchored to the edges and corners of a screen and specify input handling - semantics. This interface should be suitable for the implementation of - many desktop shell components, and a broad number of other applications - that interact with the desktop. - - - - - Create a layer surface for an existing surface. This assigns the role of - layer_surface, or raises a protocol error if another role is already - assigned. - - Creating a layer surface from a wl_surface which has a buffer attached - or committed is a client error, and any attempts by a client to attach - or manipulate a buffer prior to the first layer_surface.configure call - must also be treated as errors. - - You may pass NULL for output to allow the compositor to decide which - output to use. Generally this will be the one that the user most - recently interacted with. - - Clients can specify a namespace that defines the purpose of the layer - surface. - - - - - - - - - - - - - - - - - These values indicate which layers a surface can be rendered in. They - are ordered by z depth, bottom-most first. Traditional shell surfaces - will typically be rendered between the bottom and top layers. - Fullscreen shell surfaces are typically rendered at the top layer. - Multiple surfaces can share a single layer, and ordering within a - single layer is undefined. - - - - - - - - - - - - - This request indicates that the client will not use the layer_shell - object any more. Objects that have been created through this instance - are not affected. - - - - - - - An interface that may be implemented by a wl_surface, for surfaces that - are designed to be rendered as a layer of a stacked desktop-like - environment. - - Layer surface state (layer, size, anchor, exclusive zone, - margin, interactivity) is double-buffered, and will be applied at the - time wl_surface.commit of the corresponding wl_surface is called. - - - - - Sets the size of the surface in surface-local coordinates. The - compositor will display the surface centered with respect to its - anchors. - - If you pass 0 for either value, the compositor will assign it and - inform you of the assignment in the configure event. You must set your - anchor to opposite edges in the dimensions you omit; not doing so is a - protocol error. Both values are 0 by default. - - Size is double-buffered, see wl_surface.commit. - - - - - - - - Requests that the compositor anchor the surface to the specified edges - and corners. If two orthogonal edges are specified (e.g. 'top' and - 'left'), then the anchor point will be the intersection of the edges - (e.g. the top left corner of the output); otherwise the anchor point - will be centered on that edge, or in the center if none is specified. - - Anchor is double-buffered, see wl_surface.commit. - - - - - - - Requests that the compositor avoids occluding an area with other - surfaces. The compositor's use of this information is - implementation-dependent - do not assume that this region will not - actually be occluded. - - A positive value is only meaningful if the surface is anchored to one - edge or an edge and both perpendicular edges. If the surface is not - anchored, anchored to only two perpendicular edges (a corner), anchored - to only two parallel edges or anchored to all edges, a positive value - will be treated the same as zero. - - A positive zone is the distance from the edge in surface-local - coordinates to consider exclusive. - - Surfaces that do not wish to have an exclusive zone may instead specify - how they should interact with surfaces that do. If set to zero, the - surface indicates that it would like to be moved to avoid occluding - surfaces with a positive exclusive zone. If set to -1, the surface - indicates that it would not like to be moved to accommodate for other - surfaces, and the compositor should extend it all the way to the edges - it is anchored to. - - For example, a panel might set its exclusive zone to 10, so that - maximized shell surfaces are not shown on top of it. A notification - might set its exclusive zone to 0, so that it is moved to avoid - occluding the panel, but shell surfaces are shown underneath it. A - wallpaper or lock screen might set their exclusive zone to -1, so that - they stretch below or over the panel. - - The default value is 0. - - Exclusive zone is double-buffered, see wl_surface.commit. - - - - - - - Requests that the surface be placed some distance away from the anchor - point on the output, in surface-local coordinates. Setting this value - for edges you are not anchored to has no effect. - - The exclusive zone includes the margin. - - Margin is double-buffered, see wl_surface.commit. - - - - - - - - - - Set to 1 to request that the seat send keyboard events to this layer - surface. For layers below the shell surface layer, the seat will use - normal focus semantics. For layers above the shell surface layers, the - seat will always give exclusive keyboard focus to the top-most layer - which has keyboard interactivity set to true. - - Layer surfaces receive pointer, touch, and tablet events normally. If - you do not want to receive them, set the input region on your surface - to an empty region. - - Events is double-buffered, see wl_surface.commit. - - - - - - - This assigns an xdg_popup's parent to this layer_surface. This popup - should have been created via xdg_surface::get_popup with the parent set - to NULL, and this request must be invoked before committing the popup's - initial state. - - See the documentation of xdg_popup for more details about what an - xdg_popup is and how it is used. - - - - - - - When a configure event is received, if a client commits the - surface in response to the configure event, then the client - must make an ack_configure request sometime before the commit - request, passing along the serial of the configure event. - - If the client receives multiple configure events before it - can respond to one, it only has to ack the last configure event. - - A client is not required to commit immediately after sending - an ack_configure request - it may even ack_configure several times - before its next surface commit. - - A client may send multiple ack_configure requests before committing, but - only the last request sent before a commit indicates which configure - event the client really is responding to. - - - - - - - This request destroys the layer surface. - - - - - - The configure event asks the client to resize its surface. - - Clients should arrange their surface for the new states, and then send - an ack_configure request with the serial sent in this configure event at - some point before committing the new surface. - - The client is free to dismiss all but the last configure event it - received. - - The width and height arguments specify the size of the window in - surface-local coordinates. - - The size is a hint, in the sense that the client is free to ignore it if - it doesn't resize, pick a smaller size (to satisfy aspect ratio or - resize in steps of NxM pixels). If the client picks a smaller size and - is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the - surface will be centered on this axis. - - If the width or height arguments are zero, it means the client should - decide its own window dimension. - - - - - - - - - The closed event is sent by the compositor when the surface will no - longer be shown. The output may have been destroyed or the user may - have asked for it to be removed. Further changes to the surface will be - ignored. The client should destroy the resource after receiving this - event, and create a new surface if they so choose. - - - - - - - - - - - - - - - - - - - - - Change the layer that the surface is rendered on. - - Layer is double-buffered, see wl_surface.commit. - - - - - diff --git a/src/bar.cpp b/src/bar.cpp index 0857724e2..cfe723a39 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -1,16 +1,13 @@ -#ifdef HAVE_GTK_LAYER_SHELL -#include -#endif +#include "bar.hpp" +#include #include #include -#include "bar.hpp" #include "client.hpp" #include "factory.hpp" #include "group.hpp" -#include "wlr-layer-shell-unstable-v1-client-protocol.h" #ifdef HAVE_SWAY #include "modules/sway/bar.hpp" @@ -25,9 +22,6 @@ static constexpr const char* MIN_WIDTH_MSG = static constexpr const char* BAR_SIZE_MSG = "Bar configured (width: {}, height: {}) for output: {}"; -static constexpr const char* SIZE_DEFINED = - "{} size is defined in the config file so it will stay like that"; - const Bar::bar_mode_map Bar::PRESET_MODES = { // {"default", {// Special mode to hold the global bar configuration @@ -132,7 +126,6 @@ void from_json(const Json::Value& j, std::map& m) { } } -#ifdef HAVE_GTK_LAYER_SHELL struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { GLSSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} { output_name_ = output.name; @@ -260,260 +253,6 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { spdlog::info(BAR_SIZE_MSG, width_, height_, output_name_); } }; -#endif - -struct RawSurfaceImpl : public BarSurface, public sigc::trackable { - RawSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} { - output_ = gdk_wayland_monitor_get_wl_output(output.monitor->gobj()); - output_name_ = output.name; - - window.signal_realize().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::onRealize)); - window.signal_map_event().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::onMap)); - window.signal_configure_event().connect_notify( - sigc::mem_fun(*this, &RawSurfaceImpl::onConfigure)); - - if (window.get_realized()) { - onRealize(); - } - } - - void setExclusiveZone(bool enable) override { - exclusive_zone_ = enable; - if (layer_surface_) { - auto zone = 0; - if (enable) { - // exclusive zone already includes margin for anchored edge, - // only opposite margin should be added - if ((anchor_ & VERTICAL_ANCHOR) == VERTICAL_ANCHOR) { - zone += width_; - zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) ? margins_.right : margins_.left; - } else { - zone += height_; - zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) ? margins_.bottom : margins_.top; - } - } - spdlog::debug("Set exclusive zone {} for output {}", zone, output_name_); - zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_.get(), zone); - } - } - - void setLayer(bar_layer layer) override { - layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; - if (layer == bar_layer::TOP) { - layer_ = ZWLR_LAYER_SHELL_V1_LAYER_TOP; - } else if (layer == bar_layer::OVERLAY) { - layer_ = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; - } - // updating already mapped window - if (layer_surface_) { - if (zwlr_layer_surface_v1_get_version(layer_surface_.get()) >= - ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION) { - zwlr_layer_surface_v1_set_layer(layer_surface_.get(), layer_); - } else { - spdlog::warn("Unable to change layer: layer-shell implementation is too old"); - } - } - } - - void setMargins(const struct bar_margins& margins) override { - margins_ = margins; - // updating already mapped window - if (layer_surface_) { - zwlr_layer_surface_v1_set_margin(layer_surface_.get(), margins_.top, margins_.right, - margins_.bottom, margins_.left); - } - } - - void setPassThrough(bool enable) override { - passthrough_ = enable; - /* GTK overwrites any region changes applied directly to the wl_surface, - * thus the same GTK region API as in the GLS impl has to be used. */ - auto gdk_window = window_.get_window(); - if (gdk_window) { - Cairo::RefPtr region; - if (enable) { - region = Cairo::Region::create(); - } - gdk_window->input_shape_combine_region(region, 0, 0); - } - } - - void setPosition(Gtk::PositionType position) override { - switch (position) { - case Gtk::POS_LEFT: - anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; - break; - case Gtk::POS_RIGHT: - anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - break; - case Gtk::POS_TOP: - anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; - break; - case Gtk::POS_BOTTOM: - anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - break; - }; - - // updating already mapped window - if (layer_surface_) { - zwlr_layer_surface_v1_set_anchor(layer_surface_.get(), anchor_); - } - } - - void setSize(uint32_t width, uint32_t height) override { - configured_width_ = width_ = width; - configured_height_ = height_ = height; - // layer_shell.configure handler should update exclusive zone if size changes - window_.set_size_request(width, height); - }; - - void commit() override { - if (surface_) { - wl_surface_commit(surface_); - } - } - - private: - constexpr static uint8_t VERTICAL_ANCHOR = - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - constexpr static uint8_t HORIZONTAL_ANCHOR = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - - template - using deleter_fn = std::integral_constant; - using layer_surface_ptr = - std::unique_ptr>; - - Gtk::Window& window_; - std::string output_name_; - uint32_t configured_width_ = 0; - uint32_t configured_height_ = 0; - uint32_t width_ = 0; - uint32_t height_ = 0; - uint8_t anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; - bool exclusive_zone_ = true; - bool passthrough_ = false; - struct bar_margins margins_; - - zwlr_layer_shell_v1_layer layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; - struct wl_output* output_ = nullptr; // owned by GTK - struct wl_surface* surface_ = nullptr; // owned by GTK - layer_surface_ptr layer_surface_; - - void onRealize() { - auto gdk_window = window_.get_window()->gobj(); - gdk_wayland_window_set_use_custom_surface(gdk_window); - } - - void onMap(GdkEventAny* ev) { - static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { - .configure = onSurfaceConfigure, - .closed = onSurfaceClosed, - }; - auto client = Client::inst(); - auto gdk_window = window_.get_window()->gobj(); - surface_ = gdk_wayland_window_get_wl_surface(gdk_window); - - layer_surface_.reset(zwlr_layer_shell_v1_get_layer_surface(client->layer_shell, surface_, - output_, layer_, "waybar")); - - zwlr_layer_surface_v1_add_listener(layer_surface_.get(), &layer_surface_listener, this); - zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_.get(), false); - zwlr_layer_surface_v1_set_anchor(layer_surface_.get(), anchor_); - zwlr_layer_surface_v1_set_margin(layer_surface_.get(), margins_.top, margins_.right, - margins_.bottom, margins_.left); - - setSurfaceSize(width_, height_); - setExclusiveZone(exclusive_zone_); - setPassThrough(passthrough_); - - commit(); - wl_display_roundtrip(client->wl_display); - } - - void onConfigure(GdkEventConfigure* ev) { - /* - * GTK wants new size for the window. - * - * Prefer configured size if it's non-default. - * If the size is not set and the window is smaller than requested by GTK, request resize from - * layer surface. - */ - auto tmp_height = height_; - auto tmp_width = width_; - if (ev->height > static_cast(height_)) { - // Default minimal value - if (height_ > 1) { - spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); - } - if (configured_height_ > 1) { - spdlog::info(SIZE_DEFINED, "Height"); - } else { - tmp_height = ev->height; - } - } - if (ev->width > static_cast(width_)) { - // Default minimal value - if (width_ > 1) { - spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); - } - if (configured_width_ > 1) { - spdlog::info(SIZE_DEFINED, "Width"); - } else { - tmp_width = ev->width; - } - } - if (tmp_width != width_ || tmp_height != height_) { - setSurfaceSize(tmp_width, tmp_height); - commit(); - } - } - - void setSurfaceSize(uint32_t width, uint32_t height) { - /* If the client is anchored to two opposite edges, layer_surface.configure will return - * size without margins for the axis. - * layer_surface.set_size, however, expects size with margins for the anchored axis. - * This is not specified by wlr-layer-shell and based on actual behavior of sway. - * - * If the size for unanchored axis is not set (0), change request to 1 to avoid automatic - * assignment by the compositor. - */ - if ((anchor_ & VERTICAL_ANCHOR) == VERTICAL_ANCHOR) { - width = width > 0 ? width : 1; - if (height > 1) { - height += margins_.top + margins_.bottom; - } - } else { - height = height > 0 ? height : 1; - if (width > 1) { - width += margins_.right + margins_.left; - } - } - spdlog::debug("Set surface size {}x{} for output {}", width, height, output_name_); - zwlr_layer_surface_v1_set_size(layer_surface_.get(), width, height); - } - - static void onSurfaceConfigure(void* data, struct zwlr_layer_surface_v1* surface, uint32_t serial, - uint32_t width, uint32_t height) { - auto o = static_cast(data); - if (width != o->width_ || height != o->height_) { - o->width_ = width; - o->height_ = height; - o->window_.set_size_request(o->width_, o->height_); - o->window_.resize(o->width_, o->height_); - o->setExclusiveZone(o->exclusive_zone_); - spdlog::info(BAR_SIZE_MSG, o->width_ == 1 ? "auto" : std::to_string(o->width_), - o->height_ == 1 ? "auto" : std::to_string(o->height_), o->output_name_); - o->commit(); - } - zwlr_layer_surface_v1_ack_configure(surface, serial); - } - - static void onSurfaceClosed(void* data, struct zwlr_layer_surface_v1* /* surface */) { - auto o = static_cast(data); - o->layer_surface_.reset(); - } -}; }; // namespace waybar @@ -609,16 +348,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) output->monitor->property_geometry().signal_changed().connect( sigc::mem_fun(*this, &Bar::onOutputGeometryChanged)); -#ifdef HAVE_GTK_LAYER_SHELL - bool use_gls = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true; - if (use_gls) { - surface_impl_ = std::make_unique(window, *output); - } else -#endif - { - surface_impl_ = std::make_unique(window, *output); - } - + surface_impl_ = std::make_unique(window, *output); surface_impl_->setMargins(margins_); surface_impl_->setSize(width, height); // Position needs to be set after calculating the height due to the diff --git a/src/client.cpp b/src/client.cpp index 73c06fb87..7c59dd5ee 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,5 +1,6 @@ #include "client.hpp" +#include #include #include @@ -8,7 +9,6 @@ #include "idle-inhibit-unstable-v1-client-protocol.h" #include "util/clara.hpp" #include "util/format.hpp" -#include "wlr-layer-shell-unstable-v1-client-protocol.h" waybar::Client *waybar::Client::inst() { static auto c = new Client(); @@ -18,13 +18,8 @@ waybar::Client *waybar::Client::inst() { void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { auto client = static_cast(data); - if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { - // limit version to a highest supported by the client protocol file - version = std::min(version, zwlr_layer_shell_v1_interface.version); - client->layer_shell = static_cast( - wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version)); - } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 && - version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { + if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 && + version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { client->xdg_output_manager = static_cast(wl_registry_bind( registry, name, &zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION)); } else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) { @@ -200,7 +195,12 @@ void waybar::Client::bindInterfaces() { }; wl_registry_add_listener(registry, ®istry_listener, this); wl_display_roundtrip(wl_display); - if (layer_shell == nullptr || xdg_output_manager == nullptr) { + + if (!gtk_layer_is_supported()) { + throw std::runtime_error("The Wayland compositor does not support wlr-layer-shell protocol"); + } + + if (xdg_output_manager == nullptr) { throw std::runtime_error("Failed to acquire required resources."); } // add existing outputs and subscribe to updates From 3cb587945a753d3c50458c59811cabdff7212e2d Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 15 Jan 2024 22:10:28 -0800 Subject: [PATCH 060/407] fix: use `gtk_layer_set_keyboard_mode()` `gtk_layer_set_keyboard_interactivity()` is deprecated and was removed in gtk4-layer-shell. Note that this bumps version requirement to 0.6.0 --- meson.build | 2 +- src/bar.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 648de3fd6..ddb757f60 100644 --- a/meson.build +++ b/meson.build @@ -120,7 +120,7 @@ if libsndio.found() endif endif -gtk_layer_shell = dependency('gtk-layer-shell-0', version: ['>=0.5.0'], +gtk_layer_shell = dependency('gtk-layer-shell-0', version: ['>=0.6.0'], default_options: ['introspection=false', 'vapi=false'], fallback: ['gtk-layer-shell', 'gtk_layer_shell']) systemd = dependency('systemd', required: get_option('systemd')) diff --git a/src/bar.cpp b/src/bar.cpp index cfe723a39..e919ded2c 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -131,7 +131,7 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { output_name_ = output.name; // this has to be executed before GtkWindow.realize gtk_layer_init_for_window(window_.gobj()); - gtk_layer_set_keyboard_interactivity(window.gobj(), FALSE); + gtk_layer_set_keyboard_mode(window.gobj(), GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); gtk_layer_set_monitor(window_.gobj(), output.monitor->gobj()); gtk_layer_set_namespace(window_.gobj(), "waybar"); From f3063e86aab08786ac03e6ddd5c9004fe36a52d1 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Feb 2024 08:17:42 -0800 Subject: [PATCH 061/407] build: install man pages only for enabled modules --- include/factory.hpp | 4 +- meson.build | 264 ++++++++++++++++++++++++++------------------ src/factory.cpp | 6 +- 3 files changed, 160 insertions(+), 114 deletions(-) diff --git a/include/factory.hpp b/include/factory.hpp index ddf545da3..339f92edd 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -13,8 +13,10 @@ #include "modules/sway/window.hpp" #include "modules/sway/workspaces.hpp" #endif -#ifdef HAVE_WLR +#ifdef HAVE_WLR_TASKBAR #include "modules/wlr/taskbar.hpp" +#endif +#ifdef HAVE_WLR_WORKSPACES #include "modules/wlr/workspace_manager.hpp" #endif #ifdef HAVE_RIVER diff --git a/meson.build b/meson.build index 08de3df4e..481fded2e 100644 --- a/meson.build +++ b/meson.build @@ -187,6 +187,16 @@ src_files = files( 'src/util/css_reload_helper.cpp' ) +man_files = files( + 'man/waybar-bluetooth.5.scd', + 'man/waybar-custom.5.scd', + 'man/waybar-disk.5.scd', + 'man/waybar-idle-inhibitor.5.scd', + 'man/waybar-image.5.scd', + 'man/waybar-states.5.scd', + 'man/waybar-temperature.5.scd', +) + inc_dirs = ['include'] if is_linux @@ -205,6 +215,13 @@ if is_linux 'src/modules/memory/linux.cpp', 'src/modules/systemd_failed_units.cpp', ) + man_files += files( + 'man/waybar-battery.5.scd', + 'man/waybar-cffi.5.scd', + 'man/waybar-cpu.5.scd', + 'man/waybar-memory.5.scd', + 'man/waybar-systemd-failed-units.5.scd', + ) elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp') add_project_arguments('-DHAVE_MEMORY_BSD', language: 'cpp') @@ -218,98 +235,149 @@ elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd 'src/modules/memory/bsd.cpp', 'src/modules/memory/common.cpp', ) + man_files += files( + 'man/waybar-cffi.5.scd', + 'man/waybar-cpu.5.scd', + 'man/waybar-memory.5.scd', + ) if is_freebsd - src_files += files( - 'src/modules/battery.cpp', - ) + src_files += files('src/modules/battery.cpp') + man_files += files('man/waybar-battery.5.scd') endif endif -add_project_arguments('-DHAVE_SWAY', language: 'cpp') -src_files += [ - 'src/modules/sway/ipc/client.cpp', - 'src/modules/sway/bar.cpp', - 'src/modules/sway/mode.cpp', - 'src/modules/sway/language.cpp', - 'src/modules/sway/window.cpp', - 'src/modules/sway/workspaces.cpp', - 'src/modules/sway/scratchpad.cpp' -] +if true + add_project_arguments('-DHAVE_SWAY', language: 'cpp') + src_files += files( + 'src/modules/sway/ipc/client.cpp', + 'src/modules/sway/bar.cpp', + 'src/modules/sway/mode.cpp', + 'src/modules/sway/language.cpp', + 'src/modules/sway/window.cpp', + 'src/modules/sway/workspaces.cpp', + 'src/modules/sway/scratchpad.cpp' + ) + man_files += files( + 'man/waybar-sway-language.5.scd', + 'man/waybar-sway-mode.5.scd', + 'man/waybar-sway-scratchpad.5.scd', + 'man/waybar-sway-window.5.scd', + 'man/waybar-sway-workspaces.5.scd', + ) +endif if true - add_project_arguments('-DHAVE_WLR', language: 'cpp') - src_files += 'src/modules/wlr/taskbar.cpp' - src_files += 'src/modules/wlr/workspace_manager.cpp' - src_files += 'src/modules/wlr/workspace_manager_binding.cpp' + add_project_arguments('-DHAVE_WLR_TASKBAR', language: 'cpp') + src_files += files('src/modules/wlr/taskbar.cpp') + man_files += files('man/waybar-wlr-taskbar.5.scd') endif if true add_project_arguments('-DHAVE_RIVER', language: 'cpp') - src_files += 'src/modules/river/mode.cpp' - src_files += 'src/modules/river/tags.cpp' - src_files += 'src/modules/river/window.cpp' - src_files += 'src/modules/river/layout.cpp' + src_files += files( + 'src/modules/river/layout.cpp', + 'src/modules/river/mode.cpp', + 'src/modules/river/tags.cpp', + 'src/modules/river/window.cpp', + ) + man_files += files( + 'man/waybar-river-layout.5.scd', + 'man/waybar-river-mode.5.scd', + 'man/waybar-river-tags.5.scd', + 'man/waybar-river-window.5.scd', + ) endif if true add_project_arguments('-DHAVE_DWL', language: 'cpp') - src_files += 'src/modules/dwl/tags.cpp' + src_files += files('src/modules/dwl/tags.cpp') + man_files += files('man/waybar-dwl-tags.5.scd') endif if true add_project_arguments('-DHAVE_HYPRLAND', language: 'cpp') - src_files += 'src/modules/hyprland/backend.cpp' - src_files += 'src/modules/hyprland/window.cpp' - src_files += 'src/modules/hyprland/language.cpp' - src_files += 'src/modules/hyprland/submap.cpp' - src_files += 'src/modules/hyprland/workspaces.cpp' + src_files += files( + 'src/modules/hyprland/backend.cpp', + 'src/modules/hyprland/language.cpp', + 'src/modules/hyprland/submap.cpp', + 'src/modules/hyprland/window.cpp', + 'src/modules/hyprland/workspaces.cpp', + ) + man_files += files( + 'man/waybar-hyprland-language.5.scd', + 'man/waybar-hyprland-submap.5.scd', + 'man/waybar-hyprland-window.5.scd', + 'man/waybar-hyprland-workspaces.5.scd', + ) endif if libnl.found() and libnlgen.found() add_project_arguments('-DHAVE_LIBNL', language: 'cpp') - src_files += 'src/modules/network.cpp' + src_files += files('src/modules/network.cpp') + man_files += files('man/waybar-network.5.scd') endif if not get_option('logind').disabled() add_project_arguments('-DHAVE_GAMEMODE', '-DHAVE_LOGIND_INHIBITOR', language: 'cpp') - src_files += 'src/modules/gamemode.cpp' - src_files += 'src/modules/inhibitor.cpp' + src_files += files( + 'src/modules/gamemode.cpp', + 'src/modules/inhibitor.cpp', + ) + man_files += files( + 'man/waybar-gamemode.5.scd', + 'man/waybar-inhibitor.5.scd', + ) endif if (upower_glib.found() and not get_option('logind').disabled()) add_project_arguments('-DHAVE_UPOWER', language: 'cpp') - src_files += 'src/modules/upower/upower.cpp' - src_files += 'src/modules/upower/upower_tooltip.cpp' + src_files += files( + 'src/modules/upower/upower.cpp', + 'src/modules/upower/upower_tooltip.cpp', + ) + man_files += files('man/waybar-upower.5.scd') endif -if (pipewire.found()) +if pipewire.found() add_project_arguments('-DHAVE_PIPEWIRE', language: 'cpp') - src_files += 'src/modules/privacy/privacy.cpp' - src_files += 'src/modules/privacy/privacy_item.cpp' - src_files += 'src/util/pipewire_backend.cpp' + src_files += files( + 'src/modules/privacy/privacy.cpp', + 'src/modules/privacy/privacy_item.cpp', + 'src/util/pipewire_backend.cpp', + ) + man_files += files('man/waybar-privacy.5.scd') endif -if (playerctl.found() and not get_option('logind').disabled()) +if playerctl.found() add_project_arguments('-DHAVE_MPRIS', language: 'cpp') - src_files += 'src/modules/mpris/mpris.cpp' + src_files += files('src/modules/mpris/mpris.cpp') + man_files += files('man/waybar-mpris.5.scd') endif if libpulse.found() add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp') - src_files += 'src/modules/pulseaudio.cpp' - src_files += 'src/modules/pulseaudio_slider.cpp' - src_files += 'src/util/audio_backend.cpp' + src_files += files( + 'src/modules/pulseaudio.cpp', + 'src/modules/pulseaudio_slider.cpp', + 'src/util/audio_backend.cpp', + ) + man_files += files( + 'man/waybar-pulseaudio.5.scd', + 'man/waybar-pulseaudio-slider.5.scd', + ) endif if libjack.found() add_project_arguments('-DHAVE_LIBJACK', language: 'cpp') - src_files += 'src/modules/jack.cpp' + src_files += files('src/modules/jack.cpp') + man_files += files('man/waybar-jack.5.scd') endif if libwireplumber.found() add_project_arguments('-DHAVE_LIBWIREPLUMBER', language: 'cpp') - src_files += 'src/modules/wireplumber.cpp' + src_files += files('src/modules/wireplumber.cpp') + man_files += files('man/waybar-wireplumber.5.scd') endif if dbusmenu_gtk.found() @@ -320,25 +388,40 @@ if dbusmenu_gtk.found() 'src/modules/sni/host.cpp', 'src/modules/sni/item.cpp' ) + man_files += files( + 'man/waybar-tray.5.scd', + ) endif if libudev.found() and (is_linux or libepoll.found()) add_project_arguments('-DHAVE_LIBUDEV', language: 'cpp') - src_files += 'src/modules/backlight.cpp' - src_files += 'src/modules/backlight_slider.cpp' - src_files += 'src/util/backlight_backend.cpp' + src_files += files( + 'src/modules/backlight.cpp', + 'src/modules/backlight_slider.cpp', + 'src/util/backlight_backend.cpp', + ) + man_files += files( + 'man/waybar-backlight.5.scd', + 'man/waybar-backlight-slider.5.scd', + ) endif if libevdev.found() and (is_linux or libepoll.found()) and libinput.found() and (is_linux or libinotify.found()) add_project_arguments('-DHAVE_LIBEVDEV', language: 'cpp') add_project_arguments('-DHAVE_LIBINPUT', language: 'cpp') - src_files += 'src/modules/keyboard_state.cpp' + src_files += files('src/modules/keyboard_state.cpp') + man_files += files('man/waybar-keyboard-state.5.scd') endif if libmpdclient.found() add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') - src_files += 'src/modules/mpd/mpd.cpp' - src_files += 'src/modules/mpd/state.cpp' + src_files += files( + 'src/modules/mpd/mpd.cpp', + 'src/modules/mpd/state.cpp', + ) + man_files += files( + 'man/waybar-mpd.5.scd', + ) endif if gtk_layer_shell.found() @@ -347,7 +430,8 @@ endif if libsndio.found() add_project_arguments('-DHAVE_LIBSNDIO', language: 'cpp') - src_files += 'src/modules/sndio.cpp' + src_files += files('src/modules/sndio.cpp') + man_files += files('man/waybar-sndio.5.scd') endif if get_option('rfkill').enabled() and is_linux @@ -359,16 +443,26 @@ endif if have_chrono_timezones add_project_arguments('-DHAVE_CHRONO_TIMEZONES', language: 'cpp') - src_files += 'src/modules/clock.cpp' + src_files += files('src/modules/clock.cpp') + man_files += files('man/waybar-clock.5.scd') elif tz_dep.found() add_project_arguments('-DHAVE_LIBDATE', language: 'cpp') - src_files += 'src/modules/clock.cpp' + src_files += files('src/modules/clock.cpp') + man_files += files('man/waybar-clock.5.scd') else - src_files += 'src/modules/simpleclock.cpp' + src_files += files('src/modules/simpleclock.cpp') + man_files += files('man/waybar-clock.5.scd') endif if get_option('experimental') - add_project_arguments('-DUSE_EXPERIMENTAL', language: 'cpp') + add_project_arguments('-DHAVE_WLR_WORKSPACES', language: 'cpp') + src_files += files( + 'src/modules/wlr/workspace_manager.cpp', + 'src/modules/wlr/workspace_manager_binding.cpp', + ) + man_files += files( + 'man/waybar-wlr-workspaces.5.scd', + ) endif cava = dependency('cava', @@ -379,7 +473,8 @@ cava = dependency('cava', if cava.found() add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp') - src_files += 'src/modules/cava.cpp' + src_files += files('src/modules/cava.cpp') + man_files += files('man/waybar-cava.5.scd') endif subdir('protocol') @@ -435,7 +530,7 @@ install_data( scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() - main_manpage = configure_file( + man_files += configure_file( input: 'man/waybar.5.scd.in', output: 'waybar.5.scd', configuration: { @@ -443,60 +538,10 @@ if scdoc.found() } ) - main_manpage_path = meson.project_build_root() / '@0@'.format(main_manpage) - + fs = import('fs') mandir = get_option('mandir') - man_files = [ - main_manpage_path, - 'waybar-backlight.5.scd', - 'waybar-backlight-slider.5.scd', - 'waybar-battery.5.scd', - 'waybar-cava.5.scd', - 'waybar-cffi.5.scd', - 'waybar-clock.5.scd', - 'waybar-cpu.5.scd', - 'waybar-custom.5.scd', - 'waybar-disk.5.scd', - 'waybar-gamemode.5.scd', - 'waybar-idle-inhibitor.5.scd', - 'waybar-image.5.scd', - 'waybar-keyboard-state.5.scd', - 'waybar-memory.5.scd', - 'waybar-mpd.5.scd', - 'waybar-mpris.5.scd', - 'waybar-network.5.scd', - 'waybar-pulseaudio.5.scd', - 'waybar-pulseaudio-slider.5.scd', - 'waybar-privacy.5.scd', - 'waybar-river-mode.5.scd', - 'waybar-river-tags.5.scd', - 'waybar-river-window.5.scd', - 'waybar-river-layout.5.scd', - 'waybar-sway-language.5.scd', - 'waybar-sway-mode.5.scd', - 'waybar-sway-scratchpad.5.scd', - 'waybar-sway-window.5.scd', - 'waybar-sway-workspaces.5.scd', - 'waybar-systemd-failed-units.5.scd', - 'waybar-temperature.5.scd', - 'waybar-tray.5.scd', - 'waybar-states.5.scd', - 'waybar-wlr-taskbar.5.scd', - 'waybar-wlr-workspaces.5.scd', - 'waybar-bluetooth.5.scd', - 'waybar-sndio.5.scd', - 'waybar-upower.5.scd', - 'waybar-wireplumber.5.scd', - 'waybar-dwl-tags.5.scd', - ] - - if not get_option('logind').disabled() - man_files += 'waybar-inhibitor.5.scd' - endif - foreach file : man_files - path = '@0@'.format(file) - basename = path.split('/')[-1] + basename = fs.name(file) topic = basename.split('.')[-3] section = basename.split('.')[-2] @@ -504,8 +549,7 @@ if scdoc.found() custom_target( output, - # drops the 'man' if `path` is an absolute path - input: 'man' / path, + input: file, output: output, command: scdoc.get_variable('scdoc'), feed: true, diff --git a/src/factory.cpp b/src/factory.cpp index 2692bd859..a3b66136a 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -58,16 +58,16 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, return new waybar::modules::sway::Scratchpad(id, config_[name]); } #endif -#ifdef HAVE_WLR +#ifdef HAVE_WLR_TASKBAR if (ref == "wlr/taskbar") { return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]); } -#ifdef USE_EXPERIMENTAL +#endif +#ifdef HAVE_WLR_WORKSPACES if (ref == "wlr/workspaces") { return new waybar::modules::wlr::WorkspaceManager(id, bar_, config_[name]); } #endif -#endif #ifdef HAVE_RIVER if (ref == "river/mode") { return new waybar::modules::river::Mode(id, bar_, config_[name]); From fd5a03dc5fd9303cb1bd536ba47f50472eb5f889 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Feb 2024 08:56:43 -0800 Subject: [PATCH 062/407] build: disable catch2 unit-tests The library tests take more time to complie than the entire Waybar. --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 481fded2e..d2dbd1f94 100644 --- a/meson.build +++ b/meson.build @@ -563,6 +563,7 @@ endif catch2 = dependency( 'catch2', version: '>=3.5.1', + default_options: [ 'tests=false' ], fallback: ['catch2', 'catch2_dep'], required: get_option('tests'), ) From 543290ab07749957a8fc9111d191073af92d350b Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Feb 2024 09:06:32 -0800 Subject: [PATCH 063/407] fix: `-Wnon-virtual-dtor` warning in CssReloadHelper ``` ../include/util/css_reload_helper.hpp:15:7: warning: 'class waybar::CssReloadHelper' has virtual functions and accessible non-virtual destructor [-Wnon-virtual-dtor] ``` --- include/util/css_reload_helper.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/util/css_reload_helper.hpp b/include/util/css_reload_helper.hpp index 4826fc312..032b23826 100644 --- a/include/util/css_reload_helper.hpp +++ b/include/util/css_reload_helper.hpp @@ -16,6 +16,8 @@ class CssReloadHelper { public: CssReloadHelper(std::string cssFile, std::function callback); + virtual ~CssReloadHelper() = default; + virtual void monitorChanges(); protected: From a02bacdd5376ef77d92e66882f807b346222ef39 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 18 Feb 2024 12:01:36 +0100 Subject: [PATCH 064/407] fix build warning --- src/bar.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bar.cpp b/src/bar.cpp index 0857724e2..cc8318c2a 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -117,6 +117,7 @@ Glib::ustring to_string(Gtk::PositionType pos) { case Gtk::POS_BOTTOM: return "bottom"; } + throw std::runtime_error("Invalid Gtk::PositionType"); } /* Deserializer for JSON Object -> map From 11310b89f063a305de0d23aa4dd21d6ef365a776 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 21 Oct 2023 18:13:44 +0200 Subject: [PATCH 065/407] hyprland/workspaces: Use hyprland's persistent workspaces configuration --- include/modules/hyprland/workspaces.hpp | 25 +- src/modules/hyprland/workspaces.cpp | 317 +++++++++++++++--------- src/util/regex_collection.cpp | 2 +- 3 files changed, 217 insertions(+), 127 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index d2006fcc4..827888b89 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -68,7 +68,7 @@ class Workspace { int id() const { return m_id; }; std::string name() const { return m_name; }; std::string output() const { return m_output; }; - bool isActive() const { return m_active; }; + bool isActive() const { return m_isActive; }; bool isSpecial() const { return m_isSpecial; }; bool isPersistent() const { return m_isPersistent; }; bool isVisible() const { return m_isVisible; }; @@ -76,7 +76,7 @@ class Workspace { bool isUrgent() const { return m_isUrgent; }; bool handleClicked(GdkEventButton* bt) const; - void setActive(bool value = true) { m_active = value; }; + void setActive(bool value = true) { m_isActive = value; }; void setPersistent(bool value = true) { m_isPersistent = value; }; void setUrgent(bool value = true) { m_isUrgent = value; }; void setVisible(bool value = true) { m_isVisible = value; }; @@ -99,7 +99,7 @@ class Workspace { std::string m_name; std::string m_output; uint m_windows; - bool m_active = false; + bool m_isActive = false; bool m_isSpecial = false; bool m_isPersistent = false; bool m_isUrgent = false; @@ -135,8 +135,8 @@ class Workspaces : public AModule, public EventHandler { void onEvent(const std::string& e) override; void updateWindowCount(); void sortWorkspaces(); - void createWorkspace(Json::Value const& workspace_data, - Json::Value const& clients_data = Json::Value::nullRef); + void createWorkspace(Json::Value const& workspaceData, + Json::Value const& clientsData = Json::Value::nullRef); void removeWorkspace(std::string const& name); void setUrgentWorkspace(std::string const& windowaddress); void parseConfig(const Json::Value& config); @@ -160,16 +160,24 @@ class Workspaces : public AModule, public EventHandler { void onWindowTitleEvent(std::string const& payload); + void onConfigReloaded(); + int windowRewritePriorityFunction(std::string const& window_rule); void doUpdate(); void extendOrphans(int workspaceId, Json::Value const& clientsJson); - void registerOrphanWindow(WindowCreationPayload create_window_paylod); + void registerOrphanWindow(WindowCreationPayload create_window_payload); + + void initializeWorkspaces(); + void setCurrentMonitorId(); + void loadPersistentWorkspacesFromConfig(Json::Value const& clientsJson); + void loadPersistentWorkspacesFromWorkspaceRules(const Json::Value& clientsJson); bool m_allOutputs = false; bool m_showSpecial = false; bool m_activeOnly = false; + Json::Value m_persistentWorkspaceConfig; // Map for windows stored in workspaces not present in the current bar. // This happens when the user has multiple monitors (hence, multiple bars) @@ -184,11 +192,6 @@ class Workspaces : public AModule, public EventHandler { {"NUMBER", SortMethod::NUMBER}, {"DEFAULT", SortMethod::DEFAULT}}; - void fillPersistentWorkspaces(); - void createPersistentWorkspaces(); - std::vector m_persistentWorkspacesToCreate; - bool m_persistentCreated = false; - std::string m_format; std::map m_iconsMap; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 88b5e135d..96893179f 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -49,6 +49,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value gIPC = std::make_unique(); } + setCurrentMonitorId(); init(); registerIpc(); } @@ -64,7 +65,6 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { for (std::string &name : formatIcons.getMemberNames()) { m_iconsMap.emplace(name, formatIcons[name].asString()); } - m_iconsMap.emplace("", ""); } @@ -112,11 +112,26 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { } } + if (config_["persistent_workspaces"].isObject()) { + spdlog::warn( + "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); + } + + if (config_["persistent-workspaces"].isObject() || config_["persistent_workspaces"].isObject()) { + m_persistentWorkspaceConfig = config_["persistent-workspaces"].isObject() + ? config_["persistent-workspaces"] + : config_["persistent_workspaces"]; + } + const Json::Value &formatWindowSeparator = config["format-window-separator"]; m_formatWindowSeparator = formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " "; const Json::Value &windowRewrite = config["window-rewrite"]; + if (!windowRewrite.isObject()) { + spdlog::debug("window-rewrite is not defined or is not an object, using default rules."); + return; + } const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"]; std::string windowRewriteDefault = @@ -127,9 +142,9 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); } -void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_paylod) { - if (!create_window_paylod.isEmpty(*this)) { - m_orphanWindowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(*this); +void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) { + if (!create_window_payload.isEmpty(*this)) { + m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this); } } @@ -144,6 +159,7 @@ auto Workspaces::registerIpc() -> void { gIPC->registerForIPC("closewindow", this); gIPC->registerForIPC("movewindow", this); gIPC->registerForIPC("urgent", this); + gIPC->registerForIPC("configreloaded", this); if (windowRewriteConfigUsesTitle()) { spdlog::info( @@ -175,6 +191,7 @@ void Workspaces::doUpdate() { m_workspacesToCreate.clear(); // get all active workspaces + spdlog::trace("Getting active workspaces"); auto monitors = gIPC->getSocket1JsonReply("monitors"); std::vector visibleWorkspaces; for (Json::Value &monitor : monitors) { @@ -184,6 +201,7 @@ void Workspaces::doUpdate() { } } + spdlog::trace("Updating workspace states"); for (auto &workspace : m_workspaces) { // active workspace->setActive(workspace->name() == m_activeWorkspaceName); @@ -204,6 +222,7 @@ void Workspaces::doUpdate() { workspace->update(m_format, workspaceIcon); } + spdlog::trace("Updating window count"); bool anyWindowCreated = false; std::vector notCreated; @@ -285,6 +304,8 @@ void Workspaces::onEvent(const std::string &ev) { onWorkspaceRenamed(payload); } else if (eventName == "windowtitle") { onWindowTitleEvent(payload); + } else if (eventName == "configreloaded") { + onConfigReloaded(); } dp.emit(); @@ -302,22 +323,37 @@ void Workspaces::onWorkspaceDestroyed(std::string const &payload) { void Workspaces::onWorkspaceCreated(std::string const &workspaceName, Json::Value const &clientsData) { - const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + spdlog::debug("Workspace created: {}", workspaceName); + auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); if (!isWorkspaceIgnored(workspaceName)) { + auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); for (Json::Value workspaceJson : workspacesJson) { std::string name = workspaceJson["name"].asString(); - if (name == workspaceName && - (allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && - (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(workspaceName)) { - m_workspacesToCreate.emplace_back(workspaceJson, clientsData); - break; + if (name == workspaceName) { + if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && + (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(workspaceName)) { + for (Json::Value const &rule : workspaceRules) { + if (rule["workspaceString"].asString() == workspaceName) { + workspaceJson["persistent"] = rule["persistent"].asBool(); + break; + } + } + + m_workspacesToCreate.emplace_back(workspaceJson, clientsData); + break; + } + } else { + extendOrphans(workspaceJson["id"].asInt(), clientsData); } } + } else { + spdlog::trace("Not creating workspace because it is ignored: {}", workspaceName); } } void Workspaces::onWorkspaceMoved(std::string const &payload) { + spdlog::debug("Workspace moved: {}", payload); std::string workspaceName = payload.substr(0, payload.find(',')); std::string monitorName = payload.substr(payload.find(',') + 1); @@ -325,11 +361,13 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); onWorkspaceCreated(workspaceName, clientsData); } else { + spdlog::debug("Removing workspace because it was moved to another monitor: {}"); onWorkspaceDestroyed(workspaceName); } } void Workspaces::onWorkspaceRenamed(std::string const &payload) { + spdlog::debug("Workspace renamed: {}", payload); std::string workspaceIdStr = payload.substr(0, payload.find(',')); int workspaceId = workspaceIdStr == "special" ? -99 : std::stoi(workspaceIdStr); std::string newName = payload.substr(payload.find(',') + 1); @@ -346,10 +384,12 @@ void Workspaces::onWorkspaceRenamed(std::string const &payload) { } void Workspaces::onMonitorFocused(std::string const &payload) { + spdlog::trace("Monitor focused: {}", payload); m_activeWorkspaceName = payload.substr(payload.find(',') + 1); } void Workspaces::onWindowOpened(std::string const &payload) { + spdlog::trace("Window opened: {}", payload); updateWindowCount(); size_t lastCommaIdx = 0; size_t nextCommaIdx = payload.find(','); @@ -369,6 +409,7 @@ void Workspaces::onWindowOpened(std::string const &payload) { } void Workspaces::onWindowClosed(std::string const &addr) { + spdlog::trace("Window closed: {}", addr); updateWindowCount(); for (auto &workspace : m_workspaces) { if (workspace->closeWindow(addr)) { @@ -378,6 +419,7 @@ void Workspaces::onWindowClosed(std::string const &addr) { } void Workspaces::onWindowMoved(std::string const &payload) { + spdlog::trace("Window moved: {}", payload); updateWindowCount(); size_t lastCommaIdx = 0; size_t nextCommaIdx = payload.find(','); @@ -416,6 +458,7 @@ void Workspaces::onWindowMoved(std::string const &payload) { } void Workspaces::onWindowTitleEvent(std::string const &payload) { + spdlog::trace("Window title changed: {}", payload); std::optional> inserter; // If the window was an orphan, rename it at the orphan's vector @@ -459,6 +502,11 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { } } +void Workspaces::onConfigReloaded() { + spdlog::info("Hyprland config reloaded, reinitializing hyprland/workspaces module..."); + init(); +} + void Workspaces::updateWindowCount() { const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { @@ -515,8 +563,11 @@ std::optional Workspace::closeWindow(WindowAddress const &addr) { void Workspaces::createWorkspace(Json::Value const &workspace_data, Json::Value const &clients_data) { - // avoid recreating existing workspaces auto workspaceName = workspace_data["name"].asString(); + spdlog::debug("Creating workspace {}, persistent: {}", workspaceName, + workspace_data["persistent"].asBool() ? "true" : "false"); + + // avoid recreating existing workspaces auto workspace = std::find_if( m_workspaces.begin(), m_workspaces.end(), [workspaceName](std::unique_ptr const &w) { @@ -525,9 +576,8 @@ void Workspaces::createWorkspace(Json::Value const &workspace_data, }); if (workspace != m_workspaces.end()) { - if (workspace_data["persistent"].asBool() and !(*workspace)->isPersistent()) { - (*workspace)->setPersistent(); - } + // don't recreate workspace, but update persistency if necessary + (*workspace)->setPersistent(workspace_data["persistent"].asBool()); return; } @@ -540,6 +590,7 @@ void Workspaces::createWorkspace(Json::Value const &workspace_data, } void Workspaces::removeWorkspace(std::string const &name) { + spdlog::debug("Removing workspace {}", name); auto workspace = std::find_if(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr &x) { return (name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name(); @@ -551,7 +602,7 @@ void Workspaces::removeWorkspace(std::string const &name) { } if ((*workspace)->isPersistent()) { - // don't remove persistent workspaces, createWorkspace will take care of replacement + spdlog::trace("Not removing persistent workspace {}", name); return; } @@ -559,94 +610,106 @@ void Workspaces::removeWorkspace(std::string const &name) { m_workspaces.erase(workspace); } -void Workspaces::fillPersistentWorkspaces() { - if (config_["persistent_workspaces"].isObject()) { - spdlog::warn( - "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); - } +Json::Value createPersistentWorkspaceData(std::string const &name, std::string const &monitor) { + spdlog::trace("Creating persistent workspace: {} on monitor {}", name, monitor); + Json::Value workspaceData; + try { + // numbered persistent workspaces get the name as ID + workspaceData["id"] = name == "special" ? -99 : std::stoi(name); + } catch (const std::exception &e) { + // named persistent workspaces start with ID=0 + workspaceData["id"] = 0; + } + workspaceData["name"] = name; + workspaceData["monitor"] = monitor; + workspaceData["windows"] = 0; + workspaceData["persistent"] = true; + return workspaceData; +} - if (config_["persistent-workspaces"].isObject() || config_["persistent_workspaces"].isObject()) { - const Json::Value persistentWorkspaces = config_["persistent-workspaces"].isObject() - ? config_["persistent-workspaces"] - : config_["persistent_workspaces"]; - const std::vector keys = persistentWorkspaces.getMemberNames(); - - for (const std::string &key : keys) { - // only add if either: - // 1. key is "*" and this monitor is not already defined in the config - // 2. key is the current monitor name - bool canCreate = - (key == "*" && std::find(keys.begin(), keys.end(), m_bar.output->name) == keys.end()) || - key == m_bar.output->name; - const Json::Value &value = persistentWorkspaces[key]; - - if (value.isInt()) { - // value is a number => create that many workspaces for this monitor - if (canCreate) { - int amount = value.asInt(); - spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, - m_bar.output->name); - for (int i = 0; i < amount; i++) { - m_persistentWorkspacesToCreate.emplace_back( - std::to_string(m_monitorId * amount + i + 1)); - } +void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJson) { + spdlog::info("Loading persistent workspaces from Waybar config"); + const std::vector keys = m_persistentWorkspaceConfig.getMemberNames(); + std::vector persistentWorkspacesToCreate; + + const std::string currentMonitor = m_bar.output->name; + const bool monitorInConfig = std::find(keys.begin(), keys.end(), currentMonitor) != keys.end(); + for (const std::string &key : keys) { + // only add if either: + // 1. key is the current monitor name + // 2. key is "*" and this monitor is not already defined in the config + bool canCreate = key == currentMonitor || (key == "*" && !monitorInConfig); + const Json::Value &value = m_persistentWorkspaceConfig[key]; + spdlog::trace("Parsing persistent workspace config: {} => {}", key, value.toStyledString()); + + if (value.isInt()) { + // value is a number => create that many workspaces for this monitor + if (canCreate) { + int amount = value.asInt(); + spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, currentMonitor); + for (int i = 0; i < amount; i++) { + persistentWorkspacesToCreate.emplace_back(std::to_string(m_monitorId * amount + i + 1)); } - } else if (value.isArray() && !value.empty()) { - // value is an array => create defined workspaces for this monitor - if (canCreate) { - for (const Json::Value &workspace : value) { - if (workspace.isInt()) { - spdlog::debug("Creating workspace {} on monitor {}", workspace, m_bar.output->name); - m_persistentWorkspacesToCreate.emplace_back(std::to_string(workspace.asInt())); - } - } - } else { - // key is the workspace and value is array of monitors to create on - for (const Json::Value &monitor : value) { - if (monitor.isString() && monitor.asString() == m_bar.output->name) { - m_persistentWorkspacesToCreate.emplace_back(key); - break; - } + } + } else if (value.isArray() && !value.empty()) { + // value is an array => create defined workspaces for this monitor + if (canCreate) { + for (const Json::Value &workspace : value) { + if (workspace.isInt()) { + spdlog::debug("Creating workspace {} on monitor {}", workspace, currentMonitor); + persistentWorkspacesToCreate.emplace_back(std::to_string(workspace.asInt())); } } } else { - // this workspace should be displayed on all monitors - m_persistentWorkspacesToCreate.emplace_back(key); + // key is the workspace and value is array of monitors to create on + for (const Json::Value &monitor : value) { + if (monitor.isString() && monitor.asString() == currentMonitor) { + persistentWorkspacesToCreate.emplace_back(currentMonitor); + break; + } + } } + } else { + // this workspace should be displayed on all monitors + persistentWorkspacesToCreate.emplace_back(key); } } -} -void Workspaces::createPersistentWorkspaces() { - for (const std::string &workspaceName : m_persistentWorkspacesToCreate) { - Json::Value newWorkspace; - try { - // numbered persistent workspaces get the name as ID - newWorkspace["id"] = workspaceName == "special" ? -99 : std::stoi(workspaceName); - } catch (const std::exception &e) { - // named persistent workspaces start with ID=0 - newWorkspace["id"] = 0; - } - newWorkspace["name"] = workspaceName; - newWorkspace["monitor"] = m_bar.output->name; - newWorkspace["windows"] = 0; - newWorkspace["persistent"] = true; - - createWorkspace(newWorkspace); + for (auto const &workspace : persistentWorkspacesToCreate) { + auto const workspaceData = createPersistentWorkspaceData(workspace, m_bar.output->name); + m_workspacesToCreate.emplace_back(workspaceData, clientsJson); } } -void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) { - for (const auto &client : clientsJson) { - if (client["workspace"]["id"].asInt() == workspaceId) { - registerOrphanWindow({client}); +void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &clientsJson) { + spdlog::info("Loading persistent workspaces from Hyprland workspace rules"); + + auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); + for (Json::Value const &rule : workspaceRules) { + if (!rule["workspaceString"].isString()) { + spdlog::warn("Workspace rules: invalid workspaceString, skipping: {}", rule); + continue; + } + if (!rule["persistent"].asBool()) { + continue; + } + auto const &workspace = rule["workspaceString"].asString(); + auto const &monitor = rule["monitor"].asString(); + // create this workspace persistently if: + // 1. the allOutputs config option is enabled + // 2. the rule's monitor is the current monitor + // 3. no monitor is specified in the rule => assume it needs to be persistent on every monitor + if (allOutputs() || m_bar.output->name == monitor || monitor.empty()) { + // => persistent workspace should be shown on this monitor + auto workspaceData = createPersistentWorkspaceData(workspace, m_bar.output->name); + m_workspacesToCreate.emplace_back(workspaceData, clientsJson); + } else { + m_workspacesToRemove.emplace_back(workspace); } } } -void Workspaces::init() { - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); - +void Workspaces::setCurrentMonitorId() { // get monitor ID from name (used by persistent workspaces) m_monitorId = 0; auto monitors = gIPC->getSocket1JsonReply("monitors"); @@ -657,29 +720,58 @@ void Workspaces::init() { spdlog::error("Monitor '{}' does not have an ID? Using 0", m_bar.output->name); } else { m_monitorId = (*currentMonitor)["id"].asInt(); + spdlog::trace("Current monitor ID: {}", m_monitorId); } +} - const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - const Json::Value clientsJson = gIPC->getSocket1JsonReply("clients"); +void Workspaces::initializeWorkspaces() { + spdlog::debug("Initializing workspaces"); + + // if the workspace rules changed since last initialization, make sure we reset everything: + for (auto &workspace : m_workspaces) { + m_workspacesToRemove.push_back(workspace->name()); + } + + // get all current workspaces + auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + auto const clientsJson = gIPC->getSocket1JsonReply("clients"); for (Json::Value workspaceJson : workspacesJson) { std::string workspaceName = workspaceJson["name"].asString(); if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && (!workspaceName.starts_with("special") || showSpecial()) && !isWorkspaceIgnored(workspaceName)) { - createWorkspace(workspaceJson, clientsJson); + m_workspacesToCreate.emplace_back(workspaceJson, clientsJson); } else { extendOrphans(workspaceJson["id"].asInt(), clientsJson); } } - fillPersistentWorkspaces(); - createPersistentWorkspaces(); + spdlog::debug("Initializing persistent workspaces"); + if (m_persistentWorkspaceConfig.isObject()) { + // a persistent workspace config is defined, so use that instead of workspace rules + loadPersistentWorkspacesFromConfig(clientsJson); + } else { + // no persistent workspaces config defined, use Hyprland's workspace rules + loadPersistentWorkspacesFromWorkspaceRules(clientsJson); + } +} - updateWindowCount(); +void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) { + spdlog::trace("Extending orphans with workspace {}", workspaceId); + for (const auto &client : clientsJson) { + if (client["workspace"]["id"].asInt() == workspaceId) { + registerOrphanWindow({client}); + } + } +} - sortWorkspaces(); +void Workspaces::init() { + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + initializeWorkspaces(); + updateWindowCount(); + sortWorkspaces(); dp.emit(); } @@ -696,7 +788,8 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma m_name(workspace_data["name"].asString()), m_output(workspace_data["monitor"].asString()), // TODO:allow using monitor desc m_windows(workspace_data["windows"].asInt()), - m_active(true) { + m_isActive(true), + m_isPersistent(workspace_data["persistent"].asBool()) { if (m_name.starts_with("name:")) { m_name = m_name.substr(5); } else if (m_name.starts_with("special")) { @@ -704,10 +797,6 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma m_isSpecial = true; } - if (workspace_data.isMember("persistent")) { - m_isPersistent = workspace_data["persistent"].asBool(); - } - m_button.add_events(Gdk::BUTTON_PRESS_MASK); m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked), false); @@ -813,8 +902,7 @@ void Workspaces::sortWorkspaces() { if (a->id() == -99 || b->id() == -99) { return b->id() == -99; } - // both are 0 (not yet named persistents) / both are named specials (-98 <= ID - // <=-1) + // both are 0 (not yet named persistents) / named specials (-98 <= ID <= -1) return isNameLess; } @@ -833,6 +921,7 @@ void Workspaces::sortWorkspaces() { } std::string &Workspace::selectIcon(std::map &icons_map) { + spdlog::trace("Selecting icon for workspace {}", name()); if (isUrgent()) { auto urgentIconIt = icons_map.find("urgent"); if (urgentIconIt != icons_map.end()) { @@ -889,21 +978,19 @@ std::string &Workspace::selectIcon(std::map &icons_map } bool Workspace::handleClicked(GdkEventButton *bt) const { - if (bt->type == GDK_BUTTON_PRESS) { - try { - if (id() > 0) { // normal or numbered persistent - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); - } else if (!isSpecial()) { // named - gIPC->getSocket1Reply("dispatch workspace name:" + name()); - } else if (id() != -99) { // named special - gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); - } else { // special - gIPC->getSocket1Reply("dispatch togglespecialworkspace"); - } - return true; - } catch (const std::exception &e) { - spdlog::error("Failed to dispatch workspace: {}", e.what()); + try { + if (id() > 0) { // normal + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } else if (!isSpecial()) { // named (this includes persistent) + gIPC->getSocket1Reply("dispatch workspace name:" + name()); + } else if (id() != -99) { // named special + gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); + } else { // special + gIPC->getSocket1Reply("dispatch togglespecialworkspace"); } + return true; + } catch (const std::exception &e) { + spdlog::error("Failed to dispatch workspace: {}", e.what()); } return false; } diff --git a/src/util/regex_collection.cpp b/src/util/regex_collection.cpp index df0879c18..704d65455 100644 --- a/src/util/regex_collection.cpp +++ b/src/util/regex_collection.cpp @@ -66,4 +66,4 @@ std::string& RegexCollection::get(std::string& value) { return get(value, matched_any); } -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util From 4420447e7470b224c6de29b5947a55eaa54a95d8 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Feb 2024 01:50:40 -0800 Subject: [PATCH 066/407] fix(bar): use std::string for mode names `string_view` leads to UAF when reading custom mode definitions from the configuration. --- include/bar.hpp | 8 ++++---- src/bar.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 0cacc3d71..e218496ca 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -71,16 +71,16 @@ class BarSurface { class Bar { public: - using bar_mode_map = std::map; + using bar_mode_map = std::map; static const bar_mode_map PRESET_MODES; - static const std::string_view MODE_DEFAULT; - static const std::string_view MODE_INVISIBLE; + static const std::string MODE_DEFAULT; + static const std::string MODE_INVISIBLE; Bar(struct waybar_output *w_output, const Json::Value &); Bar(const Bar &) = delete; ~Bar(); - void setMode(const std::string_view &); + void setMode(const std::string &mode); void setVisible(bool visible); void toggle(); void handleSignal(int); diff --git a/src/bar.cpp b/src/bar.cpp index e919ded2c..f7847ae10 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -54,8 +54,8 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // .passthrough = true, .visible = true}}}; -const std::string_view Bar::MODE_DEFAULT = "default"; -const std::string_view Bar::MODE_INVISIBLE = "invisible"; +const std::string Bar::MODE_DEFAULT = "default"; +const std::string Bar::MODE_INVISIBLE = "invisible"; const std::string_view DEFAULT_BAR_ID = "bar-0"; /* Deserializer for enum bar_layer */ @@ -117,7 +117,7 @@ Glib::ustring to_string(Gtk::PositionType pos) { * Assumes that all the values in the object are deserializable to the same type. */ template ::value>> + typename = std::enable_if_t::value>> void from_json(const Json::Value& j, std::map& m) { if (j.isObject()) { for (auto it = j.begin(); it != j.end(); ++it) { @@ -409,7 +409,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) /* Need to define it here because of forward declared members */ waybar::Bar::~Bar() = default; -void waybar::Bar::setMode(const std::string_view& mode) { +void waybar::Bar::setMode(const std::string& mode) { using namespace std::literals::string_literals; auto style = window.get_style_context(); From 8a4a44896a93fce700b026dae64ff69b7c59ea35 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Feb 2024 02:55:55 -0800 Subject: [PATCH 067/407] refactor: merge BarSurface into Bar With only one implementation left, the abstraction is no longer necessary. --- include/bar.hpp | 21 +--- src/bar.cpp | 250 +++++++++++++++++++++--------------------------- 2 files changed, 114 insertions(+), 157 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index e218496ca..6dc3c03df 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -53,22 +53,6 @@ class BarIpcClient; } #endif // HAVE_SWAY -class BarSurface { - protected: - BarSurface() = default; - - public: - virtual void setExclusiveZone(bool enable) = 0; - virtual void setLayer(bar_layer layer) = 0; - virtual void setMargins(const struct bar_margins &margins) = 0; - virtual void setPassThrough(bool enable) = 0; - virtual void setPosition(Gtk::PositionType position) = 0; - virtual void setSize(uint32_t width, uint32_t height) = 0; - virtual void commit(){}; - - virtual ~BarSurface() = default; -}; - class Bar { public: using bar_mode_map = std::map; @@ -107,6 +91,8 @@ class Bar { void setupAltFormatKeyForModule(const std::string &module_name); void setupAltFormatKeyForModuleList(const char *module_list_name); void setMode(const bar_mode &); + void setPassThrough(bool passthrough); + void setPosition(Gtk::PositionType position); void onConfigure(GdkEventConfigure *ev); void configureGlobalOffset(int width, int height); void onOutputGeometryChanged(); @@ -116,8 +102,9 @@ class Bar { std::string last_mode_{MODE_DEFAULT}; struct bar_margins margins_; + uint32_t width_, height_; + bool passthrough_; - std::unique_ptr surface_impl_; Gtk::Box left_; Gtk::Box center_; Gtk::Box right_; diff --git a/src/bar.cpp b/src/bar.cpp index f7847ae10..33a8b1410 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -126,134 +126,6 @@ void from_json(const Json::Value& j, std::map& m) { } } -struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { - GLSSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} { - output_name_ = output.name; - // this has to be executed before GtkWindow.realize - gtk_layer_init_for_window(window_.gobj()); - gtk_layer_set_keyboard_mode(window.gobj(), GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); - gtk_layer_set_monitor(window_.gobj(), output.monitor->gobj()); - gtk_layer_set_namespace(window_.gobj(), "waybar"); - - window.signal_map_event().connect_notify(sigc::mem_fun(*this, &GLSSurfaceImpl::onMap)); - window.signal_configure_event().connect_notify( - sigc::mem_fun(*this, &GLSSurfaceImpl::onConfigure)); - } - - void setExclusiveZone(bool enable) override { - if (enable) { - gtk_layer_auto_exclusive_zone_enable(window_.gobj()); - } else { - gtk_layer_set_exclusive_zone(window_.gobj(), 0); - } - } - - void setMargins(const struct bar_margins& margins) override { - gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_LEFT, margins.left); - gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, margins.right); - gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_TOP, margins.top); - gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, margins.bottom); - } - - void setLayer(bar_layer value) override { - auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM; - if (value == bar_layer::TOP) { - layer = GTK_LAYER_SHELL_LAYER_TOP; - } else if (value == bar_layer::OVERLAY) { - layer = GTK_LAYER_SHELL_LAYER_OVERLAY; - } - gtk_layer_set_layer(window_.gobj(), layer); - } - - void setPassThrough(bool enable) override { - passthrough_ = enable; - auto gdk_window = window_.get_window(); - if (gdk_window) { - Cairo::RefPtr region; - if (enable) { - region = Cairo::Region::create(); - } - gdk_window->input_shape_combine_region(region, 0, 0); - } - } - - void setPosition(Gtk::PositionType position) override { - auto unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM; - orientation_ = Gtk::ORIENTATION_HORIZONTAL; - switch (position) { - case Gtk::POS_LEFT: - unanchored = GTK_LAYER_SHELL_EDGE_RIGHT; - orientation_ = Gtk::ORIENTATION_VERTICAL; - break; - case Gtk::POS_RIGHT: - unanchored = GTK_LAYER_SHELL_EDGE_LEFT; - orientation_ = Gtk::ORIENTATION_VERTICAL; - break; - case Gtk::POS_TOP: - unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM; - break; - case Gtk::POS_BOTTOM: - unanchored = GTK_LAYER_SHELL_EDGE_TOP; - break; - }; - - for (auto edge : {GTK_LAYER_SHELL_EDGE_LEFT, GTK_LAYER_SHELL_EDGE_RIGHT, - GTK_LAYER_SHELL_EDGE_TOP, GTK_LAYER_SHELL_EDGE_BOTTOM}) { - gtk_layer_set_anchor(window_.gobj(), edge, unanchored != edge); - } - - // Disable anchoring for other edges too if the width - // or the height has been set to a value other than 'auto' - // otherwise the bar will use all space - if (orientation_ == Gtk::ORIENTATION_VERTICAL && height_ > 1) { - gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, false); - gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_TOP, false); - } else if (orientation_ == Gtk::ORIENTATION_HORIZONTAL && width_ > 1) { - gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_LEFT, false); - gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, false); - } - } - - void setSize(uint32_t width, uint32_t height) override { - width_ = width; - height_ = height; - window_.set_size_request(width_, height_); - }; - - private: - Gtk::Window& window_; - Gtk::Orientation orientation_ = Gtk::ORIENTATION_HORIZONTAL; - std::string output_name_; - uint32_t width_; - uint32_t height_; - bool passthrough_ = false; - - void onMap(GdkEventAny* ev) { setPassThrough(passthrough_); } - - void onConfigure(GdkEventConfigure* ev) { - /* - * GTK wants new size for the window. - * Actual resizing and management of the exclusve zone is handled within the gtk-layer-shell - * code. This event handler only updates stored size of the window and prints some warnings. - * - * Note: forced resizing to a window smaller than required by GTK would not work with - * gtk-layer-shell. - */ - if (orientation_ == Gtk::ORIENTATION_VERTICAL) { - if (width_ > 1 && ev->width > static_cast(width_)) { - spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); - } - } else { - if (height_ > 1 && ev->height > static_cast(height_)) { - spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); - } - } - width_ = ev->width; - height_ = ev->height; - spdlog::info(BAR_SIZE_MSG, width_, height_, output_name_); - } -}; - }; // namespace waybar waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) @@ -296,8 +168,8 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) right_.set_spacing(spacing); } - uint32_t height = config["height"].isUInt() ? config["height"].asUInt() : 0; - uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0; + height_ = config["height"].isUInt() ? config["height"].asUInt() : 0; + width_ = config["width"].isUInt() ? config["width"].asUInt() : 0; if (config["margin-top"].isInt() || config["margin-right"].isInt() || config["margin-bottom"].isInt() || config["margin-left"].isInt()) { @@ -348,12 +220,23 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) output->monitor->property_geometry().signal_changed().connect( sigc::mem_fun(*this, &Bar::onOutputGeometryChanged)); - surface_impl_ = std::make_unique(window, *output); - surface_impl_->setMargins(margins_); - surface_impl_->setSize(width, height); + // this has to be executed before GtkWindow.realize + auto* gtk_window = window.gobj(); + gtk_layer_init_for_window(gtk_window); + gtk_layer_set_keyboard_mode(gtk_window, GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); + gtk_layer_set_monitor(gtk_window, output->monitor->gobj()); + gtk_layer_set_namespace(gtk_window, "waybar"); + + gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, margins_.left); + gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right); + gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_TOP, margins_.top); + gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, margins_.bottom); + + window.set_size_request(width_, height_); + // Position needs to be set after calculating the height due to the // GTK layer shell anchors logic relying on the dimensions of the bar. - surface_impl_->setPosition(position); + setPosition(position); /* Read custom modes if available */ if (auto modes = config.get("modes", {}); modes.isObject()) { @@ -430,9 +313,23 @@ void waybar::Bar::setMode(const std::string& mode) { } void waybar::Bar::setMode(const struct bar_mode& mode) { - surface_impl_->setLayer(mode.layer); - surface_impl_->setExclusiveZone(mode.exclusive); - surface_impl_->setPassThrough(mode.passthrough); + auto* gtk_window = window.gobj(); + + auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM; + if (mode.layer == bar_layer::TOP) { + layer = GTK_LAYER_SHELL_LAYER_TOP; + } else if (mode.layer == bar_layer::OVERLAY) { + layer = GTK_LAYER_SHELL_LAYER_OVERLAY; + } + gtk_layer_set_layer(gtk_window, layer); + + if (mode.exclusive) { + gtk_layer_auto_exclusive_zone_enable(gtk_window); + } else { + gtk_layer_set_exclusive_zone(gtk_window, 0); + } + + setPassThrough(passthrough_ = mode.passthrough); if (mode.visible) { window.get_style_context()->remove_class("hidden"); @@ -441,7 +338,58 @@ void waybar::Bar::setMode(const struct bar_mode& mode) { window.get_style_context()->add_class("hidden"); window.set_opacity(0); } - surface_impl_->commit(); +} + +void waybar::Bar::setPassThrough(bool passthrough) { + auto gdk_window = window.get_window(); + if (gdk_window) { + Cairo::RefPtr region; + if (passthrough) { + region = Cairo::Region::create(); + } + gdk_window->input_shape_combine_region(region, 0, 0); + } +} + +void waybar::Bar::setPosition(Gtk::PositionType position) { + std::array anchors; + anchors.fill(TRUE); + + auto orientation = (position == Gtk::POS_LEFT || position == Gtk::POS_RIGHT) + ? Gtk::ORIENTATION_VERTICAL + : Gtk::ORIENTATION_HORIZONTAL; + + switch (position) { + case Gtk::POS_LEFT: + anchors[GTK_LAYER_SHELL_EDGE_RIGHT] = FALSE; + break; + case Gtk::POS_RIGHT: + anchors[GTK_LAYER_SHELL_EDGE_LEFT] = FALSE; + break; + case Gtk::POS_BOTTOM: + anchors[GTK_LAYER_SHELL_EDGE_TOP] = FALSE; + break; + default: /* Gtk::POS_TOP */ + anchors[GTK_LAYER_SHELL_EDGE_BOTTOM] = FALSE; + break; + }; + // Disable anchoring for other edges too if the width + // or the height has been set to a value other than 'auto' + // otherwise the bar will use all space + uint32_t configured_width = config["width"].isUInt() ? config["width"].asUInt() : 0; + uint32_t configured_height = config["height"].isUInt() ? config["height"].asUInt() : 0; + if (orientation == Gtk::ORIENTATION_VERTICAL && configured_height > 1) { + anchors[GTK_LAYER_SHELL_EDGE_TOP] = FALSE; + anchors[GTK_LAYER_SHELL_EDGE_BOTTOM] = FALSE; + } else if (orientation == Gtk::ORIENTATION_HORIZONTAL && configured_width > 1) { + anchors[GTK_LAYER_SHELL_EDGE_LEFT] = FALSE; + anchors[GTK_LAYER_SHELL_EDGE_RIGHT] = FALSE; + } + + for (auto edge : {GTK_LAYER_SHELL_EDGE_LEFT, GTK_LAYER_SHELL_EDGE_RIGHT, GTK_LAYER_SHELL_EDGE_TOP, + GTK_LAYER_SHELL_EDGE_BOTTOM}) { + gtk_layer_set_anchor(window.gobj(), edge, anchors[edge]); + } } void waybar::Bar::onMap(GdkEventAny*) { @@ -451,6 +399,8 @@ void waybar::Bar::onMap(GdkEventAny*) { auto gdk_window = window.get_window()->gobj(); surface = gdk_wayland_window_get_wl_surface(gdk_window); configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window)); + + setPassThrough(passthrough_); } void waybar::Bar::setVisible(bool value) { @@ -595,7 +545,28 @@ auto waybar::Bar::setupWidgets() -> void { } void waybar::Bar::onConfigure(GdkEventConfigure* ev) { + /* + * GTK wants new size for the window. + * Actual resizing and management of the exclusve zone is handled within the gtk-layer-shell + * code. This event handler only updates stored size of the window and prints some warnings. + * + * Note: forced resizing to a window smaller than required by GTK would not work with + * gtk-layer-shell. + */ + if (orientation == Gtk::ORIENTATION_VERTICAL) { + if (width_ > 1 && ev->width > static_cast(width_)) { + spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); + } + } else { + if (height_ > 1 && ev->height > static_cast(height_)) { + spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); + } + } + width_ = ev->width; + height_ = ev->height; + configureGlobalOffset(ev->width, ev->height); + spdlog::info(BAR_SIZE_MSG, ev->width, ev->height, output->name); } void waybar::Bar::configureGlobalOffset(int width, int height) { @@ -624,8 +595,7 @@ void waybar::Bar::configureGlobalOffset(int width, int height) { else y = (monitor_geometry.height - height) / 2; break; - case Gtk::POS_TOP: - // position is top + default: /* Gtk::POS_TOP */ if (width + margins_.left + margins_.right >= monitor_geometry.width) x = margins_.left; else From 745d5687b884eec78691de0d304f6a3eee7ae863 Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Mon, 19 Feb 2024 22:23:03 +0100 Subject: [PATCH 068/407] Fixed window#waybar.swallowing for module hyprland/window --- src/modules/hyprland/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 77723bc0e..ea65b9235 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -151,7 +151,7 @@ void Window::queryActiveWorkspace() { return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); }); swallowing_ = std::any_of(workspace_windows.begin(), workspace_windows.end(), - [&](Json::Value window) { return !window["swallowing"].isNull(); }); + [&](Json::Value window) { return window["swallowing"].asString() != "0x0"; }); std::vector visible_windows; std::copy_if(workspace_windows.begin(), workspace_windows.end(), std::back_inserter(visible_windows), From bd0bf836c7099351292a9a8c42b8aa2738af4267 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 19 Feb 2024 23:07:50 +0100 Subject: [PATCH 069/407] fix: lint --- src/modules/clock.cpp | 4 ++-- src/modules/temperature.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index e83cbef03..c889a13f4 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -129,7 +129,6 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) }; } - auto waybar::modules::Clock::update() -> void { auto tz{tzList_[tzCurrIdx_] ?: current_zone()}; const zoned_time now{tz, floor(system_clock::now())}; @@ -150,7 +149,8 @@ auto waybar::modules::Clock::update() -> void { tlpText_ = std::regex_replace(tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_); tlpText_ = std::regex_replace(tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); - tlpText_ = std::regex_replace(tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); + tlpText_ = + std::regex_replace(tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); } tlpText_ = fmt_lib::vformat(locale_, tlpText_, fmt_lib::make_format_args(shiftedNow)); diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 054c9bd26..accab969c 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -24,7 +24,8 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val } } } else if (config_["hwmon-path-abs"].isString() && config_["input-filename"].isString()) { - for (const auto& hwmon : std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())) { + for (const auto& hwmon : + std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())) { if (hwmon.path().filename().string().starts_with("hwmon")) { file_path_ = hwmon.path().string() + "/" + config_["input-filename"].asString(); break; From a95b6a39c9bee1a6312559da5c9796dc92eabdd0 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Feb 2024 13:54:46 -0800 Subject: [PATCH 070/407] build: mark bluetooth as Linux-specific --- include/factory.hpp | 2 ++ meson.build | 4 ++-- src/factory.cpp | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/factory.hpp b/include/factory.hpp index 339f92edd..61002b0b1 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -82,7 +82,9 @@ #ifdef HAVE_LIBSNDIO #include "modules/sndio.hpp" #endif +#if defined(__linux__) #include "modules/bluetooth.hpp" +#endif #ifdef HAVE_LOGIND_INHIBITOR #include "modules/inhibitor.hpp" #endif diff --git a/meson.build b/meson.build index d2dbd1f94..475fcac94 100644 --- a/meson.build +++ b/meson.build @@ -162,7 +162,6 @@ src_files = files( 'src/ALabel.cpp', 'src/AIconLabel.cpp', 'src/AAppIconLabel.cpp', - 'src/modules/bluetooth.cpp', 'src/modules/custom.cpp', 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', @@ -188,7 +187,6 @@ src_files = files( ) man_files = files( - 'man/waybar-bluetooth.5.scd', 'man/waybar-custom.5.scd', 'man/waybar-disk.5.scd', 'man/waybar-idle-inhibitor.5.scd', @@ -205,6 +203,7 @@ if is_linux add_project_arguments('-DHAVE_SYSTEMD_MONITOR', language: 'cpp') src_files += files( 'src/modules/battery.cpp', + 'src/modules/bluetooth.cpp', 'src/modules/cffi.cpp', 'src/modules/cpu.cpp', 'src/modules/cpu_frequency/common.cpp', @@ -217,6 +216,7 @@ if is_linux ) man_files += files( 'man/waybar-battery.5.scd', + 'man/waybar-bluetooth.5.scd', 'man/waybar-cffi.5.scd', 'man/waybar-cpu.5.scd', 'man/waybar-memory.5.scd', diff --git a/src/factory.cpp b/src/factory.cpp index a3b66136a..e11d33ac9 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -178,9 +178,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, return new waybar::modules::Sndio(id, config_[name]); } #endif +#if defined(__linux__) if (ref == "bluetooth") { return new waybar::modules::Bluetooth(id, config_[name]); } +#endif #ifdef HAVE_LOGIND_INHIBITOR if (ref == "inhibitor") { return new waybar::modules::Inhibitor(id, bar_, config_[name]); From 41b2d0cb29d0cef9e9ce0c5f3b2cc44395cda2f8 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 19 Feb 2024 23:09:24 +0100 Subject: [PATCH 071/407] chore(workflows): concurrency --- .github/workflows/clang-format.yml | 18 +++++++++++------- .github/workflows/clang-tidy.yml | 4 ++++ .github/workflows/freebsd.yml | 4 ++++ .github/workflows/linux.yml | 4 ++++ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 46e338eff..40fd3126e 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -2,14 +2,18 @@ name: clang-format on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-format-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: DoozyX/clang-format-lint-action@v0.16.2 - name: clang-format - with: - source: '.' - extensions: 'hpp,h,cpp,c' - clangFormatVersion: 16 + - uses: actions/checkout@v3 + - uses: DoozyX/clang-format-lint-action@v0.16.2 + name: clang-format + with: + source: "." + extensions: "hpp,h,cpp,c" + clangFormatVersion: 16 diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 86dd1f731..191cabc7e 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -2,6 +2,10 @@ name: clang-tidy on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-tidy-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 46419c1d5..f0b8f69c7 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -2,6 +2,10 @@ name: freebsd on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-freebsd-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: clang: # Run actions in a FreeBSD VM on the macos-12 runner diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d97612d55..821f2c45d 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -2,6 +2,10 @@ name: linux on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-linux-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: build: strategy: From 742cd7f3714cfa5e087bd0bcf355b6febaeb6b80 Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Mon, 19 Feb 2024 23:10:10 +0100 Subject: [PATCH 072/407] Revert "Add style class for CPU state" --- include/modules/cpu.hpp | 1 - man/waybar-cpu.5.scd | 2 -- src/modules/cpu.cpp | 6 ------ 3 files changed, 9 deletions(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 449eb1b30..7f78c1650 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -22,7 +22,6 @@ class Cpu : public ALabel { private: std::vector> prev_times_; - std::string prev_state_; util::SleeperThread thread_; }; diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 64b2bde1d..484795689 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -121,5 +121,3 @@ CPU usage per core rendered as icons: # STYLE - *#cpu* -- *#cpu.* - - ** can be defined in the *config*. For more information see *states*. diff --git a/src/modules/cpu.cpp b/src/modules/cpu.cpp index 4fdb6590e..0703eaf7c 100644 --- a/src/modules/cpu.cpp +++ b/src/modules/cpu.cpp @@ -36,12 +36,6 @@ auto waybar::modules::Cpu::update() -> void { format = config_["format-" + state].asString(); } - if (!prev_state_.empty()) { - label_.get_style_context()->remove_class(prev_state_); - } - label_.get_style_context()->add_class(state); - prev_state_ = state; - if (format.empty()) { event_box_.hide(); } else { From 175852e5277a2ce85994433a5fba2b85f859d12e Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 19 Feb 2024 23:24:14 +0100 Subject: [PATCH 073/407] chore: auto label --- .github/labeler.yml | 59 +++++++++++++++++++++++++++++++++++ .github/workflows/labeler.yml | 21 +++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/labeler.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000..b412f4aaf --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,59 @@ +hyprland: + - "(hyprland)" + +network: + - "(network|wifi|ethernet)" + +bluetooth: + - "(bluetooth|bluez)" + +sway: + - "(sway)" + +cpu: + - "(cpu)" + +memory: + - "(memory|ram)" + +disk: + - "(disk|storage)" + +battery: + - "(upower|battery)" + +sni: + - "(sni|tray)" + +dwl: + - "(dwl)" + +custom: + - "(custom|module|extension|plugin|script)" + +mpd: + - "(mpd|music)" + +audio: + - "(pulseaudio|alsa|jack|audio|pirewire|wireplumber)" + +temperature: + - "(temperature|thermal|hwmon)" + +clock: + - "(clock|time|date)" + +gamemode: + - "(gamemode|game|gaming)" + +inhibitor: + - "(inhibitor|idle|lock|suspend|hibernate|logout)" + +cava: + - "(cava|audio-visualizer)" + +backlight: + - "(backlight|brightness)" + +keyboard: + - "(keyboard|keymap|layout|shortcut)" diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 000000000..321f69161 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,21 @@ +name: "Issue Labeler" +on: + issues: + types: [opened, edited] + pull_request: + types: [opened, edited] + +permissions: + issues: write + contents: read + +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: github/issue-labeler@v3.4 + with: + configuration-path: .github/labeler.yml + enable-versioned-regex: 0 + include-title: 1 + repo-token: ${{ github.token }} From ee2407496f174dbcec85cf0a92472add35891adc Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Mon, 19 Feb 2024 23:28:08 +0100 Subject: [PATCH 074/407] Revert "Implement windows formating in sway/workspaces" --- include/modules/sway/workspaces.hpp | 7 -- man/waybar-sway-workspaces.5.scd | 32 --------- src/modules/sway/workspaces.cpp | 105 +++++----------------------- 3 files changed, 16 insertions(+), 128 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 4258252a2..0efffe643 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -12,7 +12,6 @@ #include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" -#include "util/regex_collection.hpp" namespace waybar::modules::sway { @@ -28,13 +27,10 @@ class Workspaces : public AModule, public sigc::trackable { R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")"; static int convertWorkspaceNameToNum(std::string name); - static int windowRewritePriorityFunction(std::string const& window_rule); void onCmd(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&); bool filterButtons(); - static bool hasFlag(const Json::Value&, const std::string&); - void updateWindows(const Json::Value&, std::string&); Gtk::Button& addButton(const Json::Value&); void onButtonReady(const Json::Value&, Gtk::Button&); std::string getIcon(const std::string&, const Json::Value&); @@ -48,9 +44,6 @@ class Workspaces : public AModule, public sigc::trackable { std::vector high_priority_named_; std::vector workspaces_order_; Gtk::Box box_; - std::string m_formatWindowSeperator; - std::string m_windowRewriteDefault; - util::RegexCollection m_windowRewriteRules; util::JsonParser parser_; std::unordered_map buttons_; std::mutex mutex_; diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 3343b8d5a..cdb653f9d 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -82,23 +82,6 @@ warp-on-scroll: ++ default: true ++ If set to false, you can scroll to cycle through workspaces without mouse warping being enabled. If set to true this behaviour is disabled. -*window-rewrite*: ++ - typeof: object ++ - Regex rules to map window class to an icon or preferred method of representation for a workspace's window. - Keys are the rules, while the values are the methods of representation. - Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching. - -*window-rewrite-default*: - typeof: string ++ - default: "?" ++ - The default method of representation for a workspace's window. This will be used for windows whose classes do not match any of the rules in *window-rewrite*. - -*format-window-separator*: ++ - typeof: string ++ - default: " " ++ - The separator to be used between windows in a workspace. - - # FORMAT REPLACEMENTS *{value}*: Name of the workspace, as defined by sway. @@ -111,8 +94,6 @@ warp-on-scroll: ++ *{output}*: Output where the workspace is located. -*{windows}*: Result from window-rewrite - # ICONS Additional to workspace name matching, the following *format-icons* can be set. @@ -162,19 +143,6 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge } ``` -``` -"sway/workspaces": { - "format": "{name} {windows}", - "format-window-separator": " | ", - "window-rewrite-default": "{name}", - "window-format": "{name}", - "window-rewrite": { - "class": "", - "class": "k", - } -} -``` - # Style - *#workspaces button* diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index f96cccfd9..c8ec4387a 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -24,24 +24,6 @@ int Workspaces::convertWorkspaceNameToNum(std::string name) { return -1; } -int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) { - // Rules that match against title are prioritized - // Rules that don't specify if they're matching against either title or class are deprioritized - bool const hasTitle = window_rule.find("title") != std::string::npos; - bool const hasClass = window_rule.find("class") != std::string::npos; - - if (hasTitle && hasClass) { - return 3; - } - if (hasTitle) { - return 2; - } - if (hasClass) { - return 1; - } - return 0; -} - Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), bar_(bar), @@ -57,25 +39,10 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value } box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); - if (config_["format-window-separator"].isString()) { - m_formatWindowSeperator = config_["format-window-separator"].asString(); - } else { - m_formatWindowSeperator = " "; - } - const Json::Value &windowRewrite = config["window-rewrite"]; - - const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"]; - m_windowRewriteDefault = - windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; - - m_windowRewriteRules = waybar::util::RegexCollection( - windowRewrite, m_windowRewriteDefault, - [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); ipc_.subscribe(R"(["workspace"])"); - ipc_.subscribe(R"(["window"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd)); - ipc_.sendCmd(IPC_GET_TREE); + ipc_.sendCmd(IPC_GET_WORKSPACES); if (config["enable-bar-scroll"].asBool()) { auto &window = const_cast(bar_).window; window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); @@ -93,31 +60,26 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value void Workspaces::onEvent(const struct Ipc::ipc_response &res) { try { - ipc_.sendCmd(IPC_GET_TREE); + ipc_.sendCmd(IPC_GET_WORKSPACES); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } } void Workspaces::onCmd(const struct Ipc::ipc_response &res) { - if (res.type == IPC_GET_TREE) { + if (res.type == IPC_GET_WORKSPACES) { try { { std::lock_guard lock(mutex_); auto payload = parser_.parse(res.payload); workspaces_.clear(); - std::vector outputs; - std::copy_if(payload["nodes"].begin(), payload["nodes"].end(), std::back_inserter(outputs), + std::copy_if(payload.begin(), payload.end(), std::back_inserter(workspaces_), [&](const auto &workspace) { return !config_["all-outputs"].asBool() - ? workspace["name"].asString() == bar_.output->name + ? workspace["output"].asString() == bar_.output->name : true; }); - for (auto &output : outputs) { - std::copy(output["nodes"].begin(), output["nodes"].end(), - std::back_inserter(workspaces_)); - } if (config_["persistent_workspaces"].isObject()) { spdlog::warn( "persistent_workspaces is deprecated. Please change config to use " @@ -242,35 +204,6 @@ bool Workspaces::filterButtons() { return needReorder; } -bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { - if (node[flag].asBool()) { - return true; - } - - if (std::ranges::any_of(node["nodes"], [&](auto const &e) { return hasFlag(e, flag); })) { - return true; - } - return false; -} - -void Workspaces::updateWindows(const Json::Value &node, std::string &windows) { - auto format = config_["window-format"].asString(); - if (node["type"].asString() == "con" && node["name"].isString()) { - std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1); - std::string windowClass = node["app_id"].asString(); - std::string windowReprKey = fmt::format("class<{}> title<{}>", windowClass, title); - std::string window = m_windowRewriteRules.get(windowReprKey); - // allow result to have formatting - window = - fmt::format(fmt::runtime(window), fmt::arg("name", title), fmt::arg("class", windowClass)); - windows.append(window); - windows.append(m_formatWindowSeperator); - } - for (const Json::Value &child : node["nodes"]) { - updateWindows(child, windows); - } -} - auto Workspaces::update() -> void { std::lock_guard lock(mutex_); bool needReorder = filterButtons(); @@ -280,25 +213,22 @@ auto Workspaces::update() -> void { needReorder = true; } auto &button = bit == buttons_.end() ? addButton(*it) : bit->second; - if (needReorder) { - box_.reorder_child(button, it - workspaces_.begin()); - } - if (hasFlag((*it), "focused")) { + if ((*it)["focused"].asBool()) { button.get_style_context()->add_class("focused"); } else { button.get_style_context()->remove_class("focused"); } - if (hasFlag((*it), "visible")) { + if ((*it)["visible"].asBool()) { button.get_style_context()->add_class("visible"); } else { button.get_style_context()->remove_class("visible"); } - if (hasFlag((*it), "urgent")) { + if ((*it)["urgent"].asBool()) { button.get_style_context()->add_class("urgent"); } else { button.get_style_context()->remove_class("urgent"); } - if (hasFlag((*it), "target_output")) { + if ((*it)["target_output"].isString()) { button.get_style_context()->add_class("persistent"); } else { button.get_style_context()->remove_class("persistent"); @@ -312,19 +242,16 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("current_output"); } - std::string output = (*it)["name"].asString(); - std::string windows = ""; - if (config_["window-format"].isString()) { - updateWindows((*it), windows); + if (needReorder) { + box_.reorder_child(button, it - workspaces_.begin()); } + std::string output = (*it)["name"].asString(); if (config_["format"].isString()) { auto format = config_["format"].asString(); - output = fmt::format( - fmt::runtime(format), fmt::arg("icon", getIcon(output, *it)), fmt::arg("value", output), - fmt::arg("name", trimWorkspaceName(output)), fmt::arg("index", (*it)["num"].asString()), - fmt::arg("windows", - windows.substr(0, windows.length() - m_formatWindowSeperator.length())), - fmt::arg("output", (*it)["output"].asString())); + output = fmt::format(fmt::runtime(format), fmt::arg("icon", getIcon(output, *it)), + fmt::arg("value", output), fmt::arg("name", trimWorkspaceName(output)), + fmt::arg("index", (*it)["num"].asString()), + fmt::arg("output", (*it)["output"].asString())); } if (!config_["disable-markup"].asBool()) { static_cast(button.get_children()[0])->set_markup(output); From c6f5cbdf0c6f690e2e9c1ab2e61bf0f8d5583bda Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 18 Feb 2024 23:28:35 -0800 Subject: [PATCH 075/407] refactor: move all module includes to factory.cpp None of these includes are required in the header. --- include/factory.hpp | 109 ++------------------------------------------ src/factory.cpp | 106 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 108 deletions(-) diff --git a/include/factory.hpp b/include/factory.hpp index 61002b0b1..f805aab5e 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -1,114 +1,13 @@ #pragma once #include -#if defined(HAVE_CHRONO_TIMEZONES) || defined(HAVE_LIBDATE) -#include "modules/clock.hpp" -#else -#include "modules/simpleclock.hpp" -#endif -#ifdef HAVE_SWAY -#include "modules/sway/language.hpp" -#include "modules/sway/mode.hpp" -#include "modules/sway/scratchpad.hpp" -#include "modules/sway/window.hpp" -#include "modules/sway/workspaces.hpp" -#endif -#ifdef HAVE_WLR_TASKBAR -#include "modules/wlr/taskbar.hpp" -#endif -#ifdef HAVE_WLR_WORKSPACES -#include "modules/wlr/workspace_manager.hpp" -#endif -#ifdef HAVE_RIVER -#include "modules/river/layout.hpp" -#include "modules/river/mode.hpp" -#include "modules/river/tags.hpp" -#include "modules/river/window.hpp" -#endif -#ifdef HAVE_DWL -#include "modules/dwl/tags.hpp" -#endif -#ifdef HAVE_HYPRLAND -#include "modules/hyprland/backend.hpp" -#include "modules/hyprland/language.hpp" -#include "modules/hyprland/submap.hpp" -#include "modules/hyprland/window.hpp" -#include "modules/hyprland/workspaces.hpp" -#endif -#if defined(__FreeBSD__) || defined(__linux__) -#include "modules/battery.hpp" -#endif -#if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD) -#include "modules/cpu.hpp" -#include "modules/cpu_frequency.hpp" -#include "modules/cpu_usage.hpp" -#include "modules/load.hpp" -#endif -#include "modules/idle_inhibitor.hpp" -#if defined(HAVE_MEMORY_LINUX) || defined(HAVE_MEMORY_BSD) -#include "modules/memory.hpp" -#endif -#include "modules/disk.hpp" -#ifdef HAVE_DBUSMENU -#include "modules/sni/tray.hpp" -#endif -#ifdef HAVE_MPRIS -#include "modules/mpris/mpris.hpp" -#endif -#ifdef HAVE_LIBNL -#include "modules/network.hpp" -#endif -#ifdef HAVE_LIBUDEV -#include "modules/backlight.hpp" -#endif -#ifdef HAVE_LIBEVDEV -#include "modules/keyboard_state.hpp" -#endif -#ifdef HAVE_GAMEMODE -#include "modules/gamemode.hpp" -#endif -#ifdef HAVE_UPOWER -#include "modules/upower/upower.hpp" -#endif -#ifdef HAVE_PIPEWIRE -#include "modules/privacy/privacy.hpp" -#endif -#ifdef HAVE_LIBPULSE -#include "modules/pulseaudio.hpp" -#endif -#ifdef HAVE_LIBMPDCLIENT -#include "modules/mpd/mpd.hpp" -#endif -#ifdef HAVE_LIBSNDIO -#include "modules/sndio.hpp" -#endif -#if defined(__linux__) -#include "modules/bluetooth.hpp" -#endif -#ifdef HAVE_LOGIND_INHIBITOR -#include "modules/inhibitor.hpp" -#endif -#ifdef HAVE_LIBJACK -#include "modules/jack.hpp" -#endif -#ifdef HAVE_LIBWIREPLUMBER -#include "modules/wireplumber.hpp" -#endif -#ifdef HAVE_LIBCAVA -#include "modules/cava.hpp" -#endif -#ifdef HAVE_SYSTEMD_MONITOR -#include "modules/systemd_failed_units.hpp" -#endif -#include "bar.hpp" -#include "modules/cffi.hpp" -#include "modules/custom.hpp" -#include "modules/image.hpp" -#include "modules/temperature.hpp" -#include "modules/user.hpp" + +#include namespace waybar { +class Bar; + class Factory { public: Factory(const Bar& bar, const Json::Value& config); diff --git a/src/factory.cpp b/src/factory.cpp index e11d33ac9..6b709f339 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -1,12 +1,112 @@ #include "factory.hpp" -#ifdef HAVE_LIBPULSE -#include "modules/pulseaudio_slider.hpp" -#endif +#include "bar.hpp" +#if defined(HAVE_CHRONO_TIMEZONES) || defined(HAVE_LIBDATE) +#include "modules/clock.hpp" +#else +#include "modules/simpleclock.hpp" +#endif +#ifdef HAVE_SWAY +#include "modules/sway/language.hpp" +#include "modules/sway/mode.hpp" +#include "modules/sway/scratchpad.hpp" +#include "modules/sway/window.hpp" +#include "modules/sway/workspaces.hpp" +#endif +#ifdef HAVE_WLR_TASKBAR +#include "modules/wlr/taskbar.hpp" +#endif +#ifdef HAVE_WLR_WORKSPACES +#include "modules/wlr/workspace_manager.hpp" +#endif +#ifdef HAVE_RIVER +#include "modules/river/layout.hpp" +#include "modules/river/mode.hpp" +#include "modules/river/tags.hpp" +#include "modules/river/window.hpp" +#endif +#ifdef HAVE_DWL +#include "modules/dwl/tags.hpp" +#endif +#ifdef HAVE_HYPRLAND +#include "modules/hyprland/language.hpp" +#include "modules/hyprland/submap.hpp" +#include "modules/hyprland/window.hpp" +#include "modules/hyprland/workspaces.hpp" +#endif +#if defined(__FreeBSD__) || defined(__linux__) +#include "modules/battery.hpp" +#endif +#if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD) +#include "modules/cpu.hpp" +#include "modules/cpu_frequency.hpp" +#include "modules/cpu_usage.hpp" +#include "modules/load.hpp" +#endif +#include "modules/idle_inhibitor.hpp" +#if defined(HAVE_MEMORY_LINUX) || defined(HAVE_MEMORY_BSD) +#include "modules/memory.hpp" +#endif +#include "modules/disk.hpp" +#ifdef HAVE_DBUSMENU +#include "modules/sni/tray.hpp" +#endif +#ifdef HAVE_MPRIS +#include "modules/mpris/mpris.hpp" +#endif +#ifdef HAVE_LIBNL +#include "modules/network.hpp" +#endif #ifdef HAVE_LIBUDEV +#include "modules/backlight.hpp" #include "modules/backlight_slider.hpp" #endif +#ifdef HAVE_LIBEVDEV +#include "modules/keyboard_state.hpp" +#endif +#ifdef HAVE_GAMEMODE +#include "modules/gamemode.hpp" +#endif +#ifdef HAVE_UPOWER +#include "modules/upower/upower.hpp" +#endif +#ifdef HAVE_PIPEWIRE +#include "modules/privacy/privacy.hpp" +#endif +#ifdef HAVE_LIBPULSE +#include "modules/pulseaudio.hpp" +#include "modules/pulseaudio_slider.hpp" +#endif +#ifdef HAVE_LIBMPDCLIENT +#include "modules/mpd/mpd.hpp" +#endif +#ifdef HAVE_LIBSNDIO +#include "modules/sndio.hpp" +#endif +#if defined(__linux__) +#include "modules/bluetooth.hpp" +#endif +#ifdef HAVE_LOGIND_INHIBITOR +#include "modules/inhibitor.hpp" +#endif +#ifdef HAVE_LIBJACK +#include "modules/jack.hpp" +#endif +#ifdef HAVE_LIBWIREPLUMBER +#include "modules/wireplumber.hpp" +#endif +#ifdef HAVE_LIBCAVA +#include "modules/cava.hpp" +#endif +#ifdef HAVE_SYSTEMD_MONITOR +#include "modules/systemd_failed_units.hpp" +#endif +#include "modules/cffi.hpp" +#include "modules/custom.hpp" +#include "modules/image.hpp" +#include "modules/temperature.hpp" +#include "modules/user.hpp" waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {} From 4a5444d1962cc746d86b61045aead3715a5d52e2 Mon Sep 17 00:00:00 2001 From: Jeremy Huang Date: Mon, 19 Feb 2024 16:16:46 -0800 Subject: [PATCH 076/407] fix click special --- src/modules/hyprland/workspaces.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index edea96347..3e3931211 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -1001,19 +1001,21 @@ std::string &Workspace::selectIcon(std::map &icons_map } bool Workspace::handleClicked(GdkEventButton *bt) const { - try { - if (id() > 0) { // normal - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); - } else if (!isSpecial()) { // named (this includes persistent) - gIPC->getSocket1Reply("dispatch workspace name:" + name()); - } else if (id() != -99) { // named special - gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); - } else { // special - gIPC->getSocket1Reply("dispatch togglespecialworkspace"); + if (bt->type == GDK_BUTTON_PRESS) { + try { + if (id() > 0) { // normal + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } else if (!isSpecial()) { // named (this includes persistent) + gIPC->getSocket1Reply("dispatch workspace name:" + name()); + } else if (id() != -99) { // named special + gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); + } else { // special + gIPC->getSocket1Reply("dispatch togglespecialworkspace"); + } + return true; + } catch (const std::exception &e) { + spdlog::error("Failed to dispatch workspace: {}", e.what()); } - return true; - } catch (const std::exception &e) { - spdlog::error("Failed to dispatch workspace: {}", e.what()); } return false; } From bdb2f2bd1ab0b4d43ddf44efd474826481e489dc Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Feb 2024 16:28:29 -0800 Subject: [PATCH 077/407] chore: update Debian CI dependencies This should speed-up "linux (debian)" and "clang-tidy" builds and enable lints for more modules. --- Dockerfiles/debian | 52 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 7536fdfdf..0745935ec 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,11 +3,47 @@ FROM debian:sid RUN apt update && \ - apt 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 libplayerctl-dev sudo python3-venv python3-pip && \ - apt clean + apt install --no-install-recommends --no-install-suggests -y \ + build-essential \ + catch2 \ + cmake \ + git \ + gobject-introspection \ + libdbusmenu-gtk3-dev \ + libegl1-mesa-dev \ + libfmt-dev \ + libgbm-dev \ + libgirepository1.0-dev \ + libgles2-mesa-dev \ + libgtk-layer-shell-dev \ + libgtkmm-3.0-dev \ + libhowardhinnant-date-dev \ + libiniparser-dev \ + libinput-dev \ + libjack-jackd2-dev \ + libjsoncpp-dev \ + libmpdclient-dev \ + libnl-3-dev \ + libnl-genl-3-dev \ + libpixman-1-dev \ + libplayerctl-dev \ + libpugixml-dev \ + libpulse-dev \ + libsndio-dev \ + libspdlog-dev \ + libudev-dev \ + libupower-glib-dev \ + libwayland-dev \ + libwireplumber-0.4-dev \ + libxkbcommon-dev \ + libxkbregistry-dev \ + locales \ + meson \ + ninja-build \ + pkg-config \ + python3-pip \ + python3-venv \ + scdoc \ + sudo \ + wayland-protocols \ + && apt clean From d59d6e876599bbfb05d13b66746b9e4f15bfa7b9 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Feb 2024 16:33:01 -0800 Subject: [PATCH 078/407] chore: remove duplicate fedora/c++20 job definition --- .github/workflows/linux.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 821f2c45d..dc6b7edef 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -18,9 +18,6 @@ jobs: - opensuse - gentoo cpp_std: [c++20] - include: - - distro: fedora - cpp_std: c++20 runs-on: ubuntu-latest container: From 5d6acfd1d40934a2bb3c94e593f705a12a76b5c8 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Feb 2024 16:49:27 -0800 Subject: [PATCH 079/407] test: restore compatibility with older Catch2 releases --- meson.build | 1 - test/css_reload_helper.cpp | 1 - test/date.cpp | 2 +- test/main.cpp | 5 +++-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index 2678cb130..46ff9926e 100644 --- a/meson.build +++ b/meson.build @@ -558,7 +558,6 @@ endif catch2 = dependency( 'catch2', - version: '>=3.5.1', default_options: [ 'tests=false' ], fallback: ['catch2', 'catch2_dep'], required: get_option('tests'), diff --git a/test/css_reload_helper.cpp b/test/css_reload_helper.cpp index 01850bc32..f3888a835 100644 --- a/test/css_reload_helper.cpp +++ b/test/css_reload_helper.cpp @@ -4,7 +4,6 @@ #if __has_include() #include -#include #else #include #endif diff --git a/test/date.cpp b/test/date.cpp index 004d6aa86..d317f98ae 100644 --- a/test/date.cpp +++ b/test/date.cpp @@ -7,7 +7,7 @@ #if __has_include() #include -#include +#include #else #include #endif diff --git a/test/main.cpp b/test/main.cpp index daeee69e6..15e17b0fb 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -3,8 +3,9 @@ #include #include -#if __has_include() -#include +#if __has_include() +#include +#include #include #else #include From a2deff36893a3def2c95f520689803f0cc912d29 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Feb 2024 20:58:09 -0800 Subject: [PATCH 080/407] fix(clock): crash on scrolling with local timezone (`""`) in the list While we at it, eliminate use of non-portable GCC conditional expression syntax. There are no significant side-effects that would justify use of the language extension. --- src/modules/clock.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index c889a13f4..97e8a4a77 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -130,7 +130,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } auto waybar::modules::Clock::update() -> void { - auto tz{tzList_[tzCurrIdx_] ?: current_zone()}; + const auto* tz = tzList_[tzCurrIdx_] != nullptr ? tzList_[tzCurrIdx_] : current_zone(); const zoned_time now{tz, floor(system_clock::now())}; label_.set_markup(fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(now))); @@ -167,7 +167,8 @@ auto waybar::modules::Clock::getTZtext(sys_seconds now) -> std::string { std::stringstream os; for (size_t tz_idx{0}; tz_idx < tzList_.size(); ++tz_idx) { if (static_cast(tz_idx) == tzCurrIdx_) continue; - auto zt{zoned_time{tzList_[tz_idx], now}}; + const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : current_zone(); + auto zt{zoned_time{tz, now}}; os << fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(zt)) << '\n'; } From f885baba6119fd779a08ae6092ddac4a17304a15 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 19 Feb 2024 21:49:35 -0800 Subject: [PATCH 081/407] fix(clock): remove literal operator with reserved name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` ../include/util/date.hpp:34:26: warning: literal operator suffixes not preceded by ‘_’ are reserved for future standardization [-Wliteral-suffix] 34 | constexpr decltype(auto) operator""d(unsigned long long d) noexcept { ``` --- include/util/date.hpp | 4 ---- src/modules/clock.cpp | 10 +++++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/include/util/date.hpp b/include/util/date.hpp index 962c810b7..2431b766a 100644 --- a/include/util/date.hpp +++ b/include/util/date.hpp @@ -30,10 +30,6 @@ template inline auto format(const std::locale& loc, const char* spec, const T& arg) { return date::format(loc, std::regex_replace(spec, std::regex("\\{:L|\\}"), ""), arg); } - -constexpr decltype(auto) operator""d(unsigned long long d) noexcept { - return date::operator""_d(d); // very verbose, but it works -} #endif } // namespace date diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 97e8a4a77..b54a360f6 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -221,22 +221,22 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const } // Print first week prefixed with spaces if necessary case 2: { + auto d{day{1}}; auto wd{weekday{ym / 1}}; os << std::string((wd - firstdow).count() * 3, ' '); - if (currDate != ym / 1d) - os << date::format(*locale_, "{:L%e}", 1d); + if (currDate != ym / d) + os << date::format(*locale_, "{:L%e}", d); else os << "{today}"; - auto d{2d}; while (++wd != firstdow) { + ++d; + if (currDate != ym / d) os << date::format(*locale_, " {:L%e}", d); else os << " {today}"; - - ++d; } break; } From b8324be8c436776948344127743d664a9707c94e Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 20 Feb 2024 08:26:09 +0100 Subject: [PATCH 082/407] fix: token --- .github/workflows/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 321f69161..b899465f1 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -18,4 +18,4 @@ jobs: configuration-path: .github/labeler.yml enable-versioned-regex: 0 include-title: 1 - repo-token: ${{ github.token }} + repo-token: ${{ secrets.GITHUB_TOKEN }} From e42635197c1856d13e980c30342f9cb4fb8e1285 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 20 Feb 2024 08:35:28 +0100 Subject: [PATCH 083/407] chore: more labels --- .github/labeler.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index b412f4aaf..a89e734f4 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,3 +1,9 @@ +bug: + - "(crash|bug|error|coredump|freeze|segfault|issue|problem)" + +enhancement: + - "(feature|enhancement|improvement|request|suggestion)" + hyprland: - "(hyprland)" From e6aa06cdf365ea4f5ba2278eacca4d9fcdc8d198 Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Tue, 20 Feb 2024 09:39:03 +0100 Subject: [PATCH 084/407] window#waybar.swallowing -- backward compatibility --- src/modules/hyprland/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index ea65b9235..79456fdbe 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -151,7 +151,7 @@ void Window::queryActiveWorkspace() { return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); }); swallowing_ = std::any_of(workspace_windows.begin(), workspace_windows.end(), - [&](Json::Value window) { return window["swallowing"].asString() != "0x0"; }); + [&](Json::Value window) { return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; }); std::vector visible_windows; std::copy_if(workspace_windows.begin(), workspace_windows.end(), std::back_inserter(visible_windows), From 00ee538c95ce39f00a0d57cd7156c5a5c178e77b Mon Sep 17 00:00:00 2001 From: Lin Xianyi Date: Tue, 20 Feb 2024 17:51:42 +0800 Subject: [PATCH 085/407] nix: update libcava version and removal of gtk-layer-shell meson option --- nix/default.nix | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/nix/default.nix b/nix/default.nix index e26430849..bf8f2f216 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -4,27 +4,32 @@ , version }: let - catch2_3 = { - src = pkgs.fetchFromGitHub - { - owner = "catchorg"; - repo = "Catch2"; - rev = "v3.5.1"; - hash = "sha256-OyYNUfnu6h1+MfCF8O+awQ4Usad0qrdCtdZhYgOY+Vw="; - }; + libcava = rec { + version = "0.10.1"; + src = pkgs.fetchFromGitHub { + owner = "LukashonakV"; + repo = "cava"; + rev = version; + hash = "sha256-iIYKvpOWafPJB5XhDOSIW9Mb4I3A4pcgIIPQdQYEqUw="; + }; }; in -(waybar.overrideAttrs (oldAttrs: rec { - inherit version; +(waybar.overrideAttrs ( + oldAttrs: { + inherit version; - src = lib.cleanSourceWith { - filter = name: type: type != "regular" || !lib.hasSuffix ".nix" name; - src = lib.cleanSource ../.; - }; -}) -).override { - catch2_3 = pkgs.catch2_3.overrideAttrs (oldAttrs: { - version = "3.5.1"; - src = catch2_3.src; - }); -} + src = lib.cleanSourceWith { + filter = name: type: type != "regular" || !lib.hasSuffix ".nix" name; + src = lib.cleanSource ../.; + }; + + mesonFlags = lib.remove "-Dgtk-layer-shell=enabled" oldAttrs.mesonFlags; + + postUnpack = '' + pushd "$sourceRoot" + cp -R --no-preserve=mode,ownership ${libcava.src} subprojects/cava-${libcava.version} + patchShebangs . + popd + ''; + } +)) \ No newline at end of file From a45932973a1994f90ef714dda146d50837080030 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 20 Feb 2024 11:33:41 +0100 Subject: [PATCH 086/407] fix: lint --- src/modules/hyprland/window.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 79456fdbe..1af02b559 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -150,8 +150,10 @@ void Window::queryActiveWorkspace() { [&](Json::Value window) { return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); }); - swallowing_ = std::any_of(workspace_windows.begin(), workspace_windows.end(), - [&](Json::Value window) { return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; }); + swallowing_ = + std::any_of(workspace_windows.begin(), workspace_windows.end(), [&](Json::Value window) { + return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; + }); std::vector visible_windows; std::copy_if(workspace_windows.begin(), workspace_windows.end(), std::back_inserter(visible_windows), From bb843e0494b5177fbabbf13f0038e5749556c615 Mon Sep 17 00:00:00 2001 From: Jo De Boeck Date: Thu, 28 Dec 2023 00:06:09 +0200 Subject: [PATCH 087/407] Implement windows formating in sway/workspaces This implementation mimics to some extend the implementation of hyprland Signed-off-by: Jo De Boeck --- include/modules/sway/workspaces.hpp | 7 ++ man/waybar-sway-workspaces.5.scd | 32 ++++++++ src/modules/sway/workspaces.cpp | 112 ++++++++++++++++++++++++---- 3 files changed, 135 insertions(+), 16 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 0efffe643..4258252a2 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -12,6 +12,7 @@ #include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" +#include "util/regex_collection.hpp" namespace waybar::modules::sway { @@ -27,10 +28,13 @@ class Workspaces : public AModule, public sigc::trackable { R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")"; static int convertWorkspaceNameToNum(std::string name); + static int windowRewritePriorityFunction(std::string const& window_rule); void onCmd(const struct Ipc::ipc_response&); void onEvent(const struct Ipc::ipc_response&); bool filterButtons(); + static bool hasFlag(const Json::Value&, const std::string&); + void updateWindows(const Json::Value&, std::string&); Gtk::Button& addButton(const Json::Value&); void onButtonReady(const Json::Value&, Gtk::Button&); std::string getIcon(const std::string&, const Json::Value&); @@ -44,6 +48,9 @@ class Workspaces : public AModule, public sigc::trackable { std::vector high_priority_named_; std::vector workspaces_order_; Gtk::Box box_; + std::string m_formatWindowSeperator; + std::string m_windowRewriteDefault; + util::RegexCollection m_windowRewriteRules; util::JsonParser parser_; std::unordered_map buttons_; std::mutex mutex_; diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index cdb653f9d..3343b8d5a 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -82,6 +82,23 @@ warp-on-scroll: ++ default: true ++ If set to false, you can scroll to cycle through workspaces without mouse warping being enabled. If set to true this behaviour is disabled. +*window-rewrite*: ++ + typeof: object ++ + Regex rules to map window class to an icon or preferred method of representation for a workspace's window. + Keys are the rules, while the values are the methods of representation. + Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching. + +*window-rewrite-default*: + typeof: string ++ + default: "?" ++ + The default method of representation for a workspace's window. This will be used for windows whose classes do not match any of the rules in *window-rewrite*. + +*format-window-separator*: ++ + typeof: string ++ + default: " " ++ + The separator to be used between windows in a workspace. + + # FORMAT REPLACEMENTS *{value}*: Name of the workspace, as defined by sway. @@ -94,6 +111,8 @@ warp-on-scroll: ++ *{output}*: Output where the workspace is located. +*{windows}*: Result from window-rewrite + # ICONS Additional to workspace name matching, the following *format-icons* can be set. @@ -143,6 +162,19 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge } ``` +``` +"sway/workspaces": { + "format": "{name} {windows}", + "format-window-separator": " | ", + "window-rewrite-default": "{name}", + "window-format": "{name}", + "window-rewrite": { + "class": "", + "class": "k", + } +} +``` + # Style - *#workspaces button* diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index c8ec4387a..1bc9f382c 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -24,6 +24,24 @@ int Workspaces::convertWorkspaceNameToNum(std::string name) { return -1; } +int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) { + // Rules that match against title are prioritized + // Rules that don't specify if they're matching against either title or class are deprioritized + bool const hasTitle = window_rule.find("title") != std::string::npos; + bool const hasClass = window_rule.find("class") != std::string::npos; + + if (hasTitle && hasClass) { + return 3; + } + if (hasTitle) { + return 2; + } + if (hasClass) { + return 1; + } + return 0; +} + Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), bar_(bar), @@ -39,10 +57,25 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value } box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); + if (config_["format-window-separator"].isString()) { + m_formatWindowSeperator = config_["format-window-separator"].asString(); + } else { + m_formatWindowSeperator = " "; + } + const Json::Value &windowRewrite = config["window-rewrite"]; + + const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"]; + m_windowRewriteDefault = + windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; + + m_windowRewriteRules = waybar::util::RegexCollection( + windowRewrite, m_windowRewriteDefault, + [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); ipc_.subscribe(R"(["workspace"])"); + ipc_.subscribe(R"(["window"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd)); - ipc_.sendCmd(IPC_GET_WORKSPACES); + ipc_.sendCmd(IPC_GET_TREE); if (config["enable-bar-scroll"].asBool()) { auto &window = const_cast(bar_).window; window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); @@ -60,26 +93,33 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value void Workspaces::onEvent(const struct Ipc::ipc_response &res) { try { - ipc_.sendCmd(IPC_GET_WORKSPACES); + ipc_.sendCmd(IPC_GET_TREE); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } } void Workspaces::onCmd(const struct Ipc::ipc_response &res) { - if (res.type == IPC_GET_WORKSPACES) { + if (res.type == IPC_GET_TREE) { try { { std::lock_guard lock(mutex_); auto payload = parser_.parse(res.payload); workspaces_.clear(); - std::copy_if(payload.begin(), payload.end(), std::back_inserter(workspaces_), + std::vector outputs; + std::copy_if(payload["nodes"].begin(), payload["nodes"].end(), std::back_inserter(outputs), [&](const auto &workspace) { return !config_["all-outputs"].asBool() - ? workspace["output"].asString() == bar_.output->name + ? workspace["name"].asString() == bar_.output->name : true; }); + for (auto &output : outputs) { + std::copy(output["nodes"].begin(), output["nodes"].end(), + std::back_inserter(workspaces_)); + std::copy(output["floating_nodes"].begin(), output["floating_nodes"].end(), + std::back_inserter(workspaces_)); + } if (config_["persistent_workspaces"].isObject()) { spdlog::warn( "persistent_workspaces is deprecated. Please change config to use " @@ -204,6 +244,40 @@ bool Workspaces::filterButtons() { return needReorder; } +bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { + if (node[flag].asBool()) { + return true; + } + + if (std::any_of(node["nodes"].begin(), node["nodes"].end(), + [&](auto const &e) { return hasFlag(e, flag); })) { + return true; + } + return false; +} + +void Workspaces::updateWindows(const Json::Value &node, std::string &windows) { + auto format = config_["window-format"].asString(); + if ((node["type"].asString() == "con" || node["type"].asString() == "floating_con") && + node["name"].isString()) { + std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1); + std::string windowClass = node["app_id"].asString(); + std::string windowReprKey = fmt::format("class<{}> title<{}>", windowClass, title); + std::string window = m_windowRewriteRules.get(windowReprKey); + // allow result to have formatting + window = + fmt::format(fmt::runtime(window), fmt::arg("name", title), fmt::arg("class", windowClass)); + windows.append(window); + windows.append(m_formatWindowSeperator); + } + for (const Json::Value &child : node["nodes"]) { + updateWindows(child, windows); + } + for (const Json::Value &child : node["floating_nodes"]) { + updateWindows(child, windows); + } +} + auto Workspaces::update() -> void { std::lock_guard lock(mutex_); bool needReorder = filterButtons(); @@ -213,22 +287,25 @@ auto Workspaces::update() -> void { needReorder = true; } auto &button = bit == buttons_.end() ? addButton(*it) : bit->second; - if ((*it)["focused"].asBool()) { + if (needReorder) { + box_.reorder_child(button, it - workspaces_.begin()); + } + if (hasFlag((*it), "focused")) { button.get_style_context()->add_class("focused"); } else { button.get_style_context()->remove_class("focused"); } - if ((*it)["visible"].asBool()) { + if (hasFlag((*it), "visible")) { button.get_style_context()->add_class("visible"); } else { button.get_style_context()->remove_class("visible"); } - if ((*it)["urgent"].asBool()) { + if (hasFlag((*it), "urgent")) { button.get_style_context()->add_class("urgent"); } else { button.get_style_context()->remove_class("urgent"); } - if ((*it)["target_output"].isString()) { + if (hasFlag((*it), "target_output")) { button.get_style_context()->add_class("persistent"); } else { button.get_style_context()->remove_class("persistent"); @@ -242,16 +319,19 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("current_output"); } - if (needReorder) { - box_.reorder_child(button, it - workspaces_.begin()); - } std::string output = (*it)["name"].asString(); + std::string windows = ""; + if (config_["window-format"].isString()) { + updateWindows((*it), windows); + } if (config_["format"].isString()) { auto format = config_["format"].asString(); - output = fmt::format(fmt::runtime(format), fmt::arg("icon", getIcon(output, *it)), - fmt::arg("value", output), fmt::arg("name", trimWorkspaceName(output)), - fmt::arg("index", (*it)["num"].asString()), - fmt::arg("output", (*it)["output"].asString())); + output = fmt::format( + fmt::runtime(format), fmt::arg("icon", getIcon(output, *it)), fmt::arg("value", output), + fmt::arg("name", trimWorkspaceName(output)), fmt::arg("index", (*it)["num"].asString()), + fmt::arg("windows", + windows.substr(0, windows.length() - m_formatWindowSeperator.length())), + fmt::arg("output", (*it)["output"].asString())); } if (!config_["disable-markup"].asBool()) { static_cast(button.get_children()[0])->set_markup(output); From efb2eb5073a382a058477a25e1d8823f277104d5 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 20 Feb 2024 22:24:25 +0100 Subject: [PATCH 088/407] chore: update cpp-linter --- .github/workflows/clang-tidy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 191cabc7e..83d1ffc05 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -17,7 +17,7 @@ jobs: run: | meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json ninja -C build # necessary to find certain .h files (xdg, wayland, etc.) - - uses: cpp-linter/cpp-linter-action@v2.7.5 + - uses: cpp-linter/cpp-linter-action@v2.9.1 name: clang-tidy id: clang-tidy-check env: From 5fc2b97194b82a79aa1d8cfc5e4c4c7db09a31d0 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 20 Feb 2024 17:22:33 -0800 Subject: [PATCH 089/407] ci: fix clang-tidy action --- .github/workflows/clang-tidy.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 83d1ffc05..a39bd23d0 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -17,6 +17,10 @@ jobs: run: | meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json ninja -C build # necessary to find certain .h files (xdg, wayland, etc.) + - uses: actions/setup-python@v5 + with: + python-version: '3.10' # to be kept in sync with cpp-linter-action + update-environment: true # the python dist installed by the action needs LD_LIBRARY_PATH to work - uses: cpp-linter/cpp-linter-action@v2.9.1 name: clang-tidy id: clang-tidy-check From 450a3444263e40019c88a643c17493e0b6a87cf3 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Feb 2024 09:19:03 +0100 Subject: [PATCH 090/407] chore: only label issues --- .github/workflows/labeler.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index b899465f1..94dc42d2d 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -2,8 +2,6 @@ name: "Issue Labeler" on: issues: types: [opened, edited] - pull_request: - types: [opened, edited] permissions: issues: write From 514d00803c9106f6bddfa49f5779af824d19256b Mon Sep 17 00:00:00 2001 From: aokblast Date: Thu, 22 Feb 2024 04:47:09 +0800 Subject: [PATCH 091/407] feat: implement cpufreq for bsd by sysctl --- src/modules/cpu_frequency/bsd.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/modules/cpu_frequency/bsd.cpp b/src/modules/cpu_frequency/bsd.cpp index c837c1fd2..31165fa57 100644 --- a/src/modules/cpu_frequency/bsd.cpp +++ b/src/modules/cpu_frequency/bsd.cpp @@ -1,15 +1,28 @@ #include -#include // NAN +#include #include "modules/cpu_frequency.hpp" std::vector waybar::modules::CpuFrequency::parseCpuFrequencies() { - static std::vector frequencies; + std::vector frequencies; + char buffer[256]; + size_t len; + int32_t freq; + uint32_t i = 0; + + while (true) { + len = 4; + snprintf(buffer, 256, "dev.cpu.%u.freq", i); + if (sysctlbyname(buffer, &freq, &len, NULL, 0) == -1 || len <= 0) break; + frequencies.push_back(freq); + ++i; + } + if (frequencies.empty()) { - spdlog::warn( - "cpu/bsd: parseCpuFrequencies is not implemented, expect garbage in {*_frequency}"); + spdlog::warn("cpu/bsd: parseCpuFrequencies failed, not found in sysctl"); frequencies.push_back(NAN); } + return frequencies; } From 3d31e9a22a17a81fc449f5ff2cd273c0ce4bc6e8 Mon Sep 17 00:00:00 2001 From: Jonny Tischbein Date: Fri, 23 Feb 2024 18:41:45 +0100 Subject: [PATCH 092/407] mediaplayer: add exclude player option --- resources/custom_modules/mediaplayer.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index 51a48373c..4aea4171b 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -23,7 +23,7 @@ def signal_handler(sig, frame): class PlayerManager: - def __init__(self, selected_player=None): + def __init__(self, selected_player=None, excluded_player=[]): self.manager = Playerctl.PlayerManager() self.loop = GLib.MainLoop() self.manager.connect( @@ -35,11 +35,14 @@ def __init__(self, selected_player=None): signal.signal(signal.SIGTERM, signal_handler) signal.signal(signal.SIGPIPE, signal.SIG_DFL) self.selected_player = selected_player + self.excluded_player = excluded_player.split(',') if excluded_player else [] self.init_players() def init_players(self): for player in self.manager.props.player_names: + if player.name in self.excluded_player: + continue if self.selected_player is not None and self.selected_player != player.name: logger.debug(f"{player.name} is not the filtered player, skipping it") continue @@ -149,6 +152,8 @@ def parse_arguments(): # Increase verbosity with every occurrence of -v parser.add_argument("-v", "--verbose", action="count", default=0) + parser.add_argument("-x", "--exclude", "- Comma-separated list of excluded player") + # Define for which player we"re listening parser.add_argument("--player") @@ -174,7 +179,10 @@ def main(): logger.info("Creating player manager") if arguments.player: logger.info(f"Filtering for player: {arguments.player}") - player = PlayerManager(arguments.player) + if arguments.exclude: + logger.info(f"Exclude player {arguments.exclude}") + + player = PlayerManager(arguments.player, arguments.exclude) player.run() From 99c48bca36bed777ee696ff7031e40c29c01a908 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 24 Feb 2024 00:30:32 -0800 Subject: [PATCH 093/407] fix: formatting --- src/modules/cpu_frequency/bsd.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/cpu_frequency/bsd.cpp b/src/modules/cpu_frequency/bsd.cpp index 31165fa57..743fb2881 100644 --- a/src/modules/cpu_frequency/bsd.cpp +++ b/src/modules/cpu_frequency/bsd.cpp @@ -1,5 +1,4 @@ #include - #include #include "modules/cpu_frequency.hpp" From 188789592e73ea90dc1da78d56d15bdb89a125dd Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 21 Feb 2024 20:22:35 -0800 Subject: [PATCH 094/407] feat(sway/language): option to hide module with single layout --- include/modules/sway/language.hpp | 1 + man/waybar-sway-language.5.scd | 5 +++++ src/modules/sway/language.cpp | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index 3e9519f54..ea58c4f09 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -56,6 +56,7 @@ class Language : public ALabel, public sigc::trackable { Layout layout_; std::string tooltip_format_ = ""; std::map layouts_map_; + bool hide_single_; bool is_variant_displayed; std::byte displayed_short_flag = static_cast(DispayedShortFlag::None); diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index c257ed75e..1c62fd950 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. +*hide-single-layout*: ++ + typeof: bool ++ + default: false ++ + Defines visibility of the module if a single layout is configured + *tooltip-format*: ++ typeof: string ++ default: {} ++ diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index a5860bd09..a005df17c 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -19,6 +19,7 @@ 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) { + hide_single_ = config["hide-single-layout"].isBool() && config["hide-single-layout"].asBool(); is_variant_displayed = format_.find("{variant}") != std::string::npos; if (format_.find("{}") != std::string::npos || format_.find("{short}") != std::string::npos) { displayed_short_flag |= static_cast(DispayedShortFlag::ShortName); @@ -95,6 +96,10 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { auto Language::update() -> void { std::lock_guard lock(mutex_); + if (hide_single_ && layouts_map_.size() <= 1) { + event_box_.hide(); + return; + } auto display_layout = trim(fmt::format( fmt::runtime(format_), fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), From 16079eae09944fe76e8ba78b1e8d834baf4fa289 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 25 Feb 2024 00:51:52 +0100 Subject: [PATCH 095/407] update m_output --- include/modules/hyprland/workspaces.hpp | 1 + src/modules/hyprland/workspaces.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 8d46b1a11..dac61d959 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -82,6 +82,7 @@ class Workspace { void setVisible(bool value = true) { m_isVisible = value; }; void setWindows(uint value) { m_windows = value; }; void setName(std::string const& value) { m_name = value; }; + void setOutput(std::string const& value) { m_output = value; }; bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); } void insertWindow(WindowCreationPayload create_window_paylod); std::string removeWindow(WindowAddress const& addr); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3e3931211..ceb887ea0 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -208,6 +208,7 @@ void Workspaces::doUpdate() { } spdlog::trace("Updating workspace states"); + auto IPC_workspaces = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { // active workspace->setActive(workspace->name() == m_activeWorkspaceName || @@ -226,6 +227,15 @@ void Workspaces::doUpdate() { if (m_withIcon) { workspaceIcon = workspace->selectIcon(m_iconsMap); } + + // update m_output + auto IPC_workspace = std::find_if(IPC_workspaces.begin(), IPC_workspaces.end(), [&workspace](auto &w) { + auto wNameRaw = w["name"].asString(); + auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; + return wName == workspace->name(); + }); + workspace->setOutput((*IPC_workspace)["monitor"].asString()); + workspace->update(m_format, workspaceIcon); } From 4cc2800a78cb930e74ee089329c6c0c2b24c7551 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 25 Feb 2024 00:52:33 +0100 Subject: [PATCH 096/407] add 'onThisMonitor' css class --- src/modules/hyprland/workspaces.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index ceb887ea0..aa2db5247 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -871,6 +871,7 @@ void Workspace::update(const std::string &format, const std::string &icon) { addOrRemoveClass(styleContext, isPersistent(), "persistent"); addOrRemoveClass(styleContext, isUrgent(), "urgent"); addOrRemoveClass(styleContext, isVisible(), "visible"); + addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "onThisMonitor"); std::string windows; auto windowSeparator = m_workspaceManager.getWindowSeparator(); From 2540c07f1d2080876d9d58a4f12dd2718267be73 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 24 Feb 2024 18:24:39 -0800 Subject: [PATCH 097/407] chore: wrap module lists in the config "modules-right" has gotten too long, and it's easier to compare configs that way. --- resources/config | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/resources/config b/resources/config index adf03a1f3..420262db6 100644 --- a/resources/config +++ b/resources/config @@ -5,9 +5,31 @@ // "width": 1280, // Waybar width "spacing": 4, // Gaps between modules (4px) // Choose the order of the modules - "modules-left": ["sway/workspaces", "sway/mode", "sway/scratchpad", "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-left": [ + "sway/workspaces", + "sway/mode", + "sway/scratchpad", + "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 configuration // "sway/workspaces": { // "disable-scroll": true, From 05fbbc1c434bb707a168673f0bad61cc88e1f5bd Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 24 Feb 2024 18:26:02 -0800 Subject: [PATCH 098/407] style: align 'sway/mode' text with other modules Use `box-shadow` instead of borders for consistent vertical alignment. See 77c7e10 for a similar conversion of other modules. --- resources/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/style.css b/resources/style.css index 3d44829f3..6e4fcebc5 100644 --- a/resources/style.css +++ b/resources/style.css @@ -69,7 +69,7 @@ button:hover { #mode { background-color: #64727D; - border-bottom: 3px solid #ffffff; + box-shadow: inset 0 -3px #ffffff; } #clock, From edd723d95c88bcaaeee9d23dd53bbc585b67ac13 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 25 Feb 2024 11:44:43 +0100 Subject: [PATCH 099/407] Change PrivateMember styling to use trailing underscore instead of m_ in .clang-tidy --- .clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index b7a33451d..f74eae653 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -22,7 +22,7 @@ CheckOptions: - { key: readability-identifier-naming.FunctionCase, value: camelBack } - { key: readability-identifier-naming.VariableCase, value: camelBack } - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack } - - { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ } + - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ } - { key: readability-identifier-naming.EnumCase, value: CamelCase } - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } From 42f4386e2ea05783a9d42a8adf1d4c27f71e9d8e Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 25 Feb 2024 12:11:22 +0100 Subject: [PATCH 100/407] fix clang-tidy errors in hyprland module --- include/modules/hyprland/backend.hpp | 10 ++-- include/modules/hyprland/language.hpp | 2 +- include/modules/hyprland/submap.hpp | 4 +- include/modules/hyprland/window.hpp | 18 +++--- src/modules/hyprland/backend.cpp | 46 +++++++------- src/modules/hyprland/language.cpp | 25 ++++---- src/modules/hyprland/submap.cpp | 2 +- src/modules/hyprland/window.cpp | 86 ++++++++++++++------------- 8 files changed, 98 insertions(+), 95 deletions(-) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index d197df3aa..9ce0ec335 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -20,8 +20,8 @@ class IPC { public: IPC() { startIPC(); } - void registerForIPC(const std::string&, EventHandler*); - void unregisterForIPC(EventHandler*); + void registerForIPC(const std::string& ev, EventHandler* ev_handler); + void unregisterForIPC(EventHandler* handler); static std::string getSocket1Reply(const std::string& rq); Json::Value getSocket1JsonReply(const std::string& rq); @@ -30,9 +30,9 @@ class IPC { void startIPC(); void parseIPC(const std::string&); - std::mutex m_callbackMutex; - util::JsonParser m_parser; - std::list> m_callbacks; + std::mutex callbackMutex_; + util::JsonParser parser_; + std::list> callbacks_; }; inline std::unique_ptr gIPC; diff --git a/include/modules/hyprland/language.hpp b/include/modules/hyprland/language.hpp index eb220609a..47a4d69c3 100644 --- a/include/modules/hyprland/language.hpp +++ b/include/modules/hyprland/language.hpp @@ -30,7 +30,7 @@ class Language : public waybar::ALabel, public EventHandler { std::string short_description; }; - auto getLayout(const std::string&) -> Layout; + static auto getLayout(const std::string&) -> Layout; std::mutex mutex_; const Bar& bar_; diff --git a/include/modules/hyprland/submap.hpp b/include/modules/hyprland/submap.hpp index 4ff232fff..98b52efb7 100644 --- a/include/modules/hyprland/submap.hpp +++ b/include/modules/hyprland/submap.hpp @@ -14,12 +14,12 @@ namespace waybar::modules::hyprland { class Submap : public waybar::ALabel, public EventHandler { public: Submap(const std::string&, const waybar::Bar&, const Json::Value&); - virtual ~Submap(); + ~Submap() override; auto update() -> void override; private: - void onEvent(const std::string&) override; + void onEvent(const std::string& ev) override; std::mutex mutex_; const Bar& bar_; diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index ea4d83b2e..593e34220 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -25,7 +25,7 @@ class Window : public waybar::AAppIconLabel, public EventHandler { std::string last_window; std::string last_window_title; - static auto parse(const Json::Value&) -> Workspace; + static auto parse(const Json::Value& value) -> Workspace; }; struct WindowData { @@ -41,22 +41,22 @@ class Window : public waybar::AAppIconLabel, public EventHandler { static auto parse(const Json::Value&) -> WindowData; }; - auto getActiveWorkspace(const std::string&) -> Workspace; - auto getActiveWorkspace() -> Workspace; - void onEvent(const std::string&) override; + static auto getActiveWorkspace(const std::string&) -> Workspace; + static auto getActiveWorkspace() -> Workspace; + void onEvent(const std::string& ev) override; void queryActiveWorkspace(); void setClass(const std::string&, bool enable); - bool separate_outputs; + bool separateOutputs_; std::mutex mutex_; const Bar& bar_; util::JsonParser parser_; - WindowData window_data_; + WindowData windowData_; Workspace workspace_; - std::string solo_class_; - std::string last_solo_class_; + std::string soloClass_; + std::string lastSoloClass_; bool solo_; - bool all_floating_; + bool allFloating_; bool swallowing_; bool fullscreen_; }; diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 3c8313fc7..05db94ecd 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -54,22 +54,22 @@ void IPC::startIPC() { return; } - auto file = fdopen(socketfd, "r"); + auto* file = fdopen(socketfd, "r"); while (true) { - char buffer[1024]; // Hyprland socket2 events are max 1024 bytes + std::array buffer; // Hyprland socket2 events are max 1024 bytes - auto recievedCharPtr = fgets(buffer, 1024, file); + auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file); - if (!recievedCharPtr) { + if (receivedCharPtr == nullptr) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } - std::string messageRecieved(buffer); - messageRecieved = messageRecieved.substr(0, messageRecieved.find_first_of('\n')); - spdlog::debug("hyprland IPC received {}", messageRecieved); - parseIPC(messageRecieved); + std::string messageReceived(buffer.data()); + messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n')); + spdlog::debug("hyprland IPC received {}", messageReceived); + parseIPC(messageReceived); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } @@ -78,9 +78,9 @@ void IPC::startIPC() { void IPC::parseIPC(const std::string& ev) { std::string request = ev.substr(0, ev.find_first_of('>')); - std::unique_lock lock(m_callbackMutex); + std::unique_lock lock(callbackMutex_); - for (auto& [eventname, handler] : m_callbacks) { + for (auto& [eventname, handler] : callbacks_) { if (eventname == request) { handler->onEvent(ev); } @@ -88,25 +88,25 @@ void IPC::parseIPC(const std::string& ev) { } void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) { - if (!ev_handler) { + if (ev_handler == nullptr) { return; } - std::unique_lock lock(m_callbackMutex); - m_callbacks.emplace_back(ev, ev_handler); + std::unique_lock lock(callbackMutex_); + callbacks_.emplace_back(ev, ev_handler); } void IPC::unregisterForIPC(EventHandler* ev_handler) { - if (!ev_handler) { + if (ev_handler == nullptr) { return; } - std::unique_lock lock(m_callbackMutex); + std::unique_lock lock(callbackMutex_); - for (auto it = m_callbacks.begin(); it != m_callbacks.end();) { + for (auto it = callbacks_.begin(); it != callbacks_.end();) { auto& [eventname, handler] = *it; if (handler == ev_handler) { - m_callbacks.erase(it++); + callbacks_.erase(it++); } else { ++it; } @@ -135,9 +135,9 @@ std::string IPC::getSocket1Reply(const std::string& rq) { } // get the instance signature - auto instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE"); + auto* instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE"); - if (!instanceSig) { + if (instanceSig == nullptr) { spdlog::error("Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"); return ""; } @@ -169,18 +169,18 @@ std::string IPC::getSocket1Reply(const std::string& rq) { return ""; } - char buffer[8192] = {0}; + std::array buffer = {0}; std::string response; do { - sizeWritten = read(serverSocket, buffer, 8192); + sizeWritten = read(serverSocket, buffer.data(), 8192); if (sizeWritten < 0) { spdlog::error("Hyprland IPC: Couldn't read (5)"); close(serverSocket); return ""; } - response.append(buffer, sizeWritten); + response.append(buffer.data(), sizeWritten); } while (sizeWritten > 0); close(serverSocket); @@ -188,7 +188,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) { } Json::Value IPC::getSocket1JsonReply(const std::string& rq) { - return m_parser.parse(getSocket1Reply("j/" + rq)); + return parser_.parse(getSocket1Reply("j/" + rq)); } } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 5339ee9e0..549faf738 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -13,7 +13,7 @@ Language::Language(const std::string& id, const Bar& bar, const Json::Value& con : ALabel(config, "language", id, "{}", 0, true), bar_(bar) { modulesReady = true; - if (!gIPC.get()) { + if (!gIPC) { gIPC = std::make_unique(); } @@ -102,11 +102,11 @@ void Language::initLanguage() { } auto Language::getLayout(const std::string& fullName) -> Layout { - const auto CONTEXT = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES); - rxkb_context_parse_default_ruleset(CONTEXT); + auto* const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES); + rxkb_context_parse_default_ruleset(context); - rxkb_layout* layout = rxkb_layout_first(CONTEXT); - while (layout) { + rxkb_layout* layout = rxkb_layout_first(context); + while (layout != nullptr) { std::string nameOfLayout = rxkb_layout_get_description(layout); if (nameOfLayout != fullName) { @@ -115,21 +115,20 @@ auto Language::getLayout(const std::string& fullName) -> Layout { } auto name = std::string(rxkb_layout_get_name(layout)); - auto variant_ = rxkb_layout_get_variant(layout); - std::string variant = variant_ == nullptr ? "" : std::string(variant_); + const auto* variantPtr = rxkb_layout_get_variant(layout); + std::string variant = variantPtr == nullptr ? "" : std::string(variantPtr); - auto short_description_ = rxkb_layout_get_brief(layout); - std::string short_description = - short_description_ == nullptr ? "" : std::string(short_description_); + const auto* descriptionPtr = rxkb_layout_get_brief(layout); + std::string description = descriptionPtr == nullptr ? "" : std::string(descriptionPtr); - Layout info = Layout{nameOfLayout, name, variant, short_description}; + Layout info = Layout{nameOfLayout, name, variant, description}; - rxkb_context_unref(CONTEXT); + rxkb_context_unref(context); return info; } - rxkb_context_unref(CONTEXT); + rxkb_context_unref(context); spdlog::debug("hyprland language didn't find matching layout"); diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 9f2a98297..3575b4ac0 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -10,7 +10,7 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "submap", id, "{}", 0, true), bar_(bar) { modulesReady = true; - if (!gIPC.get()) { + if (!gIPC) { gIPC = std::make_unique(); } diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 1af02b559..c7d287e5f 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -17,9 +17,9 @@ namespace waybar::modules::hyprland { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { modulesReady = true; - separate_outputs = config["separate-outputs"].asBool(); + separateOutputs_ = config["separate-outputs"].asBool(); - if (!gIPC.get()) { + if (!gIPC) { gIPC = std::make_unique(); } @@ -45,18 +45,18 @@ auto Window::update() -> void { // fix ampersands std::lock_guard lg(mutex_); - std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title); - std::string window_address = workspace_.last_window; + std::string windowName = waybar::util::sanitize_string(workspace_.last_window_title); + std::string windowAddress = workspace_.last_window; - window_data_.title = window_name; + windowData_.title = windowName; if (!format_.empty()) { label_.show(); label_.set_markup(waybar::util::rewriteString( - fmt::format(fmt::runtime(format_), fmt::arg("title", window_name), - fmt::arg("initialTitle", window_data_.initial_title), - fmt::arg("class", window_data_.class_name), - fmt::arg("initialClass", window_data_.initial_class_name)), + fmt::format(fmt::runtime(format_), fmt::arg("title", windowName), + fmt::arg("initialTitle", windowData_.initial_title), + fmt::arg("class", windowData_.class_name), + fmt::arg("initialClass", windowData_.initial_class_name)), config_["rewrite"])); } else { label_.hide(); @@ -64,22 +64,22 @@ auto Window::update() -> void { setClass("empty", workspace_.windows == 0); setClass("solo", solo_); - setClass("floating", all_floating_); + setClass("floating", allFloating_); setClass("swallowing", swallowing_); setClass("fullscreen", fullscreen_); - if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) { - if (bar_.window.get_style_context()->has_class(last_solo_class_)) { - bar_.window.get_style_context()->remove_class(last_solo_class_); - spdlog::trace("Removing solo class: {}", last_solo_class_); + if (!lastSoloClass_.empty() && soloClass_ != lastSoloClass_) { + if (bar_.window.get_style_context()->has_class(lastSoloClass_)) { + bar_.window.get_style_context()->remove_class(lastSoloClass_); + spdlog::trace("Removing solo class: {}", lastSoloClass_); } } - if (!solo_class_.empty() && solo_class_ != last_solo_class_) { - bar_.window.get_style_context()->add_class(solo_class_); - spdlog::trace("Adding solo class: {}", solo_class_); + if (!soloClass_.empty() && soloClass_ != lastSoloClass_) { + bar_.window.get_style_context()->add_class(soloClass_); + spdlog::trace("Adding solo class: {}", soloClass_); } - last_solo_class_ = solo_class_; + lastSoloClass_ = soloClass_; AAppIconLabel::update(); } @@ -113,8 +113,12 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { } auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { - return Workspace{value["id"].asInt(), value["windows"].asInt(), value["lastwindow"].asString(), - value["lastwindowtitle"].asString()}; + return Workspace{ + value["id"].asInt(), + value["windows"].asInt(), + value["lastwindow"].asString(), + value["lastwindowtitle"].asString(), + }; } auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData { @@ -127,7 +131,7 @@ auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData { void Window::queryActiveWorkspace() { std::lock_guard lg(mutex_); - if (separate_outputs) { + if (separateOutputs_) { workspace_ = getActiveWorkspace(this->bar_.output->name); } else { workspace_ = getActiveWorkspace(); @@ -136,33 +140,33 @@ void Window::queryActiveWorkspace() { if (workspace_.windows > 0) { const auto clients = gIPC->getSocket1JsonReply("clients"); assert(clients.isArray()); - auto active_window = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { + auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { return window["address"] == workspace_.last_window; }); - if (active_window == std::end(clients)) { + if (activeWindow == std::end(clients)) { return; } - window_data_ = WindowData::parse(*active_window); - updateAppIconName(window_data_.class_name, window_data_.initial_class_name); - std::vector workspace_windows; - std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows), + windowData_ = WindowData::parse(*activeWindow); + updateAppIconName(windowData_.class_name, windowData_.initial_class_name); + std::vector workspaceWindows; + std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows), [&](Json::Value window) { return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); }); swallowing_ = - std::any_of(workspace_windows.begin(), workspace_windows.end(), [&](Json::Value window) { + std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) { return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; }); - std::vector visible_windows; - std::copy_if(workspace_windows.begin(), workspace_windows.end(), - std::back_inserter(visible_windows), + std::vector visibleWindows; + std::copy_if(workspaceWindows.begin(), workspaceWindows.end(), + std::back_inserter(visibleWindows), [&](Json::Value window) { return !window["hidden"].asBool(); }); - solo_ = 1 == std::count_if(visible_windows.begin(), visible_windows.end(), + solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), [&](Json::Value window) { return !window["floating"].asBool(); }); - all_floating_ = std::all_of(visible_windows.begin(), visible_windows.end(), - [&](Json::Value window) { return window["floating"].asBool(); }); - fullscreen_ = window_data_.fullscreen; + allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(), + [&](Json::Value window) { return window["floating"].asBool(); }); + fullscreen_ = windowData_.fullscreen; // Fullscreen windows look like they are solo if (fullscreen_) { @@ -170,23 +174,23 @@ void Window::queryActiveWorkspace() { } // Grouped windows have a tab bar and therefore don't look fullscreen or solo - if (window_data_.grouped) { + if (windowData_.grouped) { fullscreen_ = false; solo_ = false; } if (solo_) { - solo_class_ = window_data_.class_name; + soloClass_ = windowData_.class_name; } else { - solo_class_ = ""; + soloClass_ = ""; } } else { - window_data_ = WindowData{}; - all_floating_ = false; + windowData_ = WindowData{}; + allFloating_ = false; swallowing_ = false; fullscreen_ = false; solo_ = false; - solo_class_ = ""; + soloClass_ = ""; } } From 9bc8de88765a0c4fb76fd3fa8c0c59df0048e9b2 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 25 Feb 2024 13:46:49 +0100 Subject: [PATCH 101/407] fix clang complaints --- src/modules/hyprland/workspaces.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index aa2db5247..bae73d2e4 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -208,7 +208,7 @@ void Workspaces::doUpdate() { } spdlog::trace("Updating workspace states"); - auto IPC_workspaces = gIPC->getSocket1JsonReply("workspaces"); + auto updated_workspaces = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { // active workspace->setActive(workspace->name() == m_activeWorkspaceName || @@ -229,12 +229,13 @@ void Workspaces::doUpdate() { } // update m_output - auto IPC_workspace = std::find_if(IPC_workspaces.begin(), IPC_workspaces.end(), [&workspace](auto &w) { - auto wNameRaw = w["name"].asString(); - auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; - return wName == workspace->name(); - }); - workspace->setOutput((*IPC_workspace)["monitor"].asString()); + auto updated_workspace = + std::find_if(updated_workspaces.begin(), updated_workspaces.end(), [&workspace](auto &w) { + auto wNameRaw = w["name"].asString(); + auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; + return wName == workspace->name(); + }); + workspace->setOutput((*updated_workspace)["monitor"].asString()); workspace->update(m_format, workspaceIcon); } From 21089596448d7c1e6a75ad97b33916272f6019b3 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 25 Feb 2024 11:21:58 -0800 Subject: [PATCH 102/407] chore(config): add modeline for Emacs json-mode json-mode supports jsonc format since 1.8.0, but does not register .jsonc as a file extension. --- resources/config | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/config b/resources/config index 420262db6..f225e4fcf 100644 --- a/resources/config +++ b/resources/config @@ -1,3 +1,4 @@ +// -*- mode: jsonc -*- { // "layer": "top", // Waybar at top layer // "position": "bottom", // Waybar position (top|bottom|left|right) From 43aabf046c7b520f2447a1e3c8c601a511e88c75 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 25 Feb 2024 11:33:04 -0800 Subject: [PATCH 103/407] chore: rename config to config.jsonc Only changes the name of the default config we install and does not affect the lookup logic in any way. Man pages were already fixed in #2744 --- man/waybar.5.scd.in | 2 +- meson.build | 4 ++-- resources/{config => config.jsonc} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename resources/{config => config.jsonc} (100%) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 628bbf610..2d4de0c90 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -16,7 +16,7 @@ Valid locations for this file are: - */etc/xdg/waybar/* - *@sysconfdir@/xdg/waybar/* -A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config +A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config.jsonc Also, a minimal example configuration can be found at the bottom of this man page. # BAR CONFIGURATION diff --git a/meson.build b/meson.build index 46ff9926e..b995d5690 100644 --- a/meson.build +++ b/meson.build @@ -518,8 +518,8 @@ executable( ) install_data( - './resources/config', - './resources/style.css', + 'resources/config.jsonc', + 'resources/style.css', install_dir: sysconfdir / 'xdg/waybar' ) diff --git a/resources/config b/resources/config.jsonc similarity index 100% rename from resources/config rename to resources/config.jsonc From 0ead42e52b9839a12364aa35c06b40b6c5309357 Mon Sep 17 00:00:00 2001 From: Azazel Date: Sun, 25 Feb 2024 22:55:30 +0000 Subject: [PATCH 104/407] feat: improve search of .desktop files --- src/AAppIconLabel.cpp | 49 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index a238143b5..1ceed73bd 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -24,18 +24,57 @@ AAppIconLabel::AAppIconLabel(const Json::Value& config, const std::string& name, image_.set_pixel_size(app_icon_size_); } +std::string to_lowercase(const std::string& input) { + std::string result = input; + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return std::tolower(c); }); + return result; +} + +std::optional getFileBySuffix(const std::string& dir, const std::string& suffix, + bool check_lower_case) { + if (!std::filesystem::exists(dir)) { + return {}; + } + for (const auto& entry : std::filesystem::recursive_directory_iterator(dir)) { + if (entry.is_regular_file()) { + std::string filename = entry.path().filename().string(); + if (filename.size() < suffix.size()) { + continue; + } + if ((filename.compare(filename.size() - suffix.size(), suffix.size(), suffix) == 0) || + (check_lower_case && filename.compare(filename.size() - suffix.size(), suffix.size(), + to_lowercase(suffix)) == 0)) { + return entry.path().string(); + } + } + } + + return {}; +} + +std::optional getFileBySuffix(const std::string& dir, const std::string& suffix) { + return getFileBySuffix(dir, suffix, false); +} + std::optional getDesktopFilePath(const std::string& app_identifier, const std::string& alternative_app_identifier) { const auto data_dirs = Glib::get_system_data_dirs(); for (const auto& data_dir : data_dirs) { - const auto data_app_dir = data_dir + "applications/"; - auto desktop_file_path = data_app_dir + app_identifier + ".desktop"; - if (std::filesystem::exists(desktop_file_path)) { + const auto data_app_dir = data_dir + "/applications/"; + auto desktop_file_suffix = app_identifier + ".desktop"; + // searching for file by suffix catches cases like terminal emulator "foot" where class is + // "footclient" and desktop file is named "org.codeberg.dnkl.footclient.desktop" + auto desktop_file_path = getFileBySuffix(data_app_dir, desktop_file_suffix, true); + // "true" argument allows checking for lowercase - this catches cases where class name is + // "LibreWolf" and desktop file is named "librewolf.desktop" + if (desktop_file_path.has_value()) { return desktop_file_path; } if (!alternative_app_identifier.empty()) { - desktop_file_path = data_app_dir + alternative_app_identifier + ".desktop"; - if (std::filesystem::exists(desktop_file_path)) { + desktop_file_suffix = alternative_app_identifier + ".desktop"; + desktop_file_path = getFileBySuffix(data_app_dir, desktop_file_suffix, true); + if (desktop_file_path.has_value()) { return desktop_file_path; } } From 3a5aa5ee832a8f201f0ffe721ab3ee1774f8c143 Mon Sep 17 00:00:00 2001 From: Azazel Date: Sun, 25 Feb 2024 22:56:52 +0000 Subject: [PATCH 105/407] feat: improve default spacing and add to config --- src/AIconLabel.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/AIconLabel.cpp b/src/AIconLabel.cpp index a7e2380a5..ef379ff38 100644 --- a/src/AIconLabel.cpp +++ b/src/AIconLabel.cpp @@ -10,7 +10,14 @@ AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const : ALabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) { event_box_.remove(); box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL); - box_.set_spacing(8); + + // set aesthetic default spacing + int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : -5; + box_.set_spacing(spacing); + + int margin_top = config_["margin-top"].isInt() ? config_["margin-top"].asInt() : 6; + box_.set_margin_top(margin_top); + box_.add(image_); box_.add(label_); event_box_.add(box_); From b3ee94d87ae4606c042fc4dd739c54285038d802 Mon Sep 17 00:00:00 2001 From: Anthony Ruhier Date: Sat, 24 Feb 2024 23:57:07 +0100 Subject: [PATCH 106/407] Improve hyprland/workspaces persistency logic Fixes #2945 Split the config and rule persistency in 2 attributes, one storing the persistency as set in Waybar's config, the other one storing the persistency as set in Hyprland. It fixes some conflicts between the persistency state of a workspace as set in Waybar's config and its dynamic state in Hyprland. It allows to remove a persistent workspace in Waybar if this workspace is removed from Hyprland and if the workspace is not set as persistent in Waybar's config. --- include/modules/hyprland/workspaces.hpp | 12 +++++-- src/modules/hyprland/workspaces.cpp | 43 +++++++++++++++++-------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 8d46b1a11..41870077c 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -70,14 +70,17 @@ class Workspace { std::string output() const { return m_output; }; bool isActive() const { return m_isActive; }; bool isSpecial() const { return m_isSpecial; }; - bool isPersistent() const { return m_isPersistent; }; + bool isPersistent() const { return m_isPersistentRule || m_isPersistentConfig; }; + bool isPersistentConfig() const { return m_isPersistentConfig; }; + bool isPersistentRule() const { return m_isPersistentRule; }; bool isVisible() const { return m_isVisible; }; bool isEmpty() const { return m_windows == 0; }; bool isUrgent() const { return m_isUrgent; }; bool handleClicked(GdkEventButton* bt) const; void setActive(bool value = true) { m_isActive = value; }; - void setPersistent(bool value = true) { m_isPersistent = value; }; + void setPersistentRule(bool value = true) { m_isPersistentRule = value; }; + void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; }; void setUrgent(bool value = true) { m_isUrgent = value; }; void setVisible(bool value = true) { m_isVisible = value; }; void setWindows(uint value) { m_windows = value; }; @@ -101,7 +104,10 @@ class Workspace { uint m_windows; bool m_isActive = false; bool m_isSpecial = false; - bool m_isPersistent = false; + // m_isPersistentRule represents the persistent state in hyprland + bool m_isPersistentRule = false; + // m_isPersistentConfig represents the persistent state in the Waybar config + bool m_isPersistentConfig = false; bool m_isUrgent = false; bool m_isVisible = false; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3e3931211..882e38067 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -349,7 +349,7 @@ void Workspaces::onWorkspaceCreated(std::string const &workspaceName, (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(workspaceName)) { for (Json::Value const &rule : workspaceRules) { if (rule["workspaceString"].asString() == workspaceName) { - workspaceJson["persistent"] = rule["persistent"].asBool(); + workspaceJson["persistent-rule"] = rule["persistent"].asBool(); break; } } @@ -587,8 +587,7 @@ std::optional Workspace::closeWindow(WindowAddress const &addr) { void Workspaces::createWorkspace(Json::Value const &workspace_data, Json::Value const &clients_data) { auto workspaceName = workspace_data["name"].asString(); - spdlog::debug("Creating workspace {}, persistent: {}", workspaceName, - workspace_data["persistent"].asBool() ? "true" : "false"); + spdlog::debug("Creating workspace {}", workspaceName); // avoid recreating existing workspaces auto workspace = std::find_if( @@ -600,7 +599,22 @@ void Workspaces::createWorkspace(Json::Value const &workspace_data, if (workspace != m_workspaces.end()) { // don't recreate workspace, but update persistency if necessary - (*workspace)->setPersistent(workspace_data["persistent"].asBool()); + const auto keys = workspace_data.getMemberNames(); + + const auto *k = "persistent-rule"; + if (std::find(keys.begin(), keys.end(), k) != keys.end()) { + spdlog::debug("Set dynamic persistency of workspace {} to: {}", workspaceName, + workspace_data[k].asBool() ? "true" : "false"); + (*workspace)->setPersistentRule(workspace_data[k].asBool()); + } + + k = "persistent-config"; + if (std::find(keys.begin(), keys.end(), k) != keys.end()) { + spdlog::debug("Set config persistency of workspace {} to: {}", workspaceName, + workspace_data[k].asBool() ? "true" : "false"); + (*workspace)->setPersistentConfig(workspace_data[k].asBool()); + } + return; } @@ -624,8 +638,8 @@ void Workspaces::removeWorkspace(std::string const &name) { return; } - if ((*workspace)->isPersistent()) { - spdlog::trace("Not removing persistent workspace {}", name); + if ((*workspace)->isPersistentConfig()) { + spdlog::trace("Not removing config persistent workspace {}", name); return; } @@ -633,7 +647,7 @@ void Workspaces::removeWorkspace(std::string const &name) { m_workspaces.erase(workspace); } -Json::Value createPersistentWorkspaceData(std::string const &name, std::string const &monitor) { +Json::Value createMonitorWorkspaceData(std::string const &name, std::string const &monitor) { spdlog::trace("Creating persistent workspace: {} on monitor {}", name, monitor); Json::Value workspaceData; try { @@ -646,7 +660,6 @@ Json::Value createPersistentWorkspaceData(std::string const &name, std::string c workspaceData["name"] = name; workspaceData["monitor"] = monitor; workspaceData["windows"] = 0; - workspaceData["persistent"] = true; return workspaceData; } @@ -699,7 +712,8 @@ void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJs } for (auto const &workspace : persistentWorkspacesToCreate) { - auto const workspaceData = createPersistentWorkspaceData(workspace, m_bar.output->name); + auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name); + workspaceData["persistent-config"] = true; m_workspacesToCreate.emplace_back(workspaceData, clientsJson); } } @@ -724,7 +738,8 @@ void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &c // 3. no monitor is specified in the rule => assume it needs to be persistent on every monitor if (allOutputs() || m_bar.output->name == monitor || monitor.empty()) { // => persistent workspace should be shown on this monitor - auto workspaceData = createPersistentWorkspaceData(workspace, m_bar.output->name); + auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name); + workspaceData["persistent-rule"] = true; m_workspacesToCreate.emplace_back(workspaceData, clientsJson); } else { m_workspacesToRemove.emplace_back(workspace); @@ -774,10 +789,9 @@ void Workspaces::initializeWorkspaces() { if (m_persistentWorkspaceConfig.isObject()) { // a persistent workspace config is defined, so use that instead of workspace rules loadPersistentWorkspacesFromConfig(clientsJson); - } else { - // no persistent workspaces config defined, use Hyprland's workspace rules - loadPersistentWorkspacesFromWorkspaceRules(clientsJson); } + // load Hyprland's workspace rules + loadPersistentWorkspacesFromWorkspaceRules(clientsJson); } void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) { @@ -812,7 +826,8 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma m_output(workspace_data["monitor"].asString()), // TODO:allow using monitor desc m_windows(workspace_data["windows"].asInt()), m_isActive(true), - m_isPersistent(workspace_data["persistent"].asBool()) { + m_isPersistentRule(workspace_data["persistent-rule"].asBool()), + m_isPersistentConfig(workspace_data["persistent-config"].asBool()) { if (m_name.starts_with("name:")) { m_name = m_name.substr(5); } else if (m_name.starts_with("special")) { From d6d4d87cf7516817197f070ecd1c9a396ea0ac03 Mon Sep 17 00:00:00 2001 From: Anthony Ruhier Date: Mon, 26 Feb 2024 00:05:12 +0100 Subject: [PATCH 107/407] Attributes doc format fix from the review Co-authored-by: Tuur Vanhoutte <4633209+zjeffer@users.noreply.github.com> --- include/modules/hyprland/workspaces.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 41870077c..91ea16539 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -104,10 +104,8 @@ class Workspace { uint m_windows; bool m_isActive = false; bool m_isSpecial = false; - // m_isPersistentRule represents the persistent state in hyprland - bool m_isPersistentRule = false; - // m_isPersistentConfig represents the persistent state in the Waybar config - bool m_isPersistentConfig = false; + bool m_isPersistentRule = false; // represents the persistent state in hyprland + bool m_isPersistentConfig = false; // represents the persistent state in the Waybar config bool m_isUrgent = false; bool m_isVisible = false; From 16aced7f9ffcac1200473192712575afaa4e6513 Mon Sep 17 00:00:00 2001 From: Azazel Date: Mon, 26 Feb 2024 04:07:03 +0000 Subject: [PATCH 108/407] feat: move name and classes from label_ to box_ --- src/AIconLabel.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/AIconLabel.cpp b/src/AIconLabel.cpp index ef379ff38..051654dfc 100644 --- a/src/AIconLabel.cpp +++ b/src/AIconLabel.cpp @@ -9,17 +9,20 @@ AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const bool enable_click, bool enable_scroll) : ALabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) { event_box_.remove(); - box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL); - - // set aesthetic default spacing - int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : -5; - box_.set_spacing(spacing); + label_.unset_name(); + label_.get_style_context()->remove_class(MODULE_CLASS); + box_.get_style_context()->add_class(MODULE_CLASS); + if (!id.empty()) { + label_.get_style_context()->remove_class(id); + box_.get_style_context()->add_class(id); + } - int margin_top = config_["margin-top"].isInt() ? config_["margin-top"].asInt() : 6; - box_.set_margin_top(margin_top); + box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL); + box_.set_name(name); box_.add(image_); box_.add(label_); + event_box_.add(box_); } From 695c7863544dbb1d6e7c009bed71078f33350377 Mon Sep 17 00:00:00 2001 From: Azazel Date: Mon, 26 Feb 2024 04:17:45 +0000 Subject: [PATCH 109/407] refactor: reuse toLowerCase function --- src/AAppIconLabel.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index 1ceed73bd..0dd874257 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -24,7 +24,7 @@ AAppIconLabel::AAppIconLabel(const Json::Value& config, const std::string& name, image_.set_pixel_size(app_icon_size_); } -std::string to_lowercase(const std::string& input) { +std::string toLowerCase(const std::string& input) { std::string result = input; std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return std::tolower(c); }); @@ -44,7 +44,7 @@ std::optional getFileBySuffix(const std::string& dir, const std::st } if ((filename.compare(filename.size() - suffix.size(), suffix.size(), suffix) == 0) || (check_lower_case && filename.compare(filename.size() - suffix.size(), suffix.size(), - to_lowercase(suffix)) == 0)) { + toLowerCase(suffix)) == 0)) { return entry.path().string(); } } @@ -97,16 +97,9 @@ std::optional getIconName(const std::string& app_identifier, return app_identifier_desktop; } - const auto to_lower = [](const std::string& str) { - auto str_cpy = str; - std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(), - [](unsigned char c) { return std::tolower(c); }); - return str; - }; - const auto first_space = app_identifier.find_first_of(' '); if (first_space != std::string::npos) { - const auto first_word = to_lower(app_identifier.substr(0, first_space)); + const auto first_word = toLowerCase(app_identifier.substr(0, first_space)); if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { return first_word; } @@ -114,7 +107,7 @@ std::optional getIconName(const std::string& app_identifier, const auto first_dash = app_identifier.find_first_of('-'); if (first_dash != std::string::npos) { - const auto first_word = to_lower(app_identifier.substr(0, first_dash)); + const auto first_word = toLowerCase(app_identifier.substr(0, first_dash)); if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { return first_word; } From c38d05b04f12c0c8cd01538ead1f6bcc5d1d9603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Sun, 18 Feb 2024 22:06:21 +0100 Subject: [PATCH 110/407] Introduce power-profiles-daemon module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We introduce a module in charge to display and toggle on click the power profiles via power-profiles-daemon. https://gitlab.freedesktop.org/upower/power-profiles-daemon This daemon is pretty widespread. It's the component used by Gnome and KDE to manage the power profiles. The power management daemon is a pretty important software component for laptops and other battery-powered devices. We're using the daemon DBus interface to: - Fetch the available power profiles. - Track the active power profile. - Change the active power profile. The original author recently gave up maintenance on the project. The Upower group took over the maintenance burden… …and created a new DBus name for the project. The old name is still advertised for now. We use the old name for compatibility sake: most distributions did not release 0.20, which introduces this new DBus name. We'll likely revisit this in the future and point to the new bus name. See the inline comment for more details. Given how widespread this daemon is, I activated the module in the default configuration. --- README.md | 1 + include/modules/power_profiles_daemon.hpp | 38 ++++++ meson.build | 1 + resources/config.jsonc | 2 +- resources/style.css | 16 +++ src/factory.cpp | 4 + src/modules/power_profiles_daemon.cpp | 146 ++++++++++++++++++++++ 7 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 include/modules/power_profiles_daemon.hpp create mode 100644 src/modules/power_profiles_daemon.cpp diff --git a/README.md b/README.md index 07b11152f..65be764cf 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - Local time - Battery - UPower +- Power profiles daemon - Network - Bluetooth - Pulseaudio diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp new file mode 100644 index 000000000..71654e360 --- /dev/null +++ b/include/modules/power_profiles_daemon.hpp @@ -0,0 +1,38 @@ +# pragma once + +#include + +#include "ALabel.hpp" +#include "giomm/dbusproxy.h" + +namespace waybar::modules { + +typedef struct { + std::string name; + std::string driver; +} Profile; + +class PowerProfilesDaemon : public ALabel { + public: + PowerProfilesDaemon(const std::string&, const Json::Value&); + ~PowerProfilesDaemon(); + auto update() -> void override; + void profileChanged_cb( const Gio::DBus::Proxy::MapChangedProperties&, const std::vector&); + void populateInitState(); + virtual bool handleToggle(GdkEventButton* const& e); + private: + // Look for a profile name in the list of available profiles and + // switch activeProfile_ to it. + void switchToProfile_(std::string); + // Used to toggle/display the profiles + std::vector availableProfiles_; + // Points to the active profile in the profiles list + std::vector::iterator activeProfile_; + // Current CSS class applied to the label + std::string currentStyle_; + // DBus Proxy used to track the current active profile + Glib::RefPtr power_profiles_proxy_; + sigc::connection powerProfileChangeSignal_; +}; + +} diff --git a/meson.build b/meson.build index b995d5690..10c826208 100644 --- a/meson.build +++ b/meson.build @@ -212,6 +212,7 @@ if is_linux 'src/modules/cpu_usage/linux.cpp', 'src/modules/memory/common.cpp', 'src/modules/memory/linux.cpp', + 'src/modules/power_profiles_daemon.cpp', 'src/modules/systemd_failed_units.cpp', ) man_files += files( diff --git a/resources/config.jsonc b/resources/config.jsonc index f225e4fcf..00612136f 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -20,6 +20,7 @@ "idle_inhibitor", "pulseaudio", "network", + "power_profiles_daemon", "cpu", "memory", "temperature", @@ -188,4 +189,3 @@ // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name } } - diff --git a/resources/style.css b/resources/style.css index 6e4fcebc5..7f708ff41 100644 --- a/resources/style.css +++ b/resources/style.css @@ -87,6 +87,7 @@ button:hover { #mode, #idle_inhibitor, #scratchpad, +#power-profiles-daemon, #mpd { padding: 0 10px; color: #ffffff; @@ -139,6 +140,21 @@ button:hover { animation-direction: alternate; } +#power-profiles-daemon.performance { + background-color: #f53c3c; + color: #ffffff; +} + +#power-profiles-daemon.balanced { + background-color: #2980b9; + color: #ffffff; +} + +#power-profiles-daemon.power-saver { + background-color: #2ecc71; + color: #000000; +} + label:focus { background-color: #000000; } diff --git a/src/factory.cpp b/src/factory.cpp index 6b709f339..7dc6709ef 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -86,6 +86,7 @@ #endif #if defined(__linux__) #include "modules/bluetooth.hpp" +#include "modules/power_profiles_daemon.hpp" #endif #ifdef HAVE_LOGIND_INHIBITOR #include "modules/inhibitor.hpp" @@ -282,6 +283,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "bluetooth") { return new waybar::modules::Bluetooth(id, config_[name]); } + if (ref == "power_profiles_daemon") { + return new waybar::modules::PowerProfilesDaemon(id, config_[name]); + } #endif #ifdef HAVE_LOGIND_INHIBITOR if (ref == "inhibitor") { diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp new file mode 100644 index 000000000..f4dfd1c82 --- /dev/null +++ b/src/modules/power_profiles_daemon.cpp @@ -0,0 +1,146 @@ +#include "modules/power_profiles_daemon.hpp" + +// In the 80000 version of fmt library authors decided to optimize imports +// and moved declarations required for fmt::dynamic_format_arg_store in new +// header fmt/args.h +#if (FMT_VERSION >= 80000) +#include +#else +#include +#endif + +#include +#include +#include + + + +namespace waybar::modules { + +PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Value& config) + : ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true) +{ + // NOTE: the DBus adresses are under migration. They should be + // changed to org.freedesktop.UPower.PowerProfiles at some point. + // + // See + // https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/releases/0.20 + // + // The old name is still announced for now. Let's rather use the old + // adresses for compatibility sake. + // + // Revisit this in 2026, systems should be updated by then. + power_profiles_proxy_ = Gio::DBus::Proxy::create_for_bus_sync(Gio::DBus::BusType::BUS_TYPE_SYSTEM, + "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", + "net.hadess.PowerProfiles"); + if (!power_profiles_proxy_) { + spdlog::error("PowerProfilesDaemon: DBus error, cannot connect to net.hasdess.PowerProfile"); + } else { + // Connect active profile callback + powerProfileChangeSignal_ = power_profiles_proxy_->signal_properties_changed() + .connect(sigc::mem_fun(*this, &PowerProfilesDaemon::profileChanged_cb)); + populateInitState(); + dp.emit(); + } +} + +// Look for the profile str in our internal profiles list. Using a +// vector to store the profiles ain't the smartest move +// complexity-wise, but it makes toggling between the mode easy. This +// vector is 3 elements max, we'll be fine :P +void PowerProfilesDaemon::switchToProfile_(std::string str) { + auto pred = [str](Profile p) { return p.name == str; }; + activeProfile_ = std::find_if(availableProfiles_.begin(), availableProfiles_.end(), pred); + if (activeProfile_ == availableProfiles_.end()) { + throw std::runtime_error("FATAL, can't find the active profile in the available profiles list"); + } +} + +void PowerProfilesDaemon::populateInitState() { + // Retrieve current active profile + Glib::Variant profileStr; + power_profiles_proxy_->get_cached_property(profileStr, "ActiveProfile"); + + // Retrieve profiles list, it's aa{sv}. + using ProfilesType = std::vector>>; + Glib::Variant profilesVariant; + power_profiles_proxy_->get_cached_property(profilesVariant, "Profiles"); + Glib::ustring name, driver; + Profile profile; + for (auto & variantDict: profilesVariant.get()) { + if (auto p = variantDict.find("Profile"); p != variantDict.end()) { + name = p->second.get(); + } + if (auto d = variantDict.find("Driver"); d != variantDict.end()) { + driver = d->second.get(); + } + profile = { name, driver }; + availableProfiles_.push_back(profile); + } + + // Find the index of the current activated mode (to toggle) + std::string str = profileStr.get(); + switchToProfile_(str); + + update(); +} + +PowerProfilesDaemon::~PowerProfilesDaemon() { + if (powerProfileChangeSignal_.connected()) { + powerProfileChangeSignal_.disconnect(); + } + if (power_profiles_proxy_) { + power_profiles_proxy_.reset(); + } +} + +void PowerProfilesDaemon::profileChanged_cb(const Gio::DBus::Proxy::MapChangedProperties& changedProperties, + const std::vector& invalidatedProperties) { + if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); activeProfileVariant != changedProperties.end()) { + std::string activeProfile = Glib::VariantBase::cast_dynamic>(activeProfileVariant->second).get(); + switchToProfile_(activeProfile); + update(); + } +} + +auto PowerProfilesDaemon::update () -> void { + auto profile = (*activeProfile_); + // Set label + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("profile", profile.name)); + label_.set_markup(fmt::vformat("⚡ {profile}", store)); + if (tooltipEnabled()) { + label_.set_tooltip_text(fmt::format("Driver: {}", profile.driver)); + } + + // Set CSS class + if (!currentStyle_.empty()) { + label_.get_style_context()->remove_class(currentStyle_); + } + label_.get_style_context()->add_class(profile.name); + currentStyle_ = profile.name; + + ALabel::update(); +} + + +bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { + if (e->type == GdkEventType::GDK_BUTTON_PRESS && power_profiles_proxy_) { + activeProfile_++; + if (activeProfile_ == availableProfiles_.end()) { + activeProfile_ = availableProfiles_.begin(); + } + + using VarStr = Glib::Variant; + using SetPowerProfileVar = Glib::Variant>; + VarStr activeProfileVariant = VarStr::create(activeProfile_->name); + auto call_args = SetPowerProfileVar::create(std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); + auto container = Glib::VariantBase::cast_dynamic(call_args); + power_profiles_proxy_->call_sync("org.freedesktop.DBus.Properties.Set", container); + + update(); + } + return true; +} + +} From 968f469289b99801476ee5e8f2d38b1e296f2cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Mon, 26 Feb 2024 14:40:28 +0100 Subject: [PATCH 111/407] modules/power-profiles-daemon: run clang format --- include/modules/power_profiles_daemon.hpp | 10 +++--- src/modules/power_profiles_daemon.cpp | 43 ++++++++++++----------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index 71654e360..40a512f13 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -1,4 +1,4 @@ -# pragma once +#pragma once #include @@ -12,14 +12,16 @@ typedef struct { std::string driver; } Profile; -class PowerProfilesDaemon : public ALabel { +class PowerProfilesDaemon : public ALabel { public: PowerProfilesDaemon(const std::string&, const Json::Value&); ~PowerProfilesDaemon(); auto update() -> void override; - void profileChanged_cb( const Gio::DBus::Proxy::MapChangedProperties&, const std::vector&); + void profileChanged_cb(const Gio::DBus::Proxy::MapChangedProperties&, + const std::vector&); void populateInitState(); virtual bool handleToggle(GdkEventButton* const& e); + private: // Look for a profile name in the list of available profiles and // switch activeProfile_ to it. @@ -35,4 +37,4 @@ class PowerProfilesDaemon : public ALabel { sigc::connection powerProfileChangeSignal_; }; -} +} // namespace waybar::modules diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index f4dfd1c82..e5e379dbb 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -9,17 +9,14 @@ #include #endif -#include #include #include - - +#include namespace waybar::modules { PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Value& config) - : ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true) -{ + : ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true) { // NOTE: the DBus adresses are under migration. They should be // changed to org.freedesktop.UPower.PowerProfiles at some point. // @@ -30,15 +27,15 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // adresses for compatibility sake. // // Revisit this in 2026, systems should be updated by then. - power_profiles_proxy_ = Gio::DBus::Proxy::create_for_bus_sync(Gio::DBus::BusType::BUS_TYPE_SYSTEM, - "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", - "net.hadess.PowerProfiles"); + power_profiles_proxy_ = Gio::DBus::Proxy::create_for_bus_sync( + Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", + "net.hadess.PowerProfiles"); if (!power_profiles_proxy_) { spdlog::error("PowerProfilesDaemon: DBus error, cannot connect to net.hasdess.PowerProfile"); } else { // Connect active profile callback - powerProfileChangeSignal_ = power_profiles_proxy_->signal_properties_changed() - .connect(sigc::mem_fun(*this, &PowerProfilesDaemon::profileChanged_cb)); + powerProfileChangeSignal_ = power_profiles_proxy_->signal_properties_changed().connect( + sigc::mem_fun(*this, &PowerProfilesDaemon::profileChanged_cb)); populateInitState(); dp.emit(); } @@ -67,14 +64,14 @@ void PowerProfilesDaemon::populateInitState() { power_profiles_proxy_->get_cached_property(profilesVariant, "Profiles"); Glib::ustring name, driver; Profile profile; - for (auto & variantDict: profilesVariant.get()) { + for (auto& variantDict : profilesVariant.get()) { if (auto p = variantDict.find("Profile"); p != variantDict.end()) { name = p->second.get(); } if (auto d = variantDict.find("Driver"); d != variantDict.end()) { driver = d->second.get(); } - profile = { name, driver }; + profile = {name, driver}; availableProfiles_.push_back(profile); } @@ -94,16 +91,20 @@ PowerProfilesDaemon::~PowerProfilesDaemon() { } } -void PowerProfilesDaemon::profileChanged_cb(const Gio::DBus::Proxy::MapChangedProperties& changedProperties, - const std::vector& invalidatedProperties) { - if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); activeProfileVariant != changedProperties.end()) { - std::string activeProfile = Glib::VariantBase::cast_dynamic>(activeProfileVariant->second).get(); +void PowerProfilesDaemon::profileChanged_cb( + const Gio::DBus::Proxy::MapChangedProperties& changedProperties, + const std::vector& invalidatedProperties) { + if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); + activeProfileVariant != changedProperties.end()) { + std::string activeProfile = + Glib::VariantBase::cast_dynamic>(activeProfileVariant->second) + .get(); switchToProfile_(activeProfile); update(); } } -auto PowerProfilesDaemon::update () -> void { +auto PowerProfilesDaemon::update() -> void { auto profile = (*activeProfile_); // Set label fmt::dynamic_format_arg_store store; @@ -123,7 +124,6 @@ auto PowerProfilesDaemon::update () -> void { ALabel::update(); } - bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { if (e->type == GdkEventType::GDK_BUTTON_PRESS && power_profiles_proxy_) { activeProfile_++; @@ -132,9 +132,10 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { } using VarStr = Glib::Variant; - using SetPowerProfileVar = Glib::Variant>; + using SetPowerProfileVar = Glib::Variant>; VarStr activeProfileVariant = VarStr::create(activeProfile_->name); - auto call_args = SetPowerProfileVar::create(std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); + auto call_args = SetPowerProfileVar::create( + std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); auto container = Glib::VariantBase::cast_dynamic(call_args); power_profiles_proxy_->call_sync("org.freedesktop.DBus.Properties.Set", container); @@ -143,4 +144,4 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { return true; } -} +} // namespace waybar::modules From a7d8b1bacf08b717cb447c54e2c902bdffb24166 Mon Sep 17 00:00:00 2001 From: Azazel Date: Mon, 26 Feb 2024 20:58:38 +0000 Subject: [PATCH 112/407] feat: re-add default and configurable icon spacing --- src/AIconLabel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/AIconLabel.cpp b/src/AIconLabel.cpp index 051654dfc..ee68a22e1 100644 --- a/src/AIconLabel.cpp +++ b/src/AIconLabel.cpp @@ -20,6 +20,9 @@ AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL); box_.set_name(name); + int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : 6; + box_.set_spacing(spacing); + box_.add(image_); box_.add(label_); From c59bb509bd4585af8941db66e357b0bf9b08b2de Mon Sep 17 00:00:00 2001 From: Azazel Date: Mon, 26 Feb 2024 21:00:16 +0000 Subject: [PATCH 113/407] fix: hide icon if window is unfocused --- include/modules/hyprland/window.hpp | 1 + src/modules/hyprland/window.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index 593e34220..f2c266bd2 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -59,6 +59,7 @@ class Window : public waybar::AAppIconLabel, public EventHandler { bool allFloating_; bool swallowing_; bool fullscreen_; + bool focused_; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index c7d287e5f..ec151a7bc 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -62,6 +62,12 @@ auto Window::update() -> void { label_.hide(); } + if (focused_) { + image_.show(); + } else { + image_.hide(); + } + setClass("empty", workspace_.windows == 0); setClass("solo", solo_); setClass("floating", allFloating_); @@ -137,13 +143,16 @@ void Window::queryActiveWorkspace() { workspace_ = getActiveWorkspace(); } + focused_ = true; if (workspace_.windows > 0) { const auto clients = gIPC->getSocket1JsonReply("clients"); assert(clients.isArray()); auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { return window["address"] == workspace_.last_window; }); + if (activeWindow == std::end(clients)) { + focused_ = false; return; } @@ -185,6 +194,7 @@ void Window::queryActiveWorkspace() { soloClass_ = ""; } } else { + focused_ = false; windowData_ = WindowData{}; allFloating_ = false; swallowing_ = false; From 615c9050e7f76537dab6286764337913298cdf0b Mon Sep 17 00:00:00 2001 From: Azazel Date: Mon, 26 Feb 2024 22:52:28 +0000 Subject: [PATCH 114/407] fix: prevent icon showing when app_identifier is empty --- src/AAppIconLabel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index 0dd874257..e64e6daa5 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -59,6 +59,10 @@ std::optional getFileBySuffix(const std::string& dir, const std::st std::optional getDesktopFilePath(const std::string& app_identifier, const std::string& alternative_app_identifier) { + if (app_identifier.empty()) { + return {}; + } + const auto data_dirs = Glib::get_system_data_dirs(); for (const auto& data_dir : data_dirs) { const auto data_app_dir = data_dir + "/applications/"; From 5a887fe1efdecc1fc6a47d05b6707a42d5778c0a Mon Sep 17 00:00:00 2001 From: Jo De Boeck Date: Tue, 27 Feb 2024 23:43:00 +0200 Subject: [PATCH 115/407] Filter out special output __i3 which contains scratchpad Fixes: #2966 Signed-off-by: Jo De Boeck --- src/modules/sway/workspaces.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 1bc9f382c..eda53ddec 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -107,11 +107,16 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { auto payload = parser_.parse(res.payload); workspaces_.clear(); std::vector outputs; + bool alloutputs = config_["all-outputs"].asBool(); std::copy_if(payload["nodes"].begin(), payload["nodes"].end(), std::back_inserter(outputs), - [&](const auto &workspace) { - return !config_["all-outputs"].asBool() - ? workspace["name"].asString() == bar_.output->name - : true; + [&](const auto &output) { + if (alloutputs && output["name"].asString() != "__i3") { + return true; + } + if (output["name"].asString() == bar_.output->name) { + return true; + } + return false; }); for (auto &output : outputs) { From ba48d26dd4d528032f89e285ee3838a2da280383 Mon Sep 17 00:00:00 2001 From: Azazel Date: Wed, 28 Feb 2024 00:24:58 +0000 Subject: [PATCH 116/407] chore: amend default icon spacing --- src/AIconLabel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AIconLabel.cpp b/src/AIconLabel.cpp index ee68a22e1..d7ee666e6 100644 --- a/src/AIconLabel.cpp +++ b/src/AIconLabel.cpp @@ -20,7 +20,7 @@ AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL); box_.set_name(name); - int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : 6; + int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : 8; box_.set_spacing(spacing); box_.add(image_); From 55915f95f1eb203328ec7df297c97769be5b9fec Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 28 Feb 2024 23:56:10 -0800 Subject: [PATCH 117/407] ci: move FreeBSD to ubuntu runners With the recent runner hardware upgrade[1] and support in the cross-platform-actions[2] it became possible to use a Linux runner for this workflow. Linux-based configuration appears to be faster and stabler than macOS, so it's now recommended for use. [1]: https://github.blog/2024-01-17-github-hosted-runners-double-the-power-for-open-source/ [2]: https://github.com/cross-platform-actions/action/releases/tag/v0.23.0 --- .github/workflows/freebsd.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index f0b8f69c7..7b27fdb67 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -8,19 +8,22 @@ concurrency: jobs: clang: - # Run actions in a FreeBSD VM on the macos-12 runner + # Run actions in a FreeBSD VM on the ubuntu runner # https://github.com/actions/runner/issues/385 - for FreeBSD runner support - # https://github.com/actions/virtual-environments/issues/4060 - for lack of VirtualBox on MacOS 11 runners - runs-on: macos-12 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Test in FreeBSD VM - uses: cross-platform-actions/action@v0.21.1 + uses: cross-platform-actions/action@v0.23.0 timeout-minutes: 180 + env: + CPPFLAGS: '-isystem/usr/local/include' + LDFLAGS: '-L/usr/local/lib' with: operating_system: freebsd version: "13.2" - environment_variables: CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib + environment_variables: CPPFLAGS LDFLAGS + sync_files: runner-to-vm run: | sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf sudo pkg install -y git # subprojects/date From 162b41c4d02c4ba5147304a999bb7858603e329c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Mon, 26 Feb 2024 15:07:21 +0100 Subject: [PATCH 118/407] modules/power-profiles-daemon: apply clang-tidy suggestions --- include/modules/power_profiles_daemon.hpp | 16 +++++----- src/modules/power_profiles_daemon.cpp | 39 ++++++++++++----------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index 40a512f13..92ead7487 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -7,25 +7,25 @@ namespace waybar::modules { -typedef struct { +struct Profile { std::string name; std::string driver; -} Profile; +}; class PowerProfilesDaemon : public ALabel { public: PowerProfilesDaemon(const std::string&, const Json::Value&); - ~PowerProfilesDaemon(); + ~PowerProfilesDaemon() override; auto update() -> void override; - void profileChanged_cb(const Gio::DBus::Proxy::MapChangedProperties&, - const std::vector&); + void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties&, + const std::vector&); void populateInitState(); - virtual bool handleToggle(GdkEventButton* const& e); + bool handleToggle(GdkEventButton* const& e) override; private: // Look for a profile name in the list of available profiles and // switch activeProfile_ to it. - void switchToProfile_(std::string); + void switchToProfile(std::string const&); // Used to toggle/display the profiles std::vector availableProfiles_; // Points to the active profile in the profiles list @@ -33,7 +33,7 @@ class PowerProfilesDaemon : public ALabel { // Current CSS class applied to the label std::string currentStyle_; // DBus Proxy used to track the current active profile - Glib::RefPtr power_profiles_proxy_; + Glib::RefPtr powerProfilesProxy_; sigc::connection powerProfileChangeSignal_; }; diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index e5e379dbb..3dd43b872 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -27,15 +27,15 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // adresses for compatibility sake. // // Revisit this in 2026, systems should be updated by then. - power_profiles_proxy_ = Gio::DBus::Proxy::create_for_bus_sync( + powerProfilesProxy_ = Gio::DBus::Proxy::create_for_bus_sync( Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", "net.hadess.PowerProfiles"); - if (!power_profiles_proxy_) { + if (!powerProfilesProxy_) { spdlog::error("PowerProfilesDaemon: DBus error, cannot connect to net.hasdess.PowerProfile"); } else { // Connect active profile callback - powerProfileChangeSignal_ = power_profiles_proxy_->signal_properties_changed().connect( - sigc::mem_fun(*this, &PowerProfilesDaemon::profileChanged_cb)); + powerProfileChangeSignal_ = powerProfilesProxy_->signal_properties_changed().connect( + sigc::mem_fun(*this, &PowerProfilesDaemon::profileChangedCb)); populateInitState(); dp.emit(); } @@ -45,9 +45,9 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // vector to store the profiles ain't the smartest move // complexity-wise, but it makes toggling between the mode easy. This // vector is 3 elements max, we'll be fine :P -void PowerProfilesDaemon::switchToProfile_(std::string str) { - auto pred = [str](Profile p) { return p.name == str; }; - activeProfile_ = std::find_if(availableProfiles_.begin(), availableProfiles_.end(), pred); +void PowerProfilesDaemon::switchToProfile(std::string const& str) { + auto pred = [str](Profile const& p) { return p.name == str; }; + this->activeProfile_ = std::find_if(availableProfiles_.begin(), availableProfiles_.end(), pred); if (activeProfile_ == availableProfiles_.end()) { throw std::runtime_error("FATAL, can't find the active profile in the available profiles list"); } @@ -56,13 +56,14 @@ void PowerProfilesDaemon::switchToProfile_(std::string str) { void PowerProfilesDaemon::populateInitState() { // Retrieve current active profile Glib::Variant profileStr; - power_profiles_proxy_->get_cached_property(profileStr, "ActiveProfile"); + powerProfilesProxy_->get_cached_property(profileStr, "ActiveProfile"); // Retrieve profiles list, it's aa{sv}. using ProfilesType = std::vector>>; Glib::Variant profilesVariant; - power_profiles_proxy_->get_cached_property(profilesVariant, "Profiles"); - Glib::ustring name, driver; + powerProfilesProxy_->get_cached_property(profilesVariant, "Profiles"); + Glib::ustring name; + Glib::ustring driver; Profile profile; for (auto& variantDict : profilesVariant.get()) { if (auto p = variantDict.find("Profile"); p != variantDict.end()) { @@ -77,7 +78,7 @@ void PowerProfilesDaemon::populateInitState() { // Find the index of the current activated mode (to toggle) std::string str = profileStr.get(); - switchToProfile_(str); + switchToProfile(str); update(); } @@ -86,12 +87,12 @@ PowerProfilesDaemon::~PowerProfilesDaemon() { if (powerProfileChangeSignal_.connected()) { powerProfileChangeSignal_.disconnect(); } - if (power_profiles_proxy_) { - power_profiles_proxy_.reset(); + if (powerProfilesProxy_) { + powerProfilesProxy_.reset(); } } -void PowerProfilesDaemon::profileChanged_cb( +void PowerProfilesDaemon::profileChangedCb( const Gio::DBus::Proxy::MapChangedProperties& changedProperties, const std::vector& invalidatedProperties) { if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); @@ -99,7 +100,7 @@ void PowerProfilesDaemon::profileChanged_cb( std::string activeProfile = Glib::VariantBase::cast_dynamic>(activeProfileVariant->second) .get(); - switchToProfile_(activeProfile); + switchToProfile(activeProfile); update(); } } @@ -125,7 +126,7 @@ auto PowerProfilesDaemon::update() -> void { } bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { - if (e->type == GdkEventType::GDK_BUTTON_PRESS && power_profiles_proxy_) { + if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) { activeProfile_++; if (activeProfile_ == availableProfiles_.end()) { activeProfile_ = availableProfiles_.begin(); @@ -134,10 +135,10 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { using VarStr = Glib::Variant; using SetPowerProfileVar = Glib::Variant>; VarStr activeProfileVariant = VarStr::create(activeProfile_->name); - auto call_args = SetPowerProfileVar::create( + auto callArgs = SetPowerProfileVar::create( std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); - auto container = Glib::VariantBase::cast_dynamic(call_args); - power_profiles_proxy_->call_sync("org.freedesktop.DBus.Properties.Set", container); + auto container = Glib::VariantBase::cast_dynamic(callArgs); + powerProfilesProxy_->call_sync("org.freedesktop.DBus.Properties.Set", container); update(); } From 653c24cee17e986f9e9a3e7a30019e2b68f5ebcd Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 21 Feb 2024 23:53:27 -0800 Subject: [PATCH 119/407] feat(mpd): tone down logs if the server is not running --- src/modules/mpd/mpd.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 73062c766..188b4a16d 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -4,6 +4,7 @@ #include #include +#include #include using namespace waybar::util; @@ -254,6 +255,21 @@ std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool act } } +static bool isServerUnavailable(const std::error_code& ec) { + if (ec.category() == std::system_category()) { + switch (ec.value()) { + case ECONNREFUSED: + case ECONNRESET: + case ENETDOWN: + case ENETUNREACH: + case EHOSTDOWN: + case ENOENT: + return true; + } + } + return false; +} + void waybar::modules::MPD::tryConnect() { if (connection_ != nullptr) { return; @@ -281,6 +297,11 @@ void waybar::modules::MPD::tryConnect() { } checkErrors(connection_.get()); } + } catch (std::system_error& e) { + /* Tone down logs if it's likely that the mpd server is not running */ + auto level = isServerUnavailable(e.code()) ? spdlog::level::debug : spdlog::level::err; + spdlog::log(level, "{}: Failed to connect to MPD: {}", module_name_, e.what()); + connection_.reset(); } catch (std::runtime_error& e) { spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what()); connection_.reset(); @@ -298,6 +319,12 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) { connection_.reset(); state_ = MPD_STATE_UNKNOWN; throw std::runtime_error("Connection to MPD closed"); + case MPD_ERROR_SYSTEM: + if (auto ec = mpd_connection_get_system_error(conn); ec != 0) { + mpd_connection_clear_error(conn); + throw std::system_error(ec, std::system_category()); + } + G_GNUC_FALLTHROUGH; default: if (conn) { auto error_message = mpd_connection_get_error_message(conn); From bb60d418421866a71a9cbb0a69d0e5c7618ec2d3 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 1 Mar 2024 00:06:54 -0800 Subject: [PATCH 120/407] fix(mpd): use timers with second granularity where possible Reuse already armed timer in Disconnected state. --- include/modules/mpd/state.hpp | 3 ++- src/modules/mpd/mpd.cpp | 4 ++-- src/modules/mpd/state.cpp | 20 ++++++++++++-------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/include/modules/mpd/state.hpp b/include/modules/mpd/state.hpp index 1276e3c3d..2c9071b4e 100644 --- a/include/modules/mpd/state.hpp +++ b/include/modules/mpd/state.hpp @@ -148,6 +148,7 @@ class Stopped : public State { class Disconnected : public State { Context* const ctx_; sigc::connection timer_connection_; + int last_interval_; public: Disconnected(Context* const ctx) : ctx_{ctx} {} @@ -162,7 +163,7 @@ class Disconnected : public State { Disconnected(Disconnected const&) = delete; Disconnected& operator=(Disconnected const&) = delete; - void arm_timer(int interval) noexcept; + bool arm_timer(int interval) noexcept; void disarm_timer() noexcept; bool on_timer(); diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 188b4a16d..192e6c1ad 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -53,10 +53,10 @@ auto waybar::modules::MPD::update() -> void { void waybar::modules::MPD::queryMPD() { if (connection_ != nullptr) { - spdlog::debug("{}: fetching state information", module_name_); + spdlog::trace("{}: fetching state information", module_name_); try { fetchState(); - spdlog::debug("{}: fetch complete", module_name_); + spdlog::trace("{}: fetch complete", module_name_); } catch (std::exception const& e) { spdlog::error("{}: {}", module_name_, e.what()); state_ = MPD_STATE_UNKNOWN; diff --git a/src/modules/mpd/state.cpp b/src/modules/mpd/state.cpp index aa1a18f8e..3d7c8561e 100644 --- a/src/modules/mpd/state.cpp +++ b/src/modules/mpd/state.cpp @@ -119,7 +119,7 @@ bool Idle::on_io(Glib::IOCondition const&) { void Playing::entry() noexcept { sigc::slot timer_slot = sigc::mem_fun(*this, &Playing::on_timer); - timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 1'000); + timer_connection_ = Glib::signal_timeout().connect_seconds(timer_slot, 1); spdlog::debug("mpd: Playing: enabled 1 second periodic timer."); } @@ -327,14 +327,20 @@ void Stopped::pause() { void Stopped::update() noexcept { ctx_->do_update(); } -void Disconnected::arm_timer(int interval) noexcept { +bool Disconnected::arm_timer(int interval) noexcept { + // check if it's necessary to modify the timer + if (timer_connection_ && last_interval_ == interval) { + return true; + } // unregister timer, if present disarm_timer(); // register timer + last_interval_ = interval; sigc::slot timer_slot = sigc::mem_fun(*this, &Disconnected::on_timer); - timer_connection_ = Glib::signal_timeout().connect(timer_slot, interval); - spdlog::debug("mpd: Disconnected: enabled interval timer."); + timer_connection_ = Glib::signal_timeout().connect_seconds(timer_slot, interval); + spdlog::debug("mpd: Disconnected: enabled {}s interval timer.", interval); + return false; } void Disconnected::disarm_timer() noexcept { @@ -347,7 +353,7 @@ void Disconnected::disarm_timer() noexcept { void Disconnected::entry() noexcept { ctx_->emit(); - arm_timer(1'000); + arm_timer(1 /* second */); } void Disconnected::exit() noexcept { disarm_timer(); } @@ -376,9 +382,7 @@ bool Disconnected::on_timer() { spdlog::warn("mpd: Disconnected: error: {}", e.what()); } - arm_timer(ctx_->interval() * 1'000); - - return false; + return arm_timer(ctx_->interval()); } void Disconnected::update() noexcept { ctx_->do_update(); } From c03fa389742053ca77abb401e79bc266710e3aa5 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 1 Mar 2024 00:19:41 -0800 Subject: [PATCH 121/407] fix(mpd): use default interval in the example config 2 seconds is 2.5 times more often than the default for the module. --- resources/config.jsonc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/config.jsonc b/resources/config.jsonc index f225e4fcf..10ccfe52c 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -72,7 +72,7 @@ "format-disconnected": "Disconnected ", "format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ", "unknown-tag": "N/A", - "interval": 2, + "interval": 5, "consume-icons": { "on": " " }, From 61fed6a21474752e382f17724091b91dee273e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Fri, 1 Mar 2024 11:13:57 +0100 Subject: [PATCH 122/407] modules/power_profiles_daemon: add custom format from config We move to a single icon label format to save space on the bar. We still display the profile name and the driver in the tooltip. --- include/modules/power_profiles_daemon.hpp | 3 +++ resources/config.jsonc | 11 +++++++++++ src/modules/power_profiles_daemon.cpp | 18 ++++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index 92ead7487..72ffb3146 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -32,6 +32,9 @@ class PowerProfilesDaemon : public ALabel { std::vector::iterator activeProfile_; // Current CSS class applied to the label std::string currentStyle_; + // Format strings + std::string labelFormat_; + std::string tooltipFormat_; // DBus Proxy used to track the current active profile Glib::RefPtr powerProfilesProxy_; sigc::connection powerProfileChangeSignal_; diff --git a/resources/config.jsonc b/resources/config.jsonc index 00612136f..4e300a330 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -148,6 +148,17 @@ "battery#bat2": { "bat": "BAT2" }, + "power_profiles_daemon": { + "format": "{icon}", + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", + "tooltip": true, + "format-icons": { + "default": "", + "performance": "", + "balanced": "", + "power-saver": "" + } + }, "network": { // "interface": "wlp2*", // (Optional) To force the use of this interface "format-wifi": "{essid} ({signalStrength}%) ", diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index 3dd43b872..6b6846622 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -17,6 +17,18 @@ namespace waybar::modules { PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Value& config) : ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true) { + if (config_["format"].isString()) { + format_ = config_["format"].asString(); + } else { + format_ = "{icon}"; + } + + if (config_["tooltip-format"].isString()) { + tooltipFormat_ = config_["tooltip-format"].asString(); + } else { + tooltipFormat_ = "Power profile: {profile}\nDriver: {driver}"; + } + // NOTE: the DBus adresses are under migration. They should be // changed to org.freedesktop.UPower.PowerProfiles at some point. // @@ -110,9 +122,11 @@ auto PowerProfilesDaemon::update() -> void { // Set label fmt::dynamic_format_arg_store store; store.push_back(fmt::arg("profile", profile.name)); - label_.set_markup(fmt::vformat("⚡ {profile}", store)); + store.push_back(fmt::arg("driver", profile.driver)); + store.push_back(fmt::arg("icon", getIcon(0, profile.name))); + label_.set_markup(fmt::vformat(format_, store)); if (tooltipEnabled()) { - label_.set_tooltip_text(fmt::format("Driver: {}", profile.driver)); + label_.set_tooltip_text(fmt::vformat(tooltipFormat_, store)); } // Set CSS class From 09bb6a055dec244d0d8a26fccc79752015bd03ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Fri, 1 Mar 2024 12:33:36 +0100 Subject: [PATCH 123/407] modules/power_profiles_daemon: safely call dbus asynchronously 2 changes to address the review feedback: 1. Aleksei pointed out in this comment (https://github.com/Alexays/Waybar/pull/2971#issuecomment-1972364896) that there's no way to tell if a proxy is alive other than trying to call a method on it. We perform a little dance to check whether or not power-profiles-daemon is available on the system by calling properties.GetAll. If something responds, we assume power-profiles-daemon is installed, it's then safe to draw the widget and attach the callback to the active profile. 2. We replaced all the synchronous DBus operations by their async counterparts. --- include/modules/power_profiles_daemon.hpp | 16 +- src/modules/power_profiles_daemon.cpp | 184 ++++++++++++++-------- 2 files changed, 129 insertions(+), 71 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index 72ffb3146..bfb5c99db 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -14,18 +14,24 @@ struct Profile { class PowerProfilesDaemon : public ALabel { public: - PowerProfilesDaemon(const std::string&, const Json::Value&); + PowerProfilesDaemon(const std::string &, const Json::Value &); ~PowerProfilesDaemon() override; auto update() -> void override; - void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties&, - const std::vector&); + void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties &, + const std::vector &); + void busConnectedCb(Glib::RefPtr &r); + void getAllPropsCb(Glib::RefPtr &r); + void setPropCb(Glib::RefPtr &r); void populateInitState(); - bool handleToggle(GdkEventButton* const& e) override; + bool handleToggle(GdkEventButton *const &e) override; private: + // True if we're connected to the dbug interface. False if we're + // not. + bool connected_; // Look for a profile name in the list of available profiles and // switch activeProfile_ to it. - void switchToProfile(std::string const&); + void switchToProfile(std::string const &); // Used to toggle/display the profiles std::vector availableProfiles_; // Points to the active profile in the profiles list diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index 6b6846622..bd6a52a77 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -16,7 +16,7 @@ namespace waybar::modules { PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Value& config) - : ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true) { + : ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true), connected_(false) { if (config_["format"].isString()) { format_ = config_["format"].asString(); } else { @@ -28,6 +28,20 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu } else { tooltipFormat_ = "Power profile: {profile}\nDriver: {driver}"; } + // Fasten your seatbelt, we're up for quite a ride. The rest of the + // init is performed asynchronously. There's 2 callbacks involved. + // Here's the overall idea: + // 1. Async connect to the system bus. + // 2. In the system bus connect callback, try to call + // org.freedesktop.DBus.Properties.GetAll to see if + // power-profiles-daemon is able to respond. + // 3. In the GetAll callback, connect the activeProfile monitoring + // callback, consider the init to be successful. Meaning start + // drawing the module. + // + // There's sadly no other way around that, we have to try to call a + // method on the proxy to see whether or not something's responding + // on the other side. // NOTE: the DBus adresses are under migration. They should be // changed to org.freedesktop.UPower.PowerProfiles at some point. @@ -39,29 +53,52 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // adresses for compatibility sake. // // Revisit this in 2026, systems should be updated by then. - powerProfilesProxy_ = Gio::DBus::Proxy::create_for_bus_sync( - Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", - "net.hadess.PowerProfiles"); - if (!powerProfilesProxy_) { - spdlog::error("PowerProfilesDaemon: DBus error, cannot connect to net.hasdess.PowerProfile"); - } else { + Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", + "/net/hadess/PowerProfiles", "net.hadess.PowerProfiles", + sigc::mem_fun(*this, &PowerProfilesDaemon::busConnectedCb)); +} + +PowerProfilesDaemon::~PowerProfilesDaemon() { + if (powerProfileChangeSignal_.connected()) { + powerProfileChangeSignal_.disconnect(); + } + if (powerProfilesProxy_) { + powerProfilesProxy_.reset(); + } +} + +void PowerProfilesDaemon::busConnectedCb(Glib::RefPtr& r) { + try { + powerProfilesProxy_ = Gio::DBus::Proxy::create_for_bus_finish(r); + using GetAllProfilesVar = Glib::Variant>; + auto callArgs = GetAllProfilesVar::create(std::make_tuple("net.hadess.PowerProfiles")); + + auto container = Glib::VariantBase::cast_dynamic(callArgs); + powerProfilesProxy_->call("org.freedesktop.DBus.Properties.GetAll", + sigc::mem_fun(*this, &PowerProfilesDaemon::getAllPropsCb), container); // Connect active profile callback + } catch (const std::exception& e) { + spdlog::error("Failed to create the power profiles daemon DBus proxy"); + } +} + +// Callback for the GetAll call. +// +// We're abusing this call to make sure power-profiles-daemon is +// available on the host. We're not really using +void PowerProfilesDaemon::getAllPropsCb(Glib::RefPtr& r) { + try { + auto _ = powerProfilesProxy_->call_finish(r); + // Power-profiles-daemon responded something, we can assume it's + // available, we can safely attach the activeProfile monitoring + // now. + connected_ = true; powerProfileChangeSignal_ = powerProfilesProxy_->signal_properties_changed().connect( sigc::mem_fun(*this, &PowerProfilesDaemon::profileChangedCb)); populateInitState(); dp.emit(); - } -} - -// Look for the profile str in our internal profiles list. Using a -// vector to store the profiles ain't the smartest move -// complexity-wise, but it makes toggling between the mode easy. This -// vector is 3 elements max, we'll be fine :P -void PowerProfilesDaemon::switchToProfile(std::string const& str) { - auto pred = [str](Profile const& p) { return p.name == str; }; - this->activeProfile_ = std::find_if(availableProfiles_.begin(), availableProfiles_.end(), pred); - if (activeProfile_ == availableProfiles_.end()) { - throw std::runtime_error("FATAL, can't find the active profile in the available profiles list"); + } catch (const std::exception& err) { + spdlog::error("Failed to query power-profiles-daemon via dbus: {}", err.what()); } } @@ -95,68 +132,83 @@ void PowerProfilesDaemon::populateInitState() { update(); } -PowerProfilesDaemon::~PowerProfilesDaemon() { - if (powerProfileChangeSignal_.connected()) { - powerProfileChangeSignal_.disconnect(); - } - if (powerProfilesProxy_) { - powerProfilesProxy_.reset(); - } -} - void PowerProfilesDaemon::profileChangedCb( const Gio::DBus::Proxy::MapChangedProperties& changedProperties, const std::vector& invalidatedProperties) { - if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); - activeProfileVariant != changedProperties.end()) { - std::string activeProfile = - Glib::VariantBase::cast_dynamic>(activeProfileVariant->second) - .get(); - switchToProfile(activeProfile); - update(); + // We're likely connected if this callback gets triggered. + // But better be safe than sorry. + if (connected_) { + if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); + activeProfileVariant != changedProperties.end()) { + std::string activeProfile = + Glib::VariantBase::cast_dynamic>(activeProfileVariant->second) + .get(); + switchToProfile(activeProfile); + update(); + } } } -auto PowerProfilesDaemon::update() -> void { - auto profile = (*activeProfile_); - // Set label - fmt::dynamic_format_arg_store store; - store.push_back(fmt::arg("profile", profile.name)); - store.push_back(fmt::arg("driver", profile.driver)); - store.push_back(fmt::arg("icon", getIcon(0, profile.name))); - label_.set_markup(fmt::vformat(format_, store)); - if (tooltipEnabled()) { - label_.set_tooltip_text(fmt::vformat(tooltipFormat_, store)); +// Look for the profile str in our internal profiles list. Using a +// vector to store the profiles ain't the smartest move +// complexity-wise, but it makes toggling between the mode easy. This +// vector is 3 elements max, we'll be fine :P +void PowerProfilesDaemon::switchToProfile(std::string const& str) { + auto pred = [str](Profile const& p) { return p.name == str; }; + this->activeProfile_ = std::find_if(availableProfiles_.begin(), availableProfiles_.end(), pred); + if (activeProfile_ == availableProfiles_.end()) { + throw std::runtime_error("FATAL, can't find the active profile in the available profiles list"); } +} - // Set CSS class - if (!currentStyle_.empty()) { - label_.get_style_context()->remove_class(currentStyle_); - } - label_.get_style_context()->add_class(profile.name); - currentStyle_ = profile.name; +auto PowerProfilesDaemon::update() -> void { + if (connected_) { + auto profile = (*activeProfile_); + // Set label + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("profile", profile.name)); + store.push_back(fmt::arg("driver", profile.driver)); + store.push_back(fmt::arg("icon", getIcon(0, profile.name))); + label_.set_markup(fmt::vformat(format_, store)); + if (tooltipEnabled()) { + label_.set_tooltip_text(fmt::vformat(tooltipFormat_, store)); + } + + // Set CSS class + if (!currentStyle_.empty()) { + label_.get_style_context()->remove_class(currentStyle_); + } + label_.get_style_context()->add_class(profile.name); + currentStyle_ = profile.name; - ALabel::update(); + ALabel::update(); + } } bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { - if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) { - activeProfile_++; - if (activeProfile_ == availableProfiles_.end()) { - activeProfile_ = availableProfiles_.begin(); + if (connected_) { + if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) { + activeProfile_++; + if (activeProfile_ == availableProfiles_.end()) { + activeProfile_ = availableProfiles_.begin(); + } + + using VarStr = Glib::Variant; + using SetPowerProfileVar = Glib::Variant>; + VarStr activeProfileVariant = VarStr::create(activeProfile_->name); + auto callArgs = SetPowerProfileVar::create( + std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); + auto container = Glib::VariantBase::cast_dynamic(callArgs); + powerProfilesProxy_->call("org.freedesktop.DBus.Properties.Set", + sigc::mem_fun(*this, &PowerProfilesDaemon::setPropCb), container); } - - using VarStr = Glib::Variant; - using SetPowerProfileVar = Glib::Variant>; - VarStr activeProfileVariant = VarStr::create(activeProfile_->name); - auto callArgs = SetPowerProfileVar::create( - std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); - auto container = Glib::VariantBase::cast_dynamic(callArgs); - powerProfilesProxy_->call_sync("org.freedesktop.DBus.Properties.Set", container); - - update(); } return true; } +void PowerProfilesDaemon::setPropCb(Glib::RefPtr& r) { + auto _ = powerProfilesProxy_->call_finish(r); + update(); +} + } // namespace waybar::modules From bddc8703403e37d37f66841cd3421ef6a6406794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Fri, 1 Mar 2024 15:07:58 +0100 Subject: [PATCH 124/407] modules/power-profiles-daemon: add man page There was no way to display the default value of format-icons without breaking the table :( --- man/waybar-power-profiles-daemon.5.scd | 72 ++++++++++++++++++++++++++ meson.build | 2 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 man/waybar-power-profiles-daemon.5.scd diff --git a/man/waybar-power-profiles-daemon.5.scd b/man/waybar-power-profiles-daemon.5.scd new file mode 100644 index 000000000..435fdbdff --- /dev/null +++ b/man/waybar-power-profiles-daemon.5.scd @@ -0,0 +1,72 @@ +waybar-power-profiles-daemon(5) + +# NAME + +waybar - power-profiles-daemon module + +# DESCRIPTION + +The *power-profiles-daemon* module displays the active power-profiles-daemon profile and cycle through the available profiles on click. + +# FILES + +$XDG_CONFIG_HOME/waybar/config + +# CONFIGURATION + + +[- *Option* +:- *Typeof* +:- *Default* +:= *Description* +|[ *format* +:[ string +:[ "{icon}" +:[ Message displayed on the bar. {icon} and {profile} are respectively substituted with the icon representing the active profile and its full name. +|[ *tooltip-format* +:[ string +:[ "Power profile: {profile}\\nDriver: {driver}" +:[ Messaged displayed in the module tooltip. {icon} and {profile} are respectively substituted with the icon representing the active profile and its full name. +|[ *tooltip* +:[ bool +:[ true +:[ Display the tooltip. +|[ *format-icons* +:[ object +:[ See default value in the example below. +:[ Icons used to represent the various power-profile. *Note*: the default configuration uses the font-awesome icons. You may want to override it if you don't have this font installed on your system. + + +# CONFIGURATION EXAMPLES + +Compact display (default config): + +``` +"power_profiles_daemon": { + "format": "{icon}", + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", + "tooltip": true, + "format-icons": { + "default": "", + "performance": "", + "balanced": "", + "power-saver": "" + } +} +``` + +Display the full profile name: + +``` +"power_profiles_daemon": { + "format": "{icon} {profile}", + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", + "tooltip": true, + "format-icons": { + "default": "", + "performance": "", + "balanced": "", + "power-saver": "" + } +} +``` diff --git a/meson.build b/meson.build index 10c826208..4ce7363d4 100644 --- a/meson.build +++ b/meson.build @@ -222,6 +222,7 @@ if is_linux 'man/waybar-cpu.5.scd', 'man/waybar-memory.5.scd', 'man/waybar-systemd-failed-units.5.scd', + 'man/waybar-power-profiles-daemon.5.scd', ) elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp') @@ -578,4 +579,3 @@ if clangtidy.found() '-p', meson.project_build_root() ] + src_files) endif - From cc759a8b8f0d6c26d45093ff5586557e202751f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Fri, 1 Mar 2024 19:37:20 +0100 Subject: [PATCH 125/407] Power profiles daemon: address review comments Adding : - A missing try/catch - Glib::Error catch - Remove the useless destructor - Populate the profiles vector more efficiently - Numerous nits --- include/modules/power_profiles_daemon.hpp | 5 +- src/modules/power_profiles_daemon.cpp | 102 ++++++++++------------ 2 files changed, 48 insertions(+), 59 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index bfb5c99db..edd9fe006 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -15,7 +15,6 @@ struct Profile { class PowerProfilesDaemon : public ALabel { public: PowerProfilesDaemon(const std::string &, const Json::Value &); - ~PowerProfilesDaemon() override; auto update() -> void override; void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties &, const std::vector &); @@ -38,12 +37,10 @@ class PowerProfilesDaemon : public ALabel { std::vector::iterator activeProfile_; // Current CSS class applied to the label std::string currentStyle_; - // Format strings - std::string labelFormat_; + // Format string std::string tooltipFormat_; // DBus Proxy used to track the current active profile Glib::RefPtr powerProfilesProxy_; - sigc::connection powerProfileChangeSignal_; }; } // namespace waybar::modules diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index bd6a52a77..ae3d7443b 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -1,14 +1,6 @@ #include "modules/power_profiles_daemon.hpp" -// In the 80000 version of fmt library authors decided to optimize imports -// and moved declarations required for fmt::dynamic_format_arg_store in new -// header fmt/args.h -#if (FMT_VERSION >= 80000) #include -#else -#include -#endif - #include #include #include @@ -16,13 +8,7 @@ namespace waybar::modules { PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Value& config) - : ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true), connected_(false) { - if (config_["format"].isString()) { - format_ = config_["format"].asString(); - } else { - format_ = "{icon}"; - } - + : ALabel(config, "power-profiles-daemon", id, "{icon}", 0, false, true), connected_(false) { if (config_["tooltip-format"].isString()) { tooltipFormat_ = config_["tooltip-format"].asString(); } else { @@ -58,27 +44,19 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu sigc::mem_fun(*this, &PowerProfilesDaemon::busConnectedCb)); } -PowerProfilesDaemon::~PowerProfilesDaemon() { - if (powerProfileChangeSignal_.connected()) { - powerProfileChangeSignal_.disconnect(); - } - if (powerProfilesProxy_) { - powerProfilesProxy_.reset(); - } -} - void PowerProfilesDaemon::busConnectedCb(Glib::RefPtr& r) { try { powerProfilesProxy_ = Gio::DBus::Proxy::create_for_bus_finish(r); using GetAllProfilesVar = Glib::Variant>; auto callArgs = GetAllProfilesVar::create(std::make_tuple("net.hadess.PowerProfiles")); - - auto container = Glib::VariantBase::cast_dynamic(callArgs); powerProfilesProxy_->call("org.freedesktop.DBus.Properties.GetAll", - sigc::mem_fun(*this, &PowerProfilesDaemon::getAllPropsCb), container); + sigc::mem_fun(*this, &PowerProfilesDaemon::getAllPropsCb), callArgs); // Connect active profile callback } catch (const std::exception& e) { - spdlog::error("Failed to create the power profiles daemon DBus proxy"); + spdlog::error("Failed to create the power profiles daemon DBus proxy: {}", e.what()); + } catch (const Glib::Error& e) { + spdlog::error("Failed to create the power profiles daemon DBus proxy: {}", + std::string(e.what())); } } @@ -93,12 +71,14 @@ void PowerProfilesDaemon::getAllPropsCb(Glib::RefPtr& r) { // available, we can safely attach the activeProfile monitoring // now. connected_ = true; - powerProfileChangeSignal_ = powerProfilesProxy_->signal_properties_changed().connect( + powerProfilesProxy_->signal_properties_changed().connect( sigc::mem_fun(*this, &PowerProfilesDaemon::profileChangedCb)); populateInitState(); dp.emit(); } catch (const std::exception& err) { spdlog::error("Failed to query power-profiles-daemon via dbus: {}", err.what()); + } catch (const Glib::Error& err) { + spdlog::error("Failed to query power-profiles-daemon via dbus: {}", std::string(err.what())); } } @@ -111,18 +91,22 @@ void PowerProfilesDaemon::populateInitState() { using ProfilesType = std::vector>>; Glib::Variant profilesVariant; powerProfilesProxy_->get_cached_property(profilesVariant, "Profiles"); - Glib::ustring name; - Glib::ustring driver; - Profile profile; for (auto& variantDict : profilesVariant.get()) { + Glib::ustring name; + Glib::ustring driver; if (auto p = variantDict.find("Profile"); p != variantDict.end()) { name = p->second.get(); } if (auto d = variantDict.find("Driver"); d != variantDict.end()) { driver = d->second.get(); } - profile = {name, driver}; - availableProfiles_.push_back(profile); + if (!name.empty()) { + availableProfiles_.emplace_back(std::move(name), std::move(driver)); + } else { + spdlog::error( + "Power profiles daemon: power-profiles-daemon sent us an empty power profile name. " + "Something is wrong."); + } } // Find the index of the current activated mode (to toggle) @@ -157,12 +141,14 @@ void PowerProfilesDaemon::switchToProfile(std::string const& str) { auto pred = [str](Profile const& p) { return p.name == str; }; this->activeProfile_ = std::find_if(availableProfiles_.begin(), availableProfiles_.end(), pred); if (activeProfile_ == availableProfiles_.end()) { - throw std::runtime_error("FATAL, can't find the active profile in the available profiles list"); + spdlog::error( + "Power profile daemon: can't find the active profile {} in the available profiles list", + str); } } auto PowerProfilesDaemon::update() -> void { - if (connected_) { + if (connected_ && activeProfile_ != availableProfiles_.end()) { auto profile = (*activeProfile_); // Set label fmt::dynamic_format_arg_store store; @@ -180,35 +166,41 @@ auto PowerProfilesDaemon::update() -> void { } label_.get_style_context()->add_class(profile.name); currentStyle_ = profile.name; - - ALabel::update(); + event_box_.set_visible(true); + } else { + event_box_.set_visible(false); } + + ALabel::update(); } bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { - if (connected_) { - if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) { - activeProfile_++; - if (activeProfile_ == availableProfiles_.end()) { - activeProfile_ = availableProfiles_.begin(); - } - - using VarStr = Glib::Variant; - using SetPowerProfileVar = Glib::Variant>; - VarStr activeProfileVariant = VarStr::create(activeProfile_->name); - auto callArgs = SetPowerProfileVar::create( - std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); - auto container = Glib::VariantBase::cast_dynamic(callArgs); - powerProfilesProxy_->call("org.freedesktop.DBus.Properties.Set", - sigc::mem_fun(*this, &PowerProfilesDaemon::setPropCb), container); + if (e->type == GdkEventType::GDK_BUTTON_PRESS && connected_) { + activeProfile_++; + if (activeProfile_ == availableProfiles_.end()) { + activeProfile_ = availableProfiles_.begin(); } + + using VarStr = Glib::Variant; + using SetPowerProfileVar = Glib::Variant>; + VarStr activeProfileVariant = VarStr::create(activeProfile_->name); + auto callArgs = SetPowerProfileVar::create( + std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); + powerProfilesProxy_->call("org.freedesktop.DBus.Properties.Set", + sigc::mem_fun(*this, &PowerProfilesDaemon::setPropCb), callArgs); } return true; } void PowerProfilesDaemon::setPropCb(Glib::RefPtr& r) { - auto _ = powerProfilesProxy_->call_finish(r); - update(); + try { + auto _ = powerProfilesProxy_->call_finish(r); + update(); + } catch (const std::exception& e) { + spdlog::error("Failed to set the the active power profile: {}", e.what()); + } catch (const Glib::Error& e) { + spdlog::error("Failed to set the active power profile: {}", std::string(e.what())); + } } } // namespace waybar::modules From 5ba7c9eb6046aec3f6f1fe747cd2b806e02b2218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Sat, 2 Mar 2024 09:40:19 +0100 Subject: [PATCH 126/407] modules/power-profiles-daemon: add some right padding The icon is not really centered in the box. This is likely coming from a bogus glyph width calculation. It's not a big deal, but that's not really pleasant aesthetically-wise. Adding a bit of right padding makes it much more pleasant to watch. It does not really disrupt a wider display form, like one that explicitely writes the active profile. --- resources/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/style.css b/resources/style.css index 7f708ff41..b58593904 100644 --- a/resources/style.css +++ b/resources/style.css @@ -140,6 +140,10 @@ button:hover { animation-direction: alternate; } +#power-profiles-daemon { + padding-right: 15px; +} + #power-profiles-daemon.performance { background-color: #f53c3c; color: #ffffff; From 5578c122ab459c75e46d3029c5600eefc42bee03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Sat, 2 Mar 2024 18:37:32 +0100 Subject: [PATCH 127/407] modules/power-profiles-daemon: kebab case name in config power_profiles_daemon => power-profiles-daemon --- man/waybar-power-profiles-daemon.5.scd | 4 ++-- resources/config.jsonc | 4 ++-- src/factory.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/man/waybar-power-profiles-daemon.5.scd b/man/waybar-power-profiles-daemon.5.scd index 435fdbdff..82fad13bd 100644 --- a/man/waybar-power-profiles-daemon.5.scd +++ b/man/waybar-power-profiles-daemon.5.scd @@ -42,7 +42,7 @@ $XDG_CONFIG_HOME/waybar/config Compact display (default config): ``` -"power_profiles_daemon": { +"power-profiles-daemon": { "format": "{icon}", "tooltip-format": "Power profile: {profile}\nDriver: {driver}", "tooltip": true, @@ -58,7 +58,7 @@ Compact display (default config): Display the full profile name: ``` -"power_profiles_daemon": { +"power-profiles-daemon": { "format": "{icon} {profile}", "tooltip-format": "Power profile: {profile}\nDriver: {driver}", "tooltip": true, diff --git a/resources/config.jsonc b/resources/config.jsonc index 4e300a330..58813925a 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -20,7 +20,7 @@ "idle_inhibitor", "pulseaudio", "network", - "power_profiles_daemon", + "power-profiles-daemon", "cpu", "memory", "temperature", @@ -148,7 +148,7 @@ "battery#bat2": { "bat": "BAT2" }, - "power_profiles_daemon": { + "power-profiles-daemon": { "format": "{icon}", "tooltip-format": "Power profile: {profile}\nDriver: {driver}", "tooltip": true, diff --git a/src/factory.cpp b/src/factory.cpp index 7dc6709ef..94076201f 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -283,7 +283,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "bluetooth") { return new waybar::modules::Bluetooth(id, config_[name]); } - if (ref == "power_profiles_daemon") { + if (ref == "power-profiles-daemon") { return new waybar::modules::PowerProfilesDaemon(id, config_[name]); } #endif From 9de0e393ab7a35e6f1c1c827145f57529b8a5cca Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Sat, 2 Mar 2024 23:08:21 +0100 Subject: [PATCH 128/407] Sway-Workspaces: Fixed scrolling not working Fixes regression in bb843e0 that caused scrolling over the bar not working --- src/modules/sway/workspaces.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index eda53ddec..6464bf9aa 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -427,7 +427,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { { std::lock_guard lock(mutex_); auto it = std::find_if(workspaces_.begin(), workspaces_.end(), - [](const auto &workspace) { return workspace["focused"].asBool(); }); + [](const auto &workspace) { return hasFlag(workspace, "focused"); }); if (it == workspaces_.end()) { return true; } From df7f1fffcf56bcbd54d92c6085692236dd7ab968 Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Mon, 4 Mar 2024 13:17:30 +0100 Subject: [PATCH 129/407] feat(hyprland/workspaces): added options `move-to-monitor` and `active-per-monitor` --- include/modules/hyprland/workspaces.hpp | 4 ++ man/waybar-hyprland-workspaces.5.scd | 15 ++++++ src/modules/hyprland/workspaces.cpp | 61 ++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 91ea16539..df72f3432 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -126,6 +126,8 @@ class Workspaces : public AModule, public EventHandler { auto allOutputs() const -> bool { return m_allOutputs; } auto showSpecial() const -> bool { return m_showSpecial; } auto activeOnly() const -> bool { return m_activeOnly; } + auto moveToMonitor() const -> bool { return m_moveToMonitor; } + auto activePerMonitor() const -> bool { return m_activePerMonitor; } auto getBarOutput() const -> std::string { return m_bar.output->name; } @@ -182,6 +184,8 @@ class Workspaces : public AModule, public EventHandler { bool m_allOutputs = false; bool m_showSpecial = false; bool m_activeOnly = false; + bool m_moveToMonitor = false; + bool m_activePerMonitor = false; Json::Value m_persistentWorkspaceConfig; // Map for windows stored in workspaces not present in the current bar. diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 12c1fe391..44218f9ad 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -52,6 +52,21 @@ Addressed by *hyprland/workspaces* default: false ++ If set to true, only the active workspace will be shown. +*move-to-monitor*: ++ + typeof: bool ++ + default: false ++ + If set to true, open the workspace on the current monitor when clicking on a workspace button. + Otherwise, the workspace will open on the monitor where it was previously assigned. + Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland. + +*active-per-monitor*: ++ + typeof: bool ++ + default: false ++ + If set to true, each bar on each monitor will show its separate active + workspace being the currently focused workspace on this monitor. + Otherwise, all bars on all monitors will show the same active workspace + being the currently focused workspace on the currently focused monitor. + *ignore-workspaces*: ++ typeof: array ++ default: [] ++ diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 882e38067..458da8230 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -83,6 +83,16 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { m_activeOnly = configActiveOnly.asBool(); } + auto configMoveToMonitor = config_["move-to-monitor"]; + if (configMoveToMonitor.isBool()) { + m_moveToMonitor = configMoveToMonitor.asBool(); + } + + auto configActivePerMonitor = config_["active-per-monitor"]; + if (configActivePerMonitor.isBool()) { + m_activePerMonitor = configActivePerMonitor.asBool(); + } + auto configSortBy = config_["sort-by"]; if (configSortBy.isString()) { auto sortByStr = configSortBy.asString(); @@ -321,7 +331,14 @@ void Workspaces::onEvent(const std::string &ev) { } void Workspaces::onWorkspaceActivated(std::string const &payload) { - m_activeWorkspaceName = payload; + if (!m_activePerMonitor) { + m_activeWorkspaceName = payload; + return; + } + auto activeWorkspace = gIPC->getSocket1JsonReply("activeworkspace"); + if (m_bar.output->name == activeWorkspace["monitor"].asString()) { + m_activeWorkspaceName = payload; + } } void Workspaces::onSpecialWorkspaceActivated(std::string const &payload) { @@ -374,6 +391,16 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { if (m_bar.output->name == monitorName) { Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); onWorkspaceCreated(workspaceName, clientsData); + if (m_activePerMonitor) { + for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { + if (m_bar.output->name == monitor["name"].asString()) { + auto ws = monitor["activeWorkspace"]; + if (ws.isObject() && (ws["name"].isString())) { + m_activeWorkspaceName = ws["name"].asString(); + } + } + } + } } else { spdlog::debug("Removing workspace because it was moved to another monitor: {}"); onWorkspaceDestroyed(workspaceName); @@ -399,10 +426,13 @@ void Workspaces::onWorkspaceRenamed(std::string const &payload) { void Workspaces::onMonitorFocused(std::string const &payload) { spdlog::trace("Monitor focused: {}", payload); - m_activeWorkspaceName = payload.substr(payload.find(',') + 1); + auto monitorName = payload.substr(0, payload.find(',')); + if (!m_activePerMonitor || m_bar.output->name == monitorName) { + m_activeWorkspaceName = payload.substr(payload.find(',') + 1); + } for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { - if (monitor["name"].asString() == payload.substr(0, payload.find(','))) { + if (monitor["name"].asString() == monitorName) { auto name = monitor["specialWorkspace"]["name"].asString(); m_activeSpecialWorkspaceName = !name.starts_with("special:") ? name : name.substr(8); } @@ -804,7 +834,18 @@ void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) } void Workspaces::init() { - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + if (!m_activePerMonitor) { + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + } else { + for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { + if (m_bar.output->name == monitor["name"].asString()) { + auto ws = monitor["activeWorkspace"]; + if (ws.isObject() && (ws["name"].isString())) { + m_activeWorkspaceName = ws["name"].asString(); + } + } + } + } initializeWorkspaces(); updateWindowCount(); @@ -1019,9 +1060,17 @@ bool Workspace::handleClicked(GdkEventButton *bt) const { if (bt->type == GDK_BUTTON_PRESS) { try { if (id() > 0) { // normal - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); + } else { + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } } else if (!isSpecial()) { // named (this includes persistent) - gIPC->getSocket1Reply("dispatch workspace name:" + name()); + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); + } else { + gIPC->getSocket1Reply("dispatch workspace name:" + name()); + } } else if (id() != -99) { // named special gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); } else { // special From 25b85800a55a218f01e030e497441cc0304dd013 Mon Sep 17 00:00:00 2001 From: Merlin Sievers Date: Mon, 4 Mar 2024 20:00:57 +0100 Subject: [PATCH 130/407] Add documentation for justify option --- man/waybar-backlight.5.scd | 6 +++++- man/waybar-battery.5.scd | 6 +++++- man/waybar-bluetooth.5.scd | 6 +++++- man/waybar-cpu.5.scd | 6 +++++- man/waybar-custom.5.scd | 6 +++++- man/waybar-disk.5.scd | 6 +++++- man/waybar-hyprland-submap.5.scd | 6 +++++- man/waybar-idle-inhibitor.5.scd | 6 +++++- man/waybar-inhibitor.5.scd | 6 +++++- man/waybar-jack.5.scd | 6 +++++- man/waybar-memory.5.scd | 6 +++++- man/waybar-mpd.5.scd | 6 +++++- man/waybar-mpris.5.scd | 7 +++++-- man/waybar-network.5.scd | 6 +++++- man/waybar-pulseaudio.5.scd | 6 +++++- man/waybar-river-layout.5.scd | 6 +++++- man/waybar-river-mode.5.scd | 6 +++++- man/waybar-river-window.5.scd | 6 +++++- man/waybar-sndio.5.scd | 6 +++++- man/waybar-sway-mode.5.scd | 6 +++++- man/waybar-sway-window.5.scd | 6 +++++- man/waybar-temperature.5.scd | 6 +++++- man/waybar-wireplumber.5.scd | 6 +++++- 23 files changed, 115 insertions(+), 24 deletions(-) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index 7db18a202..b92abd12f 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -30,7 +30,11 @@ The *backlight* module displays the current backlight level. *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *rotate*: ++ typeof: integer ++ diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 52a6a2d1b..e359ea2e0 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -61,7 +61,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *rotate*: ++ typeof: integer++ diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 1fdd984ba..3808e855a 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -66,7 +66,11 @@ Addressed by *bluetooth* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 484795689..fcbd12653 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -35,7 +35,11 @@ The *cpu* module displays the current CPU utilization. *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *rotate*: ++ typeof: integer ++ diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 67e4c89c1..b968e7814 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -72,7 +72,11 @@ Addressed by *custom/* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index d466bddfc..a279718b7 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -45,7 +45,11 @@ Addressed by *disk* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index 3f8d62805..9f5429b4e 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -31,7 +31,11 @@ Addressed by *hyprland/submap* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index 287def1a3..71b3b30c6 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -33,7 +33,11 @@ screensaver, also known as "presentation mode". *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-inhibitor.5.scd b/man/waybar-inhibitor.5.scd index 1233eb7d7..47b6ffce2 100644 --- a/man/waybar-inhibitor.5.scd +++ b/man/waybar-inhibitor.5.scd @@ -37,7 +37,11 @@ See *systemd-inhibit*(1) for more information. *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-jack.5.scd b/man/waybar-jack.5.scd index 3af71b617..87a383542 100644 --- a/man/waybar-jack.5.scd +++ b/man/waybar-jack.5.scd @@ -63,7 +63,11 @@ Addressed by *jack* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index 55c74b0bd..e0252caf3 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -45,7 +45,11 @@ Addressed by *memory* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index ffef0fefd..fe6ee5a18 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -103,7 +103,11 @@ Addressed by *mpd* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 117b816c4..186d73c6a 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -119,8 +119,11 @@ The *mpris* module displays currently playing media via libplayerctl. *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index 08c86d3d6..b5580c528 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -70,7 +70,11 @@ Addressed by *network* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index e04245ee3..4bc75258f 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -56,7 +56,11 @@ Additionally, you can control the volume by scrolling *up* or *down* while the c *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *scroll-step*: ++ typeof: float ++ diff --git a/man/waybar-river-layout.5.scd b/man/waybar-river-layout.5.scd index f6f682d01..1c09d6f65 100644 --- a/man/waybar-river-layout.5.scd +++ b/man/waybar-river-layout.5.scd @@ -33,7 +33,11 @@ Addressed by *river/layout* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-river-mode.5.scd b/man/waybar-river-mode.5.scd index aea6e205e..2d63b5e1e 100644 --- a/man/waybar-river-mode.5.scd +++ b/man/waybar-river-mode.5.scd @@ -31,7 +31,11 @@ Addressed by *river/mode* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-river-window.5.scd b/man/waybar-river-window.5.scd index 9c202b2aa..dbd9f1307 100644 --- a/man/waybar-river-window.5.scd +++ b/man/waybar-river-window.5.scd @@ -31,7 +31,11 @@ Addressed by *river/window* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd index 1bb0484a7..197aaba07 100644 --- a/man/waybar-sndio.5.scd +++ b/man/waybar-sndio.5.scd @@ -32,7 +32,11 @@ cursor is over the module, and clicking on the module toggles mute. *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *scroll-step*: ++ typeof: int ++ diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index 87e70adfd..44c8b81ac 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -31,7 +31,11 @@ Addressed by *sway/mode* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 9b793f32c..037e6b55c 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -31,7 +31,11 @@ Addressed by *sway/window* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 1d6e7d2eb..ff2168eac 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -71,7 +71,11 @@ Addressed by *temperature* *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-wireplumber.5.scd b/man/waybar-wireplumber.5.scd index 5424deb6e..b08fd90f7 100644 --- a/man/waybar-wireplumber.5.scd +++ b/man/waybar-wireplumber.5.scd @@ -47,7 +47,11 @@ The *wireplumber* module displays the current volume reported by WirePlumber. *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. + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. *scroll-step*: ++ typeof: float ++ From 68889494d019705e87b519934b4625a2134336d7 Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Tue, 5 Mar 2024 11:12:07 +0100 Subject: [PATCH 131/407] Removed option `active-per-monitor` --- include/modules/hyprland/workspaces.hpp | 2 -- man/waybar-hyprland-workspaces.5.scd | 8 ----- src/modules/hyprland/workspaces.cpp | 44 +++---------------------- 3 files changed, 4 insertions(+), 50 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index df72f3432..655bc4604 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -127,7 +127,6 @@ class Workspaces : public AModule, public EventHandler { auto showSpecial() const -> bool { return m_showSpecial; } auto activeOnly() const -> bool { return m_activeOnly; } auto moveToMonitor() const -> bool { return m_moveToMonitor; } - auto activePerMonitor() const -> bool { return m_activePerMonitor; } auto getBarOutput() const -> std::string { return m_bar.output->name; } @@ -185,7 +184,6 @@ class Workspaces : public AModule, public EventHandler { bool m_showSpecial = false; bool m_activeOnly = false; bool m_moveToMonitor = false; - bool m_activePerMonitor = false; Json::Value m_persistentWorkspaceConfig; // Map for windows stored in workspaces not present in the current bar. diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 44218f9ad..584beac19 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -59,14 +59,6 @@ Addressed by *hyprland/workspaces* Otherwise, the workspace will open on the monitor where it was previously assigned. Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland. -*active-per-monitor*: ++ - typeof: bool ++ - default: false ++ - If set to true, each bar on each monitor will show its separate active - workspace being the currently focused workspace on this monitor. - Otherwise, all bars on all monitors will show the same active workspace - being the currently focused workspace on the currently focused monitor. - *ignore-workspaces*: ++ typeof: array ++ default: [] ++ diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 458da8230..3330fbc72 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -88,11 +88,6 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { m_moveToMonitor = configMoveToMonitor.asBool(); } - auto configActivePerMonitor = config_["active-per-monitor"]; - if (configActivePerMonitor.isBool()) { - m_activePerMonitor = configActivePerMonitor.asBool(); - } - auto configSortBy = config_["sort-by"]; if (configSortBy.isString()) { auto sortByStr = configSortBy.asString(); @@ -331,14 +326,7 @@ void Workspaces::onEvent(const std::string &ev) { } void Workspaces::onWorkspaceActivated(std::string const &payload) { - if (!m_activePerMonitor) { - m_activeWorkspaceName = payload; - return; - } - auto activeWorkspace = gIPC->getSocket1JsonReply("activeworkspace"); - if (m_bar.output->name == activeWorkspace["monitor"].asString()) { - m_activeWorkspaceName = payload; - } + m_activeWorkspaceName = payload; } void Workspaces::onSpecialWorkspaceActivated(std::string const &payload) { @@ -391,16 +379,6 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { if (m_bar.output->name == monitorName) { Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); onWorkspaceCreated(workspaceName, clientsData); - if (m_activePerMonitor) { - for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { - if (m_bar.output->name == monitor["name"].asString()) { - auto ws = monitor["activeWorkspace"]; - if (ws.isObject() && (ws["name"].isString())) { - m_activeWorkspaceName = ws["name"].asString(); - } - } - } - } } else { spdlog::debug("Removing workspace because it was moved to another monitor: {}"); onWorkspaceDestroyed(workspaceName); @@ -426,13 +404,10 @@ void Workspaces::onWorkspaceRenamed(std::string const &payload) { void Workspaces::onMonitorFocused(std::string const &payload) { spdlog::trace("Monitor focused: {}", payload); - auto monitorName = payload.substr(0, payload.find(',')); - if (!m_activePerMonitor || m_bar.output->name == monitorName) { - m_activeWorkspaceName = payload.substr(payload.find(',') + 1); - } + m_activeWorkspaceName = payload.substr(payload.find(',') + 1); for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { - if (monitor["name"].asString() == monitorName) { + if (monitor["name"].asString() == payload.substr(0, payload.find(','))) { auto name = monitor["specialWorkspace"]["name"].asString(); m_activeSpecialWorkspaceName = !name.starts_with("special:") ? name : name.substr(8); } @@ -834,18 +809,7 @@ void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) } void Workspaces::init() { - if (!m_activePerMonitor) { - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); - } else { - for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { - if (m_bar.output->name == monitor["name"].asString()) { - auto ws = monitor["activeWorkspace"]; - if (ws.isObject() && (ws["name"].isString())) { - m_activeWorkspaceName = ws["name"].asString(); - } - } - } - } + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); initializeWorkspaces(); updateWindowCount(); From 207e99876d7acfc43fff0bd5432ed4586ef80a4a Mon Sep 17 00:00:00 2001 From: Robin Ole Heinemann Date: Fri, 8 Mar 2024 21:48:27 +0100 Subject: [PATCH 132/407] feat: allow horizontal scroll --- src/AModule.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 9a9f13866..a451c3d60 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -51,7 +51,9 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: event_box_.add_events(Gdk::BUTTON_RELEASE_MASK); event_box_.signal_button_release_event().connect(sigc::mem_fun(*this, &AModule::handleRelease)); } - if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || enable_scroll) { + if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || + config_["on-scroll-left"].isString() || config_["on-scroll-right"].isString() || + enable_scroll) { event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll)); } @@ -179,6 +181,10 @@ bool AModule::handleScroll(GdkEventScroll* e) { eventName = "on-scroll-up"; else if (dir == SCROLL_DIR::DOWN) eventName = "on-scroll-down"; + else if (dir == SCROLL_DIR::LEFT) + eventName = "on-scroll-left"; + else if (dir == SCROLL_DIR::RIGHT) + eventName = "on-scroll-right"; // First call module actions this->AModule::doAction(eventName); From 7b3d155608d030094f813b5c1908e0017c202547 Mon Sep 17 00:00:00 2001 From: Jo De Boeck Date: Tue, 12 Mar 2024 00:09:47 +0200 Subject: [PATCH 133/407] Fix peristant workspaces for sway Fixes: #2998 Signed-off-by: Jo De Boeck --- src/modules/sway/workspaces.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 6464bf9aa..68f1ac451 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -141,12 +141,12 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { for (const std::string &p_w_name : p_workspaces_names) { const Json::Value &p_w = p_workspaces[p_w_name]; - auto it = - std::find_if(payload.begin(), payload.end(), [&p_w_name](const Json::Value &node) { - return node["name"].asString() == p_w_name; - }); + auto it = std::find_if(workspaces_.begin(), workspaces_.end(), + [&p_w_name](const Json::Value &node) { + return node["name"].asString() == p_w_name; + }); - if (it != payload.end()) { + if (it != workspaces_.end()) { continue; // already displayed by some bar } @@ -156,7 +156,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { if (output.asString() == bar_.output->name) { Json::Value v; v["name"] = p_w_name; - v["target_output"] = bar_.output->name; + v["output"] = bar_.output->name; v["num"] = convertWorkspaceNameToNum(p_w_name); workspaces_.emplace_back(std::move(v)); break; @@ -166,7 +166,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { // Adding to all outputs Json::Value v; v["name"] = p_w_name; - v["target_output"] = ""; + v["output"] = ""; v["num"] = convertWorkspaceNameToNum(p_w_name); workspaces_.emplace_back(std::move(v)); } @@ -250,6 +250,9 @@ bool Workspaces::filterButtons() { } bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { + if (!node[flag].isBool()) { + return false; + } if (node[flag].asBool()) { return true; } From 32eac3ccb738691974121b77b4af0c47d1cbe524 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 13 Mar 2024 19:46:56 +0100 Subject: [PATCH 134/407] chore: 0.10.0 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 4ce7363d4..e21ff262c 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.24', + version: '0.10.0', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From 17734f0364f3390e439acc912e94148830dee9a2 Mon Sep 17 00:00:00 2001 From: Eldar Yusupov Date: Thu, 14 Mar 2024 23:07:45 +0300 Subject: [PATCH 135/407] Add dwl/window module --- README.md | 2 +- include/modules/dwl/window.hpp | 37 ++++++++++ man/waybar-dwl-window.5.scd | 118 ++++++++++++++++++++++++++++++ man/waybar.5.scd.in | 1 + meson.build | 2 + src/factory.cpp | 4 ++ src/modules/dwl/tags.cpp | 11 +-- src/modules/dwl/window.cpp | 126 +++++++++++++++++++++++++++++++++ 8 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 include/modules/dwl/window.hpp create mode 100644 man/waybar-dwl-window.5.scd create mode 100644 src/modules/dwl/window.cpp diff --git a/README.md b/README.md index 65be764cf..c76c19d30 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ - Sway (Workspaces, Binding mode, Focused window name) - River (Mapping mode, Tags, Focused window name) - Hyprland (Window Icons, Workspaces, Focused window name) -- DWL (Tags) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc) +- DWL (Tags, Focused window name) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc) - Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Local time - Battery diff --git a/include/modules/dwl/window.hpp b/include/modules/dwl/window.hpp new file mode 100644 index 000000000..e4c964046 --- /dev/null +++ b/include/modules/dwl/window.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include + +#include "AAppIconLabel.hpp" +#include "bar.hpp" +#include "util/json.hpp" +#include "dwl-ipc-unstable-v2-client-protocol.h" + +namespace waybar::modules::dwl { + +class Window : public AAppIconLabel, public sigc::trackable { + public: + Window(const std::string&, const waybar::Bar&, const Json::Value&); + virtual ~Window() = default; + + void handle_layout(const uint32_t layout); + void handle_title(const char *title); + void handle_appid(const char *ppid); + void handle_layout_symbol(const char *layout_symbol); + void handle_frame(); + + struct zdwl_ipc_manager_v2 *status_manager_; + private: + const Bar& bar_; + + std::string title_; + std::string appid_; + std::string layout_symbol_; + uint32_t layout_; + + struct zdwl_ipc_output_v2 *output_status_; +}; + +} // namespace waybar::modules::dwl diff --git a/man/waybar-dwl-window.5.scd b/man/waybar-dwl-window.5.scd new file mode 100644 index 000000000..c2f5b93eb --- /dev/null +++ b/man/waybar-dwl-window.5.scd @@ -0,0 +1,118 @@ +waybar-dwl-window(5) + +# NAME + +waybar - dwl window module + +# DESCRIPTION + +The *window* module displays the title of the currently focused window in DWL + +# CONFIGURATION + +Addressed by *dwl/window* + +*format*: ++ + typeof: string ++ + default: {title} ++ + The format, how information should be displayed. + +*rotate*: ++ + typeof: integer ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should accept. + +*align*: ++ + typeof: float ++ + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. + +*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-click 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. + +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to disable tooltip on hover. + +*rewrite*: ++ + typeof: object ++ + Rules to rewrite the module format output. See *rewrite rules*. + +*icon*: ++ + typeof: bool ++ + default: false ++ + Option to hide the application icon. + +*icon-size*: ++ + typeof: integer ++ + default: 24 ++ + Option to change the size of the application icon. + +# FORMAT REPLACEMENTS + +*{title}*: The title of the focused window. + +*{app_id}*: The app_id of the focused window. + +*{layout}*: The layout of the focused window. + +# REWRITE RULES + +*rewrite* is an object where keys are regular expressions and values are +rewrite rules if the expression matches. Rules may contain references to +captures of the expression. + +Regular expression and replacement follow ECMA-script rules. + +If no expression matches, the format output is left unchanged. + +Invalid expressions (e.g., mismatched parentheses) are skipped. + +# EXAMPLES + +``` +"dwl/window": { + "format": "{}", + "max-length": 50, + "rewrite": { + "(.*) - Mozilla Firefox": "🌎 $1", + "(.*) - zsh": "> [$1]" + } +} +``` diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 2d4de0c90..5fe30ca86 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -310,6 +310,7 @@ A group may hide all but one element, showing them only on mouse hover. In order - *waybar-custom(5)* - *waybar-disk(5)* - *waybar-dwl-tags(5)* +- *waybar-dwl-window(5)* - *waybar-gamemode(5)* - *waybar-hyprland-language(5)* - *waybar-hyprland-submap(5)* diff --git a/meson.build b/meson.build index e21ff262c..74a037b51 100644 --- a/meson.build +++ b/meson.build @@ -293,7 +293,9 @@ endif if true add_project_arguments('-DHAVE_DWL', language: 'cpp') src_files += files('src/modules/dwl/tags.cpp') + src_files += files('src/modules/dwl/window.cpp') man_files += files('man/waybar-dwl-tags.5.scd') + man_files += files('man/waybar-dwl-window.5.scd') endif if true diff --git a/src/factory.cpp b/src/factory.cpp index 94076201f..0549fe09f 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -28,6 +28,7 @@ #endif #ifdef HAVE_DWL #include "modules/dwl/tags.hpp" +#include "modules/dwl/window.hpp" #endif #ifdef HAVE_HYPRLAND #include "modules/hyprland/language.hpp" @@ -187,6 +188,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "dwl/tags") { return new waybar::modules::dwl::Tags(id, bar_, config_[name]); } + if (ref == "dwl/window") { + return new waybar::modules::dwl::Window(id, bar_, config_[name]); + } #endif #ifdef HAVE_HYPRLAND if (ref == "hyprland/window") { diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index f36ece1d0..942c269f8 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -21,11 +21,11 @@ wl_array tags, layouts; static uint num_tags = 0; -void toggle_visibility(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { +static void toggle_visibility(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { // Intentionally empty } -void active(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t active) { +static void active(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t active) { // Intentionally empty } @@ -37,15 +37,15 @@ static void set_tag(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t tag : num_tags & ~(1 << tag); } -void set_layout_symbol(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *layout) { +static void set_layout_symbol(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *layout) { // Intentionally empty } -void title(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *title) { +static void title(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *title) { // Intentionally empty } -void dwl_frame(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { +static void dwl_frame(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { // Intentionally empty } @@ -97,6 +97,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con output_status_{nullptr} { struct wl_display *display = Client::inst()->wl_display; struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener_impl, this); wl_display_roundtrip(display); diff --git a/src/modules/dwl/window.cpp b/src/modules/dwl/window.cpp new file mode 100644 index 000000000..cfd47a05c --- /dev/null +++ b/src/modules/dwl/window.cpp @@ -0,0 +1,126 @@ +#include "modules/dwl/window.hpp" + +#include +#include +#include +#include +#include +#include + +#include "client.hpp" +#include "dwl-ipc-unstable-v2-client-protocol.h" + +#include "util/rewrite_string.hpp" + +namespace waybar::modules::dwl { + +static void toggle_visibility(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { + // Intentionally empty +} + +static void active(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t active) { + // Intentionally empty +} + +static void set_tag(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t tag, uint32_t state, + uint32_t clients, uint32_t focused) { + // Intentionally empty +} + +static void set_layout_symbol(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *layout) { + static_cast(data)->handle_layout_symbol(layout); +} + +static void title(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *title) { + static_cast(data)->handle_title(title); +} + +static void dwl_frame(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { + static_cast(data)->handle_frame(); +} + +static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t layout) { + static_cast(data)->handle_layout(layout); +} + +static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid){ + static_cast(data)->handle_appid(appid); +}; + +static const zdwl_ipc_output_v2_listener output_status_listener_impl{ + .toggle_visibility = toggle_visibility, + .active = active, + .tag = set_tag, + .layout = set_layout, + .title = title, + .appid = appid, + .layout_symbol = set_layout_symbol, + .frame = dwl_frame, +}; + +static void handle_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) { + if (std::strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) { + static_cast(data)->status_manager_ = static_cast( + (zdwl_ipc_manager_v2 *)wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 1)); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + /* Ignore event */ +} + +static const wl_registry_listener registry_listener_impl = {.global = handle_global, + .global_remove = handle_global_remove}; + +Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) + : AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar) { + struct wl_display *display = Client::inst()->wl_display; + struct wl_registry *registry = wl_display_get_registry(display); + + wl_registry_add_listener(registry, ®istry_listener_impl, this); + wl_display_roundtrip(display); + + if (!status_manager_) { + spdlog::error("dwl_status_manager_v2 not advertised"); + return; + } + + struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + output_status_ = zdwl_ipc_manager_v2_get_output(status_manager_, output); + zdwl_ipc_output_v2_add_listener(output_status_, &output_status_listener_impl, this); + zdwl_ipc_manager_v2_destroy(status_manager_); +} + +void Window::handle_title(const char *title) { + title_ = title; +} + +void Window::handle_appid(const char *appid) { + appid_ = appid; +} + +void Window::handle_layout_symbol(const char *layout_symbol) { + layout_symbol_ = layout_symbol; +} + +void Window::handle_layout(const uint32_t layout) { + layout_ = layout; +} + +void Window::handle_frame() { + label_.set_markup(waybar::util::rewriteString( + fmt::format( + fmt::runtime(format_), + fmt::arg("title", title_), + fmt::arg("layout", layout_symbol_), + fmt::arg("app_id", appid_)), + config_["rewrite"])); + updateAppIconName(appid_, ""); + updateAppIcon(); + if (tooltipEnabled()) { + label_.set_tooltip_text(title_); + } +} + +} // namespace waybar::modules::dwl From dcddddd3f1a1c1560a84caf791a21b4610f0fc7d Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Thu, 14 Mar 2024 00:58:33 -0700 Subject: [PATCH 136/407] fix(power-profiles-daemon): correctly set initial visibility The bus error when the daemon is not reachable prevents the initial update and keeps the module visible, as an empty section on the bar. Do the update explicitly before connecting to set initial visibility. While we at it, remove a couple of redundant `update()` calls. --- src/modules/power_profiles_daemon.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index ae3d7443b..ac5f7a2a6 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -42,6 +42,8 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", "net.hadess.PowerProfiles", sigc::mem_fun(*this, &PowerProfilesDaemon::busConnectedCb)); + // Schedule update to set the initial visibility + dp.emit(); } void PowerProfilesDaemon::busConnectedCb(Glib::RefPtr& r) { @@ -74,7 +76,6 @@ void PowerProfilesDaemon::getAllPropsCb(Glib::RefPtr& r) { powerProfilesProxy_->signal_properties_changed().connect( sigc::mem_fun(*this, &PowerProfilesDaemon::profileChangedCb)); populateInitState(); - dp.emit(); } catch (const std::exception& err) { spdlog::error("Failed to query power-profiles-daemon via dbus: {}", err.what()); } catch (const Glib::Error& err) { @@ -112,8 +113,6 @@ void PowerProfilesDaemon::populateInitState() { // Find the index of the current activated mode (to toggle) std::string str = profileStr.get(); switchToProfile(str); - - update(); } void PowerProfilesDaemon::profileChangedCb( @@ -128,7 +127,6 @@ void PowerProfilesDaemon::profileChangedCb( Glib::VariantBase::cast_dynamic>(activeProfileVariant->second) .get(); switchToProfile(activeProfile); - update(); } } } @@ -145,6 +143,7 @@ void PowerProfilesDaemon::switchToProfile(std::string const& str) { "Power profile daemon: can't find the active profile {} in the available profiles list", str); } + dp.emit(); } auto PowerProfilesDaemon::update() -> void { @@ -195,7 +194,7 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { void PowerProfilesDaemon::setPropCb(Glib::RefPtr& r) { try { auto _ = powerProfilesProxy_->call_finish(r); - update(); + dp.emit(); } catch (const std::exception& e) { spdlog::error("Failed to set the the active power profile: {}", e.what()); } catch (const Glib::Error& e) { From 4ccefa090280230f8c9c600d4e0ddd3f6bf14d07 Mon Sep 17 00:00:00 2001 From: luzpaz Date: Fri, 15 Mar 2024 11:05:55 -0400 Subject: [PATCH 137/407] README: tweak repology badge * Change repology badge header * Use 3 columns instead of 1 to display badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 65be764cf..32fbfa693 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Waybar is available from a number of Linux distributions: -[![Packaging status](https://repology.org/badge/vertical-allrepos/waybar.svg)](https://repology.org/project/waybar/versions) +[![Packaging status](https://repology.org/badge/vertical-allrepos/waybar.svg?columns=3&header=Waybar%20Downstream%20Packaging)](https://repology.org/project/waybar/versions) An Ubuntu PPA with more recent versions is available [here](https://launchpad.net/~nschloe/+archive/ubuntu/waybar). From 2d122367267e2e46a4cb6da8c317d8f33d7a1ee4 Mon Sep 17 00:00:00 2001 From: Nicola Revelant Date: Fri, 15 Mar 2024 16:01:07 +0100 Subject: [PATCH 138/407] Use the correct thermal zone in FreeBSD --- src/modules/temperature.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index accab969c..886ce14e7 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -9,7 +9,7 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config) : ALabel(config, "temperature", id, "{temperatureC}°C", 10) { #if defined(__FreeBSD__) -// try to read sysctl? +// FreeBSD uses sysctlbyname instead of read from a file #else auto& hwmon_path = config_["hwmon-path"]; if (hwmon_path.isString()) { @@ -37,11 +37,19 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); } + + // check if file_path_ can be used to retrive the temperature std::ifstream temp(file_path_); if (!temp.is_open()) { throw std::runtime_error("Can't open " + file_path_); } + if (!temp.good()) { + temp.close(); + throw std::runtime_error("Can't read from " + file_path_); + } + temp.close(); #endif + thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); @@ -93,11 +101,10 @@ float waybar::modules::Temperature::getTemperature() { size_t size = sizeof temp; auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; - auto sysctl_thermal = fmt::format("hw.acpi.thermal.tz{}.temperature", zone); - if (sysctlbyname("hw.acpi.thermal.tz0.temperature", &temp, &size, NULL, 0) != 0) { + if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone), &temp, &size, NULL, 0) != 0) { throw std::runtime_error( - "sysctl hw.acpi.thermal.tz0.temperature or dev.cpu.0.temperature failed"); + fmt::format("sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone)); } auto temperature_c = ((float)temp - 2732) / 10; return temperature_c; @@ -110,6 +117,9 @@ float waybar::modules::Temperature::getTemperature() { std::string line; if (temp.good()) { getline(temp, line); + } else { + temp.close(); + throw std::runtime_error("Can't read from " + file_path_); } temp.close(); auto temperature_c = std::strtol(line.c_str(), nullptr, 10) / 1000.0; From e1f876b981196c5bc1c2936eff477d429d8a8c1d Mon Sep 17 00:00:00 2001 From: Nicola Revelant Date: Fri, 15 Mar 2024 16:36:54 +0100 Subject: [PATCH 139/407] Fix fmt::format: missing argument --- src/modules/temperature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 886ce14e7..eb99d137f 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -104,7 +104,7 @@ float waybar::modules::Temperature::getTemperature() { if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone), &temp, &size, NULL, 0) != 0) { throw std::runtime_error( - fmt::format("sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone)); + fmt::format("sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone, zone)); } auto temperature_c = ((float)temp - 2732) / 10; return temperature_c; From fbf66530a32ace31600c4cbbcbf6e04dced4cfc8 Mon Sep 17 00:00:00 2001 From: Nicola Revelant Date: Fri, 15 Mar 2024 16:52:52 +0100 Subject: [PATCH 140/407] Explicit convert from std::string to const char* --- src/modules/temperature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index eb99d137f..439dd27c2 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -102,7 +102,7 @@ float waybar::modules::Temperature::getTemperature() { auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; - if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone), &temp, &size, NULL, 0) != 0) { + if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone).c_str(), &temp, &size, NULL, 0) != 0) { throw std::runtime_error( fmt::format("sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone, zone)); } From 01ff7ebb36648bf7afa6ea32c7c32733023b7e95 Mon Sep 17 00:00:00 2001 From: Nicola Revelant Date: Fri, 15 Mar 2024 17:12:31 +0100 Subject: [PATCH 141/407] Fix clang-format to src/modules/temperature.cpp --- src/modules/temperature.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 439dd27c2..f0629670c 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -102,9 +102,10 @@ float waybar::modules::Temperature::getTemperature() { auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; - if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone).c_str(), &temp, &size, NULL, 0) != 0) { - throw std::runtime_error( - fmt::format("sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone, zone)); + if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone).c_str(), &temp, &size, + NULL, 0) != 0) { + throw std::runtime_error(fmt::format( + "sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone, zone)); } auto temperature_c = ((float)temp - 2732) / 10; return temperature_c; From c5a629939895915f01767c6ec06897e8463387a5 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 16 Mar 2024 20:10:05 +0800 Subject: [PATCH 142/407] fix:dwl tag crash when use wlr-randr enable monitor --- src/modules/dwl/tags.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index f36ece1d0..6a07a878f 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -155,6 +155,9 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con } Tags::~Tags() { + if(output_status_) { + zdwl_ipc_output_v2_destroy(output_status_); + } if (status_manager_) { zdwl_ipc_manager_v2_destroy(status_manager_); } From f014a7d2e5f08260501e24d93b44bb33a4bed861 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sat, 16 Mar 2024 21:22:01 +0100 Subject: [PATCH 143/407] man docs & different css class name --- man/waybar-hyprland-workspaces.5.scd | 1 + src/modules/hyprland/workspaces.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 12c1fe391..5646df58f 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -158,3 +158,4 @@ Additional to workspace name matching, the following *format-icons* can be set. - *#workspaces button.persistent* - *#workspaces button.special* - *#workspaces button.urgent* +- *#workspaces button.hosting-monitor* (gets applied if workspace-monitor == waybar-monitor) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index bae73d2e4..1b58b4172 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -872,7 +872,7 @@ void Workspace::update(const std::string &format, const std::string &icon) { addOrRemoveClass(styleContext, isPersistent(), "persistent"); addOrRemoveClass(styleContext, isUrgent(), "urgent"); addOrRemoveClass(styleContext, isVisible(), "visible"); - addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "onThisMonitor"); + addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); std::string windows; auto windowSeparator = m_workspaceManager.getWindowSeparator(); From 736309ef1ff64b99c0f114178d6b83b4c69fb3b4 Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Sun, 17 Mar 2024 23:00:48 +0100 Subject: [PATCH 144/407] Fixed segfault --- src/modules/hyprland/workspaces.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 9e3683064..3eb408ac1 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -235,7 +235,10 @@ void Workspaces::doUpdate() { auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; return wName == workspace->name(); }); - workspace->setOutput((*updated_workspace)["monitor"].asString()); + + if (updated_workspace != updated_workspaces.end()) { + workspace->setOutput((*updated_workspace)["monitor"].asString()); + } workspace->update(m_format, workspaceIcon); } From bd8b215416cdca6ed0c929c18cede7dfb907edf0 Mon Sep 17 00:00:00 2001 From: Bartel Sielski Date: Mon, 18 Mar 2024 11:47:43 +0100 Subject: [PATCH 145/407] upower: Add 'low' and 'critical' CSS classes Add secondary CSS class based on the 'warning_level' field reported by UPower over D-Bus. This makes it possible to add custom styling when the battery is near empty. --- include/modules/upower/upower.hpp | 1 + src/modules/upower/upower.cpp | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index d763259b6..8cea8c423 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -71,6 +71,7 @@ class UPower : public AModule { GDBusConnection *login1_connection; std::unique_ptr upower_tooltip; std::string lastStatus; + const char *lastWarningLevel; bool showAltText; bool showIcon = true; bool upowerRunning; diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index 3554d43ba..c31f9ea7d 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -8,6 +8,17 @@ #include "gtkmm/tooltip.h" #include "util/gtk_icon.hpp" +static const char* getDeviceWarningLevel(UpDeviceLevel level) { + switch (level) { + case UP_DEVICE_LEVEL_CRITICAL: + return "critical"; + case UP_DEVICE_LEVEL_LOW: + return "low"; + default: + return nullptr; + } +} + namespace waybar::modules::upower { UPower::UPower(const std::string& id, const Json::Value& config) : AModule(config, "upower", id), @@ -306,6 +317,7 @@ auto UPower::update() -> void { UpDeviceKind kind; UpDeviceState state; + UpDeviceLevel level = UP_DEVICE_LEVEL_UNKNOWN; double percentage; gint64 time_empty; gint64 time_full; @@ -318,7 +330,7 @@ auto UPower::update() -> void { if (displayDevice) { g_object_get(displayDevice, "kind", &kind, "state", &state, "percentage", &percentage, "icon-name", &icon_name, "time-to-empty", &time_empty, "time-to-full", &time_full, - NULL); + "warning-level", &level, NULL); /* Every Device which is handled by Upower and which is not * UP_DEVICE_KIND_UNKNOWN (0) or UP_DEVICE_KIND_LINE_POWER (1) is a Battery */ @@ -338,6 +350,15 @@ auto UPower::update() -> void { } lastStatus = status; + const char* warning_level = getDeviceWarningLevel(level); + if (lastWarningLevel && box_.get_style_context()->has_class(lastWarningLevel)) { + box_.get_style_context()->remove_class(lastWarningLevel); + } + if (warning_level && !box_.get_style_context()->has_class(warning_level)) { + box_.get_style_context()->add_class(warning_level); + } + lastWarningLevel = warning_level; + if (devices.size() == 0 && !displayDeviceValid && hideIfEmpty) { event_box_.set_visible(false); // Call parent update From bbb69bd977745fc9b4a5b7da345426da208423f3 Mon Sep 17 00:00:00 2001 From: Bartel Sielski Date: Mon, 18 Mar 2024 12:47:36 +0100 Subject: [PATCH 146/407] upower: Initialize variables There are code paths in which some of these variables were used but not initialized, causing undefined behavior. --- src/modules/upower/upower.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index c31f9ea7d..ad4c47326 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -315,12 +315,12 @@ auto UPower::update() -> void { return; } - UpDeviceKind kind; - UpDeviceState state; + UpDeviceKind kind = UP_DEVICE_KIND_UNKNOWN; + UpDeviceState state = UP_DEVICE_STATE_UNKNOWN; UpDeviceLevel level = UP_DEVICE_LEVEL_UNKNOWN; - double percentage; - gint64 time_empty; - gint64 time_full; + double percentage = 0.0; + gint64 time_empty = 0; + gint64 time_full = 0; gchar* icon_name{(char*)'\0'}; std::string percentString{""}; std::string time_format{""}; From 67218d555437d97434b4288613cb79cb820af720 Mon Sep 17 00:00:00 2001 From: leiserfg Date: Mon, 18 Mar 2024 21:48:06 +0100 Subject: [PATCH 147/407] Make right-click to circle down ppd --- src/modules/power_profiles_daemon.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index ac5f7a2a6..eaa470232 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -39,6 +39,7 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // adresses for compatibility sake. // // Revisit this in 2026, systems should be updated by then. + Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", "net.hadess.PowerProfiles", sigc::mem_fun(*this, &PowerProfilesDaemon::busConnectedCb)); @@ -175,9 +176,16 @@ auto PowerProfilesDaemon::update() -> void { bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { if (e->type == GdkEventType::GDK_BUTTON_PRESS && connected_) { - activeProfile_++; - if (activeProfile_ == availableProfiles_.end()) { - activeProfile_ = availableProfiles_.begin(); + if (e->button == 1) /* left click */ { + activeProfile_++; + if (activeProfile_ == availableProfiles_.end()) { + activeProfile_ = availableProfiles_.begin(); + } + } else { + if (activeProfile_ == availableProfiles_.begin()) { + activeProfile_ = availableProfiles_.end(); + } + activeProfile_--; } using VarStr = Glib::Variant; From 2ffd9a94a505a2e7e933ea8303f9cf2af33c35fe Mon Sep 17 00:00:00 2001 From: Jo De Boeck Date: Tue, 19 Mar 2024 07:40:35 +0200 Subject: [PATCH 148/407] Fix peristent class on buttons Fixes: #3009 Signed-off-by: Jo De Boeck --- src/modules/sway/workspaces.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 68f1ac451..8671288b6 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -156,7 +156,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { if (output.asString() == bar_.output->name) { Json::Value v; v["name"] = p_w_name; - v["output"] = bar_.output->name; + v["target_output"] = bar_.output->name; v["num"] = convertWorkspaceNameToNum(p_w_name); workspaces_.emplace_back(std::move(v)); break; @@ -166,7 +166,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { // Adding to all outputs Json::Value v; v["name"] = p_w_name; - v["output"] = ""; + v["target_output"] = ""; v["num"] = convertWorkspaceNameToNum(p_w_name); workspaces_.emplace_back(std::move(v)); } @@ -313,7 +313,7 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("urgent"); } - if (hasFlag((*it), "target_output")) { + if ((*it)["target_output"].isString()) { button.get_style_context()->add_class("persistent"); } else { button.get_style_context()->remove_class("persistent"); From 856a34e16df9516939021c9bc42db674d0205227 Mon Sep 17 00:00:00 2001 From: hrdl <31923882+hrdl-github@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:52:40 +0100 Subject: [PATCH 149/407] Also consider floating nodes when checking for flags Fixes #3030 --- src/modules/sway/workspaces.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 68f1ac451..77e74465b 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -261,6 +261,10 @@ bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { [&](auto const &e) { return hasFlag(e, flag); })) { return true; } + if (std::any_of(node["floating_nodes"].begin(), node["floating_nodes"].end(), + [&](auto const &e) { return hasFlag(e, flag); })) { + return true; + } return false; } From cf3389e5d736825b9abb075d5f13e71474cc709f Mon Sep 17 00:00:00 2001 From: wispl Date: Mon, 18 Mar 2024 19:37:09 -0400 Subject: [PATCH 150/407] Add empty workspace style for Sway --- src/modules/sway/workspaces.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 0c9b554f2..0f85feed9 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -322,6 +322,11 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("persistent"); } + if ((*it)["nodes"].size() == 0) { + button.get_style_context()->add_class("empty"); + } else { + button.get_style_context()->remove_class("empty"); + } if ((*it)["output"].isString()) { if (((*it)["output"].asString()) == bar_.output->name) { button.get_style_context()->add_class("current_output"); From 0fcf6bcebc7d366cf82baa1947ecc971f9cee961 Mon Sep 17 00:00:00 2001 From: wispl Date: Tue, 19 Mar 2024 22:56:19 -0400 Subject: [PATCH 151/407] Document sway workspace button.empty --- man/waybar-sway-workspaces.5.scd | 1 + 1 file changed, 1 insertion(+) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 3343b8d5a..8f0ac8580 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -182,5 +182,6 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge - *#workspaces button.focused* - *#workspaces button.urgent* - *#workspaces button.persistent* +- *#workspaces button.empty* - *#workspaces button.current_output* - *#workspaces button#sway-workspace-${name}* From 6d690ad48b142bb9882ba31796bf27e5e9913da8 Mon Sep 17 00:00:00 2001 From: Mauro Guida Date: Wed, 20 Mar 2024 13:28:35 +0100 Subject: [PATCH 152/407] fix(wlr/taskbar): crash on taskbar drag and drop event --- src/modules/wlr/taskbar.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 2709584b3..d291a6a59 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -334,9 +334,7 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, } button.add_events(Gdk::BUTTON_PRESS_MASK); - button.signal_button_press_event().connect(sigc::mem_fun(*this, &Task::handle_clicked), false); - button.signal_button_release_event().connect(sigc::mem_fun(*this, &Task::handle_button_release), - false); + button.signal_button_release_event().connect(sigc::mem_fun(*this, &Task::handle_clicked), false); button.signal_motion_notify_event().connect(sigc::mem_fun(*this, &Task::handle_motion_notify), false); @@ -573,12 +571,8 @@ bool Task::handle_clicked(GdkEventButton *bt) { else spdlog::warn("Unknown action {}", action); - return true; -} - -bool Task::handle_button_release(GdkEventButton *bt) { drag_start_button = -1; - return false; + return true; } bool Task::handle_motion_notify(GdkEventMotion *mn) { From c841bf567b591eee1e5e00ce40d0039bedd3e14c Mon Sep 17 00:00:00 2001 From: Mauro Guida Date: Wed, 20 Mar 2024 15:03:25 +0100 Subject: [PATCH 153/407] fix(sway/workspaces): visible class doesn't work --- src/modules/sway/workspaces.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 0c9b554f2..5c8014db6 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -250,9 +250,6 @@ bool Workspaces::filterButtons() { } bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { - if (!node[flag].isBool()) { - return false; - } if (node[flag].asBool()) { return true; } From 2326727ccbf0456ccfd631e748955f7f67c44a4e Mon Sep 17 00:00:00 2001 From: Ryan Walklin Date: Thu, 15 Feb 2024 09:37:36 +1300 Subject: [PATCH 154/407] Update Wireplumber API to 0.5 The WP component loader API has changed to be asynchronous, so implement a (GAsyncReadyCallback)-based loader to manage them. Logging integration change was required for 0.5.0 RCs but not for the 0.5.0 release. Fix clang-tidy and clang-format warnings. Note these are significantly wider than the changes for 0.5.0 so optional beyond the existing patchset. --- include/modules/wireplumber.hpp | 5 +- meson.build | 2 +- src/modules/wireplumber.cpp | 190 ++++++++++++++++++-------------- 3 files changed, 115 insertions(+), 82 deletions(-) diff --git a/include/modules/wireplumber.hpp b/include/modules/wireplumber.hpp index 9bbf4d464..6255b95fd 100644 --- a/include/modules/wireplumber.hpp +++ b/include/modules/wireplumber.hpp @@ -17,12 +17,15 @@ class Wireplumber : public ALabel { auto update() -> void override; private: - void loadRequiredApiModules(); + void asyncLoadRequiredApiModules(); void prepare(); void activatePlugins(); static void updateVolume(waybar::modules::Wireplumber* self, uint32_t id); static void updateNodeName(waybar::modules::Wireplumber* self, uint32_t id); static void onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self); + static void onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res, + waybar::modules::Wireplumber* self); + static void onMixerApiLoaded(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self); static void onObjectManagerInstalled(waybar::modules::Wireplumber* self); static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id); static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self); diff --git a/meson.build b/meson.build index e21ff262c..120976083 100644 --- a/meson.build +++ b/meson.build @@ -92,7 +92,7 @@ libevdev = dependency('libevdev', required: get_option('libevdev')) libmpdclient = dependency('libmpdclient', required: get_option('mpd')) xkbregistry = dependency('xkbregistry') libjack = dependency('jack', required: get_option('jack')) -libwireplumber = dependency('wireplumber-0.4', required: get_option('wireplumber')) +libwireplumber = dependency('wireplumber-0.5', required: get_option('wireplumber')) libsndio = compiler.find_library('sndio', required: get_option('sndio')) if libsndio.found() diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index 51bb708d1..bd019b623 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -18,31 +18,24 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val min_step_(0.0), node_id_(0) { wp_init(WP_INIT_PIPEWIRE); - wp_core_ = wp_core_new(NULL, NULL); + wp_core_ = wp_core_new(nullptr, nullptr, nullptr); apis_ = g_ptr_array_new_with_free_func(g_object_unref); om_ = wp_object_manager_new(); prepare(); - loadRequiredApiModules(); + spdlog::debug("[{}]: connecting to pipewire...", name_); - spdlog::debug("[{}]: connecting to pipewire...", this->name_); - - if (!wp_core_connect(wp_core_)) { - spdlog::error("[{}]: Could not connect to PipeWire", this->name_); + if (wp_core_connect(wp_core_) == 0) { + spdlog::error("[{}]: Could not connect to PipeWire", name_); throw std::runtime_error("Could not connect to PipeWire\n"); } - spdlog::debug("[{}]: connected!", this->name_); + spdlog::debug("[{}]: connected!", name_); g_signal_connect_swapped(om_, "installed", (GCallback)onObjectManagerInstalled, this); - activatePlugins(); - - dp.emit(); - - event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); - event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Wireplumber::handleScroll)); + asyncLoadRequiredApiModules(); } waybar::modules::Wireplumber::~Wireplumber() { @@ -63,32 +56,36 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* return; } - auto proxy = static_cast(wp_object_manager_lookup( - self->om_, WP_TYPE_GLOBAL_PROXY, WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", id, NULL)); + auto* proxy = static_cast(wp_object_manager_lookup(self->om_, WP_TYPE_GLOBAL_PROXY, + WP_CONSTRAINT_TYPE_G_PROPERTY, + "bound-id", "=u", id, nullptr)); - if (!proxy) { + if (proxy == nullptr) { auto err = fmt::format("Object '{}' not found\n", id); spdlog::error("[{}]: {}", self->name_, err); throw std::runtime_error(err); } g_autoptr(WpProperties) properties = - WP_IS_PIPEWIRE_OBJECT(proxy) ? wp_pipewire_object_get_properties(WP_PIPEWIRE_OBJECT(proxy)) - : wp_properties_new_empty(); - g_autoptr(WpProperties) global_p = wp_global_proxy_get_global_properties(WP_GLOBAL_PROXY(proxy)); + WP_IS_PIPEWIRE_OBJECT(proxy) != 0 + ? wp_pipewire_object_get_properties(WP_PIPEWIRE_OBJECT(proxy)) + : wp_properties_new_empty(); + g_autoptr(WpProperties) globalP = wp_global_proxy_get_global_properties(WP_GLOBAL_PROXY(proxy)); properties = wp_properties_ensure_unique_owner(properties); - wp_properties_add(properties, global_p); - wp_properties_set(properties, "object.id", NULL); - auto nick = wp_properties_get(properties, "node.nick"); - auto description = wp_properties_get(properties, "node.description"); - - self->node_name_ = nick ? nick : description ? description : "Unknown node name"; + wp_properties_add(properties, globalP); + wp_properties_set(properties, "object.id", nullptr); + const auto* nick = wp_properties_get(properties, "node.nick"); + const auto* description = wp_properties_get(properties, "node.description"); + + self->node_name_ = nick != nullptr ? nick + : description != nullptr ? description + : "Unknown node name"; spdlog::debug("[{}]: Updating node name to: {}", self->name_, self->node_name_); } void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self, uint32_t id) { spdlog::debug("[{}]: updating volume", self->name_); - GVariant* variant = NULL; + GVariant* variant = nullptr; if (!isValidNodeId(id)) { spdlog::error("[{}]: '{}' is not a valid node ID. Ignoring volume update.", self->name_, id); @@ -97,7 +94,7 @@ void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* se g_signal_emit_by_name(self->mixer_api_, "get-volume", id, &variant); - if (!variant) { + if (variant == nullptr) { auto err = fmt::format("Node {} does not support volume\n", id); spdlog::error("[{}]: {}", self->name_, err); throw std::runtime_error(err); @@ -115,9 +112,9 @@ void waybar::modules::Wireplumber::onMixerChanged(waybar::modules::Wireplumber* spdlog::debug("[{}]: (onMixerChanged) - id: {}", self->name_, id); g_autoptr(WpNode) node = static_cast(wp_object_manager_lookup( - self->om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", id, NULL)); + self->om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", id, nullptr)); - if (!node) { + if (node == nullptr) { spdlog::warn("[{}]: (onMixerChanged) - Object with id {} not found", self->name_, id); return; } @@ -140,49 +137,49 @@ void waybar::modules::Wireplumber::onMixerChanged(waybar::modules::Wireplumber* void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wireplumber* self) { spdlog::debug("[{}]: (onDefaultNodesApiChanged)", self->name_); - uint32_t default_node_id; - g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &default_node_id); + uint32_t defaultNodeId; + g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &defaultNodeId); - if (!isValidNodeId(default_node_id)) { + if (!isValidNodeId(defaultNodeId)) { spdlog::warn("[{}]: '{}' is not a valid node ID. Ignoring node change.", self->name_, - default_node_id); + defaultNodeId); return; } g_autoptr(WpNode) node = static_cast( wp_object_manager_lookup(self->om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", - "=u", default_node_id, NULL)); + "=u", defaultNodeId, nullptr)); - if (!node) { + if (node == nullptr) { spdlog::warn("[{}]: (onDefaultNodesApiChanged) - Object with id {} not found", self->name_, - default_node_id); + defaultNodeId); return; } - const gchar* default_node_name = + const gchar* defaultNodeName = wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(node), "node.name"); spdlog::debug( "[{}]: (onDefaultNodesApiChanged) - got the following default node: Node(name: {}, id: {})", - self->name_, default_node_name, default_node_id); + self->name_, defaultNodeName, defaultNodeId); - if (g_strcmp0(self->default_node_name_, default_node_name) == 0) { + if (g_strcmp0(self->default_node_name_, defaultNodeName) == 0) { spdlog::debug( "[{}]: (onDefaultNodesApiChanged) - Default node has not changed. Node(name: {}, id: {}). " "Ignoring.", - self->name_, self->default_node_name_, default_node_id); + self->name_, self->default_node_name_, defaultNodeId); return; } spdlog::debug( "[{}]: (onDefaultNodesApiChanged) - Default node changed to -> Node(name: {}, id: {})", - self->name_, default_node_name, default_node_id); + self->name_, defaultNodeName, defaultNodeId); g_free(self->default_node_name_); - self->default_node_name_ = g_strdup(default_node_name); - self->node_id_ = default_node_id; - updateVolume(self, default_node_id); - updateNodeName(self, default_node_id); + self->default_node_name_ = g_strdup(defaultNodeName); + self->node_id_ = defaultNodeId; + updateVolume(self, defaultNodeId); + updateNodeName(self, defaultNodeId); } void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wireplumber* self) { @@ -190,14 +187,14 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir self->def_nodes_api_ = wp_plugin_find(self->wp_core_, "default-nodes-api"); - if (!self->def_nodes_api_) { + if (self->def_nodes_api_ == nullptr) { spdlog::error("[{}]: default nodes api is not loaded.", self->name_); throw std::runtime_error("Default nodes API is not loaded\n"); } self->mixer_api_ = wp_plugin_find(self->wp_core_, "mixer-api"); - if (!self->mixer_api_) { + if (self->mixer_api_ == nullptr) { spdlog::error("[{}]: mixer api is not loaded.", self->name_); throw std::runtime_error("Mixer api is not loaded\n"); } @@ -206,7 +203,7 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir &self->default_node_name_); g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &self->node_id_); - if (self->default_node_name_) { + if (self->default_node_name_ != nullptr) { spdlog::debug("[{}]: (onObjectManagerInstalled) - default configured node name: {} and id: {}", self->name_, self->default_node_name_, self->node_id_); } @@ -221,11 +218,11 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir void waybar::modules::Wireplumber::onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self) { - auto plugin_name = wp_plugin_get_name(WP_PLUGIN(p)); - spdlog::debug("[{}]: onPluginActivated: {}", self->name_, plugin_name); - g_autoptr(GError) error = NULL; + const auto* pluginName = wp_plugin_get_name(WP_PLUGIN(p)); + spdlog::debug("[{}]: onPluginActivated: {}", self->name_, pluginName); + g_autoptr(GError) error = nullptr; - if (!wp_object_activate_finish(p, res, &error)) { + if (wp_object_activate_finish(p, res, &error) == 0) { spdlog::error("[{}]: error activating plugin: {}", self->name_, error->message); throw std::runtime_error(error->message); } @@ -240,7 +237,7 @@ void waybar::modules::Wireplumber::activatePlugins() { for (uint16_t i = 0; i < apis_->len; i++) { WpPlugin* plugin = static_cast(g_ptr_array_index(apis_, i)); pending_plugins_++; - wp_object_activate(WP_OBJECT(plugin), WP_PLUGIN_FEATURE_ENABLED, NULL, + wp_object_activate(WP_OBJECT(plugin), WP_PLUGIN_FEATURE_ENABLED, nullptr, (GAsyncReadyCallback)onPluginActivated, this); } } @@ -248,34 +245,67 @@ void waybar::modules::Wireplumber::activatePlugins() { void waybar::modules::Wireplumber::prepare() { spdlog::debug("[{}]: preparing object manager", name_); wp_object_manager_add_interest(om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", - "=s", "Audio/Sink", NULL); + "=s", "Audio/Sink", nullptr); } -void waybar::modules::Wireplumber::loadRequiredApiModules() { - spdlog::debug("[{}]: loading required modules", name_); - g_autoptr(GError) error = NULL; +void waybar::modules::Wireplumber::onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res, + waybar::modules::Wireplumber* self) { + gboolean success = FALSE; + g_autoptr(GError) error = nullptr; - if (!wp_core_load_component(wp_core_, "libwireplumber-module-default-nodes-api", "module", NULL, - &error)) { + spdlog::debug("[{}]: callback loading default node api module", self->name_); + + success = wp_core_load_component_finish(self->wp_core_, res, &error); + + if (success == FALSE) { + spdlog::error("[{}]: default nodes API load failed", self->name_); throw std::runtime_error(error->message); } + spdlog::debug("[{}]: loaded default nodes api", self->name_); + g_ptr_array_add(self->apis_, wp_plugin_find(self->wp_core_, "default-nodes-api")); + + spdlog::debug("[{}]: loading mixer api module", self->name_); + wp_core_load_component(self->wp_core_, "libwireplumber-module-mixer-api", "module", nullptr, + "mixer-api", nullptr, (GAsyncReadyCallback)onMixerApiLoaded, self); +} - if (!wp_core_load_component(wp_core_, "libwireplumber-module-mixer-api", "module", NULL, - &error)) { +void waybar::modules::Wireplumber::onMixerApiLoaded(WpObject* p, GAsyncResult* res, + waybar::modules::Wireplumber* self) { + gboolean success = FALSE; + g_autoptr(GError) error = nullptr; + + success = wp_core_load_component_finish(self->wp_core_, res, nullptr); + + if (success == FALSE) { + spdlog::error("[{}]: mixer API load failed", self->name_); throw std::runtime_error(error->message); } - g_ptr_array_add(apis_, wp_plugin_find(wp_core_, "default-nodes-api")); - g_ptr_array_add(apis_, ({ - WpPlugin* p = wp_plugin_find(wp_core_, "mixer-api"); - g_object_set(G_OBJECT(p), "scale", 1 /* cubic */, NULL); + spdlog::debug("[{}]: loaded mixer API", self->name_); + g_ptr_array_add(self->apis_, ({ + WpPlugin* p = wp_plugin_find(self->wp_core_, "mixer-api"); + g_object_set(G_OBJECT(p), "scale", 1 /* cubic */, nullptr); p; })); + + self->activatePlugins(); + + self->dp.emit(); + + self->event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); + self->event_box_.signal_scroll_event().connect(sigc::mem_fun(*self, &Wireplumber::handleScroll)); +} + +void waybar::modules::Wireplumber::asyncLoadRequiredApiModules() { + spdlog::debug("[{}]: loading default nodes api module", name_); + wp_core_load_component(wp_core_, "libwireplumber-module-default-nodes-api", "module", nullptr, + "default-nodes-api", nullptr, (GAsyncReadyCallback)onDefaultNodesApiLoaded, + this); } auto waybar::modules::Wireplumber::update() -> void { auto format = format_; - std::string tooltip_format; + std::string tooltipFormat; if (muted_) { format = config_["format-muted"].isString() ? config_["format-muted"].asString() : format; @@ -292,12 +322,12 @@ auto waybar::modules::Wireplumber::update() -> void { getState(vol); if (tooltipEnabled()) { - if (tooltip_format.empty() && config_["tooltip-format"].isString()) { - tooltip_format = config_["tooltip-format"].asString(); + if (tooltipFormat.empty() && config_["tooltip-format"].isString()) { + tooltipFormat = config_["tooltip-format"].asString(); } - if (!tooltip_format.empty()) { - label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), + if (!tooltipFormat.empty()) { + label_.set_tooltip_text(fmt::format(fmt::runtime(tooltipFormat), fmt::arg("node_name", node_name_), fmt::arg("volume", vol), fmt::arg("icon", getIcon(vol)))); } else { @@ -317,31 +347,31 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) { if (dir == SCROLL_DIR::NONE) { return true; } - double max_volume = 1; + double maxVolume = 1; double step = 1.0 / 100.0; if (config_["scroll-step"].isDouble()) { step = config_["scroll-step"].asDouble() / 100.0; } if (config_["max-volume"].isDouble()) { - max_volume = config_["max-volume"].asDouble() / 100.0; + maxVolume = config_["max-volume"].asDouble() / 100.0; } if (step < min_step_) step = min_step_; - double new_vol = volume_; + double newVol = volume_; if (dir == SCROLL_DIR::UP) { - if (volume_ < max_volume) { - new_vol = volume_ + step; - if (new_vol > max_volume) new_vol = max_volume; + if (volume_ < maxVolume) { + newVol = volume_ + step; + if (newVol > maxVolume) newVol = maxVolume; } } else if (dir == SCROLL_DIR::DOWN) { if (volume_ > 0) { - new_vol = volume_ - step; - if (new_vol < 0) new_vol = 0; + newVol = volume_ - step; + if (newVol < 0) newVol = 0; } } - if (new_vol != volume_) { - GVariant* variant = g_variant_new_double(new_vol); + if (newVol != volume_) { + GVariant* variant = g_variant_new_double(newVol); gboolean ret; g_signal_emit_by_name(mixer_api_, "set-volume", node_id_, variant, &ret); } From fe0716bf398a69188a20f871918f555c40ae86d1 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 22 Mar 2024 23:13:10 +0100 Subject: [PATCH 155/407] fix: lint --- src/modules/dwl/tags.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index 6a07a878f..036cb1b80 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -155,7 +155,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con } Tags::~Tags() { - if(output_status_) { + if (output_status_) { zdwl_ipc_output_v2_destroy(output_status_); } if (status_manager_) { From 19f3ce6f856c11735ba0781d8bf21a49f84c4840 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 22 Mar 2024 23:21:57 +0100 Subject: [PATCH 156/407] fix: lint --- include/modules/dwl/window.hpp | 7 ++++--- src/modules/dwl/window.cpp | 28 ++++++++-------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/include/modules/dwl/window.hpp b/include/modules/dwl/window.hpp index e4c964046..6b068360c 100644 --- a/include/modules/dwl/window.hpp +++ b/include/modules/dwl/window.hpp @@ -6,14 +6,14 @@ #include "AAppIconLabel.hpp" #include "bar.hpp" -#include "util/json.hpp" #include "dwl-ipc-unstable-v2-client-protocol.h" +#include "util/json.hpp" namespace waybar::modules::dwl { class Window : public AAppIconLabel, public sigc::trackable { public: - Window(const std::string&, const waybar::Bar&, const Json::Value&); + Window(const std::string &, const waybar::Bar &, const Json::Value &); virtual ~Window() = default; void handle_layout(const uint32_t layout); @@ -23,8 +23,9 @@ class Window : public AAppIconLabel, public sigc::trackable { void handle_frame(); struct zdwl_ipc_manager_v2 *status_manager_; + private: - const Bar& bar_; + const Bar &bar_; std::string title_; std::string appid_; diff --git a/src/modules/dwl/window.cpp b/src/modules/dwl/window.cpp index cfd47a05c..4f8b02812 100644 --- a/src/modules/dwl/window.cpp +++ b/src/modules/dwl/window.cpp @@ -9,7 +9,6 @@ #include "client.hpp" #include "dwl-ipc-unstable-v2-client-protocol.h" - #include "util/rewrite_string.hpp" namespace waybar::modules::dwl { @@ -43,7 +42,7 @@ static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t static_cast(data)->handle_layout(layout); } -static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid){ +static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid) { static_cast(data)->handle_appid(appid); }; @@ -73,7 +72,7 @@ static void handle_global_remove(void *data, struct wl_registry *registry, uint3 static const wl_registry_listener registry_listener_impl = {.global = handle_global, .global_remove = handle_global_remove}; -Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) +Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) : AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar) { struct wl_display *display = Client::inst()->wl_display; struct wl_registry *registry = wl_display_get_registry(display); @@ -92,29 +91,18 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) zdwl_ipc_manager_v2_destroy(status_manager_); } -void Window::handle_title(const char *title) { - title_ = title; -} +void Window::handle_title(const char *title) { title_ = title; } -void Window::handle_appid(const char *appid) { - appid_ = appid; -} +void Window::handle_appid(const char *appid) { appid_ = appid; } -void Window::handle_layout_symbol(const char *layout_symbol) { - layout_symbol_ = layout_symbol; -} +void Window::handle_layout_symbol(const char *layout_symbol) { layout_symbol_ = layout_symbol; } -void Window::handle_layout(const uint32_t layout) { - layout_ = layout; -} +void Window::handle_layout(const uint32_t layout) { layout_ = layout; } void Window::handle_frame() { label_.set_markup(waybar::util::rewriteString( - fmt::format( - fmt::runtime(format_), - fmt::arg("title", title_), - fmt::arg("layout", layout_symbol_), - fmt::arg("app_id", appid_)), + fmt::format(fmt::runtime(format_), fmt::arg("title", title_), + fmt::arg("layout", layout_symbol_), fmt::arg("app_id", appid_)), config_["rewrite"])); updateAppIconName(appid_, ""); updateAppIcon(); From 7cd2a6c00366998fcaa4f0d7f794529f9cf9c9bc Mon Sep 17 00:00:00 2001 From: Mauro Guida Date: Sat, 23 Mar 2024 13:02:39 +0100 Subject: [PATCH 157/407] fix(sway/workspaces): Visible class doesn't work on visible and empty workspaces --- src/modules/sway/workspaces.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 68b451ce5..311073e0f 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -304,7 +304,7 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("focused"); } - if (hasFlag((*it), "visible")) { + if (hasFlag((*it), "visible") || ((*it)["output"].isString() && (*it)["nodes"].size() == 0)) { button.get_style_context()->add_class("visible"); } else { button.get_style_context()->remove_class("visible"); From 70ef406d6b39e19a7dc9d4c060548a96a1d9fdbf Mon Sep 17 00:00:00 2001 From: Sano Date: Sat, 23 Mar 2024 18:39:22 +0100 Subject: [PATCH 158/407] check for group modules array in key conversion --- src/bar.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/bar.cpp b/src/bar.cpp index 31afcd439..872632acb 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -449,7 +449,17 @@ void waybar::Bar::setupAltFormatKeyForModuleList(const char* module_list_name) { Json::Value& modules = config[module_list_name]; for (const Json::Value& module_name : modules) { if (module_name.isString()) { - setupAltFormatKeyForModule(module_name.asString()); + auto ref = module_name.asString(); + if (ref.compare(0, 6, "group/") == 0 && ref.size() > 6) { + Json::Value& group_modules = config[ref]["modules"]; + for (const Json::Value& module_name : group_modules) { + if (module_name.isString()) { + setupAltFormatKeyForModule(module_name.asString()); + } + } + } else { + setupAltFormatKeyForModule(ref); + } } } } From abd7ca2a1efb451bd27c01687621d96e1a8e1cb8 Mon Sep 17 00:00:00 2001 From: encbar5 <8845322+encbar5@users.noreply.github.com> Date: Sat, 23 Mar 2024 08:22:19 -0500 Subject: [PATCH 159/407] Fix clock on-scroll value not being used for calendar, which was broken by 86a3898 --- include/modules/clock.hpp | 1 + src/modules/clock.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 8b597c4e6..c50b7ae5b 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -41,6 +41,7 @@ class Clock final : public ALabel { const int cldMonColLen_{20}; // calendar month column length WS cldWPos_{WS::HIDDEN}; // calendar week side to print months cldCurrShift_{0}; // calendar months shift + int cldShift_{1}; // calendar months shift factor year_month_day cldYearShift_; // calendar Year mode. Cached ymd std::string cldYearCached_; // calendar Year mode. Cached calendar year_month cldMonShift_; // calendar Month mode. Cached ym diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index b54a360f6..e2cdf9fc3 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -115,6 +115,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } else cldMonCols_ = 1; if (config_[kCldPlaceholder]["on-scroll"].isInt()) { + cldShift_ = config_[kCldPlaceholder]["on-scroll"].asInt(); event_box_.add_events(Gdk::LEAVE_NOTIFY_MASK); event_box_.signal_leave_notify_event().connect([this](GdkEventCrossing*) { cldCurrShift_ = months{0}; @@ -405,10 +406,10 @@ void waybar::modules::Clock::cldModeSwitch() { cldMode_ = (cldMode_ == CldMode::YEAR) ? CldMode::MONTH : CldMode::YEAR; } void waybar::modules::Clock::cldShift_up() { - cldCurrShift_ += (months)((cldMode_ == CldMode::YEAR) ? 12 : 1); + cldCurrShift_ += (months)((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; } void waybar::modules::Clock::cldShift_down() { - cldCurrShift_ -= (months)((cldMode_ == CldMode::YEAR) ? 12 : 1); + cldCurrShift_ -= (months)((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; } void waybar::modules::Clock::tz_up() { const auto tzSize{tzList_.size()}; @@ -468,4 +469,4 @@ auto waybar::modules::Clock::get_ordinal_date(const year_month_day& today) -> st res << "th"; } return res.str(); -} \ No newline at end of file +} From e3ceaf63d11968c171f9a80f485bd40baa2e6f90 Mon Sep 17 00:00:00 2001 From: cptpcrd <31829097+cptpcrd@users.noreply.github.com> Date: Sun, 24 Mar 2024 15:39:15 -0400 Subject: [PATCH 160/407] Fix reloading config Fully clear the configuration before reloading, so that when the config is read and merged in there are no existing values which mergeConfig refuses to overwrite. --- src/config.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config.cpp b/src/config.cpp index 45f5ee38b..c43e5a632 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -162,6 +162,7 @@ void Config::load(const std::string &config) { } config_file_ = file.value(); spdlog::info("Using configuration file {}", config_file_); + config_ = Json::Value(); setupConfig(config_, config_file_, 0); } From 5056309963dec9e944da6b11a593e94bbe02db5f Mon Sep 17 00:00:00 2001 From: Lin Xianyi Date: Mon, 25 Mar 2024 22:47:37 +0800 Subject: [PATCH 161/407] nix: build against wireplumber 0.5 --- flake.lock | 6 +++--- nix/default.nix | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index 25f126441..7647478b7 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1704538339, - "narHash": "sha256-1734d3mQuux9ySvwf6axRWZRBhtcZA9Q8eftD6EZg6U=", + "lastModified": 1711163522, + "narHash": "sha256-YN/Ciidm+A0fmJPWlHBGvVkcarYWSC+s3NTPk/P+q3c=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "46ae0210ce163b3cba6c7da08840c1d63de9c701", + "rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4", "type": "github" }, "original": { diff --git a/nix/default.nix b/nix/default.nix index bf8f2f216..986e84dd2 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -25,6 +25,10 @@ in mesonFlags = lib.remove "-Dgtk-layer-shell=enabled" oldAttrs.mesonFlags; + buildInputs = (builtins.filter (p: p.pname != "wireplumber") oldAttrs.buildInputs) ++ [ + pkgs.wireplumber + ]; + postUnpack = '' pushd "$sourceRoot" cp -R --no-preserve=mode,ownership ${libcava.src} subprojects/cava-${libcava.version} @@ -32,4 +36,4 @@ in popd ''; } -)) \ No newline at end of file +)) From fe15530f34eb83792991c3a5dcabd0372f371439 Mon Sep 17 00:00:00 2001 From: Kuruyia Date: Mon, 25 Mar 2024 19:40:23 +0100 Subject: [PATCH 162/407] refactor(privacy): clean up the module --- include/util/pipewire/pipewire_backend.hpp | 17 ++- include/util/pipewire/privacy_node_info.hpp | 31 +--- meson.build | 3 +- src/modules/privacy/privacy.cpp | 4 - src/modules/privacy/privacy_item.cpp | 15 +- src/util/pipewire/pipewire_backend.cpp | 140 ++++++++++++++++++ src/util/pipewire/privacy_node_info.cpp | 56 +++++++ src/util/pipewire_backend.cpp | 155 -------------------- 8 files changed, 218 insertions(+), 203 deletions(-) create mode 100644 src/util/pipewire/pipewire_backend.cpp create mode 100644 src/util/pipewire/privacy_node_info.cpp delete mode 100644 src/util/pipewire_backend.cpp diff --git a/include/util/pipewire/pipewire_backend.hpp b/include/util/pipewire/pipewire_backend.hpp index 4e23b2825..ac70a1394 100644 --- a/include/util/pipewire/pipewire_backend.hpp +++ b/include/util/pipewire/pipewire_backend.hpp @@ -13,7 +13,8 @@ class PipewireBackend { pw_context* context_; pw_core* core_; - spa_hook registry_listener; + pw_registry* registry_; + spa_hook registryListener_; /* Hack to keep constructor inaccessible but still public. * This is required to be able to use std::make_shared. @@ -21,20 +22,22 @@ class PipewireBackend { * pointer because the destructor will manually free memory, and this could be * a problem with C++20's copy and move semantics. */ - struct private_constructor_tag {}; + struct PrivateConstructorTag {}; public: - std::mutex mutex_; - - pw_registry* registry; - sigc::signal privacy_nodes_changed_signal_event; std::unordered_map privacy_nodes; + std::mutex mutex_; static std::shared_ptr getInstance(); - PipewireBackend(private_constructor_tag tag); + // Handlers for PipeWire events + void handleRegistryEventGlobal(uint32_t id, uint32_t permissions, const char* type, + uint32_t version, const struct spa_dict* props); + void handleRegistryEventGlobalRemove(uint32_t id); + + PipewireBackend(PrivateConstructorTag tag); ~PipewireBackend(); }; } // namespace waybar::util::PipewireBackend diff --git a/include/util/pipewire/privacy_node_info.hpp b/include/util/pipewire/privacy_node_info.hpp index 3b7f446d3..7b8df0181 100644 --- a/include/util/pipewire/privacy_node_info.hpp +++ b/include/util/pipewire/privacy_node_info.hpp @@ -34,29 +34,12 @@ class PrivacyNodeInfo { void *data; - std::string get_name() { - const std::vector names{&application_name, &node_name}; - std::string name = "Unknown Application"; - for (auto &name_ : names) { - if (name_ != nullptr && name_->length() > 0) { - name = *name_; - name[0] = toupper(name[0]); - break; - } - } - return name; - } - - std::string get_icon_name() { - const std::vector names{&application_icon_name, &pipewire_access_portal_app_id, - &application_name, &node_name}; - const std::string name = "application-x-executable-symbolic"; - for (auto &name_ : names) { - if (name_ != nullptr && name_->length() > 0 && DefaultGtkIconThemeWrapper::has_icon(*name_)) { - return *name_; - } - } - return name; - } + std::string getName(); + std::string getIconName(); + + // Handlers for PipeWire events + void handleProxyEventDestroy(); + void handleNodeEventInfo(const struct pw_node_info *info); }; + } // namespace waybar::util::PipewireBackend diff --git a/meson.build b/meson.build index bbcde7f0b..dfdf08a8d 100644 --- a/meson.build +++ b/meson.build @@ -348,7 +348,8 @@ if pipewire.found() src_files += files( 'src/modules/privacy/privacy.cpp', 'src/modules/privacy/privacy_item.cpp', - 'src/util/pipewire_backend.cpp', + 'src/util/pipewire/pipewire_backend.cpp', + 'src/util/pipewire/privacy_node_info.cpp', ) man_files += files('man/waybar-privacy.5.scd') endif diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 64a1572b3..b7eede754 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -1,12 +1,8 @@ #include "modules/privacy/privacy.hpp" -#include #include -#include #include -#include -#include #include #include "AModule.hpp" diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index a0a2da573..c5b617d51 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -1,23 +1,14 @@ #include "modules/privacy/privacy_item.hpp" -#include -#include -#include - -#include -#include #include #include #include "AModule.hpp" #include "glibmm/main.h" -#include "glibmm/priorities.h" -#include "gtkmm/enums.h" #include "gtkmm/label.h" #include "gtkmm/revealer.h" #include "gtkmm/tooltip.h" #include "sigc++/adaptors/bind.h" -#include "util/gtk_icon.hpp" #include "util/pipewire/privacy_node_info.hpp" namespace waybar::modules::privacy { @@ -108,12 +99,12 @@ void PrivacyItem::update_tooltip() { // Set device icon Gtk::Image *node_icon = new Gtk::Image(); node_icon->set_pixel_size(tooltipIconSize); - node_icon->set_from_icon_name(node->get_icon_name(), Gtk::ICON_SIZE_INVALID); + node_icon->set_from_icon_name(node->getIconName(), Gtk::ICON_SIZE_INVALID); box->add(*node_icon); // Set model - Gtk::Label *node_name = new Gtk::Label(node->get_name()); - box->add(*node_name); + auto *nodeName = new Gtk::Label(node->getName()); + box->add(*nodeName); tooltip_window.add(*box); } diff --git a/src/util/pipewire/pipewire_backend.cpp b/src/util/pipewire/pipewire_backend.cpp new file mode 100644 index 000000000..044b926f2 --- /dev/null +++ b/src/util/pipewire/pipewire_backend.cpp @@ -0,0 +1,140 @@ +#include "util/pipewire/pipewire_backend.hpp" + +#include "util/pipewire/privacy_node_info.hpp" + +namespace waybar::util::PipewireBackend { + +static void getNodeInfo(void *data_, const struct pw_node_info *info) { + auto *pNodeInfo = static_cast(data_); + pNodeInfo->handleNodeEventInfo(info); + + static_cast(pNodeInfo->data)->privacy_nodes_changed_signal_event.emit(); +} + +static const struct pw_node_events NODE_EVENTS = { + .version = PW_VERSION_NODE_EVENTS, + .info = getNodeInfo, +}; + +static void proxyDestroy(void *data) { + static_cast(data)->handleProxyEventDestroy(); +} + +static const struct pw_proxy_events PROXY_EVENTS = { + .version = PW_VERSION_PROXY_EVENTS, + .destroy = proxyDestroy, +}; + +static void registryEventGlobal(void *_data, uint32_t id, uint32_t permissions, const char *type, + uint32_t version, const struct spa_dict *props) { + static_cast(_data)->handleRegistryEventGlobal(id, permissions, type, version, + props); +} + +static void registryEventGlobalRemove(void *_data, uint32_t id) { + static_cast(_data)->handleRegistryEventGlobalRemove(id); +} + +static const struct pw_registry_events REGISTRY_EVENTS = { + .version = PW_VERSION_REGISTRY_EVENTS, + .global = registryEventGlobal, + .global_remove = registryEventGlobalRemove, +}; + +PipewireBackend::PipewireBackend(PrivateConstructorTag tag) + : mainloop_(nullptr), context_(nullptr), core_(nullptr) { + pw_init(nullptr, nullptr); + mainloop_ = pw_thread_loop_new("waybar", nullptr); + if (mainloop_ == nullptr) { + throw std::runtime_error("pw_thread_loop_new() failed."); + } + context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0); + if (context_ == nullptr) { + throw std::runtime_error("pa_context_new() failed."); + } + core_ = pw_context_connect(context_, nullptr, 0); + if (core_ == nullptr) { + throw std::runtime_error("pw_context_connect() failed"); + } + registry_ = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0); + + spa_zero(registryListener_); + pw_registry_add_listener(registry_, ®istryListener_, ®ISTRY_EVENTS, this); + if (pw_thread_loop_start(mainloop_) < 0) { + throw std::runtime_error("pw_thread_loop_start() failed."); + } +} + +PipewireBackend::~PipewireBackend() { + if (registry_ != nullptr) { + pw_proxy_destroy((struct pw_proxy *)registry_); + } + + spa_zero(registryListener_); + + if (core_ != nullptr) { + pw_core_disconnect(core_); + } + + if (context_ != nullptr) { + pw_context_destroy(context_); + } + + if (mainloop_ != nullptr) { + pw_thread_loop_stop(mainloop_); + pw_thread_loop_destroy(mainloop_); + } +} + +std::shared_ptr PipewireBackend::getInstance() { + PrivateConstructorTag tag; + return std::make_shared(tag); +} + +void PipewireBackend::handleRegistryEventGlobal(uint32_t id, uint32_t permissions, const char *type, + uint32_t version, const struct spa_dict *props) { + if (props == nullptr || strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return; + + const char *lookupStr = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); + if (lookupStr == nullptr) return; + std::string mediaClass = lookupStr; + enum PrivacyNodeType mediaType = PRIVACY_NODE_TYPE_NONE; + if (mediaClass == "Stream/Input/Video") { + mediaType = PRIVACY_NODE_TYPE_VIDEO_INPUT; + } else if (mediaClass == "Stream/Input/Audio") { + mediaType = PRIVACY_NODE_TYPE_AUDIO_INPUT; + } else if (mediaClass == "Stream/Output/Audio") { + mediaType = PRIVACY_NODE_TYPE_AUDIO_OUTPUT; + } else { + return; + } + + auto *proxy = (pw_proxy *)pw_registry_bind(registry_, id, type, version, sizeof(PrivacyNodeInfo)); + + if (proxy == nullptr) return; + + auto *pNodeInfo = (PrivacyNodeInfo *)pw_proxy_get_user_data(proxy); + pNodeInfo->id = id; + pNodeInfo->data = this; + pNodeInfo->type = mediaType; + pNodeInfo->media_class = mediaClass; + + pw_proxy_add_listener(proxy, &pNodeInfo->proxy_listener, &PROXY_EVENTS, pNodeInfo); + + pw_proxy_add_object_listener(proxy, &pNodeInfo->object_listener, &NODE_EVENTS, pNodeInfo); + + privacy_nodes.insert_or_assign(id, pNodeInfo); +} + +void PipewireBackend::handleRegistryEventGlobalRemove(uint32_t id) { + mutex_.lock(); + auto iter = privacy_nodes.find(id); + if (iter != privacy_nodes.end()) { + privacy_nodes.erase(id); + } + mutex_.unlock(); + + privacy_nodes_changed_signal_event.emit(); +} + +} // namespace waybar::util::PipewireBackend diff --git a/src/util/pipewire/privacy_node_info.cpp b/src/util/pipewire/privacy_node_info.cpp new file mode 100644 index 000000000..739dc528f --- /dev/null +++ b/src/util/pipewire/privacy_node_info.cpp @@ -0,0 +1,56 @@ +#include "util/pipewire/privacy_node_info.hpp" + +namespace waybar::util::PipewireBackend { + +std::string PrivacyNodeInfo::getName() { + const std::vector names{&application_name, &node_name}; + std::string name = "Unknown Application"; + for (const auto &item : names) { + if (item != nullptr && !item->empty()) { + name = *item; + name[0] = toupper(name[0]); + break; + } + } + return name; +} + +std::string PrivacyNodeInfo::getIconName() { + const std::vector names{&application_icon_name, &pipewire_access_portal_app_id, + &application_name, &node_name}; + std::string name = "application-x-executable-symbolic"; + for (const auto &item : names) { + if (item != nullptr && !item->empty() && DefaultGtkIconThemeWrapper::has_icon(*item)) { + return *item; + } + } + return name; +} + +void PrivacyNodeInfo::handleProxyEventDestroy() { + spa_hook_remove(&proxy_listener); + spa_hook_remove(&object_listener); +} + +void PrivacyNodeInfo::handleNodeEventInfo(const struct pw_node_info *info) { + state = info->state; + + const struct spa_dict_item *item; + spa_dict_for_each(item, info->props) { + if (strcmp(item->key, PW_KEY_CLIENT_ID) == 0) { + client_id = strtoul(item->value, nullptr, 10); + } else if (strcmp(item->key, PW_KEY_MEDIA_NAME) == 0) { + media_name = item->value; + } else if (strcmp(item->key, PW_KEY_NODE_NAME) == 0) { + node_name = item->value; + } else if (strcmp(item->key, PW_KEY_APP_NAME) == 0) { + application_name = item->value; + } else if (strcmp(item->key, "pipewire.access.portal.app_id") == 0) { + pipewire_access_portal_app_id = item->value; + } else if (strcmp(item->key, PW_KEY_APP_ICON_NAME) == 0) { + application_icon_name = item->value; + } + } +} + +} // namespace waybar::util::PipewireBackend diff --git a/src/util/pipewire_backend.cpp b/src/util/pipewire_backend.cpp deleted file mode 100644 index 5fe3ba62f..000000000 --- a/src/util/pipewire_backend.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "util/pipewire/pipewire_backend.hpp" - -#include "util/pipewire/privacy_node_info.hpp" - -namespace waybar::util::PipewireBackend { - -static void get_node_info(void *data_, const struct pw_node_info *info) { - PrivacyNodeInfo *p_node_info = static_cast(data_); - PipewireBackend *backend = (PipewireBackend *)p_node_info->data; - - p_node_info->state = info->state; - - const struct spa_dict_item *item; - spa_dict_for_each(item, info->props) { - if (strcmp(item->key, PW_KEY_CLIENT_ID) == 0) { - p_node_info->client_id = strtoul(item->value, NULL, 10); - } else if (strcmp(item->key, PW_KEY_MEDIA_NAME) == 0) { - p_node_info->media_name = item->value; - } else if (strcmp(item->key, PW_KEY_NODE_NAME) == 0) { - p_node_info->node_name = item->value; - } else if (strcmp(item->key, PW_KEY_APP_NAME) == 0) { - p_node_info->application_name = item->value; - } else if (strcmp(item->key, "pipewire.access.portal.app_id") == 0) { - p_node_info->pipewire_access_portal_app_id = item->value; - } else if (strcmp(item->key, PW_KEY_APP_ICON_NAME) == 0) { - p_node_info->application_icon_name = item->value; - } - } - - backend->privacy_nodes_changed_signal_event.emit(); -} - -static const struct pw_node_events node_events = { - .version = PW_VERSION_NODE_EVENTS, - .info = get_node_info, -}; - -static void proxy_destroy(void *data) { - PrivacyNodeInfo *node = (PrivacyNodeInfo *)data; - - spa_hook_remove(&node->proxy_listener); - spa_hook_remove(&node->object_listener); -} - -static const struct pw_proxy_events proxy_events = { - .version = PW_VERSION_PROXY_EVENTS, - .destroy = proxy_destroy, -}; - -static void registry_event_global(void *_data, uint32_t id, uint32_t permissions, const char *type, - uint32_t version, const struct spa_dict *props) { - if (!props || strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return; - - const char *lookup_str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); - if (!lookup_str) return; - std::string media_class = lookup_str; - enum PrivacyNodeType media_type = PRIVACY_NODE_TYPE_NONE; - if (media_class == "Stream/Input/Video") { - media_type = PRIVACY_NODE_TYPE_VIDEO_INPUT; - } else if (media_class == "Stream/Input/Audio") { - media_type = PRIVACY_NODE_TYPE_AUDIO_INPUT; - } else if (media_class == "Stream/Output/Audio") { - media_type = PRIVACY_NODE_TYPE_AUDIO_OUTPUT; - } else { - return; - } - - PipewireBackend *backend = static_cast(_data); - struct pw_proxy *proxy = - (pw_proxy *)pw_registry_bind(backend->registry, id, type, version, sizeof(PrivacyNodeInfo)); - - if (!proxy) return; - - PrivacyNodeInfo *p_node_info = (PrivacyNodeInfo *)pw_proxy_get_user_data(proxy); - p_node_info->id = id; - p_node_info->data = backend; - p_node_info->type = media_type; - p_node_info->media_class = media_class; - - pw_proxy_add_listener(proxy, &p_node_info->proxy_listener, &proxy_events, p_node_info); - - pw_proxy_add_object_listener(proxy, &p_node_info->object_listener, &node_events, p_node_info); - - backend->privacy_nodes.insert_or_assign(id, p_node_info); -} - -static void registry_event_global_remove(void *_data, uint32_t id) { - auto backend = static_cast(_data); - - backend->mutex_.lock(); - auto iter = backend->privacy_nodes.find(id); - if (iter != backend->privacy_nodes.end()) { - backend->privacy_nodes.erase(id); - } - backend->mutex_.unlock(); - - backend->privacy_nodes_changed_signal_event.emit(); -} - -static const struct pw_registry_events registry_events = { - .version = PW_VERSION_REGISTRY_EVENTS, - .global = registry_event_global, - .global_remove = registry_event_global_remove, -}; - -PipewireBackend::PipewireBackend(private_constructor_tag tag) - : mainloop_(nullptr), context_(nullptr), core_(nullptr) { - pw_init(nullptr, nullptr); - mainloop_ = pw_thread_loop_new("waybar", nullptr); - if (mainloop_ == nullptr) { - throw std::runtime_error("pw_thread_loop_new() failed."); - } - context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0); - if (context_ == nullptr) { - throw std::runtime_error("pa_context_new() failed."); - } - core_ = pw_context_connect(context_, nullptr, 0); - if (core_ == nullptr) { - throw std::runtime_error("pw_context_connect() failed"); - } - registry = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0); - - spa_zero(registry_listener); - pw_registry_add_listener(registry, ®istry_listener, ®istry_events, this); - if (pw_thread_loop_start(mainloop_) < 0) { - throw std::runtime_error("pw_thread_loop_start() failed."); - } -} - -PipewireBackend::~PipewireBackend() { - if (registry != nullptr) { - pw_proxy_destroy((struct pw_proxy *)registry); - } - - spa_zero(registry_listener); - - if (core_ != nullptr) { - pw_core_disconnect(core_); - } - - if (context_ != nullptr) { - pw_context_destroy(context_); - } - - if (mainloop_ != nullptr) { - pw_thread_loop_stop(mainloop_); - pw_thread_loop_destroy(mainloop_); - } -} - -std::shared_ptr PipewireBackend::getInstance() { - private_constructor_tag tag; - return std::make_shared(tag); -} -} // namespace waybar::util::PipewireBackend From 9b4fc6d16b51adbf2d6e931c290555493ba5560a Mon Sep 17 00:00:00 2001 From: kvark Date: Thu, 28 Mar 2024 14:42:44 +0700 Subject: [PATCH 163/407] fix(sway/workspaces): floating_nodes and focused icon Floating nodes are not taken into account for visible and empty workspaces And fix focused icon (#3095) --- src/modules/sway/workspaces.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 311073e0f..12765333a 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -299,12 +299,13 @@ auto Workspaces::update() -> void { if (needReorder) { box_.reorder_child(button, it - workspaces_.begin()); } + bool noNodes = (*it)["nodes"].empty() && (*it)["floating_nodes"].empty(); if (hasFlag((*it), "focused")) { button.get_style_context()->add_class("focused"); } else { button.get_style_context()->remove_class("focused"); } - if (hasFlag((*it), "visible") || ((*it)["output"].isString() && (*it)["nodes"].size() == 0)) { + if (hasFlag((*it), "visible") || ((*it)["output"].isString() && noNodes )) { button.get_style_context()->add_class("visible"); } else { button.get_style_context()->remove_class("visible"); @@ -319,7 +320,7 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("persistent"); } - if ((*it)["nodes"].size() == 0) { + if (noNodes) { button.get_style_context()->add_class("empty"); } else { button.get_style_context()->remove_class("empty"); @@ -406,7 +407,7 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node } } if (key == "focused" || key == "urgent") { - if (config_["format-icons"][key].isString() && node[key].asBool()) { + if (config_["format-icons"][key].isString() && hasFlag(node, key)) { return config_["format-icons"][key].asString(); } } else if (config_["format-icons"]["persistent"].isString() && From 245043f9e7469a5b837cb8d8387bd045ead3a30d Mon Sep 17 00:00:00 2001 From: Bruce Mills Date: Mon, 1 Apr 2024 14:30:31 -0400 Subject: [PATCH 164/407] taskbar: search user directories first for desktop files --- src/modules/wlr/taskbar.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index d291a6a59..6e3e4e08b 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -30,6 +30,9 @@ namespace waybar::modules::wlr { static std::vector search_prefix() { std::vector prefixes = {""}; + std::string home_dir = std::getenv("HOME"); + prefixes.push_back(home_dir + "/.local/share/"); + auto xdg_data_dirs = std::getenv("XDG_DATA_DIRS"); if (!xdg_data_dirs) { prefixes.emplace_back("/usr/share/"); @@ -47,9 +50,6 @@ static std::vector search_prefix() { } while (end != std::string::npos); } - std::string home_dir = std::getenv("HOME"); - prefixes.push_back(home_dir + "/.local/share/"); - for (auto &p : prefixes) spdlog::debug("Using 'desktop' search path prefix: {}", p); return prefixes; From 3d15b96429fa0299e17869b9ac690169b4900228 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Tue, 9 Apr 2024 10:02:33 -0400 Subject: [PATCH 165/407] Add waybar-styles(5) manual page waybar(5) describes the configuration syntax but doesn't mention how the stylesheets are handled. This documentation would have been helpful to me as i figured out how to configure waybar. --- man/waybar-styles.5.scd.in | 34 ++++++++++++++++++++++++++++++++++ man/waybar.5.scd.in | 3 +++ meson.build | 8 ++++++++ 3 files changed, 45 insertions(+) create mode 100644 man/waybar-styles.5.scd.in diff --git a/man/waybar-styles.5.scd.in b/man/waybar-styles.5.scd.in new file mode 100644 index 000000000..ddc4c3c99 --- /dev/null +++ b/man/waybar-styles.5.scd.in @@ -0,0 +1,34 @@ +waybar-styles(5) + +# NAME + +waybar-styles - using stylesheets for waybar + +# DESCRIPTION + +Waybar uses Cascading Style Sheets (CSS) to configure its appearance. + +It uses the first file found in this search order: + +- *$XDG_CONFIG_HOME/waybar/style.css* +- *~/.config/waybar/style.css* +- *~/waybar/style.css* +- */etc/xdg/waybar/style.css* +- *@sysconfdir@/xdg/waybar/style.css* + +# EXAMPLE + +An example user-controlled stylesheet that just changes the color of the clock to be green on black, while keeping the rest of the system config the same would be: + +``` +@import url("file:///etc/xdg/waybar/style.css") + +#clock { + background: #000000; + color: #00ff00; +} +``` + +# SEE ALSO + +- *waybar(5)* diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 5fe30ca86..53613e4ab 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -19,6 +19,8 @@ Valid locations for this file are: A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config.jsonc Also, a minimal example configuration can be found at the bottom of this man page. +The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(5)* for details. + # BAR CONFIGURATION *layer* ++ @@ -347,3 +349,4 @@ A group may hide all but one element, showing them only on mouse hover. In order # SEE ALSO *sway-output(5)* +*waybar-styles(5)" diff --git a/meson.build b/meson.build index dfdf08a8d..92d1ead4b 100644 --- a/meson.build +++ b/meson.build @@ -539,6 +539,14 @@ if scdoc.found() } ) + man_files += configure_file( + input: 'man/waybar-styles.5.scd.in', + output: 'waybar-styles.5.scd', + configuration: { + 'sysconfdir': prefix / sysconfdir + } + ) + fs = import('fs') mandir = get_option('mandir') foreach file : man_files From f68ac9119a9140901f535f458221728818c0e188 Mon Sep 17 00:00:00 2001 From: Hristo Venev Date: Tue, 9 Apr 2024 20:33:53 +0300 Subject: [PATCH 166/407] Use $TZ for local time if it is set libstdc++ doesn't. --- include/modules/clock.hpp | 3 +++ src/modules/clock.cpp | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index c50b7ae5b..e7c3872c0 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -52,6 +52,9 @@ class Clock final : public ALabel { auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) -> const std::string; + // get local time zone + auto local_zone() -> const time_zone*; + // time zoned time in tooltip const bool tzInTooltip_; // if need to print time zones text std::vector tzList_; // time zones list diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index e2cdf9fc3..835374059 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -87,7 +87,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) fmtMap_.insert({3, config_[kCldPlaceholder]["format"]["today"].asString()}); cldBaseDay_ = year_month_day{ - floor(zoned_time{current_zone(), system_clock::now()}.get_local_time())} + floor(zoned_time{local_zone(), system_clock::now()}.get_local_time())} .day(); } else fmtMap_.insert({3, "{}"}); @@ -131,7 +131,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } auto waybar::modules::Clock::update() -> void { - const auto* tz = tzList_[tzCurrIdx_] != nullptr ? tzList_[tzCurrIdx_] : current_zone(); + const auto* tz = tzList_[tzCurrIdx_] != nullptr ? tzList_[tzCurrIdx_] : local_zone(); const zoned_time now{tz, floor(system_clock::now())}; label_.set_markup(fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(now))); @@ -168,7 +168,7 @@ auto waybar::modules::Clock::getTZtext(sys_seconds now) -> std::string { std::stringstream os; for (size_t tz_idx{0}; tz_idx < tzList_.size(); ++tz_idx) { if (static_cast(tz_idx) == tzCurrIdx_) continue; - const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : current_zone(); + const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : local_zone(); auto zt{zoned_time{tz, now}}; os << fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(zt)) << '\n'; } @@ -393,6 +393,18 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea return os.str(); } +auto waybar::modules::Clock::local_zone() -> const time_zone* { + const char* tz_name = getenv("TZ"); + if (tz_name) { + try { + return locate_zone(tz_name); + } catch (const std::runtime_error& e) { + spdlog::warn("Timezone: {0}. {1}", tz_name, e.what()); + } + } + return current_zone(); +} + // Actions handler auto waybar::modules::Clock::doAction(const std::string& name) -> void { if (actionMap_[name]) { From a9088c7e7d019a3eff27db8c55db8414e0638941 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 10 Apr 2024 10:20:21 +0200 Subject: [PATCH 167/407] fix: lint --- src/modules/sway/workspaces.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 12765333a..8a3b9c847 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -305,7 +305,7 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("focused"); } - if (hasFlag((*it), "visible") || ((*it)["output"].isString() && noNodes )) { + if (hasFlag((*it), "visible") || ((*it)["output"].isString() && noNodes)) { button.get_style_context()->add_class("visible"); } else { button.get_style_context()->remove_class("visible"); @@ -320,7 +320,7 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("persistent"); } - if (noNodes) { + if (noNodes) { button.get_style_context()->add_class("empty"); } else { button.get_style_context()->remove_class("empty"); From 43511992d94ef5524c8a251373a70fc9eb1ff970 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:00:50 +0200 Subject: [PATCH 168/407] feat(battery): Add {cycles} format replacement --- include/modules/battery.hpp | 2 +- man/waybar-battery.5.scd | 2 ++ src/modules/battery.cpp | 27 +++++++++++++++++++++------ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 7955e598f..6f09043b8 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -32,7 +32,7 @@ class Battery : public ALabel { void refreshBatteries(); void worker(); const std::string getAdapterStatus(uint8_t capacity) const; - const std::tuple getInfos(); + const std::tuple getInfos(); const std::string formatTimeRemaining(float hoursRemaining); void setBarClass(std::string&); diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index e359ea2e0..284803b0f 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -119,6 +119,8 @@ The *battery* module displays the current capacity and state (eg. charging) of y *{time}*: Estimate of time until full or empty. Note that this is based on the power draw at the last refresh time, not an average. +*{cycles}*: Amount of charge cycles the highest-capacity battery has seen. *(Linux only)* + # TIME FORMAT The *battery* module allows you to define how time should be formatted via *format-time*. diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 9003db6e9..06df5db21 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -181,7 +181,7 @@ static bool status_gt(const std::string& a, const std::string& b) { return false; } -const std::tuple waybar::modules::Battery::getInfos() { +const std::tuple waybar::modules::Battery::getInfos() { std::lock_guard guard(battery_list_mutex_); try { @@ -252,6 +252,9 @@ const std::tuple waybar::modules::Battery::g uint32_t time_to_full_now = 0; bool time_to_full_now_exists = false; + uint32_t largest_design_capacity = 0; + uint16_t main_bat_cycle_count = 0; + std::string status = "Unknown"; for (auto const& item : batteries_) { auto bat = item.first; @@ -353,6 +356,16 @@ const std::tuple waybar::modules::Battery::g std::ifstream(bat / "energy_full_design") >> energy_full_design; } + bool is_main_battery = charge_full_design >= largest_design_capacity; + uint16_t cycle_count = 0; + if (fs::exists(bat / "cycle_count")) { + std::ifstream(bat / "cycle_count") >> cycle_count; + } + if (is_main_battery && (cycle_count > main_bat_cycle_count)) { + largest_design_capacity = charge_full_design; + main_bat_cycle_count = cycle_count; + } + if (!voltage_now_exists) { if (power_now_exists && current_now_exists && current_now != 0) { voltage_now_exists = true; @@ -573,11 +586,11 @@ const std::tuple waybar::modules::Battery::g // still charging but not yet done if (cap == 100 && status == "Charging") status = "Full"; - return {cap, time_remaining, status, total_power / 1e6}; + return {cap, time_remaining, status, total_power / 1e6, main_bat_cycle_count}; #endif } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); - return {0, 0, "Unknown", 0}; + return {0, 0, "Unknown", 0, 0}; } } @@ -633,7 +646,7 @@ auto waybar::modules::Battery::update() -> void { return; } #endif - auto [capacity, time_remaining, status, power] = getInfos(); + auto [capacity, time_remaining, status, power, cycles] = getInfos(); if (status == "Unknown") { status = getAdapterStatus(capacity); } @@ -666,7 +679,8 @@ auto waybar::modules::Battery::update() -> void { label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), fmt::arg("timeTo", tooltip_text_default), fmt::arg("power", power), fmt::arg("capacity", capacity), - fmt::arg("time", time_remaining_formatted))); + fmt::arg("time", time_remaining_formatted), + fmt::arg("cycles", cycles))); } if (!old_status_.empty()) { label_.get_style_context()->remove_class(old_status_); @@ -687,7 +701,8 @@ auto waybar::modules::Battery::update() -> void { auto icons = std::vector{status + "-" + state, status, state}; label_.set_markup(fmt::format( fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted))); + fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), + fmt::arg("cycles", cycles))); } // Call parent update ALabel::update(); From 7f1e623f77a6b99e38a5cfc2839d0dcf59f2c393 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:30:47 +0200 Subject: [PATCH 169/407] style: Refactor battery cycle count choosing --- src/modules/battery.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 06df5db21..cc6595dc6 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -361,9 +361,12 @@ const std::tuple waybar::modules:: if (fs::exists(bat / "cycle_count")) { std::ifstream(bat / "cycle_count") >> cycle_count; } - if (is_main_battery && (cycle_count > main_bat_cycle_count)) { + if (is_main_battery) { largest_design_capacity = charge_full_design; - main_bat_cycle_count = cycle_count; + + if (cycle_count > main_bat_cycle_count) { + main_bat_cycle_count = cycle_count; + } } if (!voltage_now_exists) { From a59593fde1ccb7caef46f69a1d1fa7a9fc13f7fb Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 10 Apr 2024 18:19:55 +0200 Subject: [PATCH 170/407] feat(battery): Add {health} format replacement --- include/modules/battery.hpp | 2 +- man/waybar-battery.5.scd | 2 ++ src/modules/battery.cpp | 22 +++++++++++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 6f09043b8..0468bff11 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -32,7 +32,7 @@ class Battery : public ALabel { void refreshBatteries(); void worker(); const std::string getAdapterStatus(uint8_t capacity) const; - const std::tuple getInfos(); + const std::tuple getInfos(); const std::string formatTimeRemaining(float hoursRemaining); void setBarClass(std::string&); diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 284803b0f..25c7cacab 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -121,6 +121,8 @@ The *battery* module displays the current capacity and state (eg. charging) of y *{cycles}*: Amount of charge cycles the highest-capacity battery has seen. *(Linux only)* +*{health}*: The percentage of the highest-capacity battery's original maximum charge it can still hold. + # TIME FORMAT The *battery* module allows you to define how time should be formatted via *format-time*. diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index cc6595dc6..6d3caa27f 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -181,7 +181,7 @@ static bool status_gt(const std::string& a, const std::string& b) { return false; } -const std::tuple waybar::modules::Battery::getInfos() { +const std::tuple waybar::modules::Battery::getInfos() { std::lock_guard guard(battery_list_mutex_); try { @@ -254,6 +254,7 @@ const std::tuple waybar::modules:: uint32_t largest_design_capacity = 0; uint16_t main_bat_cycle_count = 0; + float main_bat_health_percent = 0.0f; std::string status = "Unknown"; for (auto const& item : batteries_) { @@ -367,6 +368,16 @@ const std::tuple waybar::modules:: if (cycle_count > main_bat_cycle_count) { main_bat_cycle_count = cycle_count; } + + std::string name; + std::ifstream(bat / "model_name") >> name; + spdlog::info("{} | full: {}, full_design: {}", name, energy_full, energy_full_design); + if (charge_full_exists && charge_full_design_exists) { + float bat_health_percent = ((float)charge_full_design / charge_full) * 100; + if (main_bat_health_percent == 0.0f || bat_health_percent < main_bat_health_percent) { + main_bat_health_percent = bat_health_percent; + } + } } if (!voltage_now_exists) { @@ -589,11 +600,11 @@ const std::tuple waybar::modules:: // still charging but not yet done if (cap == 100 && status == "Charging") status = "Full"; - return {cap, time_remaining, status, total_power / 1e6, main_bat_cycle_count}; + return {cap, time_remaining, status, total_power / 1e6, main_bat_cycle_count, main_bat_health_percent}; #endif } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); - return {0, 0, "Unknown", 0, 0}; + return {0, 0, "Unknown", 0, 0, 0.0f}; } } @@ -649,7 +660,7 @@ auto waybar::modules::Battery::update() -> void { return; } #endif - auto [capacity, time_remaining, status, power, cycles] = getInfos(); + auto [capacity, time_remaining, status, power, cycles, health] = getInfos(); if (status == "Unknown") { status = getAdapterStatus(capacity); } @@ -683,7 +694,8 @@ auto waybar::modules::Battery::update() -> void { fmt::arg("timeTo", tooltip_text_default), fmt::arg("power", power), fmt::arg("capacity", capacity), fmt::arg("time", time_remaining_formatted), - fmt::arg("cycles", cycles))); + fmt::arg("cycles", cycles), + fmt::arg("health", fmt::format("{:.3}", health)))); } if (!old_status_.empty()) { label_.get_style_context()->remove_class(old_status_); From 805faa47e63686d359843535e4557358f18c2c0e Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 10 Apr 2024 18:48:03 +0200 Subject: [PATCH 171/407] style: Remove debug output Oops --- src/modules/battery.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 6d3caa27f..e65a2dcfc 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -369,9 +369,6 @@ const std::tuple waybar::mo main_bat_cycle_count = cycle_count; } - std::string name; - std::ifstream(bat / "model_name") >> name; - spdlog::info("{} | full: {}, full_design: {}", name, energy_full, energy_full_design); if (charge_full_exists && charge_full_design_exists) { float bat_health_percent = ((float)charge_full_design / charge_full) * 100; if (main_bat_health_percent == 0.0f || bat_health_percent < main_bat_health_percent) { From 24690248dbdb33323de3b99c51f5706bf8652736 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Thu, 11 Apr 2024 02:40:04 +0200 Subject: [PATCH 172/407] fix: Calculate battery health the right way around I even did this originally, then got confused when my battery in particular showed 102% and, instead of checking the values I calculate with, just decided to do the stupid thing and do maths the wrong around --- src/modules/battery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index e65a2dcfc..df3712a68 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -370,7 +370,7 @@ const std::tuple waybar::mo } if (charge_full_exists && charge_full_design_exists) { - float bat_health_percent = ((float)charge_full_design / charge_full) * 100; + float bat_health_percent = ((float)charge_full / charge_full_design) * 100; if (main_bat_health_percent == 0.0f || bat_health_percent < main_bat_health_percent) { main_bat_health_percent = bat_health_percent; } From cd3d588abdc23e19acf2452ad9bd08ae21de0ec0 Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Fri, 12 Apr 2024 11:33:29 +0200 Subject: [PATCH 173/407] [hyprland/workspaces] Fix active workspace not getting updated on multi monitor setups --- src/modules/hyprland/workspaces.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 5d7436e76..671a4a8b4 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -318,7 +318,7 @@ void Workspaces::onEvent(const std::string &ev) { onWorkspaceCreated(payload); } else if (eventName == "focusedmon") { onMonitorFocused(payload); - } else if (eventName == "moveworkspace" && !allOutputs()) { + } else if (eventName == "moveworkspace") { onWorkspaceMoved(payload); } else if (eventName == "openwindow") { onWindowOpened(payload); @@ -387,6 +387,13 @@ void Workspaces::onWorkspaceCreated(std::string const &workspaceName, void Workspaces::onWorkspaceMoved(std::string const &payload) { spdlog::debug("Workspace moved: {}", payload); + + // Update active workspace + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + + if (allOutputs()) + return; + std::string workspaceName = payload.substr(0, payload.find(',')); std::string monitorName = payload.substr(payload.find(',') + 1); From 084b561d5bc93a07a3f5777bef3357cf405e60cb Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Fri, 12 Apr 2024 11:35:41 +0200 Subject: [PATCH 174/407] [hyprland/workspaces] Update window count and sort workspaces AFTER their creation --- src/modules/hyprland/workspaces.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 671a4a8b4..3c03c7083 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -194,6 +194,10 @@ void Workspaces::doUpdate() { for (auto &[workspaceData, clientsData] : m_workspacesToCreate) { createWorkspace(workspaceData, clientsData); } + if (!m_workspacesToCreate.empty()) { + updateWindowCount(); + sortWorkspaces(); + } m_workspacesToCreate.clear(); // get all active workspaces @@ -391,8 +395,7 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { // Update active workspace m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); - if (allOutputs()) - return; + if (allOutputs()) return; std::string workspaceName = payload.substr(0, payload.find(',')); std::string monitorName = payload.substr(payload.find(',') + 1); @@ -833,8 +836,6 @@ void Workspaces::init() { m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); initializeWorkspaces(); - updateWindowCount(); - sortWorkspaces(); dp.emit(); } From 421ba6e31a6c1d454a6b6f5ab9e61209682b1418 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Fri, 12 Apr 2024 18:48:54 +0200 Subject: [PATCH 175/407] fix: Add dummy information for battery cycles,health on FreeBSD --- src/modules/battery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index df3712a68..4facaaca0 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -234,7 +234,7 @@ const std::tuple waybar::mo } // spdlog::info("{} {} {} {}", capacity,time,status,rate); - return {capacity, time / 60.0, status, rate}; + return {capacity, time / 60.0, status, rate, 0, 0.0F}; #elif defined(__linux__) uint32_t total_power = 0; // μW From 986b348bc721fb9d78221a5dc720556e08a8c9fb Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Sat, 13 Apr 2024 13:11:14 +0200 Subject: [PATCH 176/407] style: Change new variables to camelCase --- src/modules/battery.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 4facaaca0..d2a24457d 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -252,9 +252,9 @@ const std::tuple waybar::mo uint32_t time_to_full_now = 0; bool time_to_full_now_exists = false; - uint32_t largest_design_capacity = 0; - uint16_t main_bat_cycle_count = 0; - float main_bat_health_percent = 0.0f; + uint32_t largestDesignCapacity = 0; + uint16_t mainBatCycleCount = 0; + float mainBatHealthPercent = 0.0F; std::string status = "Unknown"; for (auto const& item : batteries_) { @@ -357,22 +357,22 @@ const std::tuple waybar::mo std::ifstream(bat / "energy_full_design") >> energy_full_design; } - bool is_main_battery = charge_full_design >= largest_design_capacity; + bool is_main_battery = charge_full_design >= largestDesignCapacity; uint16_t cycle_count = 0; if (fs::exists(bat / "cycle_count")) { std::ifstream(bat / "cycle_count") >> cycle_count; } if (is_main_battery) { - largest_design_capacity = charge_full_design; + largestDesignCapacity = charge_full_design; - if (cycle_count > main_bat_cycle_count) { - main_bat_cycle_count = cycle_count; + if (cycle_count > mainBatCycleCount) { + mainBatCycleCount = cycle_count; } if (charge_full_exists && charge_full_design_exists) { float bat_health_percent = ((float)charge_full / charge_full_design) * 100; - if (main_bat_health_percent == 0.0f || bat_health_percent < main_bat_health_percent) { - main_bat_health_percent = bat_health_percent; + if (mainBatHealthPercent == 0.0f || bat_health_percent < main_bat_health_percent) { + mainBatHealthPercent = bat_health_percent; } } } @@ -597,7 +597,7 @@ const std::tuple waybar::mo // still charging but not yet done if (cap == 100 && status == "Charging") status = "Full"; - return {cap, time_remaining, status, total_power / 1e6, main_bat_cycle_count, main_bat_health_percent}; + return {cap, time_remaining, status, total_power / 1e6, mainBatCycleCount, mainBatHealthPercent}; #endif } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); From da47c94480459085ef1214639ae88d2bbe3f694c Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Sat, 13 Apr 2024 13:18:50 +0200 Subject: [PATCH 177/407] fix: Also use camelCase for usages of new vars --- src/modules/battery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index d2a24457d..f9f3084e5 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -371,7 +371,7 @@ const std::tuple waybar::mo if (charge_full_exists && charge_full_design_exists) { float bat_health_percent = ((float)charge_full / charge_full_design) * 100; - if (mainBatHealthPercent == 0.0f || bat_health_percent < main_bat_health_percent) { + if (mainBatHealthPercent == 0.0f || bat_health_percent < mainBatHealthPercent) { mainBatHealthPercent = bat_health_percent; } } From 3d54a6002d9521ab835570522aa514e2816d2477 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Sat, 13 Apr 2024 13:19:54 +0200 Subject: [PATCH 178/407] style: Remove superfluous 'const' on getInfo() was here before, but is an easy fix for a clang-tidy warning --- include/modules/battery.hpp | 2 +- src/modules/battery.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 0468bff11..8e1a2ad2b 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -32,7 +32,7 @@ class Battery : public ALabel { void refreshBatteries(); void worker(); const std::string getAdapterStatus(uint8_t capacity) const; - const std::tuple getInfos(); + std::tuple getInfos(); const std::string formatTimeRemaining(float hoursRemaining); void setBarClass(std::string&); diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index f9f3084e5..d4bd3e2d5 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -181,7 +181,7 @@ static bool status_gt(const std::string& a, const std::string& b) { return false; } -const std::tuple waybar::modules::Battery::getInfos() { +std::tuple waybar::modules::Battery::getInfos() { std::lock_guard guard(battery_list_mutex_); try { From 74773885c6166d190a294ad945b1f1d4d5f007ec Mon Sep 17 00:00:00 2001 From: hrdl <31923882+hrdl-github@users.noreply.github.com> Date: Tue, 9 Apr 2024 00:20:18 +0200 Subject: [PATCH 179/407] Pipewire backend: use pipewire thread lock Fixes #3047. --- src/util/pipewire/pipewire_backend.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/util/pipewire/pipewire_backend.cpp b/src/util/pipewire/pipewire_backend.cpp index 044b926f2..5bb7c19a1 100644 --- a/src/util/pipewire/pipewire_backend.cpp +++ b/src/util/pipewire/pipewire_backend.cpp @@ -48,12 +48,17 @@ PipewireBackend::PipewireBackend(PrivateConstructorTag tag) if (mainloop_ == nullptr) { throw std::runtime_error("pw_thread_loop_new() failed."); } + + pw_thread_loop_lock(mainloop_); + context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0); if (context_ == nullptr) { + pw_thread_loop_unlock(mainloop_); throw std::runtime_error("pa_context_new() failed."); } core_ = pw_context_connect(context_, nullptr, 0); if (core_ == nullptr) { + pw_thread_loop_unlock(mainloop_); throw std::runtime_error("pw_context_connect() failed"); } registry_ = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0); @@ -61,11 +66,17 @@ PipewireBackend::PipewireBackend(PrivateConstructorTag tag) spa_zero(registryListener_); pw_registry_add_listener(registry_, ®istryListener_, ®ISTRY_EVENTS, this); if (pw_thread_loop_start(mainloop_) < 0) { + pw_thread_loop_unlock(mainloop_); throw std::runtime_error("pw_thread_loop_start() failed."); } + pw_thread_loop_unlock(mainloop_); } PipewireBackend::~PipewireBackend() { + if (mainloop_ != nullptr) { + pw_thread_loop_lock(mainloop_); + } + if (registry_ != nullptr) { pw_proxy_destroy((struct pw_proxy *)registry_); } @@ -81,6 +92,7 @@ PipewireBackend::~PipewireBackend() { } if (mainloop_ != nullptr) { + pw_thread_loop_unlock(mainloop_); pw_thread_loop_stop(mainloop_); pw_thread_loop_destroy(mainloop_); } From 133dfc2e8526c8965d7e1517cfc5f89aaa1791ae Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Mon, 15 Apr 2024 13:50:41 +0200 Subject: [PATCH 180/407] Remove unused variable in Workspaces::updateWindows --- src/modules/sway/workspaces.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 8a3b9c847..925448c26 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -266,7 +266,6 @@ bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { } void Workspaces::updateWindows(const Json::Value &node, std::string &windows) { - auto format = config_["window-format"].asString(); if ((node["type"].asString() == "con" || node["type"].asString() == "floating_con") && node["name"].isString()) { std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1); From fb88c06d78cebe49310530268fee3c928fe5c239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Peter=20Dhall=C3=A9?= Date: Mon, 15 Apr 2024 21:59:35 +0200 Subject: [PATCH 181/407] calendar: add shift_reset action --- include/modules/clock.hpp | 2 ++ src/modules/clock.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index e7c3872c0..9e10fb858 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -73,6 +73,7 @@ class Clock final : public ALabel { void cldModeSwitch(); void cldShift_up(); void cldShift_down(); + void cldShift_reset(); void tz_up(); void tz_down(); // Module Action Map @@ -80,6 +81,7 @@ class Clock final : public ALabel { {"mode", &waybar::modules::Clock::cldModeSwitch}, {"shift_up", &waybar::modules::Clock::cldShift_up}, {"shift_down", &waybar::modules::Clock::cldShift_down}, + {"shift_reset", &waybar::modules::Clock::cldShift_reset}, {"tz_up", &waybar::modules::Clock::tz_up}, {"tz_down", &waybar::modules::Clock::tz_down}}; }; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 835374059..9f26b51fd 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -423,6 +423,9 @@ void waybar::modules::Clock::cldShift_up() { void waybar::modules::Clock::cldShift_down() { cldCurrShift_ -= (months)((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; } +void waybar::modules::Clock::cldShift_reset() { + cldCurrShift_ = (months)0; +} void waybar::modules::Clock::tz_up() { const auto tzSize{tzList_.size()}; if (tzSize == 1) return; From 67bf98a93e60c6db4f24f54d7505fe6a9f2dfca7 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 17 Apr 2024 14:46:35 +0200 Subject: [PATCH 182/407] style: Change more var names to camelCase --- src/modules/battery.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index d4bd3e2d5..69f1b84d5 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -357,22 +357,21 @@ std::tuple waybar::modules: std::ifstream(bat / "energy_full_design") >> energy_full_design; } - bool is_main_battery = charge_full_design >= largestDesignCapacity; - uint16_t cycle_count = 0; + uint16_t cycleCount = 0; if (fs::exists(bat / "cycle_count")) { - std::ifstream(bat / "cycle_count") >> cycle_count; + std::ifstream(bat / "cycle_count") >> cycleCount; } - if (is_main_battery) { + if (charge_full_design >= largestDesignCapacity) { largestDesignCapacity = charge_full_design; - if (cycle_count > mainBatCycleCount) { - mainBatCycleCount = cycle_count; + if (cycleCount > mainBatCycleCount) { + mainBatCycleCount = cycleCount; } if (charge_full_exists && charge_full_design_exists) { - float bat_health_percent = ((float)charge_full / charge_full_design) * 100; - if (mainBatHealthPercent == 0.0f || bat_health_percent < mainBatHealthPercent) { - mainBatHealthPercent = bat_health_percent; + float batHealthPercent = ((float)charge_full / charge_full_design) * 100; + if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) { + mainBatHealthPercent = batHealthPercent; } } } From 8ef4ddd7efa2b29a455f38dbb7eeddf4002f304d Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Apr 2024 08:34:02 +0200 Subject: [PATCH 183/407] fix: lint --- src/modules/battery.cpp | 25 +++++++++++++------------ src/modules/clock.cpp | 4 +--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 69f1b84d5..8dae43d5d 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -181,7 +181,8 @@ static bool status_gt(const std::string& a, const std::string& b) { return false; } -std::tuple waybar::modules::Battery::getInfos() { +std::tuple +waybar::modules::Battery::getInfos() { std::lock_guard guard(battery_list_mutex_); try { @@ -596,7 +597,8 @@ std::tuple waybar::modules: // still charging but not yet done if (cap == 100 && status == "Charging") status = "Full"; - return {cap, time_remaining, status, total_power / 1e6, mainBatCycleCount, mainBatHealthPercent}; + return { + cap, time_remaining, status, total_power / 1e6, mainBatCycleCount, mainBatHealthPercent}; #endif } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); @@ -686,12 +688,11 @@ auto waybar::modules::Battery::update() -> void { } else if (config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } - label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), - fmt::arg("timeTo", tooltip_text_default), - fmt::arg("power", power), fmt::arg("capacity", capacity), - fmt::arg("time", time_remaining_formatted), - fmt::arg("cycles", cycles), - fmt::arg("health", fmt::format("{:.3}", health)))); + label_.set_tooltip_text( + fmt::format(fmt::runtime(tooltip_format), fmt::arg("timeTo", tooltip_text_default), + fmt::arg("power", power), fmt::arg("capacity", capacity), + fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles), + fmt::arg("health", fmt::format("{:.3}", health)))); } if (!old_status_.empty()) { label_.get_style_context()->remove_class(old_status_); @@ -710,10 +711,10 @@ auto waybar::modules::Battery::update() -> void { } else { event_box_.show(); auto icons = std::vector{status + "-" + state, status, state}; - label_.set_markup(fmt::format( - fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), - fmt::arg("cycles", cycles))); + label_.set_markup( + fmt::format(fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), + fmt::arg("icon", getIcon(capacity, icons)), + fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles))); } // Call parent update ALabel::update(); diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 9f26b51fd..92af70dc6 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -423,9 +423,7 @@ void waybar::modules::Clock::cldShift_up() { void waybar::modules::Clock::cldShift_down() { cldCurrShift_ -= (months)((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; } -void waybar::modules::Clock::cldShift_reset() { - cldCurrShift_ = (months)0; -} +void waybar::modules::Clock::cldShift_reset() { cldCurrShift_ = (months)0; } void waybar::modules::Clock::tz_up() { const auto tzSize{tzList_.size()}; if (tzSize == 1) return; From 2673a5a4f1e2cee9f2edee57ca278697fdfdaf50 Mon Sep 17 00:00:00 2001 From: joesri <166829758+joesri@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:17:30 +0000 Subject: [PATCH 184/407] Escape tooltip in custom module --- src/modules/custom.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 5e5d70193..296a3b04a 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -214,13 +214,19 @@ void waybar::modules::Custom::parseOutputRaw() { if (i == 0) { if (config_["escape"].isBool() && config_["escape"].asBool()) { text_ = Glib::Markup::escape_text(validated_line); + tooltip_ = Glib::Markup::escape_text(validated_line); } else { text_ = validated_line; + tooltip_ = validated_line; } tooltip_ = validated_line; class_.clear(); } else if (i == 1) { - tooltip_ = validated_line; + if (config_["escape"].isBool() && config_["escape"].asBool()) { + tooltip_ = Glib::Markup::escape_text(validated_line); + } else { + tooltip_ = validated_line; + } } else if (i == 2) { class_.push_back(validated_line); } else { @@ -246,7 +252,11 @@ void waybar::modules::Custom::parseOutputJson() { } else { alt_ = parsed["alt"].asString(); } - tooltip_ = parsed["tooltip"].asString(); + if (config_["escape"].isBool() && config_["escape"].asBool()) { + tooltip_ = Glib::Markup::escape_text(parsed["tooltip"].asString()); + } else { + tooltip_ = parsed["tooltip"].asString(); + } if (parsed["class"].isString()) { class_.push_back(parsed["class"].asString()); } else if (parsed["class"].isArray()) { From f75b2ae91f25a28462f6c3d54eaf0a3e851d210c Mon Sep 17 00:00:00 2001 From: vawvaw Date: Thu, 18 Apr 2024 21:52:53 +0200 Subject: [PATCH 185/407] sway/workspaces: Fix scroll on unfocused monitor --- src/modules/sway/workspaces.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 925448c26..7517dc267 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -434,9 +434,16 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { } std::string name; { + bool alloutputs = config_["all-outputs"].asBool(); std::lock_guard lock(mutex_); - auto it = std::find_if(workspaces_.begin(), workspaces_.end(), - [](const auto &workspace) { return hasFlag(workspace, "focused"); }); + auto it = + std::find_if(workspaces_.begin(), workspaces_.end(), [alloutputs](const auto &workspace) { + if (alloutputs) { + return hasFlag(workspace, "focused"); + } + bool noNodes = workspace["nodes"].empty() && workspace["floating_nodes"].empty(); + return hasFlag(workspace, "visible") || (workspace["output"].isString() && noNodes); + }); if (it == workspaces_.end()) { return true; } From 937bf2ba5d5a9b00148519a694e2fa5c2fb4b4ae Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 19 Apr 2024 06:21:10 +0200 Subject: [PATCH 186/407] fix: lint --- src/modules/custom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 296a3b04a..ec3bb3fa4 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -226,7 +226,7 @@ void waybar::modules::Custom::parseOutputRaw() { tooltip_ = Glib::Markup::escape_text(validated_line); } else { tooltip_ = validated_line; - } + } } else if (i == 2) { class_.push_back(validated_line); } else { From 6c1125c1feaf81957e16cca924d08b56e3eb841e Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" Date: Wed, 17 Apr 2024 22:23:59 +0200 Subject: [PATCH 187/407] feat(#2989): (optional) hover for all modules --- include/AModule.hpp | 2 ++ man/waybar-styles.5.scd.in | 10 ++++++++++ resources/style.css | 5 +++++ src/AModule.cpp | 17 +++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/include/AModule.hpp b/include/AModule.hpp index c15efb006..ea692ff84 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -38,6 +38,8 @@ class AModule : public IModule { Gtk::EventBox event_box_; virtual bool handleToggle(GdkEventButton *const &ev); + virtual bool handleMouseEnter(GdkEventCrossing *const &ev); + virtual bool handleMouseLeave(GdkEventCrossing *const &ev); virtual bool handleScroll(GdkEventScroll *); virtual bool handleRelease(GdkEventButton *const &ev); diff --git a/man/waybar-styles.5.scd.in b/man/waybar-styles.5.scd.in index ddc4c3c99..0af393ef4 100644 --- a/man/waybar-styles.5.scd.in +++ b/man/waybar-styles.5.scd.in @@ -29,6 +29,16 @@ An example user-controlled stylesheet that just changes the color of the clock t } ``` +## Hover-effect + +You can apply special styling to any module for when the cursor hovers it. + +``` +#clock:hover { + background-color: #ffffff; +} +``` + # SEE ALSO - *waybar(5)* diff --git a/resources/style.css b/resources/style.css index b58593904..7e830285f 100644 --- a/resources/style.css +++ b/resources/style.css @@ -48,6 +48,11 @@ button:hover { box-shadow: inset 0 -3px #ffffff; } +/* you can set a style on hover for any module like this */ +#pulseaudio:hover { + background-color: #a37800; +} + #workspaces button { padding: 0 5px; background-color: transparent; diff --git a/src/AModule.cpp b/src/AModule.cpp index a451c3d60..a285da77c 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -27,6 +27,9 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); } + event_box_.signal_enter_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseEnter)); + event_box_.signal_leave_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave)); + // configure events' user commands // hasUserEvent is true if any element from eventMap_ is satisfying the condition in the lambda bool hasUserEvent = @@ -83,6 +86,20 @@ auto AModule::doAction(const std::string& name) -> void { } } +bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { + if (auto* module = event_box_.get_child(); module != nullptr) { + module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + } + return true; +} + +bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { + if (auto* module = event_box_.get_child(); module != nullptr) { + module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + } + return true; +} + bool AModule::handleToggle(GdkEventButton* const& e) { return handleUserEvent(e); } bool AModule::handleRelease(GdkEventButton* const& e) { return handleUserEvent(e); } From 2123995b03d41537cb48d5ecbb17d8fc240490c7 Mon Sep 17 00:00:00 2001 From: drendog <53359960+drendog@users.noreply.github.com> Date: Sun, 21 Apr 2024 18:02:26 +0200 Subject: [PATCH 188/407] fix: update clock tooltip without placeholders scenario --- src/modules/clock.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 92af70dc6..2901c0d12 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -152,6 +152,8 @@ auto waybar::modules::Clock::update() -> void { std::regex_replace(tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); tlpText_ = std::regex_replace(tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); + } else { + tlpText_ = tlpFmt_; } tlpText_ = fmt_lib::vformat(locale_, tlpText_, fmt_lib::make_format_args(shiftedNow)); From 501e63fba63b244b589faf5fe2489a7e440a9ae0 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 Apr 2024 07:53:22 +0200 Subject: [PATCH 189/407] chore: 0.10.1 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 92d1ead4b..ae388b5a6 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.10.0', + version: '0.10.1', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From 5b7d0a28109611a92334bc3686c15c1bdc059628 Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" Date: Tue, 23 Apr 2024 16:18:54 +0200 Subject: [PATCH 190/407] fix(#3162): hover event did not propagate causing issues --- src/AModule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index a285da77c..399a23e44 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -90,14 +90,14 @@ bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - return true; + return false; } bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - return true; + return false; } bool AModule::handleToggle(GdkEventButton* const& e) { return handleUserEvent(e); } From a04016e0b6033b783e4b706e764aa8b5466f81c1 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 Apr 2024 17:59:08 +0200 Subject: [PATCH 191/407] chore: 0.10.2 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ae388b5a6..ec23923ac 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.10.1', + version: '0.10.2', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From efa7dc7ba49c0479cdbe247049052491b5b0b9ef Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:34:35 +0200 Subject: [PATCH 192/407] fix(battery): Register health replacement for main format --- src/modules/battery.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 8dae43d5d..e9fa3db28 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -714,7 +714,8 @@ auto waybar::modules::Battery::update() -> void { label_.set_markup( fmt::format(fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), fmt::arg("icon", getIcon(capacity, icons)), - fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles))); + fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles), + fmt::arg("health", fmt::format("{:.3}", health)))); } // Call parent update ALabel::update(); From 54a85ea15fa8bcda0eeeba428728ed5d7670dbef Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:56:24 +0000 Subject: [PATCH 193/407] style: Apply clang-format change At least I hope I copy-pased it correctly --- src/modules/battery.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index e9fa3db28..7566e33e4 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -712,10 +712,10 @@ auto waybar::modules::Battery::update() -> void { event_box_.show(); auto icons = std::vector{status + "-" + state, status, state}; label_.set_markup( - fmt::format(fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), - fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles), - fmt::arg("health", fmt::format("{:.3}", health)))); + label_.set_markup(fmt::format( + fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), + fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), + fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health)))); } // Call parent update ALabel::update(); From 8d962430dd7c058ca77ebb037b4763baa47f7a8b Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:02:03 +0000 Subject: [PATCH 194/407] fix(battery): Remove duplicate line This is what happens when you copy-paste from GitHub actions --- src/modules/battery.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 7566e33e4..8a9c80d90 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -711,11 +711,10 @@ auto waybar::modules::Battery::update() -> void { } else { event_box_.show(); auto icons = std::vector{status + "-" + state, status, state}; - label_.set_markup( - label_.set_markup(fmt::format( - fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), - fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health)))); + label_.set_markup(fmt::format( + fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), + fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), + fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health)))); } // Call parent update ALabel::update(); From 5c4e36881916760d0741b193c1339fb62980ee3f Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:05:11 +0000 Subject: [PATCH 195/407] style(battery): Indent level It's now *inconsistent* in the file, but clang-tidy should be happy, sooo... --- src/modules/battery.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 8a9c80d90..5d822f10c 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -712,9 +712,9 @@ auto waybar::modules::Battery::update() -> void { event_box_.show(); auto icons = std::vector{status + "-" + state, status, state}; label_.set_markup(fmt::format( - fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), - fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health)))); + fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), + fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), + fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health)))); } // Call parent update ALabel::update(); From 57197b8e016b5ad42f4ae39b4860afa857b47a70 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:53:04 +0200 Subject: [PATCH 196/407] feat(battery): Also support energy_full (instead of charge_full) --- src/modules/battery.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 5d822f10c..43816d9c5 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -374,6 +374,11 @@ waybar::modules::Battery::getInfos() { if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) { mainBatHealthPercent = batHealthPercent; } + } else if (energy_full_exists && energy_full_design_exists) { + float batHealthPercent = ((float)energy_full / energy_full_design) * 100; + if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) { + mainBatHealthPercent = batHealthPercent; + } } } From a2c5a8215ba996acada6ac9229767847fbc1dc85 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:07:20 +0200 Subject: [PATCH 197/407] style(battery): Capitalize float 'F' suffix --- src/modules/battery.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 43816d9c5..327420ae5 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -371,12 +371,12 @@ waybar::modules::Battery::getInfos() { if (charge_full_exists && charge_full_design_exists) { float batHealthPercent = ((float)charge_full / charge_full_design) * 100; - if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) { + if (mainBatHealthPercent == 0.0F || batHealthPercent < mainBatHealthPercent) { mainBatHealthPercent = batHealthPercent; } } else if (energy_full_exists && energy_full_design_exists) { float batHealthPercent = ((float)energy_full / energy_full_design) * 100; - if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) { + if (mainBatHealthPercent == 0.0F || batHealthPercent < mainBatHealthPercent) { mainBatHealthPercent = batHealthPercent; } } From 9a3044a54f1d38a3618cb6369ebbcf6a91e9d996 Mon Sep 17 00:00:00 2001 From: Milo Mordaunt Date: Wed, 24 Apr 2024 18:15:40 -0400 Subject: [PATCH 198/407] Cursor change to indicate module clickability (#3108) * Indicate clickability on mouse hover * Avoid messy overrides situation * Update AModule.cpp * Update AModule.cpp * Update AModule.cpp * Update AModule.cpp --------- Co-authored-by: Alexis Rouillard --- include/AModule.hpp | 3 +++ src/AModule.cpp | 26 +++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index ea692ff84..58076655b 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -37,6 +37,8 @@ class AModule : public IModule { const Json::Value &config_; Gtk::EventBox event_box_; + virtual void setCursor(Gdk::CursorType const c); + virtual bool handleToggle(GdkEventButton *const &ev); virtual bool handleMouseEnter(GdkEventCrossing *const &ev); virtual bool handleMouseLeave(GdkEventCrossing *const &ev); @@ -46,6 +48,7 @@ class AModule : public IModule { private: bool handleUserEvent(GdkEventButton *const &ev); const bool isTooltip; + bool hasUserEvents_; std::vector pid_; gdouble distance_scrolled_y_; gdouble distance_scrolled_x_; diff --git a/src/AModule.cpp b/src/AModule.cpp index 399a23e44..082e6233a 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -15,6 +15,7 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: distance_scrolled_x_(0.0) { // Configure module action Map const Json::Value actions{config_["actions"]}; + for (Json::Value::const_iterator it = actions.begin(); it != actions.end(); ++it) { if (it.key().isString() && it->isString()) if (eventActionMap_.count(it.key().asString()) == 0) { @@ -31,17 +32,20 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: event_box_.signal_leave_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave)); // configure events' user commands - // hasUserEvent is true if any element from eventMap_ is satisfying the condition in the lambda - bool hasUserEvent = + // hasUserEvents is true if any element from eventMap_ is satisfying the condition in the lambda + bool hasUserEvents = std::find_if(eventMap_.cbegin(), eventMap_.cend(), [&config](const auto& eventEntry) { // True if there is any non-release type event return eventEntry.first.second != GdkEventType::GDK_BUTTON_RELEASE && config[eventEntry.second].isString(); }) != eventMap_.cend(); - if (enable_click || hasUserEvent) { + if (enable_click || hasUserEvents) { + hasUserEvents_ = true; event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); + } else { + hasUserEvents_ = false; } bool hasReleaseEvent = @@ -86,10 +90,21 @@ auto AModule::doAction(const std::string& name) -> void { } } + +void AModule::setCursor(Gdk::CursorType c) { + auto cursor = Gdk::Cursor::create(Gdk::HAND2); + auto gdk_window = event_box_.get_window(); + gdk_window->set_cursor(cursor); +} + bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } + + if (hasUserEvents_) { + setCursor(Gdk::HAND2); + } return false; } @@ -97,6 +112,10 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } + + if (hasUserEvents_) { + setCursor(Gdk::ARROW); + } return false; } @@ -108,6 +127,7 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) { std::string format{}; const std::map, std::string>::const_iterator& rec{ eventMap_.find(std::pair(e->button, e->type))}; + if (rec != eventMap_.cend()) { // First call module actions this->AModule::doAction(rec->second); From 61ac7e4e107ac17bb872eba8062dcda18f38abaa Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 25 Apr 2024 00:16:15 +0200 Subject: [PATCH 199/407] fix: lint --- src/AModule.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 082e6233a..bca8ec612 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -90,7 +90,6 @@ auto AModule::doAction(const std::string& name) -> void { } } - void AModule::setCursor(Gdk::CursorType c) { auto cursor = Gdk::Cursor::create(Gdk::HAND2); auto gdk_window = event_box_.get_window(); @@ -101,7 +100,7 @@ bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - + if (hasUserEvents_) { setCursor(Gdk::HAND2); } @@ -112,7 +111,7 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - + if (hasUserEvents_) { setCursor(Gdk::ARROW); } From 2481f7a2924d18ce83bc2bfc2434a06349a7bb32 Mon Sep 17 00:00:00 2001 From: clayton craft Date: Thu, 25 Apr 2024 01:36:43 -0700 Subject: [PATCH 200/407] upower: fix segfault by initializing lastWarningLevel (#3171) fixes bd8b215416cdca6ed0c929c18cede7dfb907edf0 --- include/modules/upower/upower.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index 8cea8c423..a5eb7209f 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -71,7 +71,7 @@ class UPower : public AModule { GDBusConnection *login1_connection; std::unique_ptr upower_tooltip; std::string lastStatus; - const char *lastWarningLevel; + const char *lastWarningLevel = nullptr; bool showAltText; bool showIcon = true; bool upowerRunning; From f41458ea24a57bb71b629089396c31fe4dd97f1c Mon Sep 17 00:00:00 2001 From: Tuur Vanhoutte <4633209+zjeffer@users.noreply.github.com> Date: Mon, 29 Apr 2024 19:46:28 +0200 Subject: [PATCH 201/407] Fix Hyprland socketpath changed to XDG_RUNTIME_DIR (#3183) --- src/modules/hyprland/backend.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 05db94ecd..98eb8b900 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -9,11 +9,30 @@ #include #include +#include #include #include namespace waybar::modules::hyprland { +std::filesystem::path getSocketFolder(const char* instanceSig) { + // socket path, specified by EventManager of Hyprland + static std::filesystem::path socketFolder; + if (!socketFolder.empty()) { + return socketFolder; + } + + std::filesystem::path xdgRuntimeDir = std::filesystem::path(getenv("XDG_RUNTIME_DIR")); + if (!xdgRuntimeDir.empty() && std::filesystem::exists(xdgRuntimeDir / "hypr")) { + socketFolder = xdgRuntimeDir / "hypr"; + } else { + spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr"); + socketFolder = std::filesystem::temp_directory_path() / "hypr"; + } + socketFolder = socketFolder / instanceSig; + return socketFolder; +} + void IPC::startIPC() { // will start IPC and relay events to parseIPC @@ -40,9 +59,7 @@ void IPC::startIPC() { addr.sun_family = AF_UNIX; - // socket path, specified by EventManager of Hyprland - std::string socketPath = "/tmp/hypr/" + std::string(his) + "/.socket2.sock"; - + auto socketPath = getSocketFolder(his) / ".socket2.sock"; strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); addr.sun_path[sizeof(addr.sun_path) - 1] = 0; @@ -142,12 +159,10 @@ std::string IPC::getSocket1Reply(const std::string& rq) { return ""; } - std::string instanceSigStr = std::string(instanceSig); - sockaddr_un serverAddress = {0}; serverAddress.sun_family = AF_UNIX; - std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock"; + std::string socketPath = getSocketFolder(instanceSig) / ".socket.sock"; // Use snprintf to copy the socketPath string into serverAddress.sun_path if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < From 79ae530bd29cb561d6f48773e894dd62fe353b7f Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Thu, 2 May 2024 06:31:40 +0000 Subject: [PATCH 202/407] pipewire: unbreak build on FreeBSD (#3193) --- .github/workflows/freebsd.yml | 2 +- include/util/pipewire/pipewire_backend.hpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 7b27fdb67..0b628d19b 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -29,7 +29,7 @@ jobs: sudo pkg install -y git # subprojects/date sudo pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \ libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ - pkgconf pulseaudio scdoc sndio spdlog wayland-protocols upower \ + pkgconf pipewire pulseaudio scdoc sndio spdlog wayland-protocols upower \ libinotify meson build -Dman-pages=enabled ninja -C build diff --git a/include/util/pipewire/pipewire_backend.hpp b/include/util/pipewire/pipewire_backend.hpp index ac70a1394..90fb2bb22 100644 --- a/include/util/pipewire/pipewire_backend.hpp +++ b/include/util/pipewire/pipewire_backend.hpp @@ -2,6 +2,8 @@ #include +#include + #include "util/backend_common.hpp" #include "util/pipewire/privacy_node_info.hpp" From 0b6476da32d181ee6b2cabdc5205a46a90521a75 Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" <31956036+haug1@users.noreply.github.com> Date: Thu, 2 May 2024 22:09:21 +0200 Subject: [PATCH 203/407] fix: set cursor appropriately on user event hover (#3195) --- src/AModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index bca8ec612..915c86036 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -91,7 +91,7 @@ auto AModule::doAction(const std::string& name) -> void { } void AModule::setCursor(Gdk::CursorType c) { - auto cursor = Gdk::Cursor::create(Gdk::HAND2); + auto cursor = Gdk::Cursor::create(c); auto gdk_window = event_box_.get_window(); gdk_window->set_cursor(cursor); } From 50476edc98d091573eef14b6c145251456712d4b Mon Sep 17 00:00:00 2001 From: Jacob Birkett Date: Thu, 2 May 2024 23:31:39 -0700 Subject: [PATCH 204/407] Nix Flake: Fix overlay (again) (#3196) --- flake.nix | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/flake.nix b/flake.nix index ebaeb81f1..1ef5b09c5 100644 --- a/flake.nix +++ b/flake.nix @@ -46,22 +46,25 @@ }; }); - overlays.default = final: prev: { - waybar = final.callPackage ./nix/default.nix { - # take the first "version: '...'" from meson.build - version = - (builtins.head (builtins.split "'" - (builtins.elemAt - (builtins.split " version: '" (builtins.readFile ./meson.build)) - 2))) - + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); + overlays = { + default = self.overlays.waybar; + waybar = final: prev: { + waybar = final.callPackage ./nix/default.nix { + waybar = prev.waybar; + # take the first "version: '...'" from meson.build + version = + (builtins.head (builtins.split "'" + (builtins.elemAt + (builtins.split " version: '" (builtins.readFile ./meson.build)) + 2))) + + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); + }; }; }; - packages = genSystems (pkgs: - let packages = self.overlays.default pkgs pkgs; - in packages // { - default = packages.waybar; - }); + packages = genSystems (pkgs: { + default = self.packages.${pkgs.stdenv.hostPlatform.system}.waybar; + inherit (pkgs) waybar; + }); }; } From 231d6972d7a023e9358ab7deda509baac49006cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E8=8A=AD=E8=80=81=E7=88=B9?= <37040069+stanly0726@users.noreply.github.com> Date: Fri, 3 May 2024 14:47:41 +0800 Subject: [PATCH 205/407] fix: custom module mediaplayer doesn't respect argument (#3198) fix custom module mediaplayer which doesn't consider --exclude argument on player appear --- resources/custom_modules/mediaplayer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index 4aea4171b..acc47496d 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -136,6 +136,10 @@ def on_metadata_changed(self, player, metadata, _=None): def on_player_appeared(self, _, player): logger.info(f"Player has appeared: {player.name}") + if player.name in self.excluded_player: + logger.debug( + "New player appeared, but it's in exclude player list, skipping") + return if player is not None and (self.selected_player is None or player.name == self.selected_player): self.init_player(player) else: From c4e0c569aa74d771d62dbbedb52326871492ef59 Mon Sep 17 00:00:00 2001 From: Bintang <96517350+spitulax@users.noreply.github.com> Date: Mon, 6 May 2024 15:46:10 +0700 Subject: [PATCH 206/407] flake: fix overlay not actually being applied (#3208) --- flake.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 1ef5b09c5..571c49348 100644 --- a/flake.nix +++ b/flake.nix @@ -16,7 +16,12 @@ "x86_64-linux" "aarch64-linux" ] - (system: func (import nixpkgs { inherit system; })); + (system: func (import nixpkgs { + inherit system; + overlays = with self.overlays; [ + waybar + ]; + })); mkDate = longDate: (lib.concatStringsSep "-" [ (builtins.substring 0 4 longDate) From 8e8ce0c6bcacb000c91fb2ef1308b11bab518518 Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" <31956036+haug1@users.noreply.github.com> Date: Mon, 6 May 2024 10:47:25 +0200 Subject: [PATCH 207/407] feat(#3182): style tray icon on hover (#3203) --- include/modules/sni/item.hpp | 2 ++ src/modules/sni/item.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index 1043157cd..ebc08d45f 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -76,6 +76,8 @@ class Item : public sigc::trackable { void makeMenu(); bool handleClick(GdkEventButton* const& /*ev*/); bool handleScroll(GdkEventScroll* const&); + bool handleMouseEnter(GdkEventCrossing* const&); + bool handleMouseLeave(GdkEventCrossing* const&); // smooth scrolling threshold gdouble scroll_threshold_ = 0; diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index c3de2357f..b5c0dd85f 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -8,6 +8,7 @@ #include #include +#include "gdk/gdk.h" #include "util/format.hpp" #include "util/gtk_icon.hpp" @@ -57,6 +58,8 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf 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)); + event_box.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Item::handleMouseEnter)); + event_box.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Item::handleMouseLeave)); // initial visibility event_box.show_all(); event_box.set_visible(show_passive_); @@ -69,6 +72,16 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf cancellable_, interface); } +bool Item::handleMouseEnter(GdkEventCrossing* const& e) { + event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + return false; +} + +bool Item::handleMouseLeave(GdkEventCrossing* const& e) { + event_box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + return false; +} + void Item::onConfigure(GdkEventConfigure* ev) { this->updateImage(); } void Item::proxyReady(Glib::RefPtr& result) { From a453ea3c70195803e9962a97f9243c0c78d4ecdc Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" <31956036+haug1@users.noreply.github.com> Date: Mon, 6 May 2024 10:47:52 +0200 Subject: [PATCH 208/407] fix(#3210): tooltip-format on custom modules not working in some cases (#3213) --- include/modules/custom.hpp | 1 + src/modules/custom.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/modules/custom.hpp b/include/modules/custom.hpp index 2c7ba8a80..6c17c6e45 100644 --- a/include/modules/custom.hpp +++ b/include/modules/custom.hpp @@ -35,6 +35,7 @@ class Custom : public ALabel { std::string id_; std::string alt_; std::string tooltip_; + const bool tooltip_format_enabled_; std::vector class_; int percentage_; FILE* fp_; diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index ec3bb3fa4..45e849cc0 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -10,6 +10,7 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id, name_(name), output_name_(output_name), id_(id), + tooltip_format_enabled_{config_["tooltip-format"].isString()}, percentage_(0), fp_(nullptr), pid_(-1) { @@ -166,16 +167,16 @@ auto waybar::modules::Custom::update() -> void { } else { label_.set_markup(str); if (tooltipEnabled()) { - if (text_ == tooltip_) { - if (label_.get_tooltip_markup() != str) { - label_.set_tooltip_markup(str); - } - } else if (config_["tooltip-format"].isString()) { + if (tooltip_format_enabled_) { auto tooltip = config_["tooltip-format"].asString(); tooltip = fmt::format(fmt::runtime(tooltip), text_, fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); label_.set_tooltip_markup(tooltip); + } else if (text_ == tooltip_) { + if (label_.get_tooltip_markup() != str) { + label_.set_tooltip_markup(str); + } } else { if (label_.get_tooltip_markup() != tooltip_) { label_.set_tooltip_markup(tooltip_); From fc6d708fb63497064a791b4f451eb561fe3abb37 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 May 2024 10:50:55 +0200 Subject: [PATCH 209/407] chore: disable cland-tidy for now --- .github/workflows/{clang-tidy.yml => clang-tidy.yml.bak} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{clang-tidy.yml => clang-tidy.yml.bak} (100%) diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml.bak similarity index 100% rename from .github/workflows/clang-tidy.yml rename to .github/workflows/clang-tidy.yml.bak From e7779b545855bac1e24db65deefc830fd0fbd66b Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" <31956036+haug1@users.noreply.github.com> Date: Mon, 6 May 2024 10:51:03 +0200 Subject: [PATCH 210/407] feat(#3174): hover for whole group (#3201) * feat(#3174): hover for whole group * fix: target eventbox for class also * fix: actually no reason to add handler, just override AModule * fix: actually remove existing handler as well drawer functionality still works from my testing. anything else to think abotu? * revert: keep id and class on original box * refactor: clang-format group.hpp * dev: try stop workflow --- include/group.hpp | 14 ++++++-------- src/group.cpp | 43 ++++++++++++++++--------------------------- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index 67cf43855..564d2eb52 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -11,15 +11,13 @@ namespace waybar { class Group : public AModule { public: - Group(const std::string&, const std::string&, const Json::Value&, bool); + Group(const std::string &, const std::string &, const Json::Value &, bool); virtual ~Group() = default; auto update() -> void override; - operator Gtk::Widget&() override; + operator Gtk::Widget &() override; - virtual Gtk::Box& getBox(); - void addWidget(Gtk::Widget& widget); - - bool handleMouseHover(GdkEventCrossing* const& e); + virtual Gtk::Box &getBox(); + void addWidget(Gtk::Widget &widget); protected: Gtk::Box box; @@ -28,8 +26,8 @@ class Group : public AModule { bool is_first_widget = true; bool is_drawer = false; std::string add_class_to_drawer_children; - - void addHoverHandlerTo(Gtk::Widget& widget); + bool handleMouseEnter(GdkEventCrossing *const &ev) override; + bool handleMouseLeave(GdkEventCrossing *const &ev) override; }; } // namespace waybar diff --git a/src/group.cpp b/src/group.cpp index 262cae656..b32617356 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -4,7 +4,7 @@ #include -#include "gdkmm/device.h" +#include "gtkmm/enums.h" #include "gtkmm/widget.h" namespace waybar { @@ -78,30 +78,23 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& } else { box.pack_start(revealer); } - - addHoverHandlerTo(revealer); } -} -bool Group::handleMouseHover(GdkEventCrossing* const& e) { - switch (e->type) { - case GDK_ENTER_NOTIFY: - revealer.set_reveal_child(true); - break; - case GDK_LEAVE_NOTIFY: - revealer.set_reveal_child(false); - break; - default: - break; - } + event_box_.add(box); +} - return true; +bool Group::handleMouseEnter(GdkEventCrossing* const& e) { + box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + revealer.set_reveal_child(true); + return false; } -void Group::addHoverHandlerTo(Gtk::Widget& widget) { - widget.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK); - widget.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseHover)); - widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseHover)); + + +bool Group::handleMouseLeave(GdkEventCrossing* const& e) { + box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + revealer.set_reveal_child(false); + return false; } auto Group::update() -> void { @@ -113,17 +106,13 @@ Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer void Group::addWidget(Gtk::Widget& widget) { getBox().pack_start(widget, false, false); - if (is_drawer) { - // Necessary because of GTK's hitbox detection - addHoverHandlerTo(widget); - if (!is_first_widget) { - widget.get_style_context()->add_class(add_class_to_drawer_children); - } + if (is_drawer && !is_first_widget) { + widget.get_style_context()->add_class(add_class_to_drawer_children); } is_first_widget = false; } -Group::operator Gtk::Widget&() { return box; } +Group::operator Gtk::Widget&() { return event_box_; } } // namespace waybar From df1a9c5509543cc2dbfed5f0230918ae88473b81 Mon Sep 17 00:00:00 2001 From: Eldar Yusupov Date: Mon, 6 May 2024 11:51:14 +0300 Subject: [PATCH 211/407] Remove listener when window is destroyed (#3215) --- include/modules/dwl/window.hpp | 2 +- src/modules/dwl/window.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/modules/dwl/window.hpp b/include/modules/dwl/window.hpp index 6b068360c..435863999 100644 --- a/include/modules/dwl/window.hpp +++ b/include/modules/dwl/window.hpp @@ -14,7 +14,7 @@ namespace waybar::modules::dwl { class Window : public AAppIconLabel, public sigc::trackable { public: Window(const std::string &, const waybar::Bar &, const Json::Value &); - virtual ~Window() = default; + ~Window(); void handle_layout(const uint32_t layout); void handle_title(const char *title); diff --git a/src/modules/dwl/window.cpp b/src/modules/dwl/window.cpp index 4f8b02812..870d87e4e 100644 --- a/src/modules/dwl/window.cpp +++ b/src/modules/dwl/window.cpp @@ -80,7 +80,7 @@ Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) wl_registry_add_listener(registry, ®istry_listener_impl, this); wl_display_roundtrip(display); - if (!status_manager_) { + if (status_manager_ == nullptr) { spdlog::error("dwl_status_manager_v2 not advertised"); return; } @@ -91,6 +91,12 @@ Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) zdwl_ipc_manager_v2_destroy(status_manager_); } +Window::~Window() { + if (output_status_ != nullptr) { + zdwl_ipc_output_v2_destroy(output_status_); + } +} + void Window::handle_title(const char *title) { title_ = title; } void Window::handle_appid(const char *appid) { appid_ = appid; } From 0572e02d7e04fbac20c5f881ef778ef5e6b8dd67 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 May 2024 10:51:30 +0200 Subject: [PATCH 212/407] fix: lint --- src/group.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/group.cpp b/src/group.cpp index b32617356..9f707dc94 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -89,8 +89,6 @@ bool Group::handleMouseEnter(GdkEventCrossing* const& e) { return false; } - - bool Group::handleMouseLeave(GdkEventCrossing* const& e) { box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); revealer.set_reveal_child(false); From e627879b1656ec7352e6382f80ee16d90b377aaf Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 May 2024 10:54:52 +0200 Subject: [PATCH 213/407] chore: 0.10.3 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ec23923ac..a57b17f81 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.10.2', + version: '0.10.3', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From 29917fb073c957f5610e951d3c0c3e21eb4f377c Mon Sep 17 00:00:00 2001 From: Tuur Vanhoutte <4633209+zjeffer@users.noreply.github.com> Date: Tue, 7 May 2024 08:26:05 +0200 Subject: [PATCH 214/407] Fix hyprland/language events not working with keyboard names with commas in them (#3224) --- src/modules/hyprland/language.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 549faf738..49b20218c 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -63,7 +63,7 @@ auto Language::update() -> void { void Language::onEvent(const std::string& ev) { std::lock_guard lg(mutex_); std::string kbName(begin(ev) + ev.find_last_of('>') + 1, begin(ev) + ev.find_first_of(',')); - auto layoutName = ev.substr(ev.find_first_of(',') + 1); + auto layoutName = ev.substr(ev.find_last_of(',') + 1); if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString()) return; // ignore From 2ead1bbf84ff0fdb7234b1d9332c3b3a3bb8b799 Mon Sep 17 00:00:00 2001 From: ViktarL <23121044+LukashonakV@users.noreply.github.com> Date: Tue, 7 May 2024 11:29:52 +0300 Subject: [PATCH 215/407] Upower refactoring (#3220) Signed-off-by: Viktar Lukashonak --- include/modules/upower.hpp | 92 +++++ include/modules/upower/upower.hpp | 82 ---- include/modules/upower/upower_tooltip.hpp | 33 -- meson.build | 5 +- src/factory.cpp | 4 +- src/modules/upower.cpp | 479 ++++++++++++++++++++++ src/modules/upower/upower.cpp | 418 ------------------- src/modules/upower/upower_tooltip.cpp | 160 -------- 8 files changed, 574 insertions(+), 699 deletions(-) create mode 100644 include/modules/upower.hpp delete mode 100644 include/modules/upower/upower.hpp delete mode 100644 include/modules/upower/upower_tooltip.hpp create mode 100644 src/modules/upower.cpp delete mode 100644 src/modules/upower/upower.cpp delete mode 100644 src/modules/upower/upower_tooltip.cpp diff --git a/include/modules/upower.hpp b/include/modules/upower.hpp new file mode 100644 index 000000000..b4450df26 --- /dev/null +++ b/include/modules/upower.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include + +#include + +#include "AIconLabel.hpp" + +namespace waybar::modules { + +class UPower final : public AIconLabel { + public: + UPower(const std::string &, const Json::Value &); + virtual ~UPower(); + auto update() -> void override; + + private: + const std::string NO_BATTERY{"battery-missing-symbolic"}; + + // Config + bool showIcon_{true}; + bool hideIfEmpty_{true}; + int iconSize_{20}; + int tooltip_spacing_{4}; + int tooltip_padding_{4}; + Gtk::Box contentBox_; // tooltip box + std::string tooltipFormat_; + + // UPower device info + struct upDevice_output { + UpDevice *upDevice{NULL}; + double percentage{0.0}; + double temperature{0.0}; + guint64 time_full{0u}; + guint64 time_empty{0u}; + gchar *icon_name{(char *)'\0'}; + bool upDeviceValid{false}; + UpDeviceState state; + UpDeviceKind kind; + char *nativePath{(char *)'\0'}; + char *model{(char *)'\0'}; + }; + + // Technical variables + std::string nativePath_; + std::string lastStatus_; + Glib::ustring label_markup_; + std::mutex mutex_; + Glib::RefPtr gtkTheme_; + + // Technical functions + void addDevice(UpDevice *); + void removeDevice(const gchar *); + void removeDevices(); + void resetDevices(); + void setDisplayDevice(); + const Glib::ustring getText(const upDevice_output &upDevice_, const std::string &format); + bool queryTooltipCb(int, int, bool, const Glib::RefPtr &); + + // DBUS variables + guint watcherID_; + Glib::RefPtr conn_; + guint subscrID_{0u}; + + // UPower variables + UpClient *upClient_; + upDevice_output upDevice_; // Device to display + typedef std::unordered_map Devices; + Devices devices_; + bool upRunning_{true}; + + // DBus callbacks + void getConn_cb(Glib::RefPtr &result); + void onAppear(const Glib::RefPtr &, const Glib::ustring &, + const Glib::ustring &); + void onVanished(const Glib::RefPtr &, const Glib::ustring &); + void prepareForSleep_cb(const Glib::RefPtr &connection, + const Glib::ustring &sender_name, const Glib::ustring &object_path, + const Glib::ustring &interface_name, const Glib::ustring &signal_name, + const Glib::VariantContainerBase ¶meters); + + // UPower callbacks + static void deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data); + static void deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data); + static void deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer user_data); + // UPower secondary functions + void getUpDeviceInfo(upDevice_output &upDevice_); +}; + +} // namespace waybar::modules diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp deleted file mode 100644 index a5eb7209f..000000000 --- a/include/modules/upower/upower.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include - -#include "ALabel.hpp" -#include "glibconfig.h" -#include "gtkmm/box.h" -#include "gtkmm/image.h" -#include "gtkmm/label.h" -#include "modules/upower/upower_tooltip.hpp" - -namespace waybar::modules::upower { - -class UPower : public AModule { - public: - UPower(const std::string &, const Json::Value &); - virtual ~UPower(); - auto update() -> void override; - - private: - typedef std::unordered_map Devices; - - const std::string DEFAULT_FORMAT = "{percentage}"; - const std::string DEFAULT_FORMAT_ALT = "{percentage} {time}"; - - static void deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data); - static void deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data); - static void deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer user_data); - static void prepareForSleep_cb(GDBusConnection *system_bus, const gchar *sender_name, - const gchar *object_path, const gchar *interface_name, - const gchar *signal_name, GVariant *parameters, - gpointer user_data); - static void upowerAppear(GDBusConnection *conn, const gchar *name, const gchar *name_owner, - gpointer data); - static void upowerDisappear(GDBusConnection *connection, const gchar *name, gpointer user_data); - - void removeDevice(const gchar *objectPath); - void addDevice(UpDevice *device); - void setDisplayDevice(); - void resetDevices(); - void removeDevices(); - bool show_tooltip_callback(int, int, bool, const Glib::RefPtr &tooltip); - bool handleToggle(GdkEventButton *const &) override; - std::string timeToString(gint64 time); - - const std::string getDeviceStatus(UpDeviceState &state); - - Gtk::Box box_; - Gtk::Image icon_; - Gtk::Label label_; - - // Config - bool hideIfEmpty = true; - bool tooltip_enabled = true; - uint tooltip_spacing = 4; - uint tooltip_padding = 4; - uint iconSize = 20; - std::string format = DEFAULT_FORMAT; - std::string format_alt = DEFAULT_FORMAT_ALT; - - Devices devices; - std::mutex m_Mutex; - UpClient *client; - UpDevice *displayDevice = nullptr; - guint login1_id; - GDBusConnection *login1_connection; - std::unique_ptr upower_tooltip; - std::string lastStatus; - const char *lastWarningLevel = nullptr; - bool showAltText; - bool showIcon = true; - bool upowerRunning; - guint upowerWatcher_id; - std::string nativePath_; -}; - -} // namespace waybar::modules::upower diff --git a/include/modules/upower/upower_tooltip.hpp b/include/modules/upower/upower_tooltip.hpp deleted file mode 100644 index bc99abede..000000000 --- a/include/modules/upower/upower_tooltip.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "gtkmm/box.h" -#include "gtkmm/label.h" -#include "gtkmm/window.h" - -namespace waybar::modules::upower { - -class UPowerTooltip : public Gtk::Window { - private: - typedef std::unordered_map Devices; - - const std::string getDeviceIcon(UpDeviceKind& kind); - - std::unique_ptr contentBox; - - uint iconSize; - uint tooltipSpacing; - uint tooltipPadding; - - public: - UPowerTooltip(uint iconSize, uint tooltipSpacing, uint tooltipPadding); - virtual ~UPowerTooltip(); - - uint updateTooltip(Devices& devices); -}; - -} // namespace waybar::modules::upower diff --git a/meson.build b/meson.build index a57b17f81..d7a5a4eef 100644 --- a/meson.build +++ b/meson.build @@ -335,10 +335,7 @@ endif if (upower_glib.found() and not get_option('logind').disabled()) add_project_arguments('-DHAVE_UPOWER', language: 'cpp') - src_files += files( - 'src/modules/upower/upower.cpp', - 'src/modules/upower/upower_tooltip.cpp', - ) + src_files += files('src/modules/upower.cpp') man_files += files('man/waybar-upower.5.scd') endif diff --git a/src/factory.cpp b/src/factory.cpp index 0549fe09f..ca10ef956 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -70,7 +70,7 @@ #include "modules/gamemode.hpp" #endif #ifdef HAVE_UPOWER -#include "modules/upower/upower.hpp" +#include "modules/upower.hpp" #endif #ifdef HAVE_PIPEWIRE #include "modules/privacy/privacy.hpp" @@ -130,7 +130,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, #endif #ifdef HAVE_UPOWER if (ref == "upower") { - return new waybar::modules::upower::UPower(id, config_[name]); + return new waybar::modules::UPower(id, config_[name]); } #endif #ifdef HAVE_PIPEWIRE diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp new file mode 100644 index 000000000..73c0af082 --- /dev/null +++ b/src/modules/upower.cpp @@ -0,0 +1,479 @@ +#include "modules/upower.hpp" + +#include +#include +#include + +namespace waybar::modules { + +UPower::UPower(const std::string &id, const Json::Value &config) + : AIconLabel(config, "upower", id, "{percentage}", 0, true, true, true) { + box_.set_name(name_); + box_.set_spacing(0); + box_.set_has_tooltip(AModule::tooltipEnabled()); + // Tooltip box + contentBox_.set_orientation((box_.get_orientation() == Gtk::ORIENTATION_HORIZONTAL) + ? Gtk::ORIENTATION_VERTICAL + : Gtk::ORIENTATION_HORIZONTAL); + // Get current theme + gtkTheme_ = Gtk::IconTheme::get_default(); + + // Icon Size + if (config_["icon-size"].isInt()) { + iconSize_ = config_["icon-size"].asInt(); + } + image_.set_pixel_size(iconSize_); + + // Show icon only when "show-icon" isn't set to false + if (config_["show-icon"].isBool()) showIcon_ = config_["show-icon"].asBool(); + if (!showIcon_) box_.remove(image_); + // Device user wants + if (config_["native-path"].isString()) nativePath_ = config_["native-path"].asString(); + + // Hide If Empty + if (config_["hide-if-empty"].isBool()) hideIfEmpty_ = config_["hide-if-empty"].asBool(); + + // Tooltip Spacing + if (config_["tooltip-spacing"].isInt()) tooltip_spacing_ = config_["tooltip-spacing"].asInt(); + + // Tooltip Padding + if (config_["tooltip-padding"].isInt()) { + tooltip_padding_ = config_["tooltip-padding"].asInt(); + contentBox_.set_margin_top(tooltip_padding_); + contentBox_.set_margin_bottom(tooltip_padding_); + contentBox_.set_margin_left(tooltip_padding_); + contentBox_.set_margin_right(tooltip_padding_); + } + + // Tooltip Format + if (config_["tooltip-format"].isString()) tooltipFormat_ = config_["tooltip-format"].asString(); + + // Start watching DBUS + watcherID_ = Gio::DBus::watch_name( + Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.UPower", + sigc::mem_fun(*this, &UPower::onAppear), sigc::mem_fun(*this, &UPower::onVanished), + Gio::DBus::BusNameWatcherFlags::BUS_NAME_WATCHER_FLAGS_AUTO_START); + // Get DBus async connect + Gio::DBus::Connection::get(Gio::DBus::BusType::BUS_TYPE_SYSTEM, + sigc::mem_fun(*this, &UPower::getConn_cb)); + + // Make UPower client + GError **gErr = NULL; + upClient_ = up_client_new_full(NULL, gErr); + if (upClient_ == NULL) + spdlog::error("Upower. UPower client connection error. {}", (*gErr)->message); + + // Subscribe UPower events + g_signal_connect(upClient_, "device-added", G_CALLBACK(deviceAdded_cb), this); + g_signal_connect(upClient_, "device-removed", G_CALLBACK(deviceRemoved_cb), this); + + // Subscribe tooltip query events + box_.set_has_tooltip(); + box_.signal_query_tooltip().connect(sigc::mem_fun(*this, &UPower::queryTooltipCb), false); + + resetDevices(); + setDisplayDevice(); + // Update the widget + dp.emit(); +} + +UPower::~UPower() { + if (upDevice_.upDevice != NULL) g_object_unref(upDevice_.upDevice); + if (upClient_ != NULL) g_object_unref(upClient_); + if (subscrID_ > 0u) { + conn_->signal_unsubscribe(subscrID_); + subscrID_ = 0u; + } + Gio::DBus::unwatch_name(watcherID_); + watcherID_ = 0u; + removeDevices(); +} + +static const std::string getDeviceStatus(UpDeviceState &state) { + switch (state) { + case UP_DEVICE_STATE_CHARGING: + case UP_DEVICE_STATE_PENDING_CHARGE: + return "charging"; + case UP_DEVICE_STATE_DISCHARGING: + case UP_DEVICE_STATE_PENDING_DISCHARGE: + return "discharging"; + case UP_DEVICE_STATE_FULLY_CHARGED: + return "full"; + case UP_DEVICE_STATE_EMPTY: + return "empty"; + default: + return "unknown-status"; + } +} + +static const std::string getDeviceIcon(UpDeviceKind &kind) { + switch (kind) { + case UP_DEVICE_KIND_LINE_POWER: + return "ac-adapter-symbolic"; + case UP_DEVICE_KIND_BATTERY: + return "battery-symbolic"; + case UP_DEVICE_KIND_UPS: + return "uninterruptible-power-supply-symbolic"; + case UP_DEVICE_KIND_MONITOR: + return "video-display-symbolic"; + case UP_DEVICE_KIND_MOUSE: + return "input-mouse-symbolic"; + case UP_DEVICE_KIND_KEYBOARD: + return "input-keyboard-symbolic"; + case UP_DEVICE_KIND_PDA: + return "pda-symbolic"; + case UP_DEVICE_KIND_PHONE: + return "phone-symbolic"; + case UP_DEVICE_KIND_MEDIA_PLAYER: + return "multimedia-player-symbolic"; + case UP_DEVICE_KIND_TABLET: + return "computer-apple-ipad-symbolic"; + case UP_DEVICE_KIND_COMPUTER: + return "computer-symbolic"; + case UP_DEVICE_KIND_GAMING_INPUT: + return "input-gaming-symbolic"; + case UP_DEVICE_KIND_PEN: + return "input-tablet-symbolic"; + case UP_DEVICE_KIND_TOUCHPAD: + return "input-touchpad-symbolic"; + case UP_DEVICE_KIND_MODEM: + return "modem-symbolic"; + case UP_DEVICE_KIND_NETWORK: + return "network-wired-symbolic"; + case UP_DEVICE_KIND_HEADSET: + return "audio-headset-symbolic"; + case UP_DEVICE_KIND_HEADPHONES: + return "audio-headphones-symbolic"; + case UP_DEVICE_KIND_OTHER_AUDIO: + case UP_DEVICE_KIND_SPEAKERS: + return "audio-speakers-symbolic"; + case UP_DEVICE_KIND_VIDEO: + return "camera-web-symbolic"; + case UP_DEVICE_KIND_PRINTER: + return "printer-symbolic"; + case UP_DEVICE_KIND_SCANNER: + return "scanner-symbolic"; + case UP_DEVICE_KIND_CAMERA: + return "camera-photo-symbolic"; + case UP_DEVICE_KIND_BLUETOOTH_GENERIC: + return "bluetooth-active-symbolic"; + case UP_DEVICE_KIND_TOY: + case UP_DEVICE_KIND_REMOTE_CONTROL: + case UP_DEVICE_KIND_WEARABLE: + case UP_DEVICE_KIND_LAST: + default: + return "battery-symbolic"; + } +} + +static std::string secondsToString(const std::chrono::seconds sec) { + const auto ds{std::chrono::duration_cast(sec)}; + const auto hrs{std::chrono::duration_cast(sec - ds)}; + const auto min{std::chrono::duration_cast(sec - ds - hrs)}; + std::string_view strRet{(ds.count() > 0) ? "{D}d {H}h {M}min" + : (hrs.count() > 0) ? "{H}h {M}min" + : (min.count() > 0) ? "{M}min" + : ""}; + spdlog::debug( + "UPower::secondsToString(). seconds: \"{0}\", minutes: \"{1}\", hours: \"{2}\", \ +days: \"{3}\", strRet: \"{4}\"", + sec.count(), min.count(), hrs.count(), ds.count(), strRet); + return fmt::format(fmt::runtime(strRet), fmt::arg("D", ds.count()), fmt::arg("H", hrs.count()), + fmt::arg("M", min.count())); +} + +auto UPower::update() -> void { + std::lock_guard guard{mutex_}; + // Don't update widget if the UPower service isn't running + if (!upRunning_) { + if (hideIfEmpty_) box_.hide(); + return; + } + + getUpDeviceInfo(upDevice_); + + if (upDevice_.upDevice == NULL && hideIfEmpty_) { + box_.hide(); + return; + } + /* Every Device which is handled by Upower and which is not + * UP_DEVICE_KIND_UNKNOWN (0) or UP_DEVICE_KIND_LINE_POWER (1) is a Battery + */ + const bool upDeviceValid{upDevice_.kind != UpDeviceKind::UP_DEVICE_KIND_UNKNOWN && + upDevice_.kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER}; + // Get CSS status + const auto status{getDeviceStatus(upDevice_.state)}; + // Remove last status if it exists + if (!lastStatus_.empty() && box_.get_style_context()->has_class(lastStatus_)) + box_.get_style_context()->remove_class(lastStatus_); + if (!box_.get_style_context()->has_class(status)) box_.get_style_context()->add_class(status); + lastStatus_ = status; + + if (devices_.size() == 0 && !upDeviceValid && hideIfEmpty_) { + box_.hide(); + // Call parent update + AModule::update(); + return; + } + + label_.set_markup(getText(upDevice_, format_)); + // Set icon + if (upDevice_.icon_name == NULL || !gtkTheme_->has_icon(upDevice_.icon_name)) + upDevice_.icon_name = (char *)NO_BATTERY.c_str(); + image_.set_from_icon_name(upDevice_.icon_name, Gtk::ICON_SIZE_INVALID); + + box_.show(); + + // Call parent update + ALabel::update(); +} + +void UPower::getConn_cb(Glib::RefPtr &result) { + try { + conn_ = Gio::DBus::Connection::get_finish(result); + // Subscribe DBUs events + subscrID_ = conn_->signal_subscribe(sigc::mem_fun(*this, &UPower::prepareForSleep_cb), + "org.freedesktop.login1", "org.freedesktop.login1.Manager", + "PrepareForSleep", "/org/freedesktop/login1"); + + } catch (const Glib::Error &e) { + spdlog::error("Upower. DBus connection error. {}", e.what().c_str()); + } +} + +void UPower::onAppear(const Glib::RefPtr &conn, const Glib::ustring &name, + const Glib::ustring &name_owner) { + upRunning_ = true; +} + +void UPower::onVanished(const Glib::RefPtr &conn, + const Glib::ustring &name) { + upRunning_ = false; +} + +void UPower::prepareForSleep_cb(const Glib::RefPtr &connection, + const Glib::ustring &sender_name, const Glib::ustring &object_path, + const Glib::ustring &interface_name, + const Glib::ustring &signal_name, + const Glib::VariantContainerBase ¶meters) { + if (parameters.is_of_type(Glib::VariantType("(b)"))) { + Glib::Variant sleeping; + parameters.get_child(sleeping, 0); + if (!sleeping.get()) { + resetDevices(); + setDisplayDevice(); + // Update the widget + dp.emit(); + } + } +} + +void UPower::deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data) { + UPower *up{static_cast(data)}; + up->addDevice(device); + up->setDisplayDevice(); + // Update the widget + up->dp.emit(); +} + +void UPower::deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data) { + UPower *up{static_cast(data)}; + up->removeDevice(objectPath); + up->setDisplayDevice(); + // Update the widget + up->dp.emit(); +} + +void UPower::deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer data) { + UPower *up{static_cast(data)}; + // Update the widget + up->dp.emit(); +} + +void UPower::addDevice(UpDevice *device) { + std::lock_guard guard{mutex_}; + + if (G_IS_OBJECT(device)) { + const gchar *objectPath{up_device_get_object_path(device)}; + + // Due to the device getting cleared after this event is fired, we + // create a new object pointing to its objectPath + device = up_device_new(); + upDevice_output upDevice{.upDevice = device}; + gboolean ret{up_device_set_object_path_sync(device, objectPath, NULL, NULL)}; + if (!ret) { + g_object_unref(G_OBJECT(device)); + return; + } + + if (devices_.find(objectPath) != devices_.cend()) { + auto upDevice{devices_[objectPath]}; + if (G_IS_OBJECT(upDevice.upDevice)) g_object_unref(upDevice.upDevice); + devices_.erase(objectPath); + } + + g_signal_connect(device, "notify", G_CALLBACK(deviceNotify_cb), this); + devices_.emplace(Devices::value_type(objectPath, upDevice)); + } +} + +void UPower::removeDevice(const gchar *objectPath) { + std::lock_guard guard{mutex_}; + if (devices_.find(objectPath) != devices_.cend()) { + auto upDevice{devices_[objectPath]}; + if (G_IS_OBJECT(upDevice.upDevice)) g_object_unref(upDevice.upDevice); + devices_.erase(objectPath); + } +} + +void UPower::removeDevices() { + std::lock_guard guard{mutex_}; + if (!devices_.empty()) { + auto it{devices_.cbegin()}; + while (it != devices_.cend()) { + if (G_IS_OBJECT(it->second.upDevice)) g_object_unref(it->second.upDevice); + devices_.erase(it++); + } + } +} + +// Removes all devices and adds the current devices +void UPower::resetDevices() { + // Remove all devices + removeDevices(); + + // Adds all devices + GPtrArray *newDevices = up_client_get_devices2(upClient_); + if (newDevices != NULL) + for (guint i{0}; i < newDevices->len; ++i) { + UpDevice *device{(UpDevice *)g_ptr_array_index(newDevices, i)}; + if (device && G_IS_OBJECT(device)) addDevice(device); + } +} + +void UPower::setDisplayDevice() { + std::lock_guard guard{mutex_}; + + if (nativePath_.empty()) { + upDevice_.upDevice = up_client_get_display_device(upClient_); + getUpDeviceInfo(upDevice_); + } else { + g_ptr_array_foreach( + up_client_get_devices2(upClient_), + [](gpointer data, gpointer user_data) { + upDevice_output upDevice; + auto thisPtr{static_cast(user_data)}; + upDevice.upDevice = static_cast(data); + thisPtr->getUpDeviceInfo(upDevice); + if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { + // Unref current upDevice + if (thisPtr->upDevice_.upDevice) g_object_unref(thisPtr->upDevice_.upDevice); + // Reassign new upDevice + thisPtr->upDevice_ = upDevice; + } + }, + this); + } + + if (upDevice_.upDevice) + g_signal_connect(upDevice_.upDevice, "notify", G_CALLBACK(deviceNotify_cb), this); +} + +void UPower::getUpDeviceInfo(upDevice_output &upDevice_) { + if (upDevice_.upDevice && G_IS_OBJECT(upDevice_.upDevice)) { + g_object_get(upDevice_.upDevice, "kind", &upDevice_.kind, "state", &upDevice_.state, + "percentage", &upDevice_.percentage, "icon-name", &upDevice_.icon_name, + "time-to-empty", &upDevice_.time_empty, "time-to-full", &upDevice_.time_full, + "temperature", &upDevice_.temperature, "native-path", &upDevice_.nativePath, + "model", &upDevice_.model, NULL); + spdlog::debug( + "UPower. getUpDeviceInfo. kind: \"{0}\". state: \"{1}\". percentage: \"{2}\". \ +icon_name: \"{3}\". time-to-empty: \"{4}\". time-to-full: \"{5}\". temperature: \"{6}\". \ +native_path: \"{7}\". model: \"{8}\"", + fmt::format_int(upDevice_.kind).str(), fmt::format_int(upDevice_.state).str(), + upDevice_.percentage, upDevice_.icon_name, upDevice_.time_empty, upDevice_.time_full, + upDevice_.temperature, upDevice_.nativePath, upDevice_.model); + } +} + +const Glib::ustring UPower::getText(const upDevice_output &upDevice_, const std::string &format) { + Glib::ustring ret{""}; + if (upDevice_.upDevice) { + std::string timeStr{""}; + switch (upDevice_.state) { + case UP_DEVICE_STATE_CHARGING: + case UP_DEVICE_STATE_PENDING_CHARGE: + timeStr = secondsToString(std::chrono::seconds(upDevice_.time_full)); + break; + case UP_DEVICE_STATE_DISCHARGING: + case UP_DEVICE_STATE_PENDING_DISCHARGE: + timeStr = secondsToString(std::chrono::seconds(upDevice_.time_empty)); + break; + default: + break; + } + + ret = fmt::format( + fmt::runtime(format), + fmt::arg("percentage", std::to_string((int)std::round(upDevice_.percentage)) + '%'), + fmt::arg("time", timeStr), + fmt::arg("temperature", fmt::format("{:-.2g}C", upDevice_.temperature)), + fmt::arg("model", upDevice_.model), fmt::arg("native-path", upDevice_.nativePath)); + } + + return ret; +} + +bool UPower::queryTooltipCb(int x, int y, bool keyboard_tooltip, + const Glib::RefPtr &tooltip) { + std::lock_guard guard{mutex_}; + + // Clear content box + contentBox_.forall([this](Gtk::Widget &wg) { contentBox_.remove(wg); }); + + // Fill content box with the content + for (auto pairDev : devices_) { + // Get device info + getUpDeviceInfo(pairDev.second); + + if (pairDev.second.kind != UpDeviceKind::UP_DEVICE_KIND_UNKNOWN && + pairDev.second.kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER) { + // Make box record + Gtk::Box *boxRec{new Gtk::Box{box_.get_orientation(), tooltip_spacing_}}; + contentBox_.add(*boxRec); + Gtk::Box *boxDev{new Gtk::Box{box_.get_orientation()}}; + Gtk::Box *boxUsr{new Gtk::Box{box_.get_orientation()}}; + boxRec->add(*boxDev); + boxRec->add(*boxUsr); + // Construct device box + // Set icon from kind + std::string iconNameDev{getDeviceIcon(pairDev.second.kind)}; + if (!gtkTheme_->has_icon(iconNameDev)) iconNameDev = (char *)NO_BATTERY.c_str(); + Gtk::Image *iconDev{new Gtk::Image{}}; + iconDev->set_from_icon_name(iconNameDev, Gtk::ICON_SIZE_INVALID); + iconDev->set_pixel_size(iconSize_); + boxDev->add(*iconDev); + // Set label from model + Gtk::Label *labelDev{new Gtk::Label{pairDev.second.model}}; + boxDev->add(*labelDev); + // Construct user box + // Set icon from icon state + if (pairDev.second.icon_name == NULL || !gtkTheme_->has_icon(pairDev.second.icon_name)) + pairDev.second.icon_name = (char *)NO_BATTERY.c_str(); + Gtk::Image *iconTooltip{new Gtk::Image{}}; + iconTooltip->set_from_icon_name(pairDev.second.icon_name, Gtk::ICON_SIZE_INVALID); + iconTooltip->set_pixel_size(iconSize_); + boxUsr->add(*iconTooltip); + // Set markup text + Gtk::Label *labelTooltip{new Gtk::Label{}}; + labelTooltip->set_markup(getText(pairDev.second, tooltipFormat_)); + boxUsr->add(*labelTooltip); + } + } + tooltip->set_custom(contentBox_); + contentBox_.show_all(); + + return true; +} + +} // namespace waybar::modules diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp deleted file mode 100644 index ad4c47326..000000000 --- a/src/modules/upower/upower.cpp +++ /dev/null @@ -1,418 +0,0 @@ -#include "modules/upower/upower.hpp" - -#include - -#include -#include - -#include "gtkmm/tooltip.h" -#include "util/gtk_icon.hpp" - -static const char* getDeviceWarningLevel(UpDeviceLevel level) { - switch (level) { - case UP_DEVICE_LEVEL_CRITICAL: - return "critical"; - case UP_DEVICE_LEVEL_LOW: - return "low"; - default: - return nullptr; - } -} - -namespace waybar::modules::upower { -UPower::UPower(const std::string& id, const Json::Value& config) - : AModule(config, "upower", id), - box_(Gtk::ORIENTATION_HORIZONTAL, 0), - icon_(), - label_(), - devices(), - m_Mutex(), - client(), - showAltText(false) { - // Show icon only when "show-icon" isn't set to false - if (config_["show-icon"].isBool()) { - showIcon = config_["show-icon"].asBool(); - } - - if (showIcon) { - box_.pack_start(icon_); - } - - box_.pack_start(label_); - box_.set_name(name_); - event_box_.add(box_); - - // Device user wants - if (config_["native-path"].isString()) nativePath_ = config_["native-path"].asString(); - // Icon Size - if (config_["icon-size"].isUInt()) { - iconSize = config_["icon-size"].asUInt(); - } - icon_.set_pixel_size(iconSize); - - // Hide If Empty - if (config_["hide-if-empty"].isBool()) { - hideIfEmpty = config_["hide-if-empty"].asBool(); - } - - // Format - if (config_["format"].isString()) { - format = config_["format"].asString(); - } - - // Format Alt - if (config_["format-alt"].isString()) { - format_alt = config_["format-alt"].asString(); - } - - // Tooltip Spacing - if (config_["tooltip-spacing"].isUInt()) { - tooltip_spacing = config_["tooltip-spacing"].asUInt(); - } - - // Tooltip Padding - if (config_["tooltip-padding"].isUInt()) { - tooltip_padding = config_["tooltip-padding"].asUInt(); - } - - // Tooltip - if (config_["tooltip"].isBool()) { - tooltip_enabled = config_["tooltip"].asBool(); - } - box_.set_has_tooltip(tooltip_enabled); - if (tooltip_enabled) { - // Sets the window to use when showing the tooltip - upower_tooltip = std::make_unique(iconSize, tooltip_spacing, tooltip_padding); - box_.set_tooltip_window(*upower_tooltip); - box_.signal_query_tooltip().connect(sigc::mem_fun(*this, &UPower::show_tooltip_callback)); - } - - upowerWatcher_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM, "org.freedesktop.UPower", - G_BUS_NAME_WATCHER_FLAGS_AUTO_START, upowerAppear, - upowerDisappear, this, NULL); - - client = up_client_new_full(NULL, NULL); - if (client == NULL) { - throw std::runtime_error("Unable to create UPower client!"); - } - - // Connect to Login1 PrepareForSleep signal - login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); - if (!login1_connection) { - throw std::runtime_error("Unable to connect to the SYSTEM Bus!..."); - } else { - login1_id = g_dbus_connection_signal_subscribe( - login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager", - "PrepareForSleep", "/org/freedesktop/login1", NULL, G_DBUS_SIGNAL_FLAGS_NONE, - prepareForSleep_cb, this, NULL); - } - - event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &UPower::handleToggle)); - - g_signal_connect(client, "device-added", G_CALLBACK(deviceAdded_cb), this); - g_signal_connect(client, "device-removed", G_CALLBACK(deviceRemoved_cb), this); - - resetDevices(); - setDisplayDevice(); -} - -UPower::~UPower() { - if (displayDevice != NULL) g_object_unref(displayDevice); - if (client != NULL) g_object_unref(client); - if (login1_id > 0) { - g_dbus_connection_signal_unsubscribe(login1_connection, login1_id); - login1_id = 0; - } - g_bus_unwatch_name(upowerWatcher_id); - removeDevices(); -} - -void UPower::deviceAdded_cb(UpClient* client, UpDevice* device, gpointer data) { - UPower* up = static_cast(data); - up->addDevice(device); - up->setDisplayDevice(); - // Update the widget - up->dp.emit(); -} -void UPower::deviceRemoved_cb(UpClient* client, const gchar* objectPath, gpointer data) { - UPower* up = static_cast(data); - up->removeDevice(objectPath); - up->setDisplayDevice(); - // Update the widget - up->dp.emit(); -} -void UPower::deviceNotify_cb(UpDevice* device, GParamSpec* pspec, gpointer data) { - UPower* up = static_cast(data); - // Update the widget - up->dp.emit(); -} -void UPower::prepareForSleep_cb(GDBusConnection* system_bus, const gchar* sender_name, - const gchar* object_path, const gchar* interface_name, - const gchar* signal_name, GVariant* parameters, gpointer data) { - if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)"))) { - gboolean sleeping; - g_variant_get(parameters, "(b)", &sleeping); - - if (!sleeping) { - UPower* up = static_cast(data); - up->resetDevices(); - up->setDisplayDevice(); - } - } -} -void UPower::upowerAppear(GDBusConnection* conn, const gchar* name, const gchar* name_owner, - gpointer data) { - UPower* up = static_cast(data); - up->upowerRunning = true; - up->event_box_.set_visible(true); -} -void UPower::upowerDisappear(GDBusConnection* conn, const gchar* name, gpointer data) { - UPower* up = static_cast(data); - up->upowerRunning = false; - up->event_box_.set_visible(false); -} - -void UPower::removeDevice(const gchar* objectPath) { - std::lock_guard guard(m_Mutex); - if (devices.find(objectPath) != devices.end()) { - UpDevice* device = devices[objectPath]; - if (G_IS_OBJECT(device)) { - g_object_unref(device); - } - devices.erase(objectPath); - } -} - -void UPower::addDevice(UpDevice* device) { - if (G_IS_OBJECT(device)) { - const gchar* objectPath = up_device_get_object_path(device); - - // Due to the device getting cleared after this event is fired, we - // create a new object pointing to its objectPath - gboolean ret; - device = up_device_new(); - ret = up_device_set_object_path_sync(device, objectPath, NULL, NULL); - if (!ret) { - g_object_unref(G_OBJECT(device)); - return; - } - - std::lock_guard guard(m_Mutex); - - if (devices.find(objectPath) != devices.end()) { - UpDevice* device = devices[objectPath]; - if (G_IS_OBJECT(device)) { - g_object_unref(device); - } - devices.erase(objectPath); - } - - g_signal_connect(device, "notify", G_CALLBACK(deviceNotify_cb), this); - devices.emplace(Devices::value_type(objectPath, device)); - } -} - -void UPower::setDisplayDevice() { - std::lock_guard guard(m_Mutex); - - if (nativePath_.empty()) - displayDevice = up_client_get_display_device(client); - else { - g_ptr_array_foreach( - up_client_get_devices2(client), - [](gpointer data, gpointer user_data) { - UpDevice* device{static_cast(data)}; - UPower* thisPtr{static_cast(user_data)}; - gchar* nativePath; - if (!thisPtr->displayDevice) { - g_object_get(device, "native-path", &nativePath, NULL); - if (!std::strcmp(nativePath, thisPtr->nativePath_.c_str())) - thisPtr->displayDevice = device; - } - }, - this); - } - - if (displayDevice) g_signal_connect(displayDevice, "notify", G_CALLBACK(deviceNotify_cb), this); -} - -void UPower::removeDevices() { - std::lock_guard guard(m_Mutex); - if (!devices.empty()) { - auto it = devices.cbegin(); - while (it != devices.cend()) { - if (G_IS_OBJECT(it->second)) { - g_object_unref(it->second); - } - devices.erase(it++); - } - } -} - -/** Removes all devices and adds the current devices */ -void UPower::resetDevices() { - // Removes all devices - removeDevices(); - - // Adds all devices - GPtrArray* newDevices = up_client_get_devices2(client); - for (guint i = 0; i < newDevices->len; i++) { - UpDevice* device = (UpDevice*)g_ptr_array_index(newDevices, i); - if (device && G_IS_OBJECT(device)) addDevice(device); - } - - // Update the widget - dp.emit(); -} - -bool UPower::show_tooltip_callback(int, int, bool, const Glib::RefPtr& tooltip) { - return true; -} - -const std::string UPower::getDeviceStatus(UpDeviceState& state) { - switch (state) { - case UP_DEVICE_STATE_CHARGING: - case UP_DEVICE_STATE_PENDING_CHARGE: - return "charging"; - case UP_DEVICE_STATE_DISCHARGING: - case UP_DEVICE_STATE_PENDING_DISCHARGE: - return "discharging"; - case UP_DEVICE_STATE_FULLY_CHARGED: - return "full"; - case UP_DEVICE_STATE_EMPTY: - return "empty"; - default: - return "unknown-status"; - } -} - -bool UPower::handleToggle(GdkEventButton* const& event) { - std::lock_guard guard(m_Mutex); - showAltText = !showAltText; - return AModule::handleToggle(event); -} - -std::string UPower::timeToString(gint64 time) { - if (time == 0) return ""; - float hours = (float)time / 3600; - float hours_fixed = static_cast(static_cast(hours * 10)) / 10; - float minutes = static_cast(static_cast(hours * 60 * 10)) / 10; - if (hours_fixed >= 1) { - return fmt::format("{H} h", fmt::arg("H", hours_fixed)); - } else { - return fmt::format("{M} min", fmt::arg("M", minutes)); - } -} - -auto UPower::update() -> void { - std::lock_guard guard(m_Mutex); - - // Don't update widget if the UPower service isn't running - if (!upowerRunning) { - if (hideIfEmpty) { - event_box_.set_visible(false); - } - return; - } - - UpDeviceKind kind = UP_DEVICE_KIND_UNKNOWN; - UpDeviceState state = UP_DEVICE_STATE_UNKNOWN; - UpDeviceLevel level = UP_DEVICE_LEVEL_UNKNOWN; - double percentage = 0.0; - gint64 time_empty = 0; - gint64 time_full = 0; - gchar* icon_name{(char*)'\0'}; - std::string percentString{""}; - std::string time_format{""}; - - bool displayDeviceValid{false}; - - if (displayDevice) { - g_object_get(displayDevice, "kind", &kind, "state", &state, "percentage", &percentage, - "icon-name", &icon_name, "time-to-empty", &time_empty, "time-to-full", &time_full, - "warning-level", &level, NULL); - /* Every Device which is handled by Upower and which is not - * UP_DEVICE_KIND_UNKNOWN (0) or UP_DEVICE_KIND_LINE_POWER (1) is a Battery - */ - displayDeviceValid = (kind != UpDeviceKind::UP_DEVICE_KIND_UNKNOWN && - kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER); - } - - // CSS status class - const std::string status = getDeviceStatus(state); - // Remove last status if it exists - if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) { - box_.get_style_context()->remove_class(lastStatus); - } - // Add the new status class to the Box - if (!box_.get_style_context()->has_class(status)) { - box_.get_style_context()->add_class(status); - } - lastStatus = status; - - const char* warning_level = getDeviceWarningLevel(level); - if (lastWarningLevel && box_.get_style_context()->has_class(lastWarningLevel)) { - box_.get_style_context()->remove_class(lastWarningLevel); - } - if (warning_level && !box_.get_style_context()->has_class(warning_level)) { - box_.get_style_context()->add_class(warning_level); - } - lastWarningLevel = warning_level; - - if (devices.size() == 0 && !displayDeviceValid && hideIfEmpty) { - event_box_.set_visible(false); - // Call parent update - AModule::update(); - return; - } - - event_box_.set_visible(true); - - if (displayDeviceValid) { - // Tooltip - if (tooltip_enabled) { - uint tooltipCount = upower_tooltip->updateTooltip(devices); - // Disable the tooltip if there aren't any devices in the tooltip - box_.set_has_tooltip(!devices.empty() && tooltipCount > 0); - } - - // Set percentage - percentString = std::to_string(int(percentage + 0.5)) + "%"; - - // Label format - switch (state) { - case UP_DEVICE_STATE_CHARGING: - case UP_DEVICE_STATE_PENDING_CHARGE: - time_format = timeToString(time_full); - break; - case UP_DEVICE_STATE_DISCHARGING: - case UP_DEVICE_STATE_PENDING_DISCHARGE: - time_format = timeToString(time_empty); - break; - default: - break; - } - } - std::string label_format = - fmt::format(fmt::runtime(showAltText ? format_alt : format), - fmt::arg("percentage", percentString), fmt::arg("time", time_format)); - // Only set the label text if it doesn't only contain spaces - bool onlySpaces = true; - for (auto& character : label_format) { - if (character == ' ') continue; - onlySpaces = false; - break; - } - label_.set_markup(onlySpaces ? "" : label_format); - - // Set icon - if (icon_name == NULL || !DefaultGtkIconThemeWrapper::has_icon(icon_name)) { - icon_name = (char*)"battery-missing-symbolic"; - } - icon_.set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID); - - // Call parent update - AModule::update(); -} - -} // namespace waybar::modules::upower diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp deleted file mode 100644 index 1a653f858..000000000 --- a/src/modules/upower/upower_tooltip.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "modules/upower/upower_tooltip.hpp" - -#include "gtkmm/box.h" -#include "gtkmm/enums.h" -#include "gtkmm/image.h" -#include "gtkmm/label.h" -#include "util/gtk_icon.hpp" - -namespace waybar::modules::upower { -UPowerTooltip::UPowerTooltip(uint iconSize_, uint tooltipSpacing_, uint tooltipPadding_) - : Gtk::Window(), - contentBox(std::make_unique(Gtk::ORIENTATION_VERTICAL)), - iconSize(iconSize_), - tooltipSpacing(tooltipSpacing_), - tooltipPadding(tooltipPadding_) { - // Sets the Tooltip Padding - contentBox->set_margin_top(tooltipPadding); - contentBox->set_margin_bottom(tooltipPadding); - contentBox->set_margin_left(tooltipPadding); - contentBox->set_margin_right(tooltipPadding); - - add(*contentBox); - contentBox->show(); -} - -UPowerTooltip::~UPowerTooltip() {} - -uint UPowerTooltip::updateTooltip(Devices& devices) { - // Removes all old devices - for (auto child : contentBox->get_children()) { - delete child; - } - - uint deviceCount = 0; - // Adds all valid devices - for (auto pair : devices) { - UpDevice* device = pair.second; - std::string objectPath = pair.first; - - if (!G_IS_OBJECT(device)) continue; - - Gtk::Box* box = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, tooltipSpacing); - - UpDeviceKind kind; - double percentage; - gchar* native_path; - gchar* model; - gchar* icon_name; - - g_object_get(device, "kind", &kind, "percentage", &percentage, "native-path", &native_path, - "model", &model, "icon-name", &icon_name, NULL); - - // Skip Line_Power and BAT0 devices - if (kind == UP_DEVICE_KIND_LINE_POWER || native_path == NULL || strlen(native_path) == 0 || - strcmp(native_path, "BAT0") == 0) - continue; - - Gtk::Box* modelBox = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL); - box->add(*modelBox); - // Set device icon - std::string deviceIconName = getDeviceIcon(kind); - Gtk::Image* deviceIcon = new Gtk::Image(); - deviceIcon->set_pixel_size(iconSize); - if (!DefaultGtkIconThemeWrapper::has_icon(deviceIconName)) { - deviceIconName = "battery-missing-symbolic"; - } - deviceIcon->set_from_icon_name(deviceIconName, Gtk::ICON_SIZE_INVALID); - modelBox->add(*deviceIcon); - - // Set model - if (model == NULL) model = (gchar*)""; - Gtk::Label* modelLabel = new Gtk::Label(model); - modelBox->add(*modelLabel); - - Gtk::Box* chargeBox = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL); - box->add(*chargeBox); - - // Set icon - Gtk::Image* icon = new Gtk::Image(); - icon->set_pixel_size(iconSize); - if (icon_name == NULL || !DefaultGtkIconThemeWrapper::has_icon(icon_name)) { - icon_name = (char*)"battery-missing-symbolic"; - } - icon->set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID); - chargeBox->add(*icon); - - // Set percentage - std::string percentString = std::to_string(int(percentage + 0.5)) + "%"; - Gtk::Label* percentLabel = new Gtk::Label(percentString); - chargeBox->add(*percentLabel); - - contentBox->add(*box); - - deviceCount++; - } - - contentBox->show_all(); - return deviceCount; -} - -const std::string UPowerTooltip::getDeviceIcon(UpDeviceKind& kind) { - switch (kind) { - case UP_DEVICE_KIND_LINE_POWER: - return "ac-adapter-symbolic"; - case UP_DEVICE_KIND_BATTERY: - return "battery"; - case UP_DEVICE_KIND_UPS: - return "uninterruptible-power-supply-symbolic"; - case UP_DEVICE_KIND_MONITOR: - return "video-display-symbolic"; - case UP_DEVICE_KIND_MOUSE: - return "input-mouse-symbolic"; - case UP_DEVICE_KIND_KEYBOARD: - return "input-keyboard-symbolic"; - case UP_DEVICE_KIND_PDA: - return "pda-symbolic"; - case UP_DEVICE_KIND_PHONE: - return "phone-symbolic"; - case UP_DEVICE_KIND_MEDIA_PLAYER: - return "multimedia-player-symbolic"; - case UP_DEVICE_KIND_TABLET: - return "computer-apple-ipad-symbolic"; - case UP_DEVICE_KIND_COMPUTER: - return "computer-symbolic"; - case UP_DEVICE_KIND_GAMING_INPUT: - return "input-gaming-symbolic"; - case UP_DEVICE_KIND_PEN: - return "input-tablet-symbolic"; - case UP_DEVICE_KIND_TOUCHPAD: - return "input-touchpad-symbolic"; - case UP_DEVICE_KIND_MODEM: - return "modem-symbolic"; - case UP_DEVICE_KIND_NETWORK: - return "network-wired-symbolic"; - case UP_DEVICE_KIND_HEADSET: - return "audio-headset-symbolic"; - case UP_DEVICE_KIND_HEADPHONES: - return "audio-headphones-symbolic"; - case UP_DEVICE_KIND_OTHER_AUDIO: - case UP_DEVICE_KIND_SPEAKERS: - return "audio-speakers-symbolic"; - case UP_DEVICE_KIND_VIDEO: - return "camera-web-symbolic"; - case UP_DEVICE_KIND_PRINTER: - return "printer-symbolic"; - case UP_DEVICE_KIND_SCANNER: - return "scanner-symbolic"; - case UP_DEVICE_KIND_CAMERA: - return "camera-photo-symbolic"; - case UP_DEVICE_KIND_BLUETOOTH_GENERIC: - return "bluetooth-active-symbolic"; - case UP_DEVICE_KIND_TOY: - case UP_DEVICE_KIND_REMOTE_CONTROL: - case UP_DEVICE_KIND_WEARABLE: - case UP_DEVICE_KIND_LAST: - default: - return "battery-symbolic"; - } -} -} // namespace waybar::modules::upower From e298bf922fbef2f6e3cd3717d3d3710b802fcc4a Mon Sep 17 00:00:00 2001 From: giskard Date: Wed, 8 May 2024 21:00:52 +0800 Subject: [PATCH 216/407] temperature: allow hwmon-path-abs as array --- src/modules/temperature.cpp | 49 ++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index f0629670c..922fa6395 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -1,6 +1,9 @@ #include "modules/temperature.hpp" +#include + #include +#include #if defined(__FreeBSD__) #include @@ -11,26 +14,32 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val #if defined(__FreeBSD__) // FreeBSD uses sysctlbyname instead of read from a file #else - auto& hwmon_path = config_["hwmon-path"]; - if (hwmon_path.isString()) { - file_path_ = hwmon_path.asString(); - } else if (hwmon_path.isArray()) { - // if hwmon_path is an array, loop to find first valid item - for (auto& item : hwmon_path) { - auto path = item.asString(); - if (std::filesystem::exists(path)) { - file_path_ = path; - break; - } - } - } else if (config_["hwmon-path-abs"].isString() && config_["input-filename"].isString()) { - for (const auto& hwmon : - std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())) { - if (hwmon.path().filename().string().starts_with("hwmon")) { - file_path_ = hwmon.path().string() + "/" + config_["input-filename"].asString(); - break; - } - } + auto traverseAsArray = [](const Json::Value& value, auto&& check_set_path) { + if (value.isString()) + check_set_path(value.asString()); + else if (value.isArray()) + for (const auto& item : value) + if (check_set_path(item.asString())) break; + }; + + // if hwmon_path is an array, loop to find first valid item + traverseAsArray(config_["hwmon-path"], [this](const std::string& path) { + if (!std::filesystem::exists(path)) return false; + file_path_ = path; + return true; + }); + + if (file_path_.empty() && config_["input-filename"].isString()) { + // fallback to hwmon_paths-abs + traverseAsArray(config_["hwmon-path-abs"], [this](const std::string& path) { + if (!std::filesystem::is_directory(path)) return false; + return std::ranges::any_of( + std::filesystem::directory_iterator(path), [this](const auto& hwmon) { + if (!hwmon.path().filename().string().starts_with("hwmon")) return false; + file_path_ = hwmon.path().string() + "/" + config_["input-filename"].asString(); + return true; + }); + }); } if (file_path_.empty()) { From b980dab6df22468b27b0e85f9da3343fcdff5bf6 Mon Sep 17 00:00:00 2001 From: giskard Date: Wed, 8 May 2024 21:24:54 +0800 Subject: [PATCH 217/407] doc: update waybar-temperature manual page --- man/waybar-temperature.5.scd | 1 + 1 file changed, 1 insertion(+) diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index ff2168eac..87b60d388 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -25,6 +25,7 @@ Addressed by *temperature* *hwmon-path-abs*: ++ typeof: string ++ The path of the hwmon-directory of the device, e.g. */sys/devices/pci0000:00/0000:00:18.3/hwmon*. (Note that the subdirectory *hwmon/hwmon#*, where *#* is a number is not part of the path!) Has to be used together with *input-filename*. + This can also be an array of strings, for which, it just works like *hwmon-path*. *input-filename*: ++ typeof: string ++ From 884b909e7d4c8f670785ba3fac80ec931922ab50 Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Thu, 9 May 2024 17:28:08 +0200 Subject: [PATCH 218/407] =?UTF-8?q?=E2=9C=A8=20add=20GtkMenu=20to=20the=20?= =?UTF-8?q?AModule=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit You can configure what key launch the menu with the "menu" element in the config, the xml file that describes the menu with the "menu-file" element in the config, and the actions of each buttons with the "menu-actions" field. --- include/AModule.hpp | 5 +++++ src/AModule.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/AModule.hpp b/include/AModule.hpp index 58076655b..903e00b83 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "IModule.hpp" @@ -52,6 +53,10 @@ class AModule : public IModule { std::vector pid_; gdouble distance_scrolled_y_; gdouble distance_scrolled_x_; + GObject* menu_; + std::map submenus_; + std::map menuActionsMap_; + static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data); std::map eventActionMap_; static const inline std::map, std::string> eventMap_{ {std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"}, diff --git a/src/AModule.cpp b/src/AModule.cpp index 915c86036..709a1e389 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -27,6 +27,24 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: else spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); } + // If a GTKMenu is requested in the config + if (config_["menu"].isString()) { + // Create the GTKMenu widget + GtkBuilder* builder = gtk_builder_new_from_file(config_["menu-file"].asString().c_str()); + menu_ = gtk_builder_get_object(builder, "menu"); + submenus_ = std::map(); + menuActionsMap_ = std::map(); + // Linking actions to the GTKMenu based on + for (Json::Value::const_iterator it = config_["menu-actions"].begin(); it != config_["menu-actions"].end(); ++it) { + std::string key = it.key().asString(); + submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); + menuActionsMap_[key] = it->asString(); + g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), (gpointer) menuActionsMap_[key].c_str()); + + } + // Enable click + enable_click = true; + } event_box_.signal_enter_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseEnter)); event_box_.signal_leave_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave)); @@ -66,6 +84,10 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: } } +void AModule::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) { + waybar::util::command::res res = waybar::util::command::exec((char*) data, "TLP"); +} + AModule::~AModule() { for (const auto& pid : pid_) { if (pid != -1) { @@ -133,6 +155,14 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) { format = rec->second; } + + // Check if the event is the one specified for the "menu" option + if (rec->second == config_["menu"].asString()) { + // Popup the menu + gtk_widget_show_all(GTK_WIDGET(menu_)); + gtk_menu_popup_at_pointer (GTK_MENU(menu_), reinterpret_cast(e)); + + } // Second call user scripts if (!format.empty()) { if (config_[format].isString()) From 3b87b830761615df7ed74b3877405dddb48255dc Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Thu, 9 May 2024 18:34:26 +0200 Subject: [PATCH 219/407] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20move=20GMenu=20to?= =?UTF-8?q?=20ALabel=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/ALabel.hpp | 4 ++++ include/AModule.hpp | 5 +---- src/ALabel.cpp | 22 +++++++++++++++++++++- src/AModule.cpp | 22 ---------------------- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/include/ALabel.hpp b/include/ALabel.hpp index 888c65a80..8855a3edb 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -27,6 +27,10 @@ class ALabel : public AModule { bool handleToggle(GdkEventButton *const &e) override; virtual std::string getState(uint8_t value, bool lesser = false); + + std::map submenus_; + std::map menuActionsMap_; + static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data); }; } // namespace waybar diff --git a/include/AModule.hpp b/include/AModule.hpp index 903e00b83..961f2a7f9 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -45,6 +45,7 @@ class AModule : public IModule { virtual bool handleMouseLeave(GdkEventCrossing *const &ev); virtual bool handleScroll(GdkEventScroll *); virtual bool handleRelease(GdkEventButton *const &ev); + GObject* menu_; private: bool handleUserEvent(GdkEventButton *const &ev); @@ -53,10 +54,6 @@ class AModule : public IModule { std::vector pid_; gdouble distance_scrolled_y_; gdouble distance_scrolled_x_; - GObject* menu_; - std::map submenus_; - std::map menuActionsMap_; - static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data); std::map eventActionMap_; static const inline std::map, std::string> eventMap_{ {std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"}, diff --git a/src/ALabel.cpp b/src/ALabel.cpp index b8d39df5f..4befb5b58 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -9,7 +9,7 @@ namespace waybar { ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id, const std::string& format, uint16_t interval, bool ellipsize, bool enable_click, bool enable_scroll) - : AModule(config, name, id, config["format-alt"].isString() || enable_click, enable_scroll), + : AModule(config, name, id, config["format-alt"].isString() || config["menu"].isString() || enable_click, enable_scroll), format_(config_["format"].isString() ? config_["format"].asString() : format), interval_(config_["interval"] == "once" ? std::chrono::seconds::max() @@ -51,6 +51,22 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st } } + // If a GTKMenu is requested in the config + if (config_["menu"].isString()) { + // Create the GTKMenu widget + GtkBuilder* builder = gtk_builder_new_from_file(config_["menu-file"].asString().c_str()); + menu_ = gtk_builder_get_object(builder, "menu"); + submenus_ = std::map(); + menuActionsMap_ = std::map(); + // Linking actions to the GTKMenu based on + for (Json::Value::const_iterator it = config_["menu-actions"].begin(); it != config_["menu-actions"].end(); ++it) { + std::string key = it.key().asString(); + submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); + menuActionsMap_[key] = it->asString(); + g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), (gpointer) menuActionsMap_[key].c_str()); + } + } + if (config_["justify"].isString()) { auto justify_str = config_["justify"].asString(); if (justify_str == "left") { @@ -125,6 +141,10 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { return AModule::handleToggle(e); } +void ALabel::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) { + waybar::util::command::res res = waybar::util::command::exec((char*) data, "GtkMenu"); +} + std::string ALabel::getState(uint8_t value, bool lesser) { if (!config_["states"].isObject()) { return ""; diff --git a/src/AModule.cpp b/src/AModule.cpp index 709a1e389..b2cf2fca9 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -27,24 +27,6 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: else spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); } - // If a GTKMenu is requested in the config - if (config_["menu"].isString()) { - // Create the GTKMenu widget - GtkBuilder* builder = gtk_builder_new_from_file(config_["menu-file"].asString().c_str()); - menu_ = gtk_builder_get_object(builder, "menu"); - submenus_ = std::map(); - menuActionsMap_ = std::map(); - // Linking actions to the GTKMenu based on - for (Json::Value::const_iterator it = config_["menu-actions"].begin(); it != config_["menu-actions"].end(); ++it) { - std::string key = it.key().asString(); - submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); - menuActionsMap_[key] = it->asString(); - g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), (gpointer) menuActionsMap_[key].c_str()); - - } - // Enable click - enable_click = true; - } event_box_.signal_enter_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseEnter)); event_box_.signal_leave_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave)); @@ -84,10 +66,6 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: } } -void AModule::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) { - waybar::util::command::res res = waybar::util::command::exec((char*) data, "TLP"); -} - AModule::~AModule() { for (const auto& pid : pid_) { if (pid != -1) { From 21751b2faab327e839f3d0d24960539679220eb4 Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Thu, 9 May 2024 20:59:25 +0200 Subject: [PATCH 220/407] =?UTF-8?q?=F0=9F=8E=A8=20clang-tidy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/ALabel.hpp | 4 ++-- include/AModule.hpp | 4 ++-- src/ALabel.cpp | 12 ++++++++---- src/AModule.cpp | 3 +-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/include/ALabel.hpp b/include/ALabel.hpp index 8855a3edb..a1aae9da9 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -28,9 +28,9 @@ class ALabel : public AModule { bool handleToggle(GdkEventButton *const &e) override; virtual std::string getState(uint8_t value, bool lesser = false); - std::map submenus_; + std::map submenus_; std::map menuActionsMap_; - static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data); + static void handleGtkMenuEvent(GtkMenuItem *menuitem, gpointer data); }; } // namespace waybar diff --git a/include/AModule.hpp b/include/AModule.hpp index 961f2a7f9..90f34187d 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -2,9 +2,9 @@ #include #include +#include #include #include -#include #include "IModule.hpp" @@ -45,7 +45,7 @@ class AModule : public IModule { virtual bool handleMouseLeave(GdkEventCrossing *const &ev); virtual bool handleScroll(GdkEventScroll *); virtual bool handleRelease(GdkEventButton *const &ev); - GObject* menu_; + GObject *menu_; private: bool handleUserEvent(GdkEventButton *const &ev); diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 4befb5b58..568506173 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -9,7 +9,9 @@ namespace waybar { ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id, const std::string& format, uint16_t interval, bool ellipsize, bool enable_click, bool enable_scroll) - : AModule(config, name, id, config["format-alt"].isString() || config["menu"].isString() || enable_click, enable_scroll), + : AModule(config, name, id, + config["format-alt"].isString() || config["menu"].isString() || enable_click, + enable_scroll), format_(config_["format"].isString() ? config_["format"].asString() : format), interval_(config_["interval"] == "once" ? std::chrono::seconds::max() @@ -59,11 +61,13 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st submenus_ = std::map(); menuActionsMap_ = std::map(); // Linking actions to the GTKMenu based on - for (Json::Value::const_iterator it = config_["menu-actions"].begin(); it != config_["menu-actions"].end(); ++it) { + for (Json::Value::const_iterator it = config_["menu-actions"].begin(); + it != config_["menu-actions"].end(); ++it) { std::string key = it.key().asString(); submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); menuActionsMap_[key] = it->asString(); - g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), (gpointer) menuActionsMap_[key].c_str()); + g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), + (gpointer)menuActionsMap_[key].c_str()); } } @@ -142,7 +146,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { } void ALabel::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) { - waybar::util::command::res res = waybar::util::command::exec((char*) data, "GtkMenu"); + waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu"); } std::string ALabel::getState(uint8_t value, bool lesser) { diff --git a/src/AModule.cpp b/src/AModule.cpp index b2cf2fca9..77b82ceea 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -138,8 +138,7 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) { if (rec->second == config_["menu"].asString()) { // Popup the menu gtk_widget_show_all(GTK_WIDGET(menu_)); - gtk_menu_popup_at_pointer (GTK_MENU(menu_), reinterpret_cast(e)); - + gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast(e)); } // Second call user scripts if (!format.empty()) { From 5fe99ea0e16b1e5c8986edae9abd5b106ffc3bb6 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 10 May 2024 00:00:47 +0300 Subject: [PATCH 221/407] Upower. Fix segmentation fault Signed-off-by: Viktar Lukashonak --- include/modules/upower.hpp | 1 + src/modules/upower.cpp | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/modules/upower.hpp b/include/modules/upower.hpp index b4450df26..d3499232d 100644 --- a/include/modules/upower.hpp +++ b/include/modules/upower.hpp @@ -49,6 +49,7 @@ class UPower final : public AIconLabel { Glib::ustring label_markup_; std::mutex mutex_; Glib::RefPtr gtkTheme_; + bool sleeping_; // Technical functions void addDevice(UpDevice *); diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 73c0af082..7f0b34464 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -7,7 +7,7 @@ namespace waybar::modules { UPower::UPower(const std::string &id, const Json::Value &config) - : AIconLabel(config, "upower", id, "{percentage}", 0, true, true, true) { + : AIconLabel(config, "upower", id, "{percentage}", 0, true, true, true), sleeping_{false} { box_.set_name(name_); box_.set_spacing(0); box_.set_has_tooltip(AModule::tooltipEnabled()); @@ -185,7 +185,7 @@ days: \"{3}\", strRet: \"{4}\"", auto UPower::update() -> void { std::lock_guard guard{mutex_}; // Don't update widget if the UPower service isn't running - if (!upRunning_) { + if (!upRunning_ || sleeping_) { if (hideIfEmpty_) box_.hide(); return; } @@ -262,9 +262,11 @@ void UPower::prepareForSleep_cb(const Glib::RefPtr &conne if (!sleeping.get()) { resetDevices(); setDisplayDevice(); + sleeping_ = false; // Update the widget dp.emit(); - } + } else + sleeping_ = true; } } @@ -355,6 +357,9 @@ void UPower::setDisplayDevice() { std::lock_guard guard{mutex_}; if (nativePath_.empty()) { + // Unref current upDevice + if (upDevice_.upDevice != NULL) g_object_unref(upDevice_.upDevice); + upDevice_.upDevice = up_client_get_display_device(upClient_); getUpDeviceInfo(upDevice_); } else { @@ -367,7 +372,7 @@ void UPower::setDisplayDevice() { thisPtr->getUpDeviceInfo(upDevice); if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { // Unref current upDevice - if (thisPtr->upDevice_.upDevice) g_object_unref(thisPtr->upDevice_.upDevice); + if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); // Reassign new upDevice thisPtr->upDevice_ = upDevice; } @@ -375,12 +380,12 @@ void UPower::setDisplayDevice() { this); } - if (upDevice_.upDevice) + if (upDevice_.upDevice != NULL) g_signal_connect(upDevice_.upDevice, "notify", G_CALLBACK(deviceNotify_cb), this); } void UPower::getUpDeviceInfo(upDevice_output &upDevice_) { - if (upDevice_.upDevice && G_IS_OBJECT(upDevice_.upDevice)) { + if (upDevice_.upDevice != NULL && G_IS_OBJECT(upDevice_.upDevice)) { g_object_get(upDevice_.upDevice, "kind", &upDevice_.kind, "state", &upDevice_.state, "percentage", &upDevice_.percentage, "icon-name", &upDevice_.icon_name, "time-to-empty", &upDevice_.time_empty, "time-to-full", &upDevice_.time_full, @@ -398,7 +403,7 @@ native_path: \"{7}\". model: \"{8}\"", const Glib::ustring UPower::getText(const upDevice_output &upDevice_, const std::string &format) { Glib::ustring ret{""}; - if (upDevice_.upDevice) { + if (upDevice_.upDevice != NULL) { std::string timeStr{""}; switch (upDevice_.state) { case UP_DEVICE_STATE_CHARGING: From ff84c6dbafe25e0fb85417c4c65061e942c4f21d Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 11 May 2024 15:58:54 +0200 Subject: [PATCH 222/407] fix debian dockerfile --- Dockerfiles/debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 0745935ec..f479062d6 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -34,7 +34,7 @@ RUN apt update && \ libudev-dev \ libupower-glib-dev \ libwayland-dev \ - libwireplumber-0.4-dev \ + libwireplumber-0.5-dev \ libxkbcommon-dev \ libxkbregistry-dev \ locales \ From 49afcdf71589ea1f321631b6de2070f85eb583b4 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 11 May 2024 16:16:02 +0200 Subject: [PATCH 223/407] Add GitHub action for nightly Dockerfiles building --- .github/workflows/docker.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..12927fb0e --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,31 @@ +name: Build and Push Docker Image + +on: + schedule: + # run every night at midnight + - cron: '0 0 * * *' + +jobs: + build-and-push: + runs-on: ubuntu-latest + strategy: + matrix: + os: [ 'alpine', 'archlinux', 'debian', 'fedora', 'gentoo', 'opensuse' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: Dockerfiles/${{ matrix.os }} + push: true + tags: alexays/${{ matrix.os }}:latest \ No newline at end of file From 1828a94b6c2e2bb7301e88639a4634b8d8ba5639 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 11 May 2024 15:58:45 +0200 Subject: [PATCH 224/407] clang-tidy: comment case styling options --- .clang-tidy | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index f74eae653..3d4cf2604 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -15,15 +15,15 @@ Checks: > -readability-redundant-member-init, -readability-redundant-string-init, -readability-identifier-length -CheckOptions: - - { key: readability-identifier-naming.NamespaceCase, value: lower_case } - - { key: readability-identifier-naming.ClassCase, value: CamelCase } - - { key: readability-identifier-naming.StructCase, value: CamelCase } - - { key: readability-identifier-naming.FunctionCase, value: camelBack } - - { key: readability-identifier-naming.VariableCase, value: camelBack } - - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack } - - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ } - - { key: readability-identifier-naming.EnumCase, value: CamelCase } - - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } - - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } - - { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE } +# CheckOptions: +# - { key: readability-identifier-naming.NamespaceCase, value: lower_case } +# - { key: readability-identifier-naming.ClassCase, value: CamelCase } +# - { key: readability-identifier-naming.StructCase, value: CamelCase } +# - { key: readability-identifier-naming.FunctionCase, value: camelBack } +# - { key: readability-identifier-naming.VariableCase, value: camelBack } +# - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack } +# - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ } +# - { key: readability-identifier-naming.EnumCase, value: CamelCase } +# - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } +# - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } +# - { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE } From e27488b48c98c97ba28725f07b4501490e742d75 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 11 May 2024 15:58:12 +0200 Subject: [PATCH 225/407] clang-tidy improvements in privacy module --- include/modules/privacy/privacy.hpp | 3 --- include/modules/privacy/privacy_item.hpp | 3 --- src/modules/privacy/privacy.cpp | 22 +++++++++++----------- src/modules/privacy/privacy_item.cpp | 5 +---- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/include/modules/privacy/privacy.hpp b/include/modules/privacy/privacy.hpp index b8e767686..d7656d312 100644 --- a/include/modules/privacy/privacy.hpp +++ b/include/modules/privacy/privacy.hpp @@ -1,10 +1,7 @@ #pragma once -#include -#include #include -#include "ALabel.hpp" #include "gtkmm/box.h" #include "modules/privacy/privacy_item.hpp" #include "util/pipewire/pipewire_backend.hpp" diff --git a/include/modules/privacy/privacy_item.hpp b/include/modules/privacy/privacy_item.hpp index a0e3038b5..836bd994c 100644 --- a/include/modules/privacy/privacy_item.hpp +++ b/include/modules/privacy/privacy_item.hpp @@ -2,9 +2,6 @@ #include -#include -#include -#include #include #include "gtkmm/box.h" diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index b7eede754..d3e3d4b2f 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -6,7 +6,6 @@ #include #include "AModule.hpp" -#include "gtkmm/image.h" #include "modules/privacy/privacy_item.hpp" namespace waybar::modules::privacy { @@ -46,30 +45,29 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st // Initialize each privacy module Json::Value modules = config_["modules"]; // Add Screenshare and Mic usage as default modules if none are specified - if (!modules.isArray() || modules.size() == 0) { + if (!modules.isArray() || modules.empty()) { modules = Json::Value(Json::arrayValue); - for (auto& type : {"screenshare", "audio-in"}) { + for (const auto& type : {"screenshare", "audio-in"}) { Json::Value obj = Json::Value(Json::objectValue); obj["type"] = type; modules.append(obj); } } - for (uint i = 0; i < modules.size(); i++) { - const Json::Value& module_config = modules[i]; + for (const auto& module_config : modules) { if (!module_config.isObject() || !module_config["type"].isString()) continue; const std::string type = module_config["type"].asString(); if (type == "screenshare") { - auto item = + auto* item = Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_VIDEO_INPUT, &nodes_screenshare, pos, iconSize, transition_duration); box_.add(*item); } else if (type == "audio-in") { - auto item = + auto* item = Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_INPUT, &nodes_audio_in, pos, iconSize, transition_duration); box_.add(*item); } else if (type == "audio-out") { - auto item = + auto* item = Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_OUTPUT, &nodes_audio_out, pos, iconSize, transition_duration); box_.add(*item); @@ -117,11 +115,13 @@ void Privacy::onPrivacyNodesChanged() { auto Privacy::update() -> void { mutex_.lock(); - bool screenshare, audio_in, audio_out; + bool screenshare = false; + bool audio_in = false; + bool audio_out = false; for (Gtk::Widget* widget : box_.get_children()) { - PrivacyItem* module = dynamic_cast(widget); - if (!module) continue; + auto* module = dynamic_cast(widget); + if (module == nullptr) continue; switch (module->privacy_type) { case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: screenshare = !nodes_screenshare.empty(); diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index c5b617d51..a38b95a4a 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -1,14 +1,11 @@ #include "modules/privacy/privacy_item.hpp" #include -#include -#include "AModule.hpp" #include "glibmm/main.h" #include "gtkmm/label.h" #include "gtkmm/revealer.h" #include "gtkmm/tooltip.h" -#include "sigc++/adaptors/bind.h" #include "util/pipewire/privacy_node_info.hpp" namespace waybar::modules::privacy { @@ -89,7 +86,7 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac void PrivacyItem::update_tooltip() { // Removes all old nodes - for (auto child : tooltip_window.get_children()) { + for (auto *child : tooltip_window.get_children()) { delete child; } From ba8a88acfb0103b4762e3598525377c744b79ac1 Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Tue, 14 May 2024 08:16:10 +0200 Subject: [PATCH 226/407] Do not try to compare a string that may be a null-pointer --- src/modules/upower.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 7f0b34464..69e5b79e5 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -370,6 +370,8 @@ void UPower::setDisplayDevice() { auto thisPtr{static_cast(user_data)}; upDevice.upDevice = static_cast(data); thisPtr->getUpDeviceInfo(upDevice); + if (upDevice.nativePath == nullptr) + return; if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { // Unref current upDevice if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); From 3c075bcc53cd7ff1744dcb23e6c3345b28603af0 Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Tue, 14 May 2024 08:26:44 +0200 Subject: [PATCH 227/407] Fixed formatting --- src/modules/upower.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 69e5b79e5..fbbd6c4de 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -370,8 +370,7 @@ void UPower::setDisplayDevice() { auto thisPtr{static_cast(user_data)}; upDevice.upDevice = static_cast(data); thisPtr->getUpDeviceInfo(upDevice); - if (upDevice.nativePath == nullptr) - return; + if (upDevice.nativePath == nullptr) return; if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { // Unref current upDevice if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); From 6413f25b8d3c274f1238090268c7242bc724586a Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Tue, 14 May 2024 10:13:22 +0200 Subject: [PATCH 228/407] Add config option to select UPower device based on device model. --- include/modules/upower.hpp | 1 + man/waybar-upower.5.scd | 6 ++++++ src/modules/upower.cpp | 23 +++++++++++++++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/include/modules/upower.hpp b/include/modules/upower.hpp index d3499232d..60a276dbf 100644 --- a/include/modules/upower.hpp +++ b/include/modules/upower.hpp @@ -45,6 +45,7 @@ class UPower final : public AIconLabel { // Technical variables std::string nativePath_; + std::string model_; std::string lastStatus_; Glib::ustring label_markup_; std::mutex mutex_; diff --git a/man/waybar-upower.5.scd b/man/waybar-upower.5.scd index 5e2a8eb85..2ae5f17ed 100644 --- a/man/waybar-upower.5.scd +++ b/man/waybar-upower.5.scd @@ -17,6 +17,12 @@ compatible devices in the tooltip. The battery to monitor. Refer to the https://upower.freedesktop.org/docs/UpDevice.html#UpDevice--native-path ++ Can be obtained using `upower --dump` +*model*: ++ + typeof: string ++ + default: ++ + The battery to monitor, based on the model. (this option is ignored if *native-path* is given). ++ + Can be obtained using `upower --dump` + *icon-size*: ++ typeof: integer ++ default: 20 ++ diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index fbbd6c4de..b13dcc443 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -29,6 +29,8 @@ UPower::UPower(const std::string &id, const Json::Value &config) if (!showIcon_) box_.remove(image_); // Device user wants if (config_["native-path"].isString()) nativePath_ = config_["native-path"].asString(); + // Device model user wants + if (config_["model"].isString()) model_ = config_["model"].asString(); // Hide If Empty if (config_["hide-if-empty"].isBool()) hideIfEmpty_ = config_["hide-if-empty"].asBool(); @@ -356,13 +358,13 @@ void UPower::resetDevices() { void UPower::setDisplayDevice() { std::lock_guard guard{mutex_}; - if (nativePath_.empty()) { + if (nativePath_.empty() && model_.empty()) { // Unref current upDevice if (upDevice_.upDevice != NULL) g_object_unref(upDevice_.upDevice); upDevice_.upDevice = up_client_get_display_device(upClient_); getUpDeviceInfo(upDevice_); - } else { + } else if (!nativePath_.empty()) { g_ptr_array_foreach( up_client_get_devices2(upClient_), [](gpointer data, gpointer user_data) { @@ -379,6 +381,23 @@ void UPower::setDisplayDevice() { } }, this); + } else { // if `nativePath_` is empty, but `model_` is not. + g_ptr_array_foreach( + up_client_get_devices2(upClient_), + [](gpointer data, gpointer user_data) { + upDevice_output upDevice; + auto thisPtr {static_cast(user_data)}; + upDevice.upDevice = static_cast(data); + thisPtr->getUpDeviceInfo(upDevice); + if (upDevice.model == nullptr) return; + if (0 == std::strcmp(upDevice.model, thisPtr->model_.c_str())) { + // Unref current upDevice + if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); + // Reassign new upDevice + thisPtr->upDevice_ = upDevice; + } + }, + this); } if (upDevice_.upDevice != NULL) From 28ef5b7db26c3d17347fde18e05c49ea3fd3b54c Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Tue, 14 May 2024 10:21:24 +0200 Subject: [PATCH 229/407] Fix formatting --- src/modules/upower.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index b13dcc443..6a3b90915 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -381,12 +381,12 @@ void UPower::setDisplayDevice() { } }, this); - } else { // if `nativePath_` is empty, but `model_` is not. + } else { // if `nativePath_` is empty, but `model_` is not. g_ptr_array_foreach( up_client_get_devices2(upClient_), [](gpointer data, gpointer user_data) { upDevice_output upDevice; - auto thisPtr {static_cast(user_data)}; + auto thisPtr{static_cast(user_data)}; upDevice.upDevice = static_cast(data); thisPtr->getUpDeviceInfo(upDevice); if (upDevice.model == nullptr) return; From d2a719d67c5427dffc3e431c41b96b60399576e6 Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Thu, 16 May 2024 12:37:53 +0200 Subject: [PATCH 230/407] Redo to minimize code duplication. --- src/modules/upower.cpp | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 6a3b90915..552495f85 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -364,7 +364,7 @@ void UPower::setDisplayDevice() { upDevice_.upDevice = up_client_get_display_device(upClient_); getUpDeviceInfo(upDevice_); - } else if (!nativePath_.empty()) { + } else { g_ptr_array_foreach( up_client_get_devices2(upClient_), [](gpointer data, gpointer user_data) { @@ -372,30 +372,22 @@ void UPower::setDisplayDevice() { auto thisPtr{static_cast(user_data)}; upDevice.upDevice = static_cast(data); thisPtr->getUpDeviceInfo(upDevice); - if (upDevice.nativePath == nullptr) return; - if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { - // Unref current upDevice - if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); - // Reassign new upDevice - thisPtr->upDevice_ = upDevice; - } - }, - this); - } else { // if `nativePath_` is empty, but `model_` is not. - g_ptr_array_foreach( - up_client_get_devices2(upClient_), - [](gpointer data, gpointer user_data) { - upDevice_output upDevice; - auto thisPtr{static_cast(user_data)}; - upDevice.upDevice = static_cast(data); - thisPtr->getUpDeviceInfo(upDevice); - if (upDevice.model == nullptr) return; - if (0 == std::strcmp(upDevice.model, thisPtr->model_.c_str())) { - // Unref current upDevice - if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); - // Reassign new upDevice - thisPtr->upDevice_ = upDevice; + upDevice_output displayDevice{NULL}; + if (!thisPtr->nativePath_.empty()) { + if (upDevice.nativePath == nullptr) return; + if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { + displayDevice = upDevice; + } + } else { + if (upDevice.model == nullptr) return; + if (0 == std::strcmp(upDevice.model, thisPtr->model_.c_str())) { + displayDevice = upDevice; + } } + // Unref current upDevice + if (displayDevice.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); + // Reassign new upDevice + thisPtr->upDevice_ = displayDevice; }, this); } From b288fdf8c1f0ac3b8a9f2e3995fc3b59201daaf5 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 17 May 2024 20:17:33 +0300 Subject: [PATCH 231/407] ISSUE#2240. Clock Gtk::Label as a calendar tooltip Signed-off-by: Viktar Lukashonak --- include/modules/clock.hpp | 8 +++-- src/modules/clock.cpp | 74 +++++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 9e10fb858..c212ec8b9 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -21,10 +21,12 @@ class Clock final : public ALabel { auto doAction(const std::string&) -> void override; private: - const std::locale locale_; + const std::locale m_locale_; // tooltip - const std::string tlpFmt_; - std::string tlpText_{""}; // tooltip text to print + const std::string m_tlpFmt_; + std::string m_tlpText_{""}; // tooltip text to print + const Glib::RefPtr m_tooltip_; // tooltip as a separate Gtk::Label + bool query_tlp_cb(int, int, bool, const Glib::RefPtr& tooltip); // Calendar const bool cldInTooltip_; // calendar in tooltip /* diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 2901c0d12..9f279711f 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,5 +1,6 @@ #include "modules/clock.hpp" +#include #include #include @@ -18,13 +19,14 @@ namespace fmt_lib = waybar::util::date::format; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), - locale_{std::locale(config_["locale"].isString() ? config_["locale"].asString() : "")}, - tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""}, - cldInTooltip_{tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, - tzInTooltip_{tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, + m_locale_{std::locale(config_["locale"].isString() ? config_["locale"].asString() : "")}, + m_tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""}, + m_tooltip_{new Gtk::Label()}, + cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, + tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, tzCurrIdx_{0}, - ordInTooltip_{tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { - tlpText_ = tlpFmt_; + ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { + m_tlpText_ = m_tlpFmt_; if (config_["timezones"].isArray() && !config_["timezones"].empty()) { for (const auto& zone_name : config_["timezones"]) { @@ -124,17 +126,26 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } } + label_.set_has_tooltip(true); + label_.signal_query_tooltip().connect(sigc::mem_fun(*this, &Clock::query_tlp_cb)); + thread_ = [this] { dp.emit(); thread_.sleep_for(interval_ - system_clock::now().time_since_epoch() % interval_); }; } +bool waybar::modules::Clock::query_tlp_cb(int, int, bool, + const Glib::RefPtr& tooltip) { + tooltip->set_custom(*m_tooltip_.get()); + return true; +} + auto waybar::modules::Clock::update() -> void { const auto* tz = tzList_[tzCurrIdx_] != nullptr ? tzList_[tzCurrIdx_] : local_zone(); const zoned_time now{tz, floor(system_clock::now())}; - label_.set_markup(fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(now))); + label_.set_markup(fmt_lib::vformat(m_locale_, format_, fmt_lib::make_format_args(now))); if (tooltipEnabled()) { const year_month_day today{floor(now.get_local_time())}; @@ -147,18 +158,19 @@ auto waybar::modules::Clock::update() -> void { if (ordInTooltip_) ordText_ = get_ordinal_date(shiftedDay); if (tzInTooltip_ || cldInTooltip_ || ordInTooltip_) { // std::vformat doesn't support named arguments. - tlpText_ = std::regex_replace(tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_); - tlpText_ = - std::regex_replace(tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); - tlpText_ = - std::regex_replace(tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); + m_tlpText_ = + std::regex_replace(m_tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_); + m_tlpText_ = + std::regex_replace(m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); + m_tlpText_ = + std::regex_replace(m_tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); } else { - tlpText_ = tlpFmt_; + m_tlpText_ = m_tlpFmt_; } - tlpText_ = fmt_lib::vformat(locale_, tlpText_, fmt_lib::make_format_args(shiftedNow)); - - label_.set_tooltip_markup(tlpText_); + m_tlpText_ = fmt_lib::vformat(m_locale_, m_tlpText_, fmt_lib::make_format_args(shiftedNow)); + m_tooltip_->set_markup(m_tlpText_); + label_.trigger_tooltip_query(); } ALabel::update(); @@ -172,7 +184,7 @@ auto waybar::modules::Clock::getTZtext(sys_seconds now) -> std::string { if (static_cast(tz_idx) == tzCurrIdx_) continue; const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : local_zone(); auto zt{zoned_time{tz, now}}; - os << fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(zt)) << '\n'; + os << fmt_lib::vformat(m_locale_, format_, fmt_lib::make_format_args(zt)) << '\n'; } return os.str(); @@ -190,13 +202,13 @@ auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsi } auto getCalendarLine(const year_month_day& currDate, const year_month ym, const unsigned line, - const weekday& firstdow, const std::locale* const locale_) -> std::string { + const weekday& firstdow, const std::locale* const m_locale_) -> std::string { std::ostringstream os; switch (line) { // Print month and year title case 0: { - os << date::format(*locale_, "{:L%B %Y}", ym); + os << date::format(*m_locale_, "{:L%B %Y}", ym); break; } // Print weekday names title @@ -206,7 +218,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const Glib::ustring::size_type wdLen{0}; int clen{0}; do { - wdStr = date::format(*locale_, "{:L%a}", wd); + wdStr = date::format(*m_locale_, "{:L%a}", wd); clen = ustring_clen(wdStr); wdLen = wdStr.length(); while (clen > 2) { @@ -229,7 +241,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const os << std::string((wd - firstdow).count() * 3, ' '); if (currDate != ym / d) - os << date::format(*locale_, "{:L%e}", d); + os << date::format(*m_locale_, "{:L%e}", d); else os << "{today}"; @@ -237,7 +249,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const ++d; if (currDate != ym / d) - os << date::format(*locale_, " {:L%e}", d); + os << date::format(*m_locale_, " {:L%e}", d); else os << " {today}"; } @@ -252,13 +264,13 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const auto wd{firstdow}; if (currDate != ym / d) - os << date::format(*locale_, "{:L%e}", d); + os << date::format(*m_locale_, "{:L%e}", d); else os << "{today}"; while (++wd != firstdow && ++d <= dlast) { if (currDate != ym / d) - os << date::format(*locale_, " {:L%e}", d); + os << date::format(*m_locale_, " {:L%e}", d); else os << " {today}"; } @@ -328,7 +340,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea if (line > 1) { if (line < ml[(unsigned)ymTmp.month() - 1u]) { os << fmt_lib::vformat( - locale_, fmtMap_[4], + m_locale_, fmtMap_[4], fmt_lib::make_format_args( (line == 2) ? static_cast( @@ -344,7 +356,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea os << Glib::ustring::format((cldWPos_ != WS::LEFT || line == 0) ? std::left : std::right, std::setfill(L' '), std::setw(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)), - getCalendarLine(today, ymTmp, line, firstdow, &locale_)); + getCalendarLine(today, ymTmp, line, firstdow, &m_locale_)); // Week numbers on the right if (cldWPos_ == WS::RIGHT && line > 0) { @@ -352,7 +364,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea if (line < ml[(unsigned)ymTmp.month() - 1u]) os << ' ' << fmt_lib::vformat( - locale_, fmtMap_[4], + m_locale_, fmtMap_[4], fmt_lib::make_format_args( (line == 2) ? static_cast( zoned_seconds{tz, local_days{ymTmp / 1}}) @@ -368,7 +380,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea // Apply user's formats if (line < 2) tmp << fmt_lib::vformat( - locale_, fmtMap_[line], + m_locale_, fmtMap_[line], fmt_lib::make_format_args(static_cast(os.str()))); else tmp << os.str(); @@ -380,10 +392,10 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea } os << std::regex_replace( - fmt_lib::vformat(locale_, fmtMap_[2], + fmt_lib::vformat(m_locale_, fmtMap_[2], fmt_lib::make_format_args(static_cast(tmp.str()))), std::regex("\\{today\\}"), - fmt_lib::vformat(locale_, fmtMap_[3], + fmt_lib::vformat(m_locale_, fmtMap_[3], fmt_lib::make_format_args( static_cast(date::format("{:L%e}", d))))); @@ -450,7 +462,7 @@ using deleting_unique_ptr = std::unique_ptr>; auto waybar::modules::Clock::first_day_of_week() -> weekday { #ifdef HAVE_LANGINFO_1STDAY deleting_unique_ptr::type, freelocale> posix_locale{ - newlocale(LC_ALL, locale_.name().c_str(), nullptr)}; + newlocale(LC_ALL, m_locale_.name().c_str(), nullptr)}; if (posix_locale) { const auto i{(int)((std::intptr_t)nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get()))}; const weekday wd{year_month_day{year(i / 10000) / month(i / 100 % 100) / day(i % 100)}}; From 5a1454ab310bf4abebf7a93c35f5b29d462938f6 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 18 May 2024 11:28:10 +0300 Subject: [PATCH 232/407] Cava. $XDG_CONFIG_HOME validation Signed-off-by: Viktar Lukashonak --- src/modules/cava.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 072275462..a98e5a675 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -12,7 +12,12 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) std::string strPath{config_["cava_config"].asString()}; const std::string fnd{"XDG_CONFIG_HOME"}; const std::string::size_type npos{strPath.find("$" + fnd)}; - if (npos != std::string::npos) strPath.replace(npos, fnd.length() + 1, getenv(fnd.c_str())); + if (npos != std::string::npos) { + if (const char* xdg = getenv(fnd.c_str())) + strPath.replace(npos, fnd.length() + 1, xdg); + else + spdlog::warn("Module {0}. Environment variable \"${1}\" not found", name_, fnd); + } strcpy(cfgPath, strPath.data()); } // Load cava config From b61ea62732a51e564ded6e7d4d37cd4796b014f2 Mon Sep 17 00:00:00 2001 From: wmlhwl Date: Sun, 19 May 2024 13:53:09 +0200 Subject: [PATCH 233/407] change layer for mode invisible to nullopt --- include/bar.hpp | 3 ++- src/bar.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 6dc3c03df..2f225de60 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -41,7 +42,7 @@ struct bar_margins { }; struct bar_mode { - bar_layer layer; + std::optional layer; bool exclusive; bool passthrough; bool visible; diff --git a/src/bar.cpp b/src/bar.cpp index 872632acb..5efe58897 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -43,7 +43,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // .visible = true}}, {"invisible", {// - .layer = bar_layer::BOTTOM, + .layer = std::nullopt, .exclusive = false, .passthrough = true, .visible = false}}, @@ -59,7 +59,7 @@ const std::string Bar::MODE_INVISIBLE = "invisible"; const std::string_view DEFAULT_BAR_ID = "bar-0"; /* Deserializer for enum bar_layer */ -void from_json(const Json::Value& j, bar_layer& l) { +void from_json(const Json::Value& j, std::optional& l) { if (j == "bottom") { l = bar_layer::BOTTOM; } else if (j == "top") { @@ -316,13 +316,13 @@ void waybar::Bar::setMode(const std::string& mode) { void waybar::Bar::setMode(const struct bar_mode& mode) { auto* gtk_window = window.gobj(); - auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM; - if (mode.layer == bar_layer::TOP) { - layer = GTK_LAYER_SHELL_LAYER_TOP; + if (mode.layer == bar_layer::BOTTOM) { + gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_BOTTOM); + } else if (mode.layer == bar_layer::TOP) { + gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_TOP); } else if (mode.layer == bar_layer::OVERLAY) { - layer = GTK_LAYER_SHELL_LAYER_OVERLAY; + gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_OVERLAY); } - gtk_layer_set_layer(gtk_window, layer); if (mode.exclusive) { gtk_layer_auto_exclusive_zone_enable(gtk_window); From b8e68b0e6301869d04c065905c7b811d3790ce9d Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Wed, 22 May 2024 12:18:23 +0800 Subject: [PATCH 234/407] (hyprland) fix crash when failed to parse IPC message IPC messages are parsed in a dedicated thread, and the thread terminates when an exception is not caught, which causes the waybar process to crash with SIGABORT. While this issue might be related to Hyprland, it is really annoying to see waybar crash. It would be better to catch those exceptions and report errors instead of crashing. --- src/modules/hyprland/backend.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 98eb8b900..1b47ff7dd 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -86,7 +86,14 @@ void IPC::startIPC() { std::string messageReceived(buffer.data()); messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n')); spdlog::debug("hyprland IPC received {}", messageReceived); - parseIPC(messageReceived); + + try { + parseIPC(messageReceived); + } catch (std::exception& e) { + spdlog::warn("Failed to parse IPC message: {}, reason: {}", messageReceived, e.what()); + } catch (...) { + throw; + } std::this_thread::sleep_for(std::chrono::milliseconds(1)); } From 60a613ae5106e215ce1b95268bb6f8cdbfc3a2d7 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Thu, 23 May 2024 16:14:06 +0300 Subject: [PATCH 235/407] cava bump: 0.10.2 Signed-off-by: Viktar Lukashonak --- meson.build | 2 +- src/modules/cava.cpp | 13 +------------ subprojects/cava.wrap | 8 ++++---- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/meson.build b/meson.build index d7a5a4eef..102bb8786 100644 --- a/meson.build +++ b/meson.build @@ -464,7 +464,7 @@ if get_option('experimental') endif cava = dependency('cava', - version : '>=0.10.1', + version : '>=0.10.2', required: get_option('cava'), fallback : ['cava', 'cava_dep'], not_found_message: 'cava is not found. Building waybar without cava') diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index a98e5a675..431ce5f15 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -8,18 +8,7 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) char cfgPath[PATH_MAX]; cfgPath[0] = '\0'; - if (config_["cava_config"].isString()) { - std::string strPath{config_["cava_config"].asString()}; - const std::string fnd{"XDG_CONFIG_HOME"}; - const std::string::size_type npos{strPath.find("$" + fnd)}; - if (npos != std::string::npos) { - if (const char* xdg = getenv(fnd.c_str())) - strPath.replace(npos, fnd.length() + 1, xdg); - else - spdlog::warn("Module {0}. Environment variable \"${1}\" not found", name_, fnd); - } - strcpy(cfgPath, strPath.data()); - } + if (config_["cava_config"].isString()) strcpy(cfgPath, config_["cava_config"].asString().data()); // Load cava config error_.length = 0; diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap index 19383d119..275ba114b 100644 --- a/subprojects/cava.wrap +++ b/subprojects/cava.wrap @@ -1,7 +1,7 @@ [wrap-file] -directory = cava-0.10.1 -source_url = https://github.com/LukashonakV/cava/archive/0.10.1.tar.gz -source_filename = cava-0.10.1.tar.gz -source_hash = ae8c7339908d6febeac5ab8df4576c03c9fdbca6c8e8975daf9ce68b57038bb5 +directory = cava-0.10.2 +source_url = https://github.com/LukashonakV/cava/archive/0.10.2.tar.gz +source_filename = cava-0.10.2.tar.gz +source_hash = dff78c4787c9843583086408a0a6e5bde7a5dee1fa17ae526847366846cb19c3 [provide] cava = cava_dep From d012124c03a9d1c547478b9b3c72928fcb485806 Mon Sep 17 00:00:00 2001 From: Unreal Hoang Date: Fri, 24 May 2024 09:18:25 +0900 Subject: [PATCH 236/407] cava bump: 0.10.2 for nix --- nix/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/default.nix b/nix/default.nix index 986e84dd2..9ce39a9b5 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -5,12 +5,12 @@ }: let libcava = rec { - version = "0.10.1"; + version = "0.10.2"; src = pkgs.fetchFromGitHub { owner = "LukashonakV"; repo = "cava"; rev = version; - hash = "sha256-iIYKvpOWafPJB5XhDOSIW9Mb4I3A4pcgIIPQdQYEqUw="; + hash = "sha256-jU7RQV2txruu/nUUl0TzjK4nai7G38J1rcTjO7UXumY="; }; }; in From e4353e548ad566d9ad3d7e7d0472cbfeae2870aa Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:21:23 -0500 Subject: [PATCH 237/407] .gitignore: add .ccls-cache --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 68bc0dc41..b486237ea 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ packagecache # Nix result result-* + +.ccls-cache From 9fe51af6b09a37cd7253012b3e24e8b5f8ef3c0b Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 10:58:03 -0500 Subject: [PATCH 238/407] hyprland/workspaces: break up parseConfig --- include/modules/hyprland/workspaces.hpp | 10 +++ src/modules/hyprland/workspaces.cpp | 83 ++++++++++++++----------- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 828723480..41c4b9861 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -145,7 +145,17 @@ class Workspaces : public AModule, public EventHandler { Json::Value const& clientsData = Json::Value::nullRef); void removeWorkspace(std::string const& name); void setUrgentWorkspace(std::string const& windowaddress); + + // Config void parseConfig(const Json::Value& config); + auto populateIconsMap(const Json::Value& formatIcons) -> void; + auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) -> void; + auto populateSortByConfig(const Json::Value& config) -> void; + auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; + auto populatePersistentWorkspacesConfig(const Json::Value& config) -> void; + auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; + auto populateWindowRewriteConfig(const Json::Value& config) -> void; + void registerIpc(); // workspace events diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3c03c7083..c7bffa944 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -55,54 +55,63 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value } auto Workspaces::parseConfig(const Json::Value &config) -> void { - const Json::Value &configFormat = config["format"]; - + const auto &configFormat = config["format"]; m_format = configFormat.isString() ? configFormat.asString() : "{name}"; m_withIcon = m_format.find("{icon}") != std::string::npos; if (m_withIcon && m_iconsMap.empty()) { - Json::Value formatIcons = config["format-icons"]; - for (std::string &name : formatIcons.getMemberNames()) { - m_iconsMap.emplace(name, formatIcons[name].asString()); - } - m_iconsMap.emplace("", ""); + populateIconsMap(config["format-icons"]); } - auto configAllOutputs = config_["all-outputs"]; - if (configAllOutputs.isBool()) { - m_allOutputs = configAllOutputs.asBool(); - } + populateBoolConfig(config, "all-outputs", m_allOutputs); + populateBoolConfig(config, "show-special", m_showSpecial); + populateBoolConfig(config, "active-only", m_activeOnly); + populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); - auto configShowSpecial = config_["show-special"]; - if (configShowSpecial.isBool()) { - m_showSpecial = configShowSpecial.asBool(); - } + populateSortByConfig(config); + + populateIgnoreWorkspacesConfig(config); + + populatePersistentWorkspacesConfig(config); + + populateFormatWindowSeparatorConfig(config); + + populateWindowRewriteConfig(config); +} - auto configActiveOnly = config_["active-only"]; - if (configActiveOnly.isBool()) { - m_activeOnly = configActiveOnly.asBool(); +auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { + for (const auto &name : formatIcons.getMemberNames()) { + m_iconsMap.emplace(name, formatIcons[name].asString()); } + m_iconsMap.emplace("", ""); +} - auto configMoveToMonitor = config_["move-to-monitor"]; - if (configMoveToMonitor.isBool()) { - m_moveToMonitor = configMoveToMonitor.asBool(); +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) + -> void { + auto configValue = config[key]; + if (configValue.isBool()) { + member = configValue.asBool(); } +} - auto configSortBy = config_["sort-by"]; +auto Workspaces::populateSortByConfig(const Json::Value &config) -> void { + auto configSortBy = config["sort-by"]; if (configSortBy.isString()) { auto sortByStr = configSortBy.asString(); try { m_sortBy = m_enumParser.parseStringToEnum(sortByStr, m_sortMap); } catch (const std::invalid_argument &e) { - // Handle the case where the string is not a valid enum representation. m_sortBy = SortMethod::DEFAULT; - g_warning("Invalid string representation for sort-by. Falling back to default sort method."); + spdlog::warn( + "Invalid string representation for sort-by. Falling back to default sort method."); } } +} - Json::Value ignoreWorkspaces = config["ignore-workspaces"]; +auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> void { + auto ignoreWorkspaces = config["ignore-workspaces"]; if (ignoreWorkspaces.isArray()) { - for (Json::Value &workspaceRegex : ignoreWorkspaces) { + for (const auto &workspaceRegex : ignoreWorkspaces) { if (workspaceRegex.isString()) { std::string ruleString = workspaceRegex.asString(); try { @@ -116,29 +125,31 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { } } } +} - if (config_["persistent_workspaces"].isObject()) { +auto Workspaces::populatePersistentWorkspacesConfig(const Json::Value &config) -> void { + if (config.isMember("persistent-workspaces") || config.isMember("persistent_workspaces")) { spdlog::warn( "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); + m_persistentWorkspaceConfig = + config.get("persistent-workspaces", config.get("persistent_workspaces", Json::Value())); } +} - if (config_["persistent-workspaces"].isObject() || config_["persistent_workspaces"].isObject()) { - m_persistentWorkspaceConfig = config_["persistent-workspaces"].isObject() - ? config_["persistent-workspaces"] - : config_["persistent_workspaces"]; - } - - const Json::Value &formatWindowSeparator = config["format-window-separator"]; +auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void { + auto formatWindowSeparator = config["format-window-separator"]; m_formatWindowSeparator = formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " "; +} - const Json::Value &windowRewrite = config["window-rewrite"]; +auto Workspaces::populateWindowRewriteConfig(const Json::Value &config) -> void { + const auto &windowRewrite = config["window-rewrite"]; if (!windowRewrite.isObject()) { spdlog::debug("window-rewrite is not defined or is not an object, using default rules."); return; } - const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"]; + const auto &windowRewriteDefaultConfig = config["window-rewrite-default"]; std::string windowRewriteDefault = windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; From d73051e98063ec03b95356f1eb3a3ee319f3f15a Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 13:53:39 -0500 Subject: [PATCH 239/407] hyprland/workspaces: break up doUpdate --- include/modules/hyprland/workspaces.hpp | 6 ++ src/modules/hyprland/workspaces.cpp | 77 +++++++++++++------------ 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 41c4b9861..3c19bc4bb 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -181,7 +181,13 @@ class Workspaces : public AModule, public EventHandler { int windowRewritePriorityFunction(std::string const& window_rule); + // Update methods void doUpdate(); + void removeWorkspacesToRemove(); + void createWorkspacesToCreate(); + std::vector getVisibleWorkspaces(); + void updateWorkspaceStates(const std::vector& visibleWorkspaces); + bool updateWindowsToCreate(); void extendOrphans(int workspaceId, Json::Value const& clientsJson); void registerOrphanWindow(WindowCreationPayload create_window_payload); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index c7bffa944..b535794fc 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -69,13 +69,9 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); populateSortByConfig(config); - populateIgnoreWorkspacesConfig(config); - populatePersistentWorkspacesConfig(config); - populateFormatWindowSeparatorConfig(config); - populateWindowRewriteConfig(config); } @@ -195,14 +191,31 @@ auto Workspaces::registerIpc() -> void { void Workspaces::doUpdate() { std::unique_lock lock(m_mutex); - // remove workspaces that wait to be removed - for (auto &elem : m_workspacesToRemove) { - removeWorkspace(elem); + removeWorkspacesToRemove(); + createWorkspacesToCreate(); + + std::vector visibleWorkspaces = getVisibleWorkspaces(); + + updateWorkspaceStates(visibleWorkspaces); + updateWindowCount(); + sortWorkspaces(); + + bool anyWindowCreated = updateWindowsToCreate(); + + if (anyWindowCreated) { + dp.emit(); + } +} + +void Workspaces::removeWorkspacesToRemove() { + for (const auto &workspaceName : m_workspacesToRemove) { + removeWorkspace(workspaceName); } m_workspacesToRemove.clear(); +} - // add workspaces that wait to be created - for (auto &[workspaceData, clientsData] : m_workspacesToCreate) { +void Workspaces::createWorkspacesToCreate() { + for (const auto &[workspaceData, clientsData] : m_workspacesToCreate) { createWorkspace(workspaceData, clientsData); } if (!m_workspacesToCreate.empty()) { @@ -210,63 +223,55 @@ void Workspaces::doUpdate() { sortWorkspaces(); } m_workspacesToCreate.clear(); +} - // get all active workspaces - spdlog::trace("Getting active workspaces"); - auto monitors = gIPC->getSocket1JsonReply("monitors"); +std::vector Workspaces::getVisibleWorkspaces() { std::vector visibleWorkspaces; - for (Json::Value &monitor : monitors) { + auto monitors = gIPC->getSocket1JsonReply("monitors"); + for (const auto &monitor : monitors) { auto ws = monitor["activeWorkspace"]; - if (ws.isObject() && (ws["name"].isString())) { + if (ws.isObject() && ws["name"].isString()) { visibleWorkspaces.push_back(ws["name"].asString()); } auto sws = monitor["specialWorkspace"]; auto name = sws["name"].asString(); - if (sws.isObject() && (sws["name"].isString()) && !name.empty()) { + if (sws.isObject() && sws["name"].isString() && !name.empty()) { visibleWorkspaces.push_back(!name.starts_with("special:") ? name : name.substr(8)); } } + return visibleWorkspaces; +} - spdlog::trace("Updating workspace states"); - auto updated_workspaces = gIPC->getSocket1JsonReply("workspaces"); +void Workspaces::updateWorkspaceStates(const std::vector &visibleWorkspaces) { + auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { - // active workspace->setActive(workspace->name() == m_activeWorkspaceName || workspace->name() == m_activeSpecialWorkspaceName); - // disable urgency if workspace is active if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { workspace->setUrgent(false); } - - // visible workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), workspace->name()) != visibleWorkspaces.end()); - - // set workspace icon std::string &workspaceIcon = m_iconsMap[""]; if (m_withIcon) { workspaceIcon = workspace->selectIcon(m_iconsMap); } - - // update m_output - auto updated_workspace = - std::find_if(updated_workspaces.begin(), updated_workspaces.end(), [&workspace](auto &w) { + auto updatedWorkspace = std::find_if( + updatedWorkspaces.begin(), updatedWorkspaces.end(), [&workspace](const auto &w) { auto wNameRaw = w["name"].asString(); auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; return wName == workspace->name(); }); - - if (updated_workspace != updated_workspaces.end()) { - workspace->setOutput((*updated_workspace)["monitor"].asString()); + if (updatedWorkspace != updatedWorkspaces.end()) { + workspace->setOutput((*updatedWorkspace)["monitor"].asString()); } - workspace->update(m_format, workspaceIcon); } +} - spdlog::trace("Updating window count"); +bool Workspaces::updateWindowsToCreate() { bool anyWindowCreated = false; std::vector notCreated; - for (auto &windowPayload : m_windowsToCreate) { bool created = false; for (auto &workspace : m_workspaces) { @@ -285,13 +290,9 @@ void Workspaces::doUpdate() { } } } - - if (anyWindowCreated) { - dp.emit(); - } - m_windowsToCreate.clear(); m_windowsToCreate = notCreated; + return anyWindowCreated; } auto Workspaces::update() -> void { From 07c91c200a284f308f2e621b5a360b175f33374e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:07:12 -0500 Subject: [PATCH 240/407] hyprland/workspaces: break up headers --- .../hyprland/windowcreationpayload.hpp | 61 ++++++++++++ include/modules/hyprland/workspace.hpp | 88 ++++++++++++++++++ include/modules/hyprland/workspaces.hpp | 93 +------------------ 3 files changed, 151 insertions(+), 91 deletions(-) create mode 100644 include/modules/hyprland/windowcreationpayload.hpp create mode 100644 include/modules/hyprland/workspace.hpp diff --git a/include/modules/hyprland/windowcreationpayload.hpp b/include/modules/hyprland/windowcreationpayload.hpp new file mode 100644 index 000000000..45229ed42 --- /dev/null +++ b/include/modules/hyprland/windowcreationpayload.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "modules/hyprland/backend.hpp" +#include "util/enum.hpp" +#include "util/regex_collection.hpp" + +using WindowAddress = std::string; + +namespace waybar::modules::hyprland { + +class Workspaces; + +class WindowCreationPayload { + public: + WindowCreationPayload(std::string workspace_name, WindowAddress window_address, + std::string window_repr); + WindowCreationPayload(std::string workspace_name, WindowAddress window_address, + std::string window_class, std::string window_title); + WindowCreationPayload(Json::Value const& client_data); + + int incrementTimeSpentUncreated(); + bool isEmpty(Workspaces& workspace_manager); + bool reprIsReady() const { return std::holds_alternative(m_window); } + std::string repr(Workspaces& workspace_manager); + + std::string getWorkspaceName() const { return m_workspaceName; } + WindowAddress getAddress() const { return m_windowAddress; } + + void moveToWorksace(std::string& new_workspace_name); + + private: + void clearAddr(); + void clearWorkspaceName(); + + using Repr = std::string; + using ClassAndTitle = std::pair; + std::variant m_window; + + WindowAddress m_windowAddress; + std::string m_workspaceName; + + int m_timeSpentUncreated = 0; +}; + +} // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/workspace.hpp b/include/modules/hyprland/workspace.hpp new file mode 100644 index 000000000..f1fea4e8c --- /dev/null +++ b/include/modules/hyprland/workspace.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "modules/hyprland/backend.hpp" +#include "modules/hyprland/windowcreationpayload.hpp" +#include "util/enum.hpp" +#include "util/regex_collection.hpp" + +using WindowAddress = std::string; + +namespace waybar::modules::hyprland { + +class Workspaces; +class Workspace { + public: + explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager, + const Json::Value& clients_data = Json::Value::nullRef); + std::string& selectIcon(std::map& icons_map); + Gtk::Button& button() { return m_button; }; + + int id() const { return m_id; }; + std::string name() const { return m_name; }; + std::string output() const { return m_output; }; + bool isActive() const { return m_isActive; }; + bool isSpecial() const { return m_isSpecial; }; + bool isPersistent() const { return m_isPersistentRule || m_isPersistentConfig; }; + bool isPersistentConfig() const { return m_isPersistentConfig; }; + bool isPersistentRule() const { return m_isPersistentRule; }; + bool isVisible() const { return m_isVisible; }; + bool isEmpty() const { return m_windows == 0; }; + bool isUrgent() const { return m_isUrgent; }; + + bool handleClicked(GdkEventButton* bt) const; + void setActive(bool value = true) { m_isActive = value; }; + void setPersistentRule(bool value = true) { m_isPersistentRule = value; }; + void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; }; + void setUrgent(bool value = true) { m_isUrgent = value; }; + void setVisible(bool value = true) { m_isVisible = value; }; + void setWindows(uint value) { m_windows = value; }; + void setName(std::string const& value) { m_name = value; }; + void setOutput(std::string const& value) { m_output = value; }; + bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); } + void insertWindow(WindowCreationPayload create_window_paylod); + std::string removeWindow(WindowAddress const& addr); + void initializeWindowMap(const Json::Value& clients_data); + + bool onWindowOpened(WindowCreationPayload const& create_window_paylod); + std::optional closeWindow(WindowAddress const& addr); + + void update(const std::string& format, const std::string& icon); + + private: + Workspaces& m_workspaceManager; + + int m_id; + std::string m_name; + std::string m_output; + uint m_windows; + bool m_isActive = false; + bool m_isSpecial = false; + bool m_isPersistentRule = false; // represents the persistent state in hyprland + bool m_isPersistentConfig = false; // represents the persistent state in the Waybar config + bool m_isUrgent = false; + bool m_isVisible = false; + + std::map m_windowMap; + + Gtk::Button m_button; + Gtk::Box m_content; + Gtk::Label m_label; +}; + +} // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 3c19bc4bb..23e3e27fe 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -17,6 +17,8 @@ #include "AModule.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" +#include "modules/hyprland/windowcreationpayload.hpp" +#include "modules/hyprland/workspace.hpp" #include "util/enum.hpp" #include "util/regex_collection.hpp" @@ -26,97 +28,6 @@ namespace waybar::modules::hyprland { class Workspaces; -class WindowCreationPayload { - public: - WindowCreationPayload(std::string workspace_name, WindowAddress window_address, - std::string window_repr); - WindowCreationPayload(std::string workspace_name, WindowAddress window_address, - std::string window_class, std::string window_title); - WindowCreationPayload(Json::Value const& client_data); - - int incrementTimeSpentUncreated(); - bool isEmpty(Workspaces& workspace_manager); - bool reprIsReady() const { return std::holds_alternative(m_window); } - std::string repr(Workspaces& workspace_manager); - - std::string getWorkspaceName() const { return m_workspaceName; } - WindowAddress getAddress() const { return m_windowAddress; } - - void moveToWorksace(std::string& new_workspace_name); - - private: - void clearAddr(); - void clearWorkspaceName(); - - using Repr = std::string; - using ClassAndTitle = std::pair; - std::variant m_window; - - WindowAddress m_windowAddress; - std::string m_workspaceName; - - int m_timeSpentUncreated = 0; -}; - -class Workspace { - public: - explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager, - const Json::Value& clients_data = Json::Value::nullRef); - std::string& selectIcon(std::map& icons_map); - Gtk::Button& button() { return m_button; }; - - int id() const { return m_id; }; - std::string name() const { return m_name; }; - std::string output() const { return m_output; }; - bool isActive() const { return m_isActive; }; - bool isSpecial() const { return m_isSpecial; }; - bool isPersistent() const { return m_isPersistentRule || m_isPersistentConfig; }; - bool isPersistentConfig() const { return m_isPersistentConfig; }; - bool isPersistentRule() const { return m_isPersistentRule; }; - bool isVisible() const { return m_isVisible; }; - bool isEmpty() const { return m_windows == 0; }; - bool isUrgent() const { return m_isUrgent; }; - - bool handleClicked(GdkEventButton* bt) const; - void setActive(bool value = true) { m_isActive = value; }; - void setPersistentRule(bool value = true) { m_isPersistentRule = value; }; - void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; }; - void setUrgent(bool value = true) { m_isUrgent = value; }; - void setVisible(bool value = true) { m_isVisible = value; }; - void setWindows(uint value) { m_windows = value; }; - void setName(std::string const& value) { m_name = value; }; - void setOutput(std::string const& value) { m_output = value; }; - bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); } - void insertWindow(WindowCreationPayload create_window_paylod); - std::string removeWindow(WindowAddress const& addr); - void initializeWindowMap(const Json::Value& clients_data); - - bool onWindowOpened(WindowCreationPayload const& create_window_paylod); - std::optional closeWindow(WindowAddress const& addr); - - void update(const std::string& format, const std::string& icon); - - private: - Workspaces& m_workspaceManager; - - int m_id; - std::string m_name; - std::string m_output; - uint m_windows; - bool m_isActive = false; - bool m_isSpecial = false; - bool m_isPersistentRule = false; // represents the persistent state in hyprland - bool m_isPersistentConfig = false; // represents the persistent state in the Waybar config - bool m_isUrgent = false; - bool m_isVisible = false; - - std::map m_windowMap; - - Gtk::Button m_button; - Gtk::Box m_content; - Gtk::Label m_label; -}; - class Workspaces : public AModule, public EventHandler { public: Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); From 56319a470588a2110204c8fc954f477c52a11a7e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:21:07 -0500 Subject: [PATCH 241/407] hyprland/workspaces: break up implementations --- meson.build | 2 + .../hyprland/windowcreationpayload.cpp | 110 +++++++ src/modules/hyprland/workspace.cpp | 213 +++++++++++++ src/modules/hyprland/workspaces.cpp | 294 ------------------ 4 files changed, 325 insertions(+), 294 deletions(-) create mode 100644 src/modules/hyprland/windowcreationpayload.cpp create mode 100644 src/modules/hyprland/workspace.cpp diff --git a/meson.build b/meson.build index 102bb8786..1af8e2b21 100644 --- a/meson.build +++ b/meson.build @@ -305,7 +305,9 @@ if true 'src/modules/hyprland/language.cpp', 'src/modules/hyprland/submap.cpp', 'src/modules/hyprland/window.cpp', + 'src/modules/hyprland/workspace.cpp', 'src/modules/hyprland/workspaces.cpp', + 'src/modules/hyprland/windowcreationpayload.cpp', ) man_files += files( 'man/waybar-hyprland-language.5.scd', diff --git a/src/modules/hyprland/windowcreationpayload.cpp b/src/modules/hyprland/windowcreationpayload.cpp new file mode 100644 index 000000000..261edcbc5 --- /dev/null +++ b/src/modules/hyprland/windowcreationpayload.cpp @@ -0,0 +1,110 @@ +#include "modules/hyprland/windowcreationpayload.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#include "modules/hyprland/workspaces.hpp" +#include "util/regex_collection.hpp" + +namespace waybar::modules::hyprland { +WindowCreationPayload::WindowCreationPayload(std::string workspace_name, + WindowAddress window_address, std::string window_repr) + : m_window(std::move(window_repr)), + m_windowAddress(std::move(window_address)), + m_workspaceName(std::move(workspace_name)) { + clearAddr(); + clearWorkspaceName(); +} + +WindowCreationPayload::WindowCreationPayload(std::string workspace_name, + WindowAddress window_address, std::string window_class, + std::string window_title) + : m_window(std::make_pair(std::move(window_class), std::move(window_title))), + m_windowAddress(std::move(window_address)), + m_workspaceName(std::move(workspace_name)) { + clearAddr(); + clearWorkspaceName(); +} + +WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data) + : m_window(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), + m_windowAddress(client_data["address"].asString()), + m_workspaceName(client_data["workspace"]["name"].asString()) { + clearAddr(); + clearWorkspaceName(); +} + +std::string WindowCreationPayload::repr(Workspaces &workspace_manager) { + if (std::holds_alternative(m_window)) { + return std::get(m_window); + } + if (std::holds_alternative(m_window)) { + auto [window_class, window_title] = std::get(m_window); + return workspace_manager.getRewrite(window_class, window_title); + } + // Unreachable + spdlog::error("WorkspaceWindow::repr: Unreachable"); + throw std::runtime_error("WorkspaceWindow::repr: Unreachable"); +} + +bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) { + if (std::holds_alternative(m_window)) { + return std::get(m_window).empty(); + } + if (std::holds_alternative(m_window)) { + auto [window_class, window_title] = std::get(m_window); + return (window_class.empty() && + (!workspace_manager.windowRewriteConfigUsesTitle() || window_title.empty())); + } + // Unreachable + spdlog::error("WorkspaceWindow::isEmpty: Unreachable"); + throw std::runtime_error("WorkspaceWindow::isEmpty: Unreachable"); +} + +int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; } + +void WindowCreationPayload::clearAddr() { + // substr(2, ...) is necessary because Hyprland's JSON follows this format: + // 0x{ADDR} + // While Hyprland's IPC follows this format: + // {ADDR} + static const std::string ADDR_PREFIX = "0x"; + static const int ADDR_PREFIX_LEN = ADDR_PREFIX.length(); + + if (m_windowAddress.starts_with(ADDR_PREFIX)) { + m_windowAddress = + m_windowAddress.substr(ADDR_PREFIX_LEN, m_windowAddress.length() - ADDR_PREFIX_LEN); + } +} + +void WindowCreationPayload::clearWorkspaceName() { + // The workspace name may optionally feature "special:" at the beginning. + // If so, we need to remove it because the workspace is saved WITHOUT the + // special qualifier. The reasoning is that not all of Hyprland's IPC events + // use this qualifier, so it's better to be consistent about our uses. + + static const std::string SPECIAL_QUALIFIER_PREFIX = "special:"; + static const int SPECIAL_QUALIFIER_PREFIX_LEN = SPECIAL_QUALIFIER_PREFIX.length(); + + if (m_workspaceName.starts_with(SPECIAL_QUALIFIER_PREFIX)) { + m_workspaceName = m_workspaceName.substr( + SPECIAL_QUALIFIER_PREFIX_LEN, m_workspaceName.length() - SPECIAL_QUALIFIER_PREFIX_LEN); + } + + std::size_t spaceFound = m_workspaceName.find(' '); + if (spaceFound != std::string::npos) { + m_workspaceName.erase(m_workspaceName.begin() + spaceFound, m_workspaceName.end()); + } +} + +void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) { + m_workspaceName = new_workspace_name; +} + +} // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp new file mode 100644 index 000000000..1e3c05f3e --- /dev/null +++ b/src/modules/hyprland/workspace.cpp @@ -0,0 +1,213 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "modules/hyprland/workspaces.hpp" +#include "util/regex_collection.hpp" + +namespace waybar::modules::hyprland { + +void Workspace::initializeWindowMap(const Json::Value &clients_data) { + m_windowMap.clear(); + for (auto client : clients_data) { + if (client["workspace"]["id"].asInt() == id()) { + insertWindow({client}); + } + } +} + +void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { + if (!create_window_paylod.isEmpty(m_workspaceManager)) { + m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager); + } +}; + +std::string Workspace::removeWindow(WindowAddress const &addr) { + std::string windowRepr = m_windowMap[addr]; + m_windowMap.erase(addr); + return windowRepr; +} + +bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) { + if (create_window_paylod.getWorkspaceName() == name()) { + insertWindow(create_window_paylod); + return true; + } + return false; +} + +std::optional Workspace::closeWindow(WindowAddress const &addr) { + if (m_windowMap.contains(addr)) { + return removeWindow(addr); + } + return std::nullopt; +} + +Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager, + const Json::Value &clients_data) + : m_workspaceManager(workspace_manager), + m_id(workspace_data["id"].asInt()), + m_name(workspace_data["name"].asString()), + m_output(workspace_data["monitor"].asString()), // TODO:allow using monitor desc + m_windows(workspace_data["windows"].asInt()), + m_isActive(true), + m_isPersistentRule(workspace_data["persistent-rule"].asBool()), + m_isPersistentConfig(workspace_data["persistent-config"].asBool()) { + if (m_name.starts_with("name:")) { + m_name = m_name.substr(5); + } else if (m_name.starts_with("special")) { + m_name = m_id == -99 ? m_name : m_name.substr(8); + m_isSpecial = true; + } + + m_button.add_events(Gdk::BUTTON_PRESS_MASK); + m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked), + false); + + m_button.set_relief(Gtk::RELIEF_NONE); + m_content.set_center_widget(m_label); + m_button.add(m_content); + + initializeWindowMap(clients_data); +} + +void addOrRemoveClass(const Glib::RefPtr &context, bool condition, + const std::string &class_name) { + if (condition) { + context->add_class(class_name); + } else { + context->remove_class(class_name); + } +} + +void Workspace::update(const std::string &format, const std::string &icon) { + // clang-format off + if (this->m_workspaceManager.activeOnly() && \ + !this->isActive() && \ + !this->isPersistent() && \ + !this->isVisible() && \ + !this->isSpecial()) { + // clang-format on + // if activeOnly is true, hide if not active, persistent, visible or special + m_button.hide(); + return; + } + m_button.show(); + + auto styleContext = m_button.get_style_context(); + addOrRemoveClass(styleContext, isActive(), "active"); + addOrRemoveClass(styleContext, isSpecial(), "special"); + addOrRemoveClass(styleContext, isEmpty(), "empty"); + addOrRemoveClass(styleContext, isPersistent(), "persistent"); + addOrRemoveClass(styleContext, isUrgent(), "urgent"); + addOrRemoveClass(styleContext, isVisible(), "visible"); + addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); + + std::string windows; + auto windowSeparator = m_workspaceManager.getWindowSeparator(); + + bool isNotFirst = false; + + for (auto &[_pid, window_repr] : m_windowMap) { + if (isNotFirst) { + windows.append(windowSeparator); + } + isNotFirst = true; + windows.append(window_repr); + } + + m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), + fmt::arg("name", name()), fmt::arg("icon", icon), + fmt::arg("windows", windows))); +} + +std::string &Workspace::selectIcon(std::map &icons_map) { + spdlog::trace("Selecting icon for workspace {}", name()); + if (isUrgent()) { + auto urgentIconIt = icons_map.find("urgent"); + if (urgentIconIt != icons_map.end()) { + return urgentIconIt->second; + } + } + + if (isActive()) { + auto activeIconIt = icons_map.find("active"); + if (activeIconIt != icons_map.end()) { + return activeIconIt->second; + } + } + + if (isSpecial()) { + auto specialIconIt = icons_map.find("special"); + if (specialIconIt != icons_map.end()) { + return specialIconIt->second; + } + } + + auto namedIconIt = icons_map.find(name()); + if (namedIconIt != icons_map.end()) { + return namedIconIt->second; + } + + if (isVisible()) { + auto visibleIconIt = icons_map.find("visible"); + if (visibleIconIt != icons_map.end()) { + return visibleIconIt->second; + } + } + + if (isEmpty()) { + auto emptyIconIt = icons_map.find("empty"); + if (emptyIconIt != icons_map.end()) { + return emptyIconIt->second; + } + } + + if (isPersistent()) { + auto persistentIconIt = icons_map.find("persistent"); + if (persistentIconIt != icons_map.end()) { + return persistentIconIt->second; + } + } + + auto defaultIconIt = icons_map.find("default"); + if (defaultIconIt != icons_map.end()) { + return defaultIconIt->second; + } + + return m_name; +} + +bool Workspace::handleClicked(GdkEventButton *bt) const { + if (bt->type == GDK_BUTTON_PRESS) { + try { + if (id() > 0) { // normal + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); + } else { + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } + } else if (!isSpecial()) { // named (this includes persistent) + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); + } else { + gIPC->getSocket1Reply("dispatch workspace name:" + name()); + } + } else if (id() != -99) { // named special + gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); + } else { // special + gIPC->getSocket1Reply("dispatch togglespecialworkspace"); + } + return true; + } catch (const std::exception &e) { + spdlog::error("Failed to dispatch workspace: {}", e.what()); + } + } + return false; +} +} // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index b535794fc..2074ad544 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -589,42 +589,6 @@ void Workspaces::updateWindowCount() { } } -void Workspace::initializeWindowMap(const Json::Value &clients_data) { - m_windowMap.clear(); - for (auto client : clients_data) { - if (client["workspace"]["id"].asInt() == id()) { - insertWindow({client}); - } - } -} - -void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { - if (!create_window_paylod.isEmpty(m_workspaceManager)) { - m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager); - } -}; - -std::string Workspace::removeWindow(WindowAddress const &addr) { - std::string windowRepr = m_windowMap[addr]; - m_windowMap.erase(addr); - return windowRepr; -} - -bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) { - if (create_window_paylod.getWorkspaceName() == name()) { - insertWindow(create_window_paylod); - return true; - } - return false; -} - -std::optional Workspace::closeWindow(WindowAddress const &addr) { - if (m_windowMap.contains(addr)) { - return removeWindow(addr); - } - return std::nullopt; -} - void Workspaces::createWorkspace(Json::Value const &workspace_data, Json::Value const &clients_data) { auto workspaceName = workspace_data["name"].asString(); @@ -857,84 +821,6 @@ Workspaces::~Workspaces() { std::lock_guard lg(m_mutex); } -Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager, - const Json::Value &clients_data) - : m_workspaceManager(workspace_manager), - m_id(workspace_data["id"].asInt()), - m_name(workspace_data["name"].asString()), - m_output(workspace_data["monitor"].asString()), // TODO:allow using monitor desc - m_windows(workspace_data["windows"].asInt()), - m_isActive(true), - m_isPersistentRule(workspace_data["persistent-rule"].asBool()), - m_isPersistentConfig(workspace_data["persistent-config"].asBool()) { - if (m_name.starts_with("name:")) { - m_name = m_name.substr(5); - } else if (m_name.starts_with("special")) { - m_name = m_id == -99 ? m_name : m_name.substr(8); - m_isSpecial = true; - } - - m_button.add_events(Gdk::BUTTON_PRESS_MASK); - m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked), - false); - - m_button.set_relief(Gtk::RELIEF_NONE); - m_content.set_center_widget(m_label); - m_button.add(m_content); - - initializeWindowMap(clients_data); -} - -void addOrRemoveClass(const Glib::RefPtr &context, bool condition, - const std::string &class_name) { - if (condition) { - context->add_class(class_name); - } else { - context->remove_class(class_name); - } -} - -void Workspace::update(const std::string &format, const std::string &icon) { - // clang-format off - if (this->m_workspaceManager.activeOnly() && \ - !this->isActive() && \ - !this->isPersistent() && \ - !this->isVisible() && \ - !this->isSpecial()) { - // clang-format on - // if activeOnly is true, hide if not active, persistent, visible or special - m_button.hide(); - return; - } - m_button.show(); - - auto styleContext = m_button.get_style_context(); - addOrRemoveClass(styleContext, isActive(), "active"); - addOrRemoveClass(styleContext, isSpecial(), "special"); - addOrRemoveClass(styleContext, isEmpty(), "empty"); - addOrRemoveClass(styleContext, isPersistent(), "persistent"); - addOrRemoveClass(styleContext, isUrgent(), "urgent"); - addOrRemoveClass(styleContext, isVisible(), "visible"); - addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); - - std::string windows; - auto windowSeparator = m_workspaceManager.getWindowSeparator(); - - bool isNotFirst = false; - - for (auto &[_pid, window_repr] : m_windowMap) { - if (isNotFirst) { - windows.append(windowSeparator); - } - isNotFirst = true; - windows.append(window_repr); - } - - m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), - fmt::arg("name", name()), fmt::arg("icon", icon), - fmt::arg("windows", windows))); -} - void Workspaces::sortWorkspaces() { std::sort(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr &a, std::unique_ptr &b) { @@ -998,91 +884,6 @@ void Workspaces::sortWorkspaces() { } } -std::string &Workspace::selectIcon(std::map &icons_map) { - spdlog::trace("Selecting icon for workspace {}", name()); - if (isUrgent()) { - auto urgentIconIt = icons_map.find("urgent"); - if (urgentIconIt != icons_map.end()) { - return urgentIconIt->second; - } - } - - if (isActive()) { - auto activeIconIt = icons_map.find("active"); - if (activeIconIt != icons_map.end()) { - return activeIconIt->second; - } - } - - if (isSpecial()) { - auto specialIconIt = icons_map.find("special"); - if (specialIconIt != icons_map.end()) { - return specialIconIt->second; - } - } - - auto namedIconIt = icons_map.find(name()); - if (namedIconIt != icons_map.end()) { - return namedIconIt->second; - } - - if (isVisible()) { - auto visibleIconIt = icons_map.find("visible"); - if (visibleIconIt != icons_map.end()) { - return visibleIconIt->second; - } - } - - if (isEmpty()) { - auto emptyIconIt = icons_map.find("empty"); - if (emptyIconIt != icons_map.end()) { - return emptyIconIt->second; - } - } - - if (isPersistent()) { - auto persistentIconIt = icons_map.find("persistent"); - if (persistentIconIt != icons_map.end()) { - return persistentIconIt->second; - } - } - - auto defaultIconIt = icons_map.find("default"); - if (defaultIconIt != icons_map.end()) { - return defaultIconIt->second; - } - - return m_name; -} - -bool Workspace::handleClicked(GdkEventButton *bt) const { - if (bt->type == GDK_BUTTON_PRESS) { - try { - if (id() > 0) { // normal - if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); - } else { - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); - } - } else if (!isSpecial()) { // named (this includes persistent) - if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); - } else { - gIPC->getSocket1Reply("dispatch workspace name:" + name()); - } - } else if (id() != -99) { // named special - gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); - } else { // special - gIPC->getSocket1Reply("dispatch togglespecialworkspace"); - } - return true; - } catch (const std::exception &e) { - spdlog::error("Failed to dispatch workspace: {}", e.what()); - } - } - return false; -} - void Workspaces::setUrgentWorkspace(std::string const &windowaddress) { const Json::Value clientsJson = gIPC->getSocket1JsonReply("clients"); int workspaceId = -1; @@ -1113,99 +914,4 @@ std::string Workspaces::getRewrite(std::string window_class, std::string window_ return fmt::format(fmt::runtime(rewriteRule), fmt::arg("class", window_class), fmt::arg("title", window_title)); } - -WindowCreationPayload::WindowCreationPayload(std::string workspace_name, - WindowAddress window_address, std::string window_repr) - : m_window(std::move(window_repr)), - m_windowAddress(std::move(window_address)), - m_workspaceName(std::move(workspace_name)) { - clearAddr(); - clearWorkspaceName(); -} - -WindowCreationPayload::WindowCreationPayload(std::string workspace_name, - WindowAddress window_address, std::string window_class, - std::string window_title) - : m_window(std::make_pair(std::move(window_class), std::move(window_title))), - m_windowAddress(std::move(window_address)), - m_workspaceName(std::move(workspace_name)) { - clearAddr(); - clearWorkspaceName(); -} - -WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data) - : m_window(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), - m_windowAddress(client_data["address"].asString()), - m_workspaceName(client_data["workspace"]["name"].asString()) { - clearAddr(); - clearWorkspaceName(); -} - -std::string WindowCreationPayload::repr(Workspaces &workspace_manager) { - if (std::holds_alternative(m_window)) { - return std::get(m_window); - } - if (std::holds_alternative(m_window)) { - auto [window_class, window_title] = std::get(m_window); - return workspace_manager.getRewrite(window_class, window_title); - } - // Unreachable - spdlog::error("WorkspaceWindow::repr: Unreachable"); - throw std::runtime_error("WorkspaceWindow::repr: Unreachable"); -} - -bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) { - if (std::holds_alternative(m_window)) { - return std::get(m_window).empty(); - } - if (std::holds_alternative(m_window)) { - auto [window_class, window_title] = std::get(m_window); - return (window_class.empty() && - (!workspace_manager.windowRewriteConfigUsesTitle() || window_title.empty())); - } - // Unreachable - spdlog::error("WorkspaceWindow::isEmpty: Unreachable"); - throw std::runtime_error("WorkspaceWindow::isEmpty: Unreachable"); -} - -int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; } - -void WindowCreationPayload::clearAddr() { - // substr(2, ...) is necessary because Hyprland's JSON follows this format: - // 0x{ADDR} - // While Hyprland's IPC follows this format: - // {ADDR} - static const std::string ADDR_PREFIX = "0x"; - static const int ADDR_PREFIX_LEN = ADDR_PREFIX.length(); - - if (m_windowAddress.starts_with(ADDR_PREFIX)) { - m_windowAddress = - m_windowAddress.substr(ADDR_PREFIX_LEN, m_windowAddress.length() - ADDR_PREFIX_LEN); - } -} - -void WindowCreationPayload::clearWorkspaceName() { - // The workspace name may optionally feature "special:" at the beginning. - // If so, we need to remove it because the workspace is saved WITHOUT the - // special qualifier. The reasoning is that not all of Hyprland's IPC events - // use this qualifier, so it's better to be consistent about our uses. - - static const std::string SPECIAL_QUALIFIER_PREFIX = "special:"; - static const int SPECIAL_QUALIFIER_PREFIX_LEN = SPECIAL_QUALIFIER_PREFIX.length(); - - if (m_workspaceName.starts_with(SPECIAL_QUALIFIER_PREFIX)) { - m_workspaceName = m_workspaceName.substr( - SPECIAL_QUALIFIER_PREFIX_LEN, m_workspaceName.length() - SPECIAL_QUALIFIER_PREFIX_LEN); - } - - std::size_t spaceFound = m_workspaceName.find(' '); - if (spaceFound != std::string::npos) { - m_workspaceName.erase(m_workspaceName.begin() + spaceFound, m_workspaceName.end()); - } -} - -void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) { - m_workspaceName = new_workspace_name; -} - } // namespace waybar::modules::hyprland From 82ae474002d673f39d5ed3410fae334b0177bd74 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:29:03 -0500 Subject: [PATCH 242/407] hyprland/workspace: sort methods --- src/modules/hyprland/workspace.cpp | 185 +++++++++++++++-------------- 1 file changed, 93 insertions(+), 92 deletions(-) diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index 1e3c05f3e..694d3b0d6 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -12,42 +12,6 @@ namespace waybar::modules::hyprland { -void Workspace::initializeWindowMap(const Json::Value &clients_data) { - m_windowMap.clear(); - for (auto client : clients_data) { - if (client["workspace"]["id"].asInt() == id()) { - insertWindow({client}); - } - } -} - -void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { - if (!create_window_paylod.isEmpty(m_workspaceManager)) { - m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager); - } -}; - -std::string Workspace::removeWindow(WindowAddress const &addr) { - std::string windowRepr = m_windowMap[addr]; - m_windowMap.erase(addr); - return windowRepr; -} - -bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) { - if (create_window_paylod.getWorkspaceName() == name()) { - insertWindow(create_window_paylod); - return true; - } - return false; -} - -std::optional Workspace::closeWindow(WindowAddress const &addr) { - if (m_windowMap.contains(addr)) { - return removeWindow(addr); - } - return std::nullopt; -} - Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager, const Json::Value &clients_data) : m_workspaceManager(workspace_manager), @@ -85,45 +49,68 @@ void addOrRemoveClass(const Glib::RefPtr &context, bool condi } } -void Workspace::update(const std::string &format, const std::string &icon) { - // clang-format off - if (this->m_workspaceManager.activeOnly() && \ - !this->isActive() && \ - !this->isPersistent() && \ - !this->isVisible() && \ - !this->isSpecial()) { - // clang-format on - // if activeOnly is true, hide if not active, persistent, visible or special - m_button.hide(); - return; +std::optional Workspace::closeWindow(WindowAddress const &addr) { + if (m_windowMap.contains(addr)) { + return removeWindow(addr); } - m_button.show(); + return std::nullopt; +} - auto styleContext = m_button.get_style_context(); - addOrRemoveClass(styleContext, isActive(), "active"); - addOrRemoveClass(styleContext, isSpecial(), "special"); - addOrRemoveClass(styleContext, isEmpty(), "empty"); - addOrRemoveClass(styleContext, isPersistent(), "persistent"); - addOrRemoveClass(styleContext, isUrgent(), "urgent"); - addOrRemoveClass(styleContext, isVisible(), "visible"); - addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); +bool Workspace::handleClicked(GdkEventButton *bt) const { + if (bt->type == GDK_BUTTON_PRESS) { + try { + if (id() > 0) { // normal + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); + } else { + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } + } else if (!isSpecial()) { // named (this includes persistent) + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); + } else { + gIPC->getSocket1Reply("dispatch workspace name:" + name()); + } + } else if (id() != -99) { // named special + gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); + } else { // special + gIPC->getSocket1Reply("dispatch togglespecialworkspace"); + } + return true; + } catch (const std::exception &e) { + spdlog::error("Failed to dispatch workspace: {}", e.what()); + } + } + return false; +} - std::string windows; - auto windowSeparator = m_workspaceManager.getWindowSeparator(); +void Workspace::initializeWindowMap(const Json::Value &clients_data) { + m_windowMap.clear(); + for (auto client : clients_data) { + if (client["workspace"]["id"].asInt() == id()) { + insertWindow({client}); + } + } +} - bool isNotFirst = false; +void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { + if (!create_window_paylod.isEmpty(m_workspaceManager)) { + m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager); + } +}; - for (auto &[_pid, window_repr] : m_windowMap) { - if (isNotFirst) { - windows.append(windowSeparator); - } - isNotFirst = true; - windows.append(window_repr); +bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) { + if (create_window_paylod.getWorkspaceName() == name()) { + insertWindow(create_window_paylod); + return true; } + return false; +} - m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), - fmt::arg("name", name()), fmt::arg("icon", icon), - fmt::arg("windows", windows))); +std::string Workspace::removeWindow(WindowAddress const &addr) { + std::string windowRepr = m_windowMap[addr]; + m_windowMap.erase(addr); + return windowRepr; } std::string &Workspace::selectIcon(std::map &icons_map) { @@ -183,31 +170,45 @@ std::string &Workspace::selectIcon(std::map &icons_map return m_name; } -bool Workspace::handleClicked(GdkEventButton *bt) const { - if (bt->type == GDK_BUTTON_PRESS) { - try { - if (id() > 0) { // normal - if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); - } else { - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); - } - } else if (!isSpecial()) { // named (this includes persistent) - if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); - } else { - gIPC->getSocket1Reply("dispatch workspace name:" + name()); - } - } else if (id() != -99) { // named special - gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); - } else { // special - gIPC->getSocket1Reply("dispatch togglespecialworkspace"); - } - return true; - } catch (const std::exception &e) { - spdlog::error("Failed to dispatch workspace: {}", e.what()); +void Workspace::update(const std::string &format, const std::string &icon) { + // clang-format off + if (this->m_workspaceManager.activeOnly() && \ + !this->isActive() && \ + !this->isPersistent() && \ + !this->isVisible() && \ + !this->isSpecial()) { + // clang-format on + // if activeOnly is true, hide if not active, persistent, visible or special + m_button.hide(); + return; + } + m_button.show(); + + auto styleContext = m_button.get_style_context(); + addOrRemoveClass(styleContext, isActive(), "active"); + addOrRemoveClass(styleContext, isSpecial(), "special"); + addOrRemoveClass(styleContext, isEmpty(), "empty"); + addOrRemoveClass(styleContext, isPersistent(), "persistent"); + addOrRemoveClass(styleContext, isUrgent(), "urgent"); + addOrRemoveClass(styleContext, isVisible(), "visible"); + addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); + + std::string windows; + auto windowSeparator = m_workspaceManager.getWindowSeparator(); + + bool isNotFirst = false; + + for (auto &[_pid, window_repr] : m_windowMap) { + if (isNotFirst) { + windows.append(windowSeparator); } + isNotFirst = true; + windows.append(window_repr); } - return false; + + m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), + fmt::arg("name", name()), fmt::arg("icon", icon), + fmt::arg("windows", windows))); } + } // namespace waybar::modules::hyprland From 9ba9d57c8cd140035741633bacf7f3a8568112f9 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:30:31 -0500 Subject: [PATCH 243/407] hyprland/windowcreationpayload: sort methods --- .../hyprland/windowcreationpayload.cpp | 75 ++++++++++--------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/src/modules/hyprland/windowcreationpayload.cpp b/src/modules/hyprland/windowcreationpayload.cpp index 261edcbc5..22febc598 100644 --- a/src/modules/hyprland/windowcreationpayload.cpp +++ b/src/modules/hyprland/windowcreationpayload.cpp @@ -13,6 +13,15 @@ #include "util/regex_collection.hpp" namespace waybar::modules::hyprland { + +WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data) + : m_window(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), + m_windowAddress(client_data["address"].asString()), + m_workspaceName(client_data["workspace"]["name"].asString()) { + clearAddr(); + clearWorkspaceName(); +} + WindowCreationPayload::WindowCreationPayload(std::string workspace_name, WindowAddress window_address, std::string window_repr) : m_window(std::move(window_repr)), @@ -32,43 +41,6 @@ WindowCreationPayload::WindowCreationPayload(std::string workspace_name, clearWorkspaceName(); } -WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data) - : m_window(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), - m_windowAddress(client_data["address"].asString()), - m_workspaceName(client_data["workspace"]["name"].asString()) { - clearAddr(); - clearWorkspaceName(); -} - -std::string WindowCreationPayload::repr(Workspaces &workspace_manager) { - if (std::holds_alternative(m_window)) { - return std::get(m_window); - } - if (std::holds_alternative(m_window)) { - auto [window_class, window_title] = std::get(m_window); - return workspace_manager.getRewrite(window_class, window_title); - } - // Unreachable - spdlog::error("WorkspaceWindow::repr: Unreachable"); - throw std::runtime_error("WorkspaceWindow::repr: Unreachable"); -} - -bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) { - if (std::holds_alternative(m_window)) { - return std::get(m_window).empty(); - } - if (std::holds_alternative(m_window)) { - auto [window_class, window_title] = std::get(m_window); - return (window_class.empty() && - (!workspace_manager.windowRewriteConfigUsesTitle() || window_title.empty())); - } - // Unreachable - spdlog::error("WorkspaceWindow::isEmpty: Unreachable"); - throw std::runtime_error("WorkspaceWindow::isEmpty: Unreachable"); -} - -int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; } - void WindowCreationPayload::clearAddr() { // substr(2, ...) is necessary because Hyprland's JSON follows this format: // 0x{ADDR} @@ -103,8 +75,37 @@ void WindowCreationPayload::clearWorkspaceName() { } } +bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) { + if (std::holds_alternative(m_window)) { + return std::get(m_window).empty(); + } + if (std::holds_alternative(m_window)) { + auto [window_class, window_title] = std::get(m_window); + return (window_class.empty() && + (!workspace_manager.windowRewriteConfigUsesTitle() || window_title.empty())); + } + // Unreachable + spdlog::error("WorkspaceWindow::isEmpty: Unreachable"); + throw std::runtime_error("WorkspaceWindow::isEmpty: Unreachable"); +} + +int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; } + void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) { m_workspaceName = new_workspace_name; } +std::string WindowCreationPayload::repr(Workspaces &workspace_manager) { + if (std::holds_alternative(m_window)) { + return std::get(m_window); + } + if (std::holds_alternative(m_window)) { + auto [window_class, window_title] = std::get(m_window); + return workspace_manager.getRewrite(window_class, window_title); + } + // Unreachable + spdlog::error("WorkspaceWindow::repr: Unreachable"); + throw std::runtime_error("WorkspaceWindow::repr: Unreachable"); +} + } // namespace waybar::modules::hyprland From f5bb086460fe8bd1995c05f9fc1b530d236a1bcb Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:41:52 -0500 Subject: [PATCH 244/407] hyprland/workspaces: sort methods --- include/modules/hyprland/workspaces.hpp | 2 + src/modules/hyprland/workspaces.cpp | 824 ++++++++++++------------ 2 files changed, 415 insertions(+), 411 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 23e3e27fe..e99bf40c0 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -54,6 +54,8 @@ class Workspaces : public AModule, public EventHandler { void sortWorkspaces(); void createWorkspace(Json::Value const& workspaceData, Json::Value const& clientsData = Json::Value::nullRef); + + Json::Value createMonitorWorkspaceData(std::string const& name, std::string const& monitor); void removeWorkspace(std::string const& name); void setUrgentWorkspace(std::string const& windowaddress); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 2074ad544..eca30134e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -13,26 +13,6 @@ namespace waybar::modules::hyprland { -int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) { - // Rules that match against title are prioritized - // Rules that don't specify if they're matching against either title or class are deprioritized - bool const hasTitle = window_rule.find("title") != std::string::npos; - bool const hasClass = window_rule.find("class") != std::string::npos; - - if (hasTitle && hasClass) { - m_anyWindowRewriteRuleUsesTitle = true; - return 3; - } - if (hasTitle) { - m_anyWindowRewriteRuleUsesTitle = true; - return 2; - } - if (hasClass) { - return 1; - } - return 0; -} - Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, false), m_bar(bar), m_box(bar.orientation, 0) { modulesReady = true; @@ -54,132 +34,87 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value registerIpc(); } -auto Workspaces::parseConfig(const Json::Value &config) -> void { - const auto &configFormat = config["format"]; - m_format = configFormat.isString() ? configFormat.asString() : "{name}"; - m_withIcon = m_format.find("{icon}") != std::string::npos; - - if (m_withIcon && m_iconsMap.empty()) { - populateIconsMap(config["format-icons"]); - } +Workspaces::~Workspaces() { + gIPC->unregisterForIPC(this); + // wait for possible event handler to finish + std::lock_guard lg(m_mutex); +} - populateBoolConfig(config, "all-outputs", m_allOutputs); - populateBoolConfig(config, "show-special", m_showSpecial); - populateBoolConfig(config, "active-only", m_activeOnly); - populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); +void Workspaces::init() { + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); - populateSortByConfig(config); - populateIgnoreWorkspacesConfig(config); - populatePersistentWorkspacesConfig(config); - populateFormatWindowSeparatorConfig(config); - populateWindowRewriteConfig(config); + initializeWorkspaces(); + dp.emit(); } -auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { - for (const auto &name : formatIcons.getMemberNames()) { - m_iconsMap.emplace(name, formatIcons[name].asString()); +Json::Value Workspaces::createMonitorWorkspaceData(std::string const &name, + std::string const &monitor) { + spdlog::trace("Creating persistent workspace: {} on monitor {}", name, monitor); + Json::Value workspaceData; + try { + // numbered persistent workspaces get the name as ID + workspaceData["id"] = name == "special" ? -99 : std::stoi(name); + } catch (const std::exception &e) { + // named persistent workspaces start with ID=0 + workspaceData["id"] = 0; } - m_iconsMap.emplace("", ""); + workspaceData["name"] = name; + workspaceData["monitor"] = monitor; + workspaceData["windows"] = 0; + return workspaceData; } -auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) - -> void { - auto configValue = config[key]; - if (configValue.isBool()) { - member = configValue.asBool(); - } -} +void Workspaces::createWorkspace(Json::Value const &workspace_data, + Json::Value const &clients_data) { + auto workspaceName = workspace_data["name"].asString(); + spdlog::debug("Creating workspace {}", workspaceName); -auto Workspaces::populateSortByConfig(const Json::Value &config) -> void { - auto configSortBy = config["sort-by"]; - if (configSortBy.isString()) { - auto sortByStr = configSortBy.asString(); - try { - m_sortBy = m_enumParser.parseStringToEnum(sortByStr, m_sortMap); - } catch (const std::invalid_argument &e) { - m_sortBy = SortMethod::DEFAULT; - spdlog::warn( - "Invalid string representation for sort-by. Falling back to default sort method."); - } - } -} + // avoid recreating existing workspaces + auto workspace = std::find_if( + m_workspaces.begin(), m_workspaces.end(), + [workspaceName](std::unique_ptr const &w) { + return (workspaceName.starts_with("special:") && workspaceName.substr(8) == w->name()) || + workspaceName == w->name(); + }); -auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> void { - auto ignoreWorkspaces = config["ignore-workspaces"]; - if (ignoreWorkspaces.isArray()) { - for (const auto &workspaceRegex : ignoreWorkspaces) { - if (workspaceRegex.isString()) { - std::string ruleString = workspaceRegex.asString(); - try { - const std::regex rule{ruleString, std::regex_constants::icase}; - m_ignoreWorkspaces.emplace_back(rule); - } catch (const std::regex_error &e) { - spdlog::error("Invalid rule {}: {}", ruleString, e.what()); - } - } else { - spdlog::error("Not a string: '{}'", workspaceRegex); - } - } - } -} + if (workspace != m_workspaces.end()) { + // don't recreate workspace, but update persistency if necessary + const auto keys = workspace_data.getMemberNames(); -auto Workspaces::populatePersistentWorkspacesConfig(const Json::Value &config) -> void { - if (config.isMember("persistent-workspaces") || config.isMember("persistent_workspaces")) { - spdlog::warn( - "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); - m_persistentWorkspaceConfig = - config.get("persistent-workspaces", config.get("persistent_workspaces", Json::Value())); - } -} + const auto *k = "persistent-rule"; + if (std::find(keys.begin(), keys.end(), k) != keys.end()) { + spdlog::debug("Set dynamic persistency of workspace {} to: {}", workspaceName, + workspace_data[k].asBool() ? "true" : "false"); + (*workspace)->setPersistentRule(workspace_data[k].asBool()); + } -auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void { - auto formatWindowSeparator = config["format-window-separator"]; - m_formatWindowSeparator = - formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " "; -} + k = "persistent-config"; + if (std::find(keys.begin(), keys.end(), k) != keys.end()) { + spdlog::debug("Set config persistency of workspace {} to: {}", workspaceName, + workspace_data[k].asBool() ? "true" : "false"); + (*workspace)->setPersistentConfig(workspace_data[k].asBool()); + } -auto Workspaces::populateWindowRewriteConfig(const Json::Value &config) -> void { - const auto &windowRewrite = config["window-rewrite"]; - if (!windowRewrite.isObject()) { - spdlog::debug("window-rewrite is not defined or is not an object, using default rules."); return; } - const auto &windowRewriteDefaultConfig = config["window-rewrite-default"]; - std::string windowRewriteDefault = - windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; - - m_windowRewriteRules = util::RegexCollection( - windowRewrite, windowRewriteDefault, - [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); + // create new workspace + m_workspaces.emplace_back(std::make_unique(workspace_data, *this, clients_data)); + Gtk::Button &newWorkspaceButton = m_workspaces.back()->button(); + m_box.pack_start(newWorkspaceButton, false, false); + sortWorkspaces(); + newWorkspaceButton.show_all(); } -void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) { - if (!create_window_payload.isEmpty(*this)) { - m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this); +void Workspaces::createWorkspacesToCreate() { + for (const auto &[workspaceData, clientsData] : m_workspacesToCreate) { + createWorkspace(workspaceData, clientsData); } -} - -auto Workspaces::registerIpc() -> void { - gIPC->registerForIPC("workspace", this); - gIPC->registerForIPC("activespecial", this); - gIPC->registerForIPC("createworkspace", this); - gIPC->registerForIPC("destroyworkspace", this); - gIPC->registerForIPC("focusedmon", this); - gIPC->registerForIPC("moveworkspace", this); - gIPC->registerForIPC("renameworkspace", this); - gIPC->registerForIPC("openwindow", this); - gIPC->registerForIPC("closewindow", this); - gIPC->registerForIPC("movewindow", this); - gIPC->registerForIPC("urgent", this); - gIPC->registerForIPC("configreloaded", this); - - if (windowRewriteConfigUsesTitle()) { - spdlog::info( - "Registering for Hyprland's 'windowtitle' events because a user-defined window " - "rewrite rule uses the 'title' field."); - gIPC->registerForIPC("windowtitle", this); + if (!m_workspacesToCreate.empty()) { + updateWindowCount(); + sortWorkspaces(); } + m_workspacesToCreate.clear(); } /** @@ -207,22 +142,25 @@ void Workspaces::doUpdate() { } } -void Workspaces::removeWorkspacesToRemove() { - for (const auto &workspaceName : m_workspacesToRemove) { - removeWorkspace(workspaceName); +void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) { + spdlog::trace("Extending orphans with workspace {}", workspaceId); + for (const auto &client : clientsJson) { + if (client["workspace"]["id"].asInt() == workspaceId) { + registerOrphanWindow({client}); + } } - m_workspacesToRemove.clear(); } -void Workspaces::createWorkspacesToCreate() { - for (const auto &[workspaceData, clientsData] : m_workspacesToCreate) { - createWorkspace(workspaceData, clientsData); - } - if (!m_workspacesToCreate.empty()) { - updateWindowCount(); - sortWorkspaces(); +std::string Workspaces::getRewrite(std::string window_class, std::string window_title) { + std::string windowReprKey; + if (windowRewriteConfigUsesTitle()) { + windowReprKey = fmt::format("class<{}> title<{}>", window_class, window_title); + } else { + windowReprKey = fmt::format("class<{}>", window_class); } - m_workspacesToCreate.clear(); + auto const rewriteRule = m_windowRewriteRules.get(windowReprKey); + return fmt::format(fmt::runtime(rewriteRule), fmt::arg("class", window_class), + fmt::arg("title", window_title)); } std::vector Workspaces::getVisibleWorkspaces() { @@ -242,81 +180,139 @@ std::vector Workspaces::getVisibleWorkspaces() { return visibleWorkspaces; } -void Workspaces::updateWorkspaceStates(const std::vector &visibleWorkspaces) { - auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces"); +void Workspaces::initializeWorkspaces() { + spdlog::debug("Initializing workspaces"); + + // if the workspace rules changed since last initialization, make sure we reset everything: for (auto &workspace : m_workspaces) { - workspace->setActive(workspace->name() == m_activeWorkspaceName || - workspace->name() == m_activeSpecialWorkspaceName); - if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { - workspace->setUrgent(false); - } - workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), - workspace->name()) != visibleWorkspaces.end()); - std::string &workspaceIcon = m_iconsMap[""]; - if (m_withIcon) { - workspaceIcon = workspace->selectIcon(m_iconsMap); + m_workspacesToRemove.push_back(workspace->name()); + } + + // get all current workspaces + auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + auto const clientsJson = gIPC->getSocket1JsonReply("clients"); + + for (Json::Value workspaceJson : workspacesJson) { + std::string workspaceName = workspaceJson["name"].asString(); + if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && + (!workspaceName.starts_with("special") || showSpecial()) && + !isWorkspaceIgnored(workspaceName)) { + m_workspacesToCreate.emplace_back(workspaceJson, clientsJson); + } else { + extendOrphans(workspaceJson["id"].asInt(), clientsJson); } - auto updatedWorkspace = std::find_if( - updatedWorkspaces.begin(), updatedWorkspaces.end(), [&workspace](const auto &w) { - auto wNameRaw = w["name"].asString(); - auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; - return wName == workspace->name(); - }); - if (updatedWorkspace != updatedWorkspaces.end()) { - workspace->setOutput((*updatedWorkspace)["monitor"].asString()); + } + + spdlog::debug("Initializing persistent workspaces"); + if (m_persistentWorkspaceConfig.isObject()) { + // a persistent workspace config is defined, so use that instead of workspace rules + loadPersistentWorkspacesFromConfig(clientsJson); + } + // load Hyprland's workspace rules + loadPersistentWorkspacesFromWorkspaceRules(clientsJson); +} + +bool isDoubleSpecial(std::string const &workspace_name) { + // Hyprland's IPC sometimes reports the creation of workspaces strangely named + // `special:special:`. This function checks for that and is used + // to avoid creating (and then removing) such workspaces. + // See hyprwm/Hyprland#3424 for more info. + return workspace_name.find("special:special:") != std::string::npos; +} + +bool Workspaces::isWorkspaceIgnored(std::string const &name) { + for (auto &rule : m_ignoreWorkspaces) { + if (std::regex_match(name, rule)) { + return true; + break; } - workspace->update(m_format, workspaceIcon); } + + return false; } -bool Workspaces::updateWindowsToCreate() { - bool anyWindowCreated = false; - std::vector notCreated; - for (auto &windowPayload : m_windowsToCreate) { - bool created = false; - for (auto &workspace : m_workspaces) { - if (workspace->onWindowOpened(windowPayload)) { - created = true; - anyWindowCreated = true; - break; +void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJson) { + spdlog::info("Loading persistent workspaces from Waybar config"); + const std::vector keys = m_persistentWorkspaceConfig.getMemberNames(); + std::vector persistentWorkspacesToCreate; + + const std::string currentMonitor = m_bar.output->name; + const bool monitorInConfig = std::find(keys.begin(), keys.end(), currentMonitor) != keys.end(); + for (const std::string &key : keys) { + // only add if either: + // 1. key is the current monitor name + // 2. key is "*" and this monitor is not already defined in the config + bool canCreate = key == currentMonitor || (key == "*" && !monitorInConfig); + const Json::Value &value = m_persistentWorkspaceConfig[key]; + spdlog::trace("Parsing persistent workspace config: {} => {}", key, value.toStyledString()); + + if (value.isInt()) { + // value is a number => create that many workspaces for this monitor + if (canCreate) { + int amount = value.asInt(); + spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, currentMonitor); + for (int i = 0; i < amount; i++) { + persistentWorkspacesToCreate.emplace_back(std::to_string(m_monitorId * amount + i + 1)); + } } - } - if (!created) { - static auto const WINDOW_CREATION_TIMEOUT = 2; - if (windowPayload.incrementTimeSpentUncreated() < WINDOW_CREATION_TIMEOUT) { - notCreated.push_back(windowPayload); + } else if (value.isArray() && !value.empty()) { + // value is an array => create defined workspaces for this monitor + if (canCreate) { + for (const Json::Value &workspace : value) { + if (workspace.isInt()) { + spdlog::debug("Creating workspace {} on monitor {}", workspace, currentMonitor); + persistentWorkspacesToCreate.emplace_back(std::to_string(workspace.asInt())); + } + } } else { - registerOrphanWindow(windowPayload); + // key is the workspace and value is array of monitors to create on + for (const Json::Value &monitor : value) { + if (monitor.isString() && monitor.asString() == currentMonitor) { + persistentWorkspacesToCreate.emplace_back(currentMonitor); + break; + } + } } + } else { + // this workspace should be displayed on all monitors + persistentWorkspacesToCreate.emplace_back(key); } } - m_windowsToCreate.clear(); - m_windowsToCreate = notCreated; - return anyWindowCreated; -} -auto Workspaces::update() -> void { - doUpdate(); - AModule::update(); + for (auto const &workspace : persistentWorkspacesToCreate) { + auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name); + workspaceData["persistent-config"] = true; + m_workspacesToCreate.emplace_back(workspaceData, clientsJson); + } } -bool isDoubleSpecial(std::string const &workspace_name) { - // Hyprland's IPC sometimes reports the creation of workspaces strangely named - // `special:special:`. This function checks for that and is used - // to avoid creating (and then removing) such workspaces. - // See hyprwm/Hyprland#3424 for more info. - return workspace_name.find("special:special:") != std::string::npos; -} +void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &clientsJson) { + spdlog::info("Loading persistent workspaces from Hyprland workspace rules"); -bool Workspaces::isWorkspaceIgnored(std::string const &name) { - for (auto &rule : m_ignoreWorkspaces) { - if (std::regex_match(name, rule)) { - return true; - break; + auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); + for (Json::Value const &rule : workspaceRules) { + if (!rule["workspaceString"].isString()) { + spdlog::warn("Workspace rules: invalid workspaceString, skipping: {}", rule); + continue; + } + if (!rule["persistent"].asBool()) { + continue; + } + auto const &workspace = rule["workspaceString"].asString(); + auto const &monitor = rule["monitor"].asString(); + // create this workspace persistently if: + // 1. the allOutputs config option is enabled + // 2. the rule's monitor is the current monitor + // 3. no monitor is specified in the rule => assume it needs to be persistent on every monitor + if (allOutputs() || m_bar.output->name == monitor || monitor.empty()) { + // => persistent workspace should be shown on this monitor + auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name); + workspaceData["persistent-rule"] = true; + m_workspacesToCreate.emplace_back(workspaceData, clientsJson); + } else { + m_workspacesToRemove.emplace_back(workspace); } } - - return false; } void Workspaces::onEvent(const std::string &ev) { @@ -569,187 +565,160 @@ void Workspaces::onConfigReloaded() { init(); } -void Workspaces::updateWindowCount() { - const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - for (auto &workspace : m_workspaces) { - auto workspaceJson = - std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) { - return x["name"].asString() == workspace->name() || - (workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name()); - }); - uint32_t count = 0; - if (workspaceJson != workspacesJson.end()) { - try { - count = (*workspaceJson)["windows"].asUInt(); - } catch (const std::exception &e) { - spdlog::error("Failed to update window count: {}", e.what()); - } - } - workspace->setWindows(count); +auto Workspaces::parseConfig(const Json::Value &config) -> void { + const auto &configFormat = config["format"]; + m_format = configFormat.isString() ? configFormat.asString() : "{name}"; + m_withIcon = m_format.find("{icon}") != std::string::npos; + + if (m_withIcon && m_iconsMap.empty()) { + populateIconsMap(config["format-icons"]); } -} -void Workspaces::createWorkspace(Json::Value const &workspace_data, - Json::Value const &clients_data) { - auto workspaceName = workspace_data["name"].asString(); - spdlog::debug("Creating workspace {}", workspaceName); + populateBoolConfig(config, "all-outputs", m_allOutputs); + populateBoolConfig(config, "show-special", m_showSpecial); + populateBoolConfig(config, "active-only", m_activeOnly); + populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); - // avoid recreating existing workspaces - auto workspace = std::find_if( - m_workspaces.begin(), m_workspaces.end(), - [workspaceName](std::unique_ptr const &w) { - return (workspaceName.starts_with("special:") && workspaceName.substr(8) == w->name()) || - workspaceName == w->name(); - }); + populateSortByConfig(config); + populateIgnoreWorkspacesConfig(config); + populatePersistentWorkspacesConfig(config); + populateFormatWindowSeparatorConfig(config); + populateWindowRewriteConfig(config); +} - if (workspace != m_workspaces.end()) { - // don't recreate workspace, but update persistency if necessary - const auto keys = workspace_data.getMemberNames(); +auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { + for (const auto &name : formatIcons.getMemberNames()) { + m_iconsMap.emplace(name, formatIcons[name].asString()); + } + m_iconsMap.emplace("", ""); +} - const auto *k = "persistent-rule"; - if (std::find(keys.begin(), keys.end(), k) != keys.end()) { - spdlog::debug("Set dynamic persistency of workspace {} to: {}", workspaceName, - workspace_data[k].asBool() ? "true" : "false"); - (*workspace)->setPersistentRule(workspace_data[k].asBool()); - } +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) + -> void { + auto configValue = config[key]; + if (configValue.isBool()) { + member = configValue.asBool(); + } +} - k = "persistent-config"; - if (std::find(keys.begin(), keys.end(), k) != keys.end()) { - spdlog::debug("Set config persistency of workspace {} to: {}", workspaceName, - workspace_data[k].asBool() ? "true" : "false"); - (*workspace)->setPersistentConfig(workspace_data[k].asBool()); +auto Workspaces::populateSortByConfig(const Json::Value &config) -> void { + auto configSortBy = config["sort-by"]; + if (configSortBy.isString()) { + auto sortByStr = configSortBy.asString(); + try { + m_sortBy = m_enumParser.parseStringToEnum(sortByStr, m_sortMap); + } catch (const std::invalid_argument &e) { + m_sortBy = SortMethod::DEFAULT; + spdlog::warn( + "Invalid string representation for sort-by. Falling back to default sort method."); } + } +} - return; +auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> void { + auto ignoreWorkspaces = config["ignore-workspaces"]; + if (ignoreWorkspaces.isArray()) { + for (const auto &workspaceRegex : ignoreWorkspaces) { + if (workspaceRegex.isString()) { + std::string ruleString = workspaceRegex.asString(); + try { + const std::regex rule{ruleString, std::regex_constants::icase}; + m_ignoreWorkspaces.emplace_back(rule); + } catch (const std::regex_error &e) { + spdlog::error("Invalid rule {}: {}", ruleString, e.what()); + } + } else { + spdlog::error("Not a string: '{}'", workspaceRegex); + } + } } +} - // create new workspace - m_workspaces.emplace_back(std::make_unique(workspace_data, *this, clients_data)); - Gtk::Button &newWorkspaceButton = m_workspaces.back()->button(); - m_box.pack_start(newWorkspaceButton, false, false); - sortWorkspaces(); - newWorkspaceButton.show_all(); +auto Workspaces::populatePersistentWorkspacesConfig(const Json::Value &config) -> void { + if (config.isMember("persistent-workspaces") || config.isMember("persistent_workspaces")) { + spdlog::warn( + "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); + m_persistentWorkspaceConfig = + config.get("persistent-workspaces", config.get("persistent_workspaces", Json::Value())); + } } -void Workspaces::removeWorkspace(std::string const &name) { - spdlog::debug("Removing workspace {}", name); - auto workspace = - std::find_if(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr &x) { - return (name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name(); - }); +auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void { + auto formatWindowSeparator = config["format-window-separator"]; + m_formatWindowSeparator = + formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " "; +} - if (workspace == m_workspaces.end()) { - // happens when a workspace on another monitor is destroyed +auto Workspaces::populateWindowRewriteConfig(const Json::Value &config) -> void { + const auto &windowRewrite = config["window-rewrite"]; + if (!windowRewrite.isObject()) { + spdlog::debug("window-rewrite is not defined or is not an object, using default rules."); return; } - if ((*workspace)->isPersistentConfig()) { - spdlog::trace("Not removing config persistent workspace {}", name); - return; - } + const auto &windowRewriteDefaultConfig = config["window-rewrite-default"]; + std::string windowRewriteDefault = + windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; - m_box.remove(workspace->get()->button()); - m_workspaces.erase(workspace); + m_windowRewriteRules = util::RegexCollection( + windowRewrite, windowRewriteDefault, + [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); } -Json::Value createMonitorWorkspaceData(std::string const &name, std::string const &monitor) { - spdlog::trace("Creating persistent workspace: {} on monitor {}", name, monitor); - Json::Value workspaceData; - try { - // numbered persistent workspaces get the name as ID - workspaceData["id"] = name == "special" ? -99 : std::stoi(name); - } catch (const std::exception &e) { - // named persistent workspaces start with ID=0 - workspaceData["id"] = 0; +void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) { + if (!create_window_payload.isEmpty(*this)) { + m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this); } - workspaceData["name"] = name; - workspaceData["monitor"] = monitor; - workspaceData["windows"] = 0; - return workspaceData; } -void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJson) { - spdlog::info("Loading persistent workspaces from Waybar config"); - const std::vector keys = m_persistentWorkspaceConfig.getMemberNames(); - std::vector persistentWorkspacesToCreate; - - const std::string currentMonitor = m_bar.output->name; - const bool monitorInConfig = std::find(keys.begin(), keys.end(), currentMonitor) != keys.end(); - for (const std::string &key : keys) { - // only add if either: - // 1. key is the current monitor name - // 2. key is "*" and this monitor is not already defined in the config - bool canCreate = key == currentMonitor || (key == "*" && !monitorInConfig); - const Json::Value &value = m_persistentWorkspaceConfig[key]; - spdlog::trace("Parsing persistent workspace config: {} => {}", key, value.toStyledString()); +auto Workspaces::registerIpc() -> void { + gIPC->registerForIPC("workspace", this); + gIPC->registerForIPC("activespecial", this); + gIPC->registerForIPC("createworkspace", this); + gIPC->registerForIPC("destroyworkspace", this); + gIPC->registerForIPC("focusedmon", this); + gIPC->registerForIPC("moveworkspace", this); + gIPC->registerForIPC("renameworkspace", this); + gIPC->registerForIPC("openwindow", this); + gIPC->registerForIPC("closewindow", this); + gIPC->registerForIPC("movewindow", this); + gIPC->registerForIPC("urgent", this); + gIPC->registerForIPC("configreloaded", this); - if (value.isInt()) { - // value is a number => create that many workspaces for this monitor - if (canCreate) { - int amount = value.asInt(); - spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, currentMonitor); - for (int i = 0; i < amount; i++) { - persistentWorkspacesToCreate.emplace_back(std::to_string(m_monitorId * amount + i + 1)); - } - } - } else if (value.isArray() && !value.empty()) { - // value is an array => create defined workspaces for this monitor - if (canCreate) { - for (const Json::Value &workspace : value) { - if (workspace.isInt()) { - spdlog::debug("Creating workspace {} on monitor {}", workspace, currentMonitor); - persistentWorkspacesToCreate.emplace_back(std::to_string(workspace.asInt())); - } - } - } else { - // key is the workspace and value is array of monitors to create on - for (const Json::Value &monitor : value) { - if (monitor.isString() && monitor.asString() == currentMonitor) { - persistentWorkspacesToCreate.emplace_back(currentMonitor); - break; - } - } - } - } else { - // this workspace should be displayed on all monitors - persistentWorkspacesToCreate.emplace_back(key); - } + if (windowRewriteConfigUsesTitle()) { + spdlog::info( + "Registering for Hyprland's 'windowtitle' events because a user-defined window " + "rewrite rule uses the 'title' field."); + gIPC->registerForIPC("windowtitle", this); } +} - for (auto const &workspace : persistentWorkspacesToCreate) { - auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name); - workspaceData["persistent-config"] = true; - m_workspacesToCreate.emplace_back(workspaceData, clientsJson); +void Workspaces::removeWorkspacesToRemove() { + for (const auto &workspaceName : m_workspacesToRemove) { + removeWorkspace(workspaceName); } + m_workspacesToRemove.clear(); } -void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &clientsJson) { - spdlog::info("Loading persistent workspaces from Hyprland workspace rules"); +void Workspaces::removeWorkspace(std::string const &name) { + spdlog::debug("Removing workspace {}", name); + auto workspace = + std::find_if(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr &x) { + return (name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name(); + }); - auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); - for (Json::Value const &rule : workspaceRules) { - if (!rule["workspaceString"].isString()) { - spdlog::warn("Workspace rules: invalid workspaceString, skipping: {}", rule); - continue; - } - if (!rule["persistent"].asBool()) { - continue; - } - auto const &workspace = rule["workspaceString"].asString(); - auto const &monitor = rule["monitor"].asString(); - // create this workspace persistently if: - // 1. the allOutputs config option is enabled - // 2. the rule's monitor is the current monitor - // 3. no monitor is specified in the rule => assume it needs to be persistent on every monitor - if (allOutputs() || m_bar.output->name == monitor || monitor.empty()) { - // => persistent workspace should be shown on this monitor - auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name); - workspaceData["persistent-rule"] = true; - m_workspacesToCreate.emplace_back(workspaceData, clientsJson); - } else { - m_workspacesToRemove.emplace_back(workspace); - } + if (workspace == m_workspaces.end()) { + // happens when a workspace on another monitor is destroyed + return; + } + + if ((*workspace)->isPersistentConfig()) { + spdlog::trace("Not removing config persistent workspace {}", name); + return; } + + m_box.remove(workspace->get()->button()); + m_workspaces.erase(workspace); } void Workspaces::setCurrentMonitorId() { @@ -767,60 +736,6 @@ void Workspaces::setCurrentMonitorId() { } } -void Workspaces::initializeWorkspaces() { - spdlog::debug("Initializing workspaces"); - - // if the workspace rules changed since last initialization, make sure we reset everything: - for (auto &workspace : m_workspaces) { - m_workspacesToRemove.push_back(workspace->name()); - } - - // get all current workspaces - auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - auto const clientsJson = gIPC->getSocket1JsonReply("clients"); - - for (Json::Value workspaceJson : workspacesJson) { - std::string workspaceName = workspaceJson["name"].asString(); - if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && - (!workspaceName.starts_with("special") || showSpecial()) && - !isWorkspaceIgnored(workspaceName)) { - m_workspacesToCreate.emplace_back(workspaceJson, clientsJson); - } else { - extendOrphans(workspaceJson["id"].asInt(), clientsJson); - } - } - - spdlog::debug("Initializing persistent workspaces"); - if (m_persistentWorkspaceConfig.isObject()) { - // a persistent workspace config is defined, so use that instead of workspace rules - loadPersistentWorkspacesFromConfig(clientsJson); - } - // load Hyprland's workspace rules - loadPersistentWorkspacesFromWorkspaceRules(clientsJson); -} - -void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) { - spdlog::trace("Extending orphans with workspace {}", workspaceId); - for (const auto &client : clientsJson) { - if (client["workspace"]["id"].asInt() == workspaceId) { - registerOrphanWindow({client}); - } - } -} - -void Workspaces::init() { - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); - - initializeWorkspaces(); - dp.emit(); -} - -Workspaces::~Workspaces() { - gIPC->unregisterForIPC(this); - // wait for possible event handler to finish - std::lock_guard lg(m_mutex); -} - void Workspaces::sortWorkspaces() { std::sort(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr &a, std::unique_ptr &b) { @@ -903,15 +818,102 @@ void Workspaces::setUrgentWorkspace(std::string const &windowaddress) { } } -std::string Workspaces::getRewrite(std::string window_class, std::string window_title) { - std::string windowReprKey; - if (windowRewriteConfigUsesTitle()) { - windowReprKey = fmt::format("class<{}> title<{}>", window_class, window_title); - } else { - windowReprKey = fmt::format("class<{}>", window_class); +auto Workspaces::update() -> void { + doUpdate(); + AModule::update(); +} + +void Workspaces::updateWindowCount() { + const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + for (auto &workspace : m_workspaces) { + auto workspaceJson = + std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) { + return x["name"].asString() == workspace->name() || + (workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name()); + }); + uint32_t count = 0; + if (workspaceJson != workspacesJson.end()) { + try { + count = (*workspaceJson)["windows"].asUInt(); + } catch (const std::exception &e) { + spdlog::error("Failed to update window count: {}", e.what()); + } + } + workspace->setWindows(count); } - auto const rewriteRule = m_windowRewriteRules.get(windowReprKey); - return fmt::format(fmt::runtime(rewriteRule), fmt::arg("class", window_class), - fmt::arg("title", window_title)); } + +bool Workspaces::updateWindowsToCreate() { + bool anyWindowCreated = false; + std::vector notCreated; + for (auto &windowPayload : m_windowsToCreate) { + bool created = false; + for (auto &workspace : m_workspaces) { + if (workspace->onWindowOpened(windowPayload)) { + created = true; + anyWindowCreated = true; + break; + } + } + if (!created) { + static auto const WINDOW_CREATION_TIMEOUT = 2; + if (windowPayload.incrementTimeSpentUncreated() < WINDOW_CREATION_TIMEOUT) { + notCreated.push_back(windowPayload); + } else { + registerOrphanWindow(windowPayload); + } + } + } + m_windowsToCreate.clear(); + m_windowsToCreate = notCreated; + return anyWindowCreated; +} + +void Workspaces::updateWorkspaceStates(const std::vector &visibleWorkspaces) { + auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces"); + for (auto &workspace : m_workspaces) { + workspace->setActive(workspace->name() == m_activeWorkspaceName || + workspace->name() == m_activeSpecialWorkspaceName); + if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { + workspace->setUrgent(false); + } + workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), + workspace->name()) != visibleWorkspaces.end()); + std::string &workspaceIcon = m_iconsMap[""]; + if (m_withIcon) { + workspaceIcon = workspace->selectIcon(m_iconsMap); + } + auto updatedWorkspace = std::find_if( + updatedWorkspaces.begin(), updatedWorkspaces.end(), [&workspace](const auto &w) { + auto wNameRaw = w["name"].asString(); + auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; + return wName == workspace->name(); + }); + if (updatedWorkspace != updatedWorkspaces.end()) { + workspace->setOutput((*updatedWorkspace)["monitor"].asString()); + } + workspace->update(m_format, workspaceIcon); + } +} + +int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) { + // Rules that match against title are prioritized + // Rules that don't specify if they're matching against either title or class are deprioritized + bool const hasTitle = window_rule.find("title") != std::string::npos; + bool const hasClass = window_rule.find("class") != std::string::npos; + + if (hasTitle && hasClass) { + m_anyWindowRewriteRuleUsesTitle = true; + return 3; + } + if (hasTitle) { + m_anyWindowRewriteRuleUsesTitle = true; + return 2; + } + if (hasClass) { + return 1; + } + return 0; +} + } // namespace waybar::modules::hyprland From af87388eb43ff7ce0c01ef3b1630db37325fc082 Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Tue, 28 May 2024 09:13:11 +0200 Subject: [PATCH 245/407] Update docker.yml --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 12927fb0e..37042b161 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -28,4 +28,4 @@ jobs: context: . file: Dockerfiles/${{ matrix.os }} push: true - tags: alexays/${{ matrix.os }}:latest \ No newline at end of file + tags: alexays/waybar:${{ matrix.os }} From a4a4be3381faa7ddae690bd1b9851c558e72ba3e Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 28 May 2024 09:19:21 +0200 Subject: [PATCH 246/407] fix: lint --- include/bar.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bar.hpp b/include/bar.hpp index 2f225de60..6900da479 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -8,8 +8,8 @@ #include #include -#include #include +#include #include #include "AModule.hpp" From 1a9952d0c06eb5df35dc24662d055c7c30026f0d Mon Sep 17 00:00:00 2001 From: John Titor <50095635+JohnRTitor@users.noreply.github.com> Date: Tue, 28 May 2024 13:08:45 +0530 Subject: [PATCH 247/407] workflows: add nix-test workflow Checks the flake Builds and tests the package --- .github/workflows/nix-tests.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/nix-tests.yml diff --git a/.github/workflows/nix-tests.yml b/.github/workflows/nix-tests.yml new file mode 100644 index 000000000..8859ecb5d --- /dev/null +++ b/.github/workflows/nix-tests.yml @@ -0,0 +1,17 @@ +name: "Nix-Tests" +on: + pull_request: + push: +jobs: + nix-flake-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v27 + with: + extra_nix_config: | + experimental-features = nix-command flakes + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - run: nix flake show + - run: nix flake check --print-build-logs + - run: nix build --print-build-logs From b6ca3ea4d9d57aa1bfca2e2600e4dcabea36ec62 Mon Sep 17 00:00:00 2001 From: John Titor <50095635+JohnRTitor@users.noreply.github.com> Date: Tue, 28 May 2024 13:25:40 +0530 Subject: [PATCH 248/407] worflows: add update-flake-lock action automatically updates the nix flake lock file runs once a month --- .github/workflows/nix-update-flake-lock.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/nix-update-flake-lock.yml diff --git a/.github/workflows/nix-update-flake-lock.yml b/.github/workflows/nix-update-flake-lock.yml new file mode 100644 index 000000000..2b65c329d --- /dev/null +++ b/.github/workflows/nix-update-flake-lock.yml @@ -0,0 +1,21 @@ +name: update-flake-lock +on: + workflow_dispatch: # allows manual triggering + schedule: + - cron: '0 0 1 * *' # Run monthly + push: + paths: + - 'flake.nix' +jobs: + lockfile: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v27 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - name: Update flake.lock + uses: DeterminateSystems/update-flake-lock@v21 From c3581fb66ba23f8466358ce920f9038ce7607ab2 Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 17:20:31 +0200 Subject: [PATCH 249/407] =?UTF-8?q?=F0=9F=A5=85=20only=20check=20menu=20if?= =?UTF-8?q?=20speciifed=20in=20the=20conf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/AModule.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 77b82ceea..9948edabc 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -134,11 +134,14 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) { format = rec->second; } - // Check if the event is the one specified for the "menu" option - if (rec->second == config_["menu"].asString()) { - // Popup the menu - gtk_widget_show_all(GTK_WIDGET(menu_)); - gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast(e)); + // Check that a menu has been configured + if (config_["menu"].isString()) { + // Check if the event is the one specified for the "menu" option + if (rec->second == config_["menu"].asString()) { + // Popup the menu + gtk_widget_show_all(GTK_WIDGET(menu_)); + gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast(e)); + } } // Second call user scripts if (!format.empty()) { From 29e3d8c371d6dee3979ac3b2b2c365605e1b5057 Mon Sep 17 00:00:00 2001 From: alttabber Date: Tue, 28 May 2024 16:57:47 +0200 Subject: [PATCH 250/407] Hide non-visible special workspaces --- include/modules/hyprland/workspaces.hpp | 2 ++ man/waybar-hyprland-workspaces.5.scd | 5 +++++ src/modules/hyprland/workspace.cpp | 4 ++++ src/modules/hyprland/workspaces.cpp | 1 + 4 files changed, 12 insertions(+) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index e99bf40c0..0432b8703 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -38,6 +38,7 @@ class Workspaces : public AModule, public EventHandler { auto allOutputs() const -> bool { return m_allOutputs; } auto showSpecial() const -> bool { return m_showSpecial; } auto activeOnly() const -> bool { return m_activeOnly; } + auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; } auto moveToMonitor() const -> bool { return m_moveToMonitor; } auto getBarOutput() const -> std::string { return m_bar.output->name; } @@ -113,6 +114,7 @@ class Workspaces : public AModule, public EventHandler { bool m_allOutputs = false; bool m_showSpecial = false; bool m_activeOnly = false; + bool m_specialVisibleOnly = false; bool m_moveToMonitor = false; Json::Value m_persistentWorkspaceConfig; diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 2d0641b4e..406ada7a2 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -42,6 +42,11 @@ Addressed by *hyprland/workspaces* default: false ++ If set to true, special workspaces will be shown. +*special-visible-only*: ++ + typeof: bool ++ + default: false ++ + If this and show-special are to true, special workspaces will be shown only if visible. + *all-outputs*: ++ typeof: bool ++ default: false ++ diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index 694d3b0d6..bf0a33681 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -182,6 +182,10 @@ void Workspace::update(const std::string &format, const std::string &icon) { m_button.hide(); return; } + if (this->m_workspaceManager.specialVisibleOnly() && this->isSpecial() && !this->isVisible()) { + m_button.hide(); + return; + } m_button.show(); auto styleContext = m_button.get_style_context(); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index eca30134e..3b129375c 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -576,6 +576,7 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { populateBoolConfig(config, "all-outputs", m_allOutputs); populateBoolConfig(config, "show-special", m_showSpecial); + populateBoolConfig(config, "special-visible-only", m_specialVisibleOnly); populateBoolConfig(config, "active-only", m_activeOnly); populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); From 24e8766aaaf5b6f8862d819790c7aa53c8fcb02f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 10:57:57 -0500 Subject: [PATCH 251/407] hyprland/backend: use /tmp Was hardcoded to /tmp in previous versions --- src/modules/hyprland/backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 1b47ff7dd..29c65633b 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -27,7 +27,7 @@ std::filesystem::path getSocketFolder(const char* instanceSig) { socketFolder = xdgRuntimeDir / "hypr"; } else { spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr"); - socketFolder = std::filesystem::temp_directory_path() / "hypr"; + socketFolder = std::filesystem::path("/tmp") / "hypr"; } socketFolder = socketFolder / instanceSig; return socketFolder; From f3ed5ca5afe3966f1af429eb5171526388dc18a6 Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 18:08:31 +0200 Subject: [PATCH 252/407] =?UTF-8?q?=F0=9F=8C=B1=20update=20default=20confi?= =?UTF-8?q?g=20with=20a=20menu=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/config.jsonc | 15 ++++++++++++- resources/custom_modules/power_menu.xml | 28 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 resources/custom_modules/power_menu.xml diff --git a/resources/config.jsonc b/resources/config.jsonc index 329275b11..7e0771f51 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -30,7 +30,8 @@ "battery", "battery#bat2", "clock", - "tray" + "tray", + "custom/power" ], // Modules configuration // "sway/workspaces": { @@ -198,5 +199,17 @@ "escape": true, "exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name + }, + "custom/power": { + "format" : "⏻ ", + "tooltip": false, + "menu": "on-click", + "menu-file": "$HOME/.config/waybar/power_menu.xml", // Menu file in resources folder + "menu-actions": { + "shutdown": "shutdown", + "reboot": "reboot", + "suspend": "systemctl suspend", + "hibernate": "systemctl hibernate" + } } } diff --git a/resources/custom_modules/power_menu.xml b/resources/custom_modules/power_menu.xml new file mode 100644 index 000000000..aa2a42cae --- /dev/null +++ b/resources/custom_modules/power_menu.xml @@ -0,0 +1,28 @@ + + + + + + Suspend + + + + + Hibernate + + + + + Shutdown + + + + + + + + Reboot + + + + From 161c8c4c47e219a3c457e01a355e7b6fde029228 Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 19:30:06 +0200 Subject: [PATCH 253/407] =?UTF-8?q?=F0=9F=A5=85=20do=20not=20crash=20when?= =?UTF-8?q?=20unable=20to=20make=20the=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the menu cannot be built (file not existing, or wrongly formatted), the menu is not created and a warning with an explanaition is displayed. --- src/ALabel.cpp | 49 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 568506173..5497c62ac 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -2,6 +2,8 @@ #include +#include +#include #include namespace waybar { @@ -56,18 +58,41 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st // If a GTKMenu is requested in the config if (config_["menu"].isString()) { // Create the GTKMenu widget - GtkBuilder* builder = gtk_builder_new_from_file(config_["menu-file"].asString().c_str()); - menu_ = gtk_builder_get_object(builder, "menu"); - submenus_ = std::map(); - menuActionsMap_ = std::map(); - // Linking actions to the GTKMenu based on - for (Json::Value::const_iterator it = config_["menu-actions"].begin(); - it != config_["menu-actions"].end(); ++it) { - std::string key = it.key().asString(); - submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); - menuActionsMap_[key] = it->asString(); - g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), - (gpointer)menuActionsMap_[key].c_str()); + try { + // Check that the file exists + std::string menuFile = config_["menu-file"].asString(); + // Read the menu descriptor file + std::ifstream file(menuFile); + if (!file.is_open()) { + throw std::runtime_error("Failed to open file: " + menuFile); + } + std::stringstream fileContent; + fileContent << file.rdbuf(); + GtkBuilder* builder = gtk_builder_new(); + + // Make the GtkBuilder and check for errors in his parsing + if (!gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr)) { + throw std::runtime_error("Error found in the file " + menuFile); + } + + menu_ = gtk_builder_get_object(builder, "menu"); + if (!menu_) { + throw std::runtime_error("Failed to get 'menu' object from GtkBuilder"); + } + submenus_ = std::map(); + menuActionsMap_ = std::map(); + + // Linking actions to the GTKMenu based on + for (Json::Value::const_iterator it = config_["menu-actions"].begin(); + it != config_["menu-actions"].end(); ++it) { + std::string key = it.key().asString(); + submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); + menuActionsMap_[key] = it->asString(); + g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), + (gpointer)menuActionsMap_[key].c_str()); + } + } catch (std::runtime_error& e) { + spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what()); } } From d9f2e0f7d28c2107e266aeffd0dcd929a8d8bf4d Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 20:42:12 +0200 Subject: [PATCH 254/407] =?UTF-8?q?=F0=9F=93=9D=20add=20menu=20config=20in?= =?UTF-8?q?formations=20in=20manpages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- man/waybar-backlight.5.scd | 13 +++++++++++++ man/waybar-battery.5.scd | 13 +++++++++++++ man/waybar-bluetooth.5.scd | 13 +++++++++++++ man/waybar-cava.5.scd | 12 ++++++++++++ man/waybar-clock.5.scd | 12 ++++++++++++ man/waybar-custom.5.scd | 13 +++++++++++++ man/waybar-disk.5.scd | 13 +++++++++++++ man/waybar-hyprland-language.5.scd | 13 +++++++++++++ man/waybar-hyprland-submap.5.scd | 13 +++++++++++++ man/waybar-idle-inhibitor.5.scd | 13 +++++++++++++ man/waybar-inhibitor.5.scd | 13 +++++++++++++ man/waybar-jack.5.scd | 13 +++++++++++++ man/waybar-memory.5.scd | 13 +++++++++++++ man/waybar-mpd.5.scd | 13 +++++++++++++ man/waybar-network.5.scd | 13 +++++++++++++ man/waybar-pulseaudio.5.scd | 13 +++++++++++++ man/waybar-river-layout.5.scd | 13 +++++++++++++ man/waybar-river-mode.5.scd | 13 +++++++++++++ man/waybar-river-window.5.scd | 13 +++++++++++++ man/waybar-sndio.5.scd | 13 +++++++++++++ man/waybar-sway-language.5.scd | 13 +++++++++++++ man/waybar-sway-mode.5.scd | 13 +++++++++++++ man/waybar-sway-scratchpad.5.scd | 13 +++++++++++++ man/waybar-systemd-failed-units.5.scd | 13 +++++++++++++ man/waybar-temperature.5.scd | 13 +++++++++++++ man/waybar-upower.5.scd | 13 +++++++++++++ man/waybar-wireplumber.5.scd | 13 +++++++++++++ 27 files changed, 349 insertions(+) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index b92abd12f..1f674fc00 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -81,6 +81,19 @@ The *backlight* module displays the current backlight level. default: 1.0 ++ The speed at which to change the brightness when scrolling. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLE: ``` diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 25c7cacab..4fe9650a7 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -109,6 +109,19 @@ The *battery* module displays the current capacity and state (eg. charging) of y default: false ++ Option to enable battery compatibility if not detected. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{capacity}*: Capacity in percentage diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 3808e855a..1783dab32 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -129,6 +129,19 @@ Addressed by *bluetooth* typeof: string ++ This format is used to define how each connected device should be displayed within the *device_enumerate* format replacement in the tooltip menu. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{status}*: Status of the bluetooth device. diff --git a/man/waybar-cava.5.scd b/man/waybar-cava.5.scd index cf75441b3..2a7e8f678 100644 --- a/man/waybar-cava.5.scd +++ b/man/waybar-cava.5.scd @@ -120,6 +120,18 @@ libcava lives in: :[ string :[ /dev/stdout :[ It's impossible to set it. Waybar sets it to = /dev/stdout for internal needs +|[ *menu* +:[ string +:[ +:[ Action that popups the menu. +|[ *menu-file* +:[ string +:[ +:[ Location of the menu descriptor file. There need to be an element of type GtkMenu with id *menu* +|[ *menu-actions* +:[ array +:[ +:[ The actions corresponding to the buttons of the menu. Configuration can be provided as: - The only cava configuration file which is provided through *cava_config*. The rest configuration can be skipped diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index e8ef7bed9..40aedd152 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -84,6 +84,18 @@ $XDG_CONFIG_HOME/waybar/config ++ :[ string :[ same as format :[ Tooltip on hover +|[ *menu* +:[ string +:[ +:[ Action that popups the menu. +|[ *menu-file* +:[ string +:[ +:[ Location of the menu descriptor file. There need to be an element of type GtkMenu with id *menu* +|[ *menu-actions* +:[ array +:[ +:[ The actions corresponding to the buttons of the menu. View all valid format options in *strftime(3)* or have a look https://en.cppreference.com/w/cpp/chrono/duration/formatter diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index a62c93126..df866ae14 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -121,6 +121,19 @@ Addressed by *custom/* default: false ++ Option to enable escaping of script output. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # RETURN-TYPE When *return-type* is set to *json*, Waybar expects the *exec*-script to output its data in JSON format. diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index a279718b7..df9ca4e5a 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -93,6 +93,19 @@ Addressed by *disk* typeof: string ++ Use with specific_free, specific_used, and specific_total to force calculation to always be in a certain unit. Accepts kB, kiB, MB, Mib, GB, GiB, TB, TiB. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{percentage_used}*: Percentage of disk in use. diff --git a/man/waybar-hyprland-language.5.scd b/man/waybar-hyprland-language.5.scd index dba7dbcac..33b28ae4e 100644 --- a/man/waybar-hyprland-language.5.scd +++ b/man/waybar-hyprland-language.5.scd @@ -25,6 +25,19 @@ Addressed by *hyprland/language* typeof: string ++ Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index 3f23784da..64398e614 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -80,6 +80,19 @@ Addressed by *hyprland/submap* default: Default ++ Option to set the submap name to display when not in an active submap. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLES diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index 71b3b30c6..f7677634c 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -89,6 +89,19 @@ screensaver, also known as "presentation mode". typeof: string ++ This format is used when the inhibit is deactivated. +*menu*: ++ + typeof: string ++ + Action that popups the menu. Cannot be "on-click". + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{status}*: status (*activated* or *deactivated*) diff --git a/man/waybar-inhibitor.5.scd b/man/waybar-inhibitor.5.scd index 47b6ffce2..679a5c4b0 100644 --- a/man/waybar-inhibitor.5.scd +++ b/man/waybar-inhibitor.5.scd @@ -76,6 +76,19 @@ See *systemd-inhibit*(1) for more information. default: true ++ Option to disable tooltip on hover. +*menu*: ++ + typeof: string ++ + Action that popups the menu. Cannot be "on-click". + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{status}*: status (*activated* or *deactivated*) diff --git a/man/waybar-jack.5.scd b/man/waybar-jack.5.scd index 87a383542..573b36c27 100644 --- a/man/waybar-jack.5.scd +++ b/man/waybar-jack.5.scd @@ -85,6 +85,19 @@ Addressed by *jack* typeof: string ++ Command to execute when the module is updated. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{load}*: The current CPU load estimated by JACK. diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index e0252caf3..7738c576d 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -84,6 +84,19 @@ Addressed by *memory* default: true ++ Option to disable tooltip on hover. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{percentage}*: Percentage of memory in use. diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index fe6ee5a18..2f1bdf208 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -162,6 +162,19 @@ Addressed by *mpd* default: {} ++ Icon to show depending on the "single" option (*{ "on": "...", "off": "..." }*) +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS ## WHEN PLAYING/PAUSED diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index b5580c528..cc0b470b1 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -129,6 +129,19 @@ Addressed by *network* typeof: string ++ This format is used when the displayed interface is disabled. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{ifname}*: Name of the network interface. diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index 4bc75258f..8d3761c4a 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -113,6 +113,19 @@ Additionally, you can control the volume by scrolling *up* or *down* while the c typeof: array ++ Sinks in this list will not be shown as active sink by Waybar. Entries should be the sink's description field. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name. diff --git a/man/waybar-river-layout.5.scd b/man/waybar-river-layout.5.scd index 1c09d6f65..4fb23085b 100644 --- a/man/waybar-river-layout.5.scd +++ b/man/waybar-river-layout.5.scd @@ -51,6 +51,19 @@ Addressed by *river/layout* typeof: string ++ Command to execute when you right-click on the module. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLE ``` diff --git a/man/waybar-river-mode.5.scd b/man/waybar-river-mode.5.scd index 2d63b5e1e..5769a9a2c 100644 --- a/man/waybar-river-mode.5.scd +++ b/man/waybar-river-mode.5.scd @@ -65,6 +65,19 @@ Addressed by *river/mode* typeof: double ++ Threshold to be used when scrolling. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLES ``` diff --git a/man/waybar-river-window.5.scd b/man/waybar-river-window.5.scd index dbd9f1307..7e661f438 100644 --- a/man/waybar-river-window.5.scd +++ b/man/waybar-river-window.5.scd @@ -49,6 +49,19 @@ Addressed by *river/window* typeof: string ++ Command to execute when you right-click on the module. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLES ``` diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd index 197aaba07..f8d1615d4 100644 --- a/man/waybar-sndio.5.scd +++ b/man/waybar-sndio.5.scd @@ -74,6 +74,19 @@ cursor is over the module, and clicking on the module toggles mute. typeof: double ++ Threshold to be used when scrolling. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{volume}*: Volume in percentage. diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index 1c62fd950..a1fc5d08a 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -32,6 +32,19 @@ Addressed by *sway/language* default: true ++ Option to disable tooltip on hover. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{short}*: Short name of layout (e.g. "us"). Equals to {}. diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index 44c8b81ac..1fcf3cf82 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -70,6 +70,19 @@ Addressed by *sway/mode* default: true ++ Option to disable tooltip on hover. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLES ``` diff --git a/man/waybar-sway-scratchpad.5.scd b/man/waybar-sway-scratchpad.5.scd index 64c43db6b..5ae104bcf 100644 --- a/man/waybar-sway-scratchpad.5.scd +++ b/man/waybar-sway-scratchpad.5.scd @@ -36,6 +36,19 @@ Addressed by *sway/scratchpad* default: {app}: {title} ++ The format, how information in the tooltip should be displayed. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{icon}*: Icon, as defined in *format-icons*. diff --git a/man/waybar-systemd-failed-units.5.scd b/man/waybar-systemd-failed-units.5.scd index ac92c533e..ada3ab8b5 100644 --- a/man/waybar-systemd-failed-units.5.scd +++ b/man/waybar-systemd-failed-units.5.scd @@ -36,6 +36,19 @@ Addressed by *systemd-failed-units* default: *true* ++ Option to hide this module when there is no failing units. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{nr_failed_system}*: Number of failed units from systemwide (PID=1) systemd. diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index ff2168eac..8b84ef6c3 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -110,6 +110,19 @@ Addressed by *temperature* default: true ++ Option to disable tooltip on hover. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{temperatureC}*: Temperature in Celsius. diff --git a/man/waybar-upower.5.scd b/man/waybar-upower.5.scd index 5e2a8eb85..5ec0222dc 100644 --- a/man/waybar-upower.5.scd +++ b/man/waybar-upower.5.scd @@ -62,6 +62,19 @@ compatible devices in the tooltip. default: true ++ Option to disable battery icon. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{percentage}*: The battery capacity in percentage diff --git a/man/waybar-wireplumber.5.scd b/man/waybar-wireplumber.5.scd index b08fd90f7..770ff0d5c 100644 --- a/man/waybar-wireplumber.5.scd +++ b/man/waybar-wireplumber.5.scd @@ -87,6 +87,19 @@ The *wireplumber* module displays the current volume reported by WirePlumber. default: 100 ++ The maximum volume that can be set, in percentage. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{volume}*: Volume in percentage. From 8220dbb513fe2cf9051bb685eb19784f8e3f4f2f Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 20:42:47 +0200 Subject: [PATCH 255/407] =?UTF-8?q?=F0=9F=93=9D=20add=20a=20wayba-menu=20e?= =?UTF-8?q?ntry=20for=20documenting=20popup=20menus.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- man/waybar-menu.5.scd | 111 ++++++++++++++++++++++++++++++++++++++++++ meson.build | 1 + 2 files changed, 112 insertions(+) create mode 100644 man/waybar-menu.5.scd diff --git a/man/waybar-menu.5.scd b/man/waybar-menu.5.scd new file mode 100644 index 000000000..10484e41c --- /dev/null +++ b/man/waybar-menu.5.scd @@ -0,0 +1,111 @@ +waybar-menu(5) + +# NAME + +waybar - menu property + +# OVERVIEW + + +Some modules support a 'menu', which allows to have a popup menu whan a defined +click is done over the module. + +# PROPERTIES + +A module that implements a 'menu' needs 3 properties defined in its config : + +*menu*: ++ + typeof: string ++ + Action that popups the menu. The possibles actions are : + +[- *Option* +:- *Description* +|[ *on-click* +:< When you left-click on the module +|[ *on-click-release* +:< When you release left button on the module +|[ *on-double-click* +:< When you double left click on the module +|[ *on-triple-click* +:< When you triple left click on the module +|[ *on-click-middle* +:< When you middle click on the module using mousewheel +|[ *on-click-middle-release* +:< When you release mousewheel button on the module +|[ *on-double-click-middle* +:< When you double middle click on the module +|[ *on-triple-click-middle* +:< When you triple middle click on the module +|[ *on-click-right* +:< When you right click on the module using +|[ *on-click-right-release* +:< When you release right button on the module +|[ *on-double-click-right* +:< When you double right click on the module +|[ *on-triple-click-right* +:< When you triple middle click on the module +|[ *on-click-backward* +:< When you click on the module using mouse backward button +|[ *on-click-backward-release* +:< When you release mouse backward button on the module +|[ *on-double-click-backward* +:< When you double click on the module using mouse backward button +|[ *on-triple-click-backward* +:< When you triple click on the module using mouse backawrd button +|[ *on-click-forward* +:< When you click on the module using mouse forward button +|[ *on-click-forward-release* +:< When you release mouse forward button on the module +|[ *on-double-click-forward* +:< When you double click on the module using mouse forward button +|[ *on-triple-click-forward* +:< When you triple click on the module using mouse forward button + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu*. + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. The identifiers of + each actions needs to exists as an id in the 'menu-file' for it to be linked + properly. + +# EXAMPLE + +``` +"custom/power": { + "format" : "⏻ ", + "tooltip": false, + "menu": "on-click", + "menu-file": "~/.config/waybar/power_menu.xml", + "menu-actions": { + "shutdown": "shutdown", + "reboot": "reboot", + "suspend": "systemctl suspend", + "hibernate": "systemctl hibernate", + }, +}, +``` + +# STYLING MENUS + +- *menu* + Style for the menu + +- *menuitem* + Style for items in the menu + +# EXAMPLE: + +``` +menu { + border-radius: 15px; + background: #161320; + color: #B5E8E0; +} +menuitem { + border-radius: 15px; +} +``` diff --git a/meson.build b/meson.build index a57b17f81..56b772a54 100644 --- a/meson.build +++ b/meson.build @@ -192,6 +192,7 @@ man_files = files( 'man/waybar-idle-inhibitor.5.scd', 'man/waybar-image.5.scd', 'man/waybar-states.5.scd', + 'man/waybar-menu.5.scd', 'man/waybar-temperature.5.scd', ) From 885290d907ac49b4d6289b0e35daf01faedc698a Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 21:02:07 +0200 Subject: [PATCH 256/407] =?UTF-8?q?=F0=9F=93=9D=20improve=20waybar-menu=20?= =?UTF-8?q?file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- man/waybar-menu.5.scd | 62 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/man/waybar-menu.5.scd b/man/waybar-menu.5.scd index 10484e41c..19790ed47 100644 --- a/man/waybar-menu.5.scd +++ b/man/waybar-menu.5.scd @@ -72,23 +72,65 @@ A module that implements a 'menu' needs 3 properties defined in its config : each actions needs to exists as an id in the 'menu-file' for it to be linked properly. +# MENU-FILE + +The menu-file is an `.xml` file representing a GtkBuilder. Documentation for it +can be found here : https://docs.gtk.org/gtk4/class.Builder.html + +Here, it needs to have an element of type GtkMenu with id "menu". Eeach actions +in *menu-actions* are linked to elements in the *menu-file* file by the id of +the elements. + # EXAMPLE +Module config : ``` "custom/power": { - "format" : "⏻ ", - "tooltip": false, - "menu": "on-click", - "menu-file": "~/.config/waybar/power_menu.xml", - "menu-actions": { - "shutdown": "shutdown", - "reboot": "reboot", - "suspend": "systemctl suspend", - "hibernate": "systemctl hibernate", - }, + "format" : "⏻ ", + "tooltip": false, + "menu": "on-click", + "menu-file": "~/.config/waybar/power_menu.xml", + "menu-actions": { + "shutdown": "shutdown", + "reboot": "reboot", + "suspend": "systemctl suspend", + "hibernate": "systemctl hibernate", + }, }, ``` +~/.config/waybar/power_menu.xml : +``` + + + + + + Suspend + + + + + Hibernate + + + + + Shutdown + + + + + + + + Reboot + + + + +``` + # STYLING MENUS - *menu* From 8adb0a5644db9e204277ceba8c548e516f0139c5 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 15:03:22 -0500 Subject: [PATCH 257/407] .github/workflows: fix meson deprecations --- .github/workflows/freebsd.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 0b628d19b..7effb4840 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -31,6 +31,6 @@ jobs: libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ pkgconf pipewire pulseaudio scdoc sndio spdlog wayland-protocols upower \ libinotify - meson build -Dman-pages=enabled + meson setup build -Dman-pages=enabled ninja -C build meson test -C build --no-rebuild --print-errorlogs --suite waybar diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index dc6b7edef..ae0f1f7f4 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: configure - run: meson -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build + run: meson setup -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build - name: build run: ninja -C build - name: test From 381fe830081c4ce8fa7236b7f1e4e744f12487fe Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 15:14:24 -0500 Subject: [PATCH 258/407] Makefile: fix meson deprecations --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b1dbfc6e6..3bb11199e 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,11 @@ default: build build: - meson build + meson setup build ninja -C build build-debug: - meson build --buildtype=debug + meson setup build --buildtype=debug ninja -C build install: build From c5b5b64dfa45bd30ff2b3cc7d158728dea5619cd Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 15:33:16 -0500 Subject: [PATCH 259/407] modules/temperature: remove unused import --- src/modules/temperature.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 922fa6395..889109826 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -1,7 +1,5 @@ #include "modules/temperature.hpp" -#include - #include #include From cf66604f85562ccee510b5e8a0ef402d31f94075 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 30 May 2024 19:35:32 +0200 Subject: [PATCH 260/407] fix fedora image --- Dockerfiles/fedora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index 5892159c7..9dc0337bd 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -29,6 +29,6 @@ RUN dnf install -y @c-development \ 'pkgconfig(wayland-client)' \ 'pkgconfig(wayland-cursor)' \ 'pkgconfig(wayland-protocols)' \ - 'pkgconfig(wireplumber-0.4)' \ + 'pkgconfig(wireplumber-0.5)' \ 'pkgconfig(xkbregistry)' && \ dnf clean all -y From 532a90259b68b08385648aa9332de78f1df709bf Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 30 May 2024 20:18:33 +0200 Subject: [PATCH 261/407] Dont fail docker image builds when another build fails --- .github/workflows/docker.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 37042b161..d9fc5d3e2 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -9,6 +9,7 @@ jobs: build-and-push: runs-on: ubuntu-latest strategy: + fail-fast: false # don't fail the other jobs if one of the images fails to build matrix: os: [ 'alpine', 'archlinux', 'debian', 'fedora', 'gentoo', 'opensuse' ] From e9350cf25ff61a6e4525ca88b39ab6174ef04cf1 Mon Sep 17 00:00:00 2001 From: Jack Wilsdon Date: Fri, 31 May 2024 14:31:29 +0000 Subject: [PATCH 262/407] Fix format replacement names --- man/waybar-mpris.5.scd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 186d73c6a..455fcb177 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -142,11 +142,11 @@ The *mpris* module displays currently playing media via libplayerctl. *player-icons*: ++ typeof: map[string]string ++ - Allows setting _{player-icon}_ based on player-name property. + Allows setting _{player_icon}_ based on player-name property. *status-icons*: ++ typeof: map[string]string ++ - Allows setting _{status-icon}_ based on player status (playing, paused, stopped). + Allows setting _{status_icon}_ based on player status (playing, paused, stopped). # FORMAT REPLACEMENTS From 1474cc626d0bc353bd27fb682cf372150ee94523 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 1 Jun 2024 00:09:05 +0000 Subject: [PATCH 263/407] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/44d0940ea560dee511026a53f0e2e2cde489b4d4?narHash=sha256-YN/Ciidm%2BA0fmJPWlHBGvVkcarYWSC%2Bs3NTPk/P%2Bq3c%3D' (2024-03-23) → 'github:NixOS/nixpkgs/ad57eef4ef0659193044870c731987a6df5cf56b?narHash=sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs%3D' (2024-05-29) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 7647478b7..f35322ece 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1711163522, - "narHash": "sha256-YN/Ciidm+A0fmJPWlHBGvVkcarYWSC+s3NTPk/P+q3c=", + "lastModified": 1716948383, + "narHash": "sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4", + "rev": "ad57eef4ef0659193044870c731987a6df5cf56b", "type": "github" }, "original": { From 4fbd4f212a80009e92d179f9e96a50562891c2e8 Mon Sep 17 00:00:00 2001 From: giskard Date: Thu, 9 May 2024 01:45:21 +0800 Subject: [PATCH 264/407] privacy: consider only configured modules along with the local clang-tidy warning fixes --- src/modules/privacy/privacy.cpp | 81 +++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index d3e3d4b2f..97996c336 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -53,23 +53,22 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st modules.append(obj); } } - for (const auto& module_config : modules) { - if (!module_config.isObject() || !module_config["type"].isString()) continue; - const std::string type = module_config["type"].asString(); - if (type == "screenshare") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_VIDEO_INPUT, - &nodes_screenshare, pos, iconSize, transition_duration); - box_.add(*item); - } else if (type == "audio-in") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_INPUT, - &nodes_audio_in, pos, iconSize, transition_duration); - box_.add(*item); - } else if (type == "audio-out") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_OUTPUT, - &nodes_audio_out, pos, iconSize, transition_duration); + + std::map > typeMap = { + {"screenshare", {&nodes_screenshare, PRIVACY_NODE_TYPE_VIDEO_INPUT}}, + {"audio-in", {&nodes_audio_in, PRIVACY_NODE_TYPE_AUDIO_INPUT}}, + {"audio-out", {&nodes_audio_out, PRIVACY_NODE_TYPE_AUDIO_OUTPUT}}, + }; + + for (const auto& module : modules) { + if (!module.isObject() || !module["type"].isString()) continue; + const std::string type = module["type"].asString(); + + auto iter = typeMap.find(type); + if (iter != typeMap.end()) { + auto& [nodePtr, nodeType] = iter->second; + auto* item = Gtk::make_managed(module, nodeType, nodePtr, pos, iconSize, + transition_duration); box_.add(*item); } } @@ -114,26 +113,35 @@ void Privacy::onPrivacyNodesChanged() { } auto Privacy::update() -> void { - mutex_.lock(); - bool screenshare = false; - bool audio_in = false; - bool audio_out = false; + // set in modules or not + bool setScreenshare = false; + bool setAudioIn = false; + bool setAudioOut = false; + // used or not + bool useScreenshare = false; + bool useAudioIn = false; + bool useAudioOut = false; + + mutex_.lock(); for (Gtk::Widget* widget : box_.get_children()) { auto* module = dynamic_cast(widget); if (module == nullptr) continue; switch (module->privacy_type) { case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: - screenshare = !nodes_screenshare.empty(); - module->set_in_use(screenshare); + setScreenshare = true; + useScreenshare = !nodes_screenshare.empty(); + module->set_in_use(useScreenshare); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT: - audio_in = !nodes_audio_in.empty(); - module->set_in_use(audio_in); + setAudioIn = true; + useAudioIn = !nodes_audio_in.empty(); + module->set_in_use(useAudioIn); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT: - audio_out = !nodes_audio_out.empty(); - module->set_in_use(audio_out); + setAudioOut = true; + useAudioOut = !nodes_audio_out.empty(); + module->set_in_use(useAudioOut); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_NONE: break; @@ -142,25 +150,28 @@ auto Privacy::update() -> void { mutex_.unlock(); // Hide the whole widget if none are in use - bool is_visible = screenshare || audio_in || audio_out; - if (is_visible != event_box_.get_visible()) { + bool isVisible = (setScreenshare && useScreenshare) || (setAudioIn && useAudioIn) || + (setAudioOut && useAudioOut); + + if (isVisible != event_box_.get_visible()) { // Disconnect any previous connection so that it doesn't get activated in // the future, hiding the module when it should be visible visibility_conn.disconnect(); - if (is_visible) { + if (isVisible) { event_box_.set_visible(true); } else { // Hides the widget when all of the privacy_item revealers animations // have finished animating visibility_conn = Glib::signal_timeout().connect( sigc::track_obj( - [this] { + [this, setScreenshare, setAudioOut, setAudioIn]() { mutex_.lock(); - bool screenshare = !nodes_screenshare.empty(); - bool audio_in = !nodes_audio_in.empty(); - bool audio_out = !nodes_audio_out.empty(); + bool visible = false; + visible |= setScreenshare && !nodes_screenshare.empty(); + visible |= setAudioIn && !nodes_audio_in.empty(); + visible |= setAudioOut && !nodes_audio_out.empty(); mutex_.unlock(); - event_box_.set_visible(screenshare || audio_in || audio_out); + event_box_.set_visible(visible); return false; }, *this), From 02eaa8b46e791ec9a47780ebb0cc36cea430e040 Mon Sep 17 00:00:00 2001 From: williammmm <91826947+williamwith4ms@users.noreply.github.com> Date: Mon, 3 Jun 2024 19:46:55 +0100 Subject: [PATCH 265/407] escape & in mediaplayer --- resources/custom_modules/mediaplayer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index acc47496d..d1bb72b4d 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -113,6 +113,7 @@ def on_metadata_changed(self, player, metadata, _=None): player_name = player.props.player_name artist = player.get_artist() title = player.get_title() + title = title.replace("&", "&") track_info = "" if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]: From 76c2f3166ef6003fe5f206925ff5dd7eb0dde6a4 Mon Sep 17 00:00:00 2001 From: Nicolas Lenz Date: Wed, 5 Jun 2024 19:58:27 +0200 Subject: [PATCH 266/407] format RegexCollection output using match results --- include/util/regex_collection.hpp | 4 ++-- src/util/regex_collection.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/util/regex_collection.hpp b/include/util/regex_collection.hpp index 5ea2882e0..fe958461d 100644 --- a/include/util/regex_collection.hpp +++ b/include/util/regex_collection.hpp @@ -36,7 +36,7 @@ class RegexCollection { std::map regex_cache; std::string default_repr; - std::string& find_match(std::string& value, bool& matched_any); + std::string find_match(std::string& value, bool& matched_any); public: RegexCollection() = default; @@ -48,4 +48,4 @@ class RegexCollection { std::string& get(std::string& value); }; -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util diff --git a/src/util/regex_collection.cpp b/src/util/regex_collection.cpp index 704d65455..db2f30ea1 100644 --- a/src/util/regex_collection.cpp +++ b/src/util/regex_collection.cpp @@ -31,11 +31,12 @@ RegexCollection::RegexCollection(const Json::Value& map, std::string default_rep std::sort(rules.begin(), rules.end(), [](Rule& a, Rule& b) { return a.priority > b.priority; }); } -std::string& RegexCollection::find_match(std::string& value, bool& matched_any) { +std::string RegexCollection::find_match(std::string& value, bool& matched_any) { for (auto& rule : rules) { - if (std::regex_search(value, rule.rule)) { + std::smatch match; + if (std::regex_search(value, match, rule.rule)) { matched_any = true; - return rule.repr; + return match.format(rule.repr.data()); } } From d0a8c1d90dace8cccdb23e0da6d1e288770cc151 Mon Sep 17 00:00:00 2001 From: Nicolas Lenz Date: Wed, 5 Jun 2024 20:16:30 +0200 Subject: [PATCH 267/407] document capturing in window-rewrite --- man/waybar-hyprland-workspaces.5.scd | 1 + man/waybar-sway-workspaces.5.scd | 1 + 2 files changed, 2 insertions(+) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 406ada7a2..686f8aa75 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -147,6 +147,7 @@ Additional to workspace name matching, the following *format-icons* can be set. "class title<.*github.*>": "", // Windows whose class is "firefox" and title contains "github". Note that "class" always comes first. "foot": "", // Windows that contain "foot" in either class or title. For optimization reasons, it will only match against a title if at least one other window explicitly matches against a title. "code": "󰨞", + "title<.* - (.*) - VSCodium>": "codium $1" // captures part of the window title and formats it into output } } ``` diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 8f0ac8580..a65a999ba 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -171,6 +171,7 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge "window-rewrite": { "class": "", "class": "k", + "title<.* - (.*) - VSCodium>": "codium $1" // captures part of the window title and formats it into output } } ``` From 1b1442e3ba8393edcd7f3a4b10ab212d35c3f523 Mon Sep 17 00:00:00 2001 From: zspher <66728045+zspher@users.noreply.github.com> Date: Thu, 6 Jun 2024 03:23:47 +0800 Subject: [PATCH 268/407] fix: taskbar not applying empty class on empty --- src/modules/wlr/taskbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 6e3e4e08b..41d467482 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -894,7 +894,7 @@ void Taskbar::move_button(Gtk::Button &bt, int pos) { box_.reorder_child(bt, pos void Taskbar::remove_button(Gtk::Button &bt) { box_.remove(bt); - if (tasks_.empty()) { + if (box_.get_children().empty()) { box_.get_style_context()->add_class("empty"); } } From 637b220f820424f644e5785117695683b0888ddd Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 6 Jun 2024 15:24:01 -0700 Subject: [PATCH 269/407] sway/workspaces: Correct behavior when "current-only" is set The `current-only` workspace setting should display only the active workspace name as determined by its `focused` attribute. However, according to the `get_tree` output, workspaces that contain a focused window will report `"focused": false` and the window will report `"focused": true.` In this case, Waybar will not display a workspace name at all. This change updates the logic for determining if a workspace is focused by also looking for a focused window. --- src/modules/sway/workspaces.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 7517dc267..086ed5d10 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -501,7 +501,16 @@ std::string Workspaces::trimWorkspaceName(std::string name) { void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) { if (config_["current-only"].asBool()) { - if (node["focused"].asBool()) { + // If a workspace has a focused container then get_tree will say + // that the workspace itself isn't focused. Therefore we need to + // check if any of its nodes are focused as well. + bool focused = node["focused"].asBool() || + std::any_of(node["nodes"].begin(), node["nodes"].end(), + [](const auto &child) { + return child["focused"].asBool(); + }); + + if (focused) { button.show(); } else { button.hide(); From e1a6d513cca4ffa29ea60d38ab5d8e060965a1cd Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 13:01:00 -0500 Subject: [PATCH 270/407] test/config: add hyprland-workspaces config --- test/config.cpp | 39 ++++++++++++++++++++++++++++ test/config/hyprland-workspaces.json | 37 ++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 test/config/hyprland-workspaces.json diff --git a/test/config.cpp b/test/config.cpp index ad3df0651..c60519ce2 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -117,3 +117,42 @@ TEST_CASE("Load multiple bar config with include", "[config]") { REQUIRE(data.size() == 4); REQUIRE(data[0]["output"].asString() == "OUT-0"); } + +TEST_CASE("Load Hyprland Workspaces bar config", "[config]") { + waybar::Config conf; + conf.load("test/config/hyprland-workspaces.json"); + + auto& data = conf.getConfig(); + auto hyprland = data[0]["hyprland/workspaces"]; + auto hyprland_window_rewrite = data[0]["hyprland/workspaces"]["window-rewrite"]; + auto hyprland_format_icons = data[0]["hyprland/workspaces"]["format-icons"]; + auto hyprland_persistent_workspaces = data[0]["hyprland/workspaces"]["persistent-workspaces"]; + + REQUIRE(data.isArray()); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["height"].asInt() == 20); + REQUIRE(data[0]["layer"].asString() == "bottom"); + REQUIRE(data[0]["output"].isArray()); + REQUIRE(data[0]["output"][0].asString() == "HDMI-0"); + REQUIRE(data[0]["output"][1].asString() == "DP-0"); + + REQUIRE(hyprland["active-only"].asBool() == true); + REQUIRE(hyprland["all-outputs"].asBool() == false); + REQUIRE(hyprland["move-to-monitor"].asBool() == true); + REQUIRE(hyprland["format"].asString() == "{icon} {windows}"); + REQUIRE(hyprland["format-window-separator"].asString() == " "); + REQUIRE(hyprland["on-scroll-down"].asString() == "hyprctl dispatch workspace e-1"); + REQUIRE(hyprland["on-scroll-up"].asString() == "hyprctl dispatch workspace e+1"); + REQUIRE(hyprland["show-special"].asBool() == true); + REQUIRE(hyprland["window-rewrite-default"].asString() == ""); + REQUIRE(hyprland["window-rewrite-separator"].asString() == " "); + REQUIRE(hyprland_format_icons["1"].asString() == "󰎤"); + REQUIRE(hyprland_format_icons["2"].asString() == "󰎧"); + REQUIRE(hyprland_format_icons["3"].asString() == "󰎪"); + REQUIRE(hyprland_format_icons["default"].asString() == ""); + REQUIRE(hyprland_format_icons["empty"].asString() == "󱓼"); + REQUIRE(hyprland_format_icons["urgent"].asString() == "󱨇"); + REQUIRE(hyprland_persistent_workspaces["1"].asString() == "HDMI-0"); + REQUIRE(hyprland_window_rewrite["title"].asString() == ""); + REQUIRE(hyprland["sort-by"].asString() == "number"); +} diff --git a/test/config/hyprland-workspaces.json b/test/config/hyprland-workspaces.json new file mode 100644 index 000000000..dd733897f --- /dev/null +++ b/test/config/hyprland-workspaces.json @@ -0,0 +1,37 @@ +[ + { + "height": 20, + "layer": "bottom", + "output": [ + "HDMI-0", + "DP-0" + ], + "hyprland/workspaces": { + "active-only": true, + "all-outputs": false, + "show-special": true, + "move-to-monitor": true, + "format": "{icon} {windows}", + "format-window-separator": " ", + "format-icons": { + "1": "󰎤", + "2": "󰎧", + "3": "󰎪", + "default": "", + "empty": "󱓼", + "urgent": "󱨇" + }, + "persistent-workspaces": { + "1": "HDMI-0" + }, + "on-scroll-down": "hyprctl dispatch workspace e-1", + "on-scroll-up": "hyprctl dispatch workspace e+1", + "window-rewrite": { + "title": "" + }, + "window-rewrite-default": "", + "window-rewrite-separator": " ", + "sort-by": "number" + } + } +] From 1b3b45779aa308aa05067c98c33e173073890711 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 18:52:19 -0500 Subject: [PATCH 271/407] modules/hyprland/backend: add getSocketFolder to header --- include/modules/hyprland/backend.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index 9ce0ec335..b327482cf 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -37,4 +38,5 @@ class IPC { inline std::unique_ptr gIPC; inline bool modulesReady = false; +std::filesystem::path getSocketFolder(const char* instanceSig); }; // namespace waybar::modules::hyprland From 0055ee69102441558bc8e72348c689540221dc57 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 18:52:42 -0500 Subject: [PATCH 272/407] modules/hyprland/workspaces: remove unneccesary visibleWorkspaces variable --- include/modules/hyprland/workspaces.hpp | 2 +- src/modules/hyprland/workspaces.cpp | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 0432b8703..6c6e09329 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -100,7 +100,7 @@ class Workspaces : public AModule, public EventHandler { void removeWorkspacesToRemove(); void createWorkspacesToCreate(); std::vector getVisibleWorkspaces(); - void updateWorkspaceStates(const std::vector& visibleWorkspaces); + void updateWorkspaceStates(); bool updateWindowsToCreate(); void extendOrphans(int workspaceId, Json::Value const& clientsJson); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3b129375c..3209243cb 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -128,10 +128,7 @@ void Workspaces::doUpdate() { removeWorkspacesToRemove(); createWorkspacesToCreate(); - - std::vector visibleWorkspaces = getVisibleWorkspaces(); - - updateWorkspaceStates(visibleWorkspaces); + updateWorkspaceStates(); updateWindowCount(); sortWorkspaces(); @@ -870,7 +867,8 @@ bool Workspaces::updateWindowsToCreate() { return anyWindowCreated; } -void Workspaces::updateWorkspaceStates(const std::vector &visibleWorkspaces) { +void Workspaces::updateWorkspaceStates() { + const std::vector visibleWorkspaces = getVisibleWorkspaces(); auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { workspace->setActive(workspace->name() == m_activeWorkspaceName || From 749f46f86f1aa0d44227a2888d50d2551080beb5 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 21:50:52 -0500 Subject: [PATCH 273/407] test/fixtures: Add GlibTestsFixture --- test/SafeSignal.cpp | 2 +- test/{ => fixtures}/GlibTestsFixture.hpp | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename test/{ => fixtures}/GlibTestsFixture.hpp (100%) diff --git a/test/SafeSignal.cpp b/test/SafeSignal.cpp index f496d7ab2..341e8e2ea 100644 --- a/test/SafeSignal.cpp +++ b/test/SafeSignal.cpp @@ -10,7 +10,7 @@ #include #include -#include "GlibTestsFixture.hpp" +#include "fixtures/GlibTestsFixture.hpp" using namespace waybar; diff --git a/test/GlibTestsFixture.hpp b/test/fixtures/GlibTestsFixture.hpp similarity index 100% rename from test/GlibTestsFixture.hpp rename to test/fixtures/GlibTestsFixture.hpp From 87eaa75b8a8f53ea1cc877e31bdb1839956e83c9 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 21:52:54 -0500 Subject: [PATCH 274/407] test/hyprland/backend: init --- test/hyprland/backend.cpp | 110 ++++++++++++++++++++++++++++++++++++++ test/hyprland/meson.build | 34 ++++++++++++ test/meson.build | 4 ++ 3 files changed, 148 insertions(+) create mode 100644 test/hyprland/backend.cpp create mode 100644 test/hyprland/meson.build diff --git a/test/hyprland/backend.cpp b/test/hyprland/backend.cpp new file mode 100644 index 000000000..edb51c559 --- /dev/null +++ b/test/hyprland/backend.cpp @@ -0,0 +1,110 @@ +#include +#if __has_include() +#include +#else +#include +#endif +#include +#include + +#include "modules/hyprland/backend.hpp" + +namespace fs = std::filesystem; +namespace hyprland = waybar::modules::hyprland; + +class testRunListener : public Catch::EventListenerBase { + public: + using Catch::EventListenerBase::EventListenerBase; + + void testCaseStarting(Catch::TestCaseInfo const&) override { + // TODO: reset state of module here + } +}; + +CATCH_REGISTER_LISTENER(testRunListener) + +TEST_CASE("GetSocketFolderTest", "[getSocketFolder]") { + SECTION("XDGRuntimeDirExists") { + // Test case: XDG_RUNTIME_DIR exists and contains "hypr" directory + // Arrange + std::cout << "Starting XDGRuntimeDirExists " << '\n'; + const char* instanceSig = "instance_sig"; + + fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; + std::cout << "Temp dir: " << tempDir << '\n'; + + fs::path expectedPath = tempDir / "hypr" / instanceSig; + std::cout << "Expected path: " << expectedPath << '\n'; + + fs::create_directories(tempDir / "hypr" / instanceSig); + + setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); + + // Act/* + std::cout << "Getting socket folder" << '\n'; + fs::path actualPath = hyprland::getSocketFolder(instanceSig); + + // Assert expected result + REQUIRE(actualPath == expectedPath); + + // Cleanup + fs::remove_all(tempDir); + + std::cout << "Finishing XDGRuntimeDirExists " << '\n'; + } + + // TODO: properly clear state so we can actually test these.... + /* SECTION("XDGRuntimeDirDoesNotExist") { */ + /* // Test case: XDG_RUNTIME_DIR does not exist */ + /* // Arrange */ + /* std::cout << "Starting XDGRuntimeDirDoesNotExist " << '\n'; */ + /* const char* instanceSig = "instance_sig"; */ + /**/ + /* std::cout << "Current XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ + /**/ + /* unsetenv("XDG_RUNTIME_DIR"); */ + /**/ + /* std::cout << "New XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ + /**/ + /* // Act */ + /* fs::path actualPath = hyprland::getSocketFolder(instanceSig); */ + /**/ + /* // Assert expected result */ + /* fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; */ + /* REQUIRE(actualPath == expectedPath); */ + /**/ + /* // Cleanup */ + /* std::cout << "Finishing XDGRuntimeDirDoesNotExist " << '\n'; */ + /* } */ + /**/ + /* SECTION("XDGRuntimeDirExistsNoHyprDir") { */ + /* // Test case: XDG_RUNTIME_DIR exists but does not contain "hypr" directory */ + /* // Arrange */ + /* std::cout << "Starting XDGRuntimeDirExistsNoHyprDir " << '\n'; */ + /**/ + /* const char* instanceSig = "instance_sig"; */ + /**/ + /* fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; */ + /* std::cout << "Temp dir: " << tempDir << '\n'; */ + /**/ + /* fs::create_directories(tempDir); */ + /**/ + /* setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); */ + /**/ + /* std::cout << "Current XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ + /**/ + /* // Act */ + /* fs::path actualPath = hyprland::getSocketFolder(instanceSig); */ + /**/ + /* // Assert expected result */ + /* fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; */ + /* std::cout << "Expected path: " << expectedPath << '\n'; */ + /**/ + /* REQUIRE(actualPath == expectedPath); */ + /**/ + /* // Cleanup */ + /* fs::remove_all(tempDir); */ + /**/ + /* std::cout << "Finishing XDGRuntimeDirExistsNoHyprDir " << '\n'; */ + /* } */ +} diff --git a/test/hyprland/meson.build b/test/hyprland/meson.build new file mode 100644 index 000000000..1f8e3a8bd --- /dev/null +++ b/test/hyprland/meson.build @@ -0,0 +1,34 @@ +test_inc = include_directories('../../include') + +test_dep = [ + catch2, + fmt, + gtkmm, + jsoncpp, + spdlog, +] + +test_src = files( + '../main.cpp', + '../JsonParser.cpp', + '../SafeSignal.cpp', + '../config.cpp', + '../css_reload_helper.cpp', + '../../src/config.cpp', + '../../src/util/css_reload_helper.cpp', + 'backend.cpp', + '../../src/modules/hyprland/backend.cpp' +) + +hyprland_test = executable( + 'hyprland_test', + test_src, + dependencies: test_dep, + include_directories: test_inc, +) + +test( + 'hyprland', + hyprland_test, + workdir: meson.project_source_root(), +) diff --git a/test/meson.build b/test/meson.build index 7c9226712..2c5f9c767 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,4 +1,5 @@ test_inc = include_directories('../include') + test_dep = [ catch2, fmt, @@ -6,6 +7,7 @@ test_dep = [ jsoncpp, spdlog, ] + test_src = files( 'main.cpp', 'JsonParser.cpp', @@ -33,3 +35,5 @@ test( waybar_test, workdir: meson.project_source_root(), ) + +subdir('hyprland') From 58e7abba2c2c7a8d5f3b258b7c79919030cbc70f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 8 Jun 2024 22:30:01 -0500 Subject: [PATCH 275/407] tests: split into separate binaries --- test/hyprland/meson.build | 6 ---- test/meson.build | 10 +----- test/{ => utils}/JsonParser.cpp | 0 test/{ => utils}/SafeSignal.cpp | 0 test/{ => utils}/css_reload_helper.cpp | 0 test/{ => utils}/date.cpp | 0 .../{ => utils}/fixtures/GlibTestsFixture.hpp | 0 test/utils/meson.build | 36 +++++++++++++++++++ 8 files changed, 37 insertions(+), 15 deletions(-) rename test/{ => utils}/JsonParser.cpp (100%) rename test/{ => utils}/SafeSignal.cpp (100%) rename test/{ => utils}/css_reload_helper.cpp (100%) rename test/{ => utils}/date.cpp (100%) rename test/{ => utils}/fixtures/GlibTestsFixture.hpp (100%) create mode 100644 test/utils/meson.build diff --git a/test/hyprland/meson.build b/test/hyprland/meson.build index 1f8e3a8bd..533022fc8 100644 --- a/test/hyprland/meson.build +++ b/test/hyprland/meson.build @@ -10,12 +10,6 @@ test_dep = [ test_src = files( '../main.cpp', - '../JsonParser.cpp', - '../SafeSignal.cpp', - '../config.cpp', - '../css_reload_helper.cpp', - '../../src/config.cpp', - '../../src/util/css_reload_helper.cpp', 'backend.cpp', '../../src/modules/hyprland/backend.cpp' ) diff --git a/test/meson.build b/test/meson.build index 2c5f9c767..ea430c504 100644 --- a/test/meson.build +++ b/test/meson.build @@ -10,19 +10,10 @@ test_dep = [ test_src = files( 'main.cpp', - 'JsonParser.cpp', - 'SafeSignal.cpp', 'config.cpp', - 'css_reload_helper.cpp', '../src/config.cpp', - '../src/util/css_reload_helper.cpp', ) -if tz_dep.found() - test_dep += tz_dep - test_src += files('date.cpp') -endif - waybar_test = executable( 'waybar_test', test_src, @@ -36,4 +27,5 @@ test( workdir: meson.project_source_root(), ) +subdir('utils') subdir('hyprland') diff --git a/test/JsonParser.cpp b/test/utils/JsonParser.cpp similarity index 100% rename from test/JsonParser.cpp rename to test/utils/JsonParser.cpp diff --git a/test/SafeSignal.cpp b/test/utils/SafeSignal.cpp similarity index 100% rename from test/SafeSignal.cpp rename to test/utils/SafeSignal.cpp diff --git a/test/css_reload_helper.cpp b/test/utils/css_reload_helper.cpp similarity index 100% rename from test/css_reload_helper.cpp rename to test/utils/css_reload_helper.cpp diff --git a/test/date.cpp b/test/utils/date.cpp similarity index 100% rename from test/date.cpp rename to test/utils/date.cpp diff --git a/test/fixtures/GlibTestsFixture.hpp b/test/utils/fixtures/GlibTestsFixture.hpp similarity index 100% rename from test/fixtures/GlibTestsFixture.hpp rename to test/utils/fixtures/GlibTestsFixture.hpp diff --git a/test/utils/meson.build b/test/utils/meson.build new file mode 100644 index 000000000..b7b3665a8 --- /dev/null +++ b/test/utils/meson.build @@ -0,0 +1,36 @@ +test_inc = include_directories('../../include') + +test_dep = [ + catch2, + fmt, + gtkmm, + jsoncpp, + spdlog, +] +test_src = files( + '../main.cpp', + '../config.cpp', + '../../src/config.cpp', + 'JsonParser.cpp', + 'SafeSignal.cpp', + 'css_reload_helper.cpp', + '../../src/util/css_reload_helper.cpp', +) + +if tz_dep.found() + test_dep += tz_dep + test_src += files('date.cpp') +endif + +utils_test = executable( + 'utils_test', + test_src, + dependencies: test_dep, + include_directories: test_inc, +) + +test( + 'utils', + utils_test, + workdir: meson.project_source_root(), +) From fa2e21dfd5f4870043c9ec7843e2e609e9a21521 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 8 Jun 2024 23:52:58 -0500 Subject: [PATCH 276/407] modules/hyprland/backend: move getSocketFolder to class --- include/modules/hyprland/backend.hpp | 5 ++++- src/modules/hyprland/backend.cpp | 23 +++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index b327482cf..11e73d8f6 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -26,6 +26,10 @@ class IPC { static std::string getSocket1Reply(const std::string& rq); Json::Value getSocket1JsonReply(const std::string& rq); + static std::filesystem::path getSocketFolder(const char* instanceSig); + + protected: + static std::filesystem::path socketFolder_; private: void startIPC(); @@ -38,5 +42,4 @@ class IPC { inline std::unique_ptr gIPC; inline bool modulesReady = false; -std::filesystem::path getSocketFolder(const char* instanceSig); }; // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 29c65633b..05d16d05e 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -15,22 +15,25 @@ namespace waybar::modules::hyprland { -std::filesystem::path getSocketFolder(const char* instanceSig) { +std::filesystem::path IPC::socketFolder_; + +std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { // socket path, specified by EventManager of Hyprland - static std::filesystem::path socketFolder; - if (!socketFolder.empty()) { - return socketFolder; + if (!socketFolder_.empty()) { + spdlog::warn("socketFolder already set, using {}", socketFolder_.c_str()); + return socketFolder_; + } } std::filesystem::path xdgRuntimeDir = std::filesystem::path(getenv("XDG_RUNTIME_DIR")); if (!xdgRuntimeDir.empty() && std::filesystem::exists(xdgRuntimeDir / "hypr")) { - socketFolder = xdgRuntimeDir / "hypr"; + socketFolder_ = xdgRuntimeDir / "hypr"; } else { spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr"); - socketFolder = std::filesystem::path("/tmp") / "hypr"; + socketFolder_ = std::filesystem::path("/tmp") / "hypr"; } - socketFolder = socketFolder / instanceSig; - return socketFolder; + socketFolder_ = socketFolder_ / instanceSig; + return socketFolder_; } void IPC::startIPC() { @@ -59,7 +62,7 @@ void IPC::startIPC() { addr.sun_family = AF_UNIX; - auto socketPath = getSocketFolder(his) / ".socket2.sock"; + auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock"; strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); addr.sun_path[sizeof(addr.sun_path) - 1] = 0; @@ -169,7 +172,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) { sockaddr_un serverAddress = {0}; serverAddress.sun_family = AF_UNIX; - std::string socketPath = getSocketFolder(instanceSig) / ".socket.sock"; + std::string socketPath = IPC::getSocketFolder(instanceSig) / ".socket.sock"; // Use snprintf to copy the socketPath string into serverAddress.sun_path if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < From 959422f143b37729dda3ae5ab14f5c4c0d71734b Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 8 Jun 2024 23:53:19 -0500 Subject: [PATCH 277/407] modules/hyprland/backend: protect against crash when XDG_RUNTIME_DIR not set --- src/modules/hyprland/backend.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 05d16d05e..a39fcd694 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -23,15 +23,21 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { spdlog::warn("socketFolder already set, using {}", socketFolder_.c_str()); return socketFolder_; } + + const char* xdgRuntimeDirEnv = std::getenv("XDG_RUNTIME_DIR"); + std::filesystem::path xdgRuntimeDir; + // Only set path if env variable is set + if (xdgRuntimeDirEnv) { + xdgRuntimeDir = std::filesystem::path(xdgRuntimeDirEnv); } - std::filesystem::path xdgRuntimeDir = std::filesystem::path(getenv("XDG_RUNTIME_DIR")); if (!xdgRuntimeDir.empty() && std::filesystem::exists(xdgRuntimeDir / "hypr")) { socketFolder_ = xdgRuntimeDir / "hypr"; } else { spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr"); socketFolder_ = std::filesystem::path("/tmp") / "hypr"; } + socketFolder_ = socketFolder_ / instanceSig; return socketFolder_; } From b3658318391664c83844335ad2a07cdcb3279b82 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 9 Jun 2024 10:18:42 -0500 Subject: [PATCH 278/407] test/hyprland/backend: fix --- test/hyprland/backend.cpp | 123 ++++++---------------- test/hyprland/fixtures/IPCTestFixture.hpp | 16 +++ 2 files changed, 50 insertions(+), 89 deletions(-) create mode 100644 test/hyprland/fixtures/IPCTestFixture.hpp diff --git a/test/hyprland/backend.cpp b/test/hyprland/backend.cpp index edb51c559..e6b209da4 100644 --- a/test/hyprland/backend.cpp +++ b/test/hyprland/backend.cpp @@ -4,107 +4,52 @@ #else #include #endif -#include -#include +#include "fixtures/IPCTestFixture.hpp" #include "modules/hyprland/backend.hpp" namespace fs = std::filesystem; namespace hyprland = waybar::modules::hyprland; -class testRunListener : public Catch::EventListenerBase { - public: - using Catch::EventListenerBase::EventListenerBase; +TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirExists", "[getSocketFolder]") { + // Test case: XDG_RUNTIME_DIR exists and contains "hypr" directory + // Arrange + tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; + fs::path expectedPath = tempDir / "hypr" / instanceSig; + fs::create_directories(tempDir / "hypr" / instanceSig); + setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); - void testCaseStarting(Catch::TestCaseInfo const&) override { - // TODO: reset state of module here - } -}; + // Act + fs::path actualPath = getSocketFolder(instanceSig); -CATCH_REGISTER_LISTENER(testRunListener) - -TEST_CASE("GetSocketFolderTest", "[getSocketFolder]") { - SECTION("XDGRuntimeDirExists") { - // Test case: XDG_RUNTIME_DIR exists and contains "hypr" directory - // Arrange - std::cout << "Starting XDGRuntimeDirExists " << '\n'; - const char* instanceSig = "instance_sig"; - - fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; - std::cout << "Temp dir: " << tempDir << '\n'; - - fs::path expectedPath = tempDir / "hypr" / instanceSig; - std::cout << "Expected path: " << expectedPath << '\n'; - - fs::create_directories(tempDir / "hypr" / instanceSig); + // Assert expected result + REQUIRE(actualPath == expectedPath); +} - setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); +TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirDoesNotExist", "[getSocketFolder]") { + // Test case: XDG_RUNTIME_DIR does not exist + // Arrange + unsetenv("XDG_RUNTIME_DIR"); + fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; - // Act/* - std::cout << "Getting socket folder" << '\n'; - fs::path actualPath = hyprland::getSocketFolder(instanceSig); + // Act + fs::path actualPath = getSocketFolder(instanceSig); - // Assert expected result - REQUIRE(actualPath == expectedPath); + // Assert expected result + REQUIRE(actualPath == expectedPath); +} - // Cleanup - fs::remove_all(tempDir); +TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirExistsNoHyprDir", "[getSocketFolder]") { + // Test case: XDG_RUNTIME_DIR exists but does not contain "hypr" directory + // Arrange + fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; + fs::create_directories(tempDir); + setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); + fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; - std::cout << "Finishing XDGRuntimeDirExists " << '\n'; - } + // Act + fs::path actualPath = getSocketFolder(instanceSig); - // TODO: properly clear state so we can actually test these.... - /* SECTION("XDGRuntimeDirDoesNotExist") { */ - /* // Test case: XDG_RUNTIME_DIR does not exist */ - /* // Arrange */ - /* std::cout << "Starting XDGRuntimeDirDoesNotExist " << '\n'; */ - /* const char* instanceSig = "instance_sig"; */ - /**/ - /* std::cout << "Current XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ - /**/ - /* unsetenv("XDG_RUNTIME_DIR"); */ - /**/ - /* std::cout << "New XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ - /**/ - /* // Act */ - /* fs::path actualPath = hyprland::getSocketFolder(instanceSig); */ - /**/ - /* // Assert expected result */ - /* fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; */ - /* REQUIRE(actualPath == expectedPath); */ - /**/ - /* // Cleanup */ - /* std::cout << "Finishing XDGRuntimeDirDoesNotExist " << '\n'; */ - /* } */ - /**/ - /* SECTION("XDGRuntimeDirExistsNoHyprDir") { */ - /* // Test case: XDG_RUNTIME_DIR exists but does not contain "hypr" directory */ - /* // Arrange */ - /* std::cout << "Starting XDGRuntimeDirExistsNoHyprDir " << '\n'; */ - /**/ - /* const char* instanceSig = "instance_sig"; */ - /**/ - /* fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; */ - /* std::cout << "Temp dir: " << tempDir << '\n'; */ - /**/ - /* fs::create_directories(tempDir); */ - /**/ - /* setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); */ - /**/ - /* std::cout << "Current XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ - /**/ - /* // Act */ - /* fs::path actualPath = hyprland::getSocketFolder(instanceSig); */ - /**/ - /* // Assert expected result */ - /* fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; */ - /* std::cout << "Expected path: " << expectedPath << '\n'; */ - /**/ - /* REQUIRE(actualPath == expectedPath); */ - /**/ - /* // Cleanup */ - /* fs::remove_all(tempDir); */ - /**/ - /* std::cout << "Finishing XDGRuntimeDirExistsNoHyprDir " << '\n'; */ - /* } */ + // Assert expected result + REQUIRE(actualPath == expectedPath); } diff --git a/test/hyprland/fixtures/IPCTestFixture.hpp b/test/hyprland/fixtures/IPCTestFixture.hpp new file mode 100644 index 000000000..3b04b3b0e --- /dev/null +++ b/test/hyprland/fixtures/IPCTestFixture.hpp @@ -0,0 +1,16 @@ +#include "modules/hyprland/backend.hpp" + +namespace fs = std::filesystem; +namespace hyprland = waybar::modules::hyprland; + +class IPCTestFixture : public hyprland::IPC { + public: + IPCTestFixture() : IPC() { IPC::socketFolder_ = ""; } + ~IPCTestFixture() { fs::remove_all(tempDir); } + + protected: + const char* instanceSig = "instance_sig"; + fs::path tempDir = fs::temp_directory_path() / "hypr_test"; + + private: +}; From 08c5df3633e728864283d45e7f7f32970bb93311 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 9 Jun 2024 13:05:47 -0500 Subject: [PATCH 279/407] modules/sway/workspaces: clang-format fix --- src/modules/sway/workspaces.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 086ed5d10..2adde69ca 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -505,10 +505,8 @@ void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) { // that the workspace itself isn't focused. Therefore we need to // check if any of its nodes are focused as well. bool focused = node["focused"].asBool() || - std::any_of(node["nodes"].begin(), node["nodes"].end(), - [](const auto &child) { - return child["focused"].asBool(); - }); + std::any_of(node["nodes"].begin(), node["nodes"].end(), + [](const auto &child) { return child["focused"].asBool(); }); if (focused) { button.show(); From 16ff5ee99b7c9be3ff6e7a22f5569dc59f1be5c7 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 9 Jun 2024 13:31:46 -0500 Subject: [PATCH 280/407] .github/workflows/linux: fail-fast --- .github/workflows/linux.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ae0f1f7f4..c36f68e2d 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -9,6 +9,7 @@ concurrency: jobs: build: strategy: + fail-fast: false matrix: distro: - alpine From 06fa931de9ea1e27b500c0f6d7ee77a29b233299 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 9 Jun 2024 13:38:45 -0500 Subject: [PATCH 281/407] Dockerfiles/opensuse: add python3-packaging dependency --- Dockerfiles/opensuse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index bdb42fbfb..6ac3e058a 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 libxkbregistry-devel scdoc playerctl-devel + 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 playerctl-devel python3-packaging From 71bb2b64bfe906c10773195d79339200c1930745 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 9 Jun 2024 15:08:43 -0500 Subject: [PATCH 282/407] subprojects/spdlog: bump spdlog Fixes alpine build and is a commonly distributed version --- subprojects/spdlog.wrap | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/subprojects/spdlog.wrap b/subprojects/spdlog.wrap index 69ef566fa..08004c901 100644 --- a/subprojects/spdlog.wrap +++ b/subprojects/spdlog.wrap @@ -1,12 +1,13 @@ [wrap-file] -directory = spdlog-1.11.0 -source_url = https://github.com/gabime/spdlog/archive/v1.11.0.tar.gz -source_filename = v1.11.0.tar.gz -source_hash = ca5cae8d6cac15dae0ec63b21d6ad3530070650f68076f3a4a862ca293a858bb -patch_filename = spdlog_1.11.0-2_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.11.0-2/get_patch -patch_hash = db1364fe89502ac67f245a6c8c51290a52afd74a51eed26fa9ecb5b3443df57a -wrapdb_version = 1.11.0-2 +directory = spdlog-1.12.0 +source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.12.0.tar.gz +source_filename = spdlog-1.12.0.tar.gz +source_hash = 4dccf2d10f410c1e2feaff89966bfc49a1abb29ef6f08246335b110e001e09a9 +patch_filename = spdlog_1.12.0-2_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.12.0-2/get_patch +patch_hash = 9596972d1eb2e0a69cea4a53273ca7bbbcb9b2fa872cd734864fc7232dc2d573 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.12.0-2/spdlog-1.12.0.tar.gz +wrapdb_version = 1.12.0-2 [provide] spdlog = spdlog_dep From 07737867660a37c4507c1e0db6b2fbd52d92bace Mon Sep 17 00:00:00 2001 From: giskard Date: Thu, 9 May 2024 01:45:21 +0800 Subject: [PATCH 283/407] privacy: consider only configured modules along with the local clang-tidy warning fixes --- src/modules/privacy/privacy.cpp | 81 +++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index d3e3d4b2f..97996c336 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -53,23 +53,22 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st modules.append(obj); } } - for (const auto& module_config : modules) { - if (!module_config.isObject() || !module_config["type"].isString()) continue; - const std::string type = module_config["type"].asString(); - if (type == "screenshare") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_VIDEO_INPUT, - &nodes_screenshare, pos, iconSize, transition_duration); - box_.add(*item); - } else if (type == "audio-in") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_INPUT, - &nodes_audio_in, pos, iconSize, transition_duration); - box_.add(*item); - } else if (type == "audio-out") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_OUTPUT, - &nodes_audio_out, pos, iconSize, transition_duration); + + std::map > typeMap = { + {"screenshare", {&nodes_screenshare, PRIVACY_NODE_TYPE_VIDEO_INPUT}}, + {"audio-in", {&nodes_audio_in, PRIVACY_NODE_TYPE_AUDIO_INPUT}}, + {"audio-out", {&nodes_audio_out, PRIVACY_NODE_TYPE_AUDIO_OUTPUT}}, + }; + + for (const auto& module : modules) { + if (!module.isObject() || !module["type"].isString()) continue; + const std::string type = module["type"].asString(); + + auto iter = typeMap.find(type); + if (iter != typeMap.end()) { + auto& [nodePtr, nodeType] = iter->second; + auto* item = Gtk::make_managed(module, nodeType, nodePtr, pos, iconSize, + transition_duration); box_.add(*item); } } @@ -114,26 +113,35 @@ void Privacy::onPrivacyNodesChanged() { } auto Privacy::update() -> void { - mutex_.lock(); - bool screenshare = false; - bool audio_in = false; - bool audio_out = false; + // set in modules or not + bool setScreenshare = false; + bool setAudioIn = false; + bool setAudioOut = false; + // used or not + bool useScreenshare = false; + bool useAudioIn = false; + bool useAudioOut = false; + + mutex_.lock(); for (Gtk::Widget* widget : box_.get_children()) { auto* module = dynamic_cast(widget); if (module == nullptr) continue; switch (module->privacy_type) { case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: - screenshare = !nodes_screenshare.empty(); - module->set_in_use(screenshare); + setScreenshare = true; + useScreenshare = !nodes_screenshare.empty(); + module->set_in_use(useScreenshare); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT: - audio_in = !nodes_audio_in.empty(); - module->set_in_use(audio_in); + setAudioIn = true; + useAudioIn = !nodes_audio_in.empty(); + module->set_in_use(useAudioIn); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT: - audio_out = !nodes_audio_out.empty(); - module->set_in_use(audio_out); + setAudioOut = true; + useAudioOut = !nodes_audio_out.empty(); + module->set_in_use(useAudioOut); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_NONE: break; @@ -142,25 +150,28 @@ auto Privacy::update() -> void { mutex_.unlock(); // Hide the whole widget if none are in use - bool is_visible = screenshare || audio_in || audio_out; - if (is_visible != event_box_.get_visible()) { + bool isVisible = (setScreenshare && useScreenshare) || (setAudioIn && useAudioIn) || + (setAudioOut && useAudioOut); + + if (isVisible != event_box_.get_visible()) { // Disconnect any previous connection so that it doesn't get activated in // the future, hiding the module when it should be visible visibility_conn.disconnect(); - if (is_visible) { + if (isVisible) { event_box_.set_visible(true); } else { // Hides the widget when all of the privacy_item revealers animations // have finished animating visibility_conn = Glib::signal_timeout().connect( sigc::track_obj( - [this] { + [this, setScreenshare, setAudioOut, setAudioIn]() { mutex_.lock(); - bool screenshare = !nodes_screenshare.empty(); - bool audio_in = !nodes_audio_in.empty(); - bool audio_out = !nodes_audio_out.empty(); + bool visible = false; + visible |= setScreenshare && !nodes_screenshare.empty(); + visible |= setAudioIn && !nodes_audio_in.empty(); + visible |= setAudioOut && !nodes_audio_out.empty(); mutex_.unlock(); - event_box_.set_visible(screenshare || audio_in || audio_out); + event_box_.set_visible(visible); return false; }, *this), From e8d91eb14bdc70759c8b1d34a677489911eebed8 Mon Sep 17 00:00:00 2001 From: giskard Date: Tue, 14 May 2024 14:39:30 +0800 Subject: [PATCH 284/407] mpris: hide on current player vanished --- src/modules/mpris/mpris.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index eea9a82b7..1e8741f86 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -433,6 +433,7 @@ auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlaye if (std::string(player_name->name) == mpris->player_) { mpris->player = nullptr; + mpris->event_box_.set_visible(false); mpris->dp.emit(); } } From 7721dcdae80553584cfaf53f857335dfd2b07bc1 Mon Sep 17 00:00:00 2001 From: giskard Date: Tue, 14 May 2024 15:00:05 +0800 Subject: [PATCH 285/407] mpris: some clang-tidy fix --- src/modules/mpris/mpris.cpp | 72 ++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 1e8741f86..ed383b0ce 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -96,9 +96,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } if (config_["dynamic-order"].isArray()) { dynamic_order_.clear(); - for (auto it = config_["dynamic-order"].begin(); it != config_["dynamic-order"].end(); ++it) { - if (it->isString()) { - dynamic_order_.push_back(it->asString()); + for (const auto& item : config_["dynamic-order"]) { + if (item.isString()) { + dynamic_order_.push_back(item.asString()); } } } @@ -110,10 +110,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) player_ = config_["player"].asString(); } if (config_["ignored-players"].isArray()) { - for (auto it = config_["ignored-players"].begin(); it != config_["ignored-players"].end(); - ++it) { - if (it->isString()) { - ignored_players_.push_back(it->asString()); + for (const auto& item : config_["ignored-players"]) { + if (item.isString()) { + ignored_players_.push_back(item.asString()); } } } @@ -146,8 +145,8 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) throw std::runtime_error(fmt::format("unable to list players: {}", error->message)); } - for (auto p = players; p != NULL; p = p->next) { - auto pn = static_cast(p->data); + for (auto* p = players; p != nullptr; p = p->next) { + auto* pn = static_cast(p->data); if (strcmp(pn->name, player_.c_str()) == 0) { player = playerctl_player_new_from_name(pn, &error); break; @@ -180,17 +179,14 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } Mpris::~Mpris() { - if (manager != NULL) g_object_unref(manager); - if (player != NULL) g_object_unref(player); + if (manager != nullptr) g_object_unref(manager); + if (player != nullptr) g_object_unref(player); } auto Mpris::getIconFromJson(const Json::Value& icons, const std::string& key) -> std::string { if (icons.isObject()) { - if (icons[key].isString()) { - return icons[key].asString(); - } else if (icons["default"].isString()) { - return icons["default"].asString(); - } + if (icons[key].isString()) return icons[key].asString(); + if (icons["default"].isString()) return icons["default"].asString(); } return ""; } @@ -205,7 +201,7 @@ size_t utf8_truncate(std::string& str, size_t width = std::string::npos) { size_t total_width = 0; - for (gchar *data = str.data(), *end = data + str.size(); data;) { + for (gchar *data = str.data(), *end = data + str.size(); data != nullptr;) { gunichar c = g_utf8_get_char_validated(data, end - data); if (c == -1U || c == -2U) { // invalid unicode, treat string as ascii @@ -269,7 +265,7 @@ auto Mpris::getLengthStr(const PlayerInfo& info, bool truncated) -> std::string auto length = info.length.value(); return (truncated && length.substr(0, 3) == "00:") ? length.substr(3) : length; } - return std::string(); + return {}; } auto Mpris::getPositionStr(const PlayerInfo& info, bool truncated) -> std::string { @@ -277,7 +273,7 @@ auto Mpris::getPositionStr(const PlayerInfo& info, bool truncated) -> std::strin auto position = info.position.value(); return (truncated && position.substr(0, 3) == "00:") ? position.substr(3) : position; } - return std::string(); + return {}; } auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> std::string { @@ -319,33 +315,33 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> size_t totalLen = 0; - for (auto it = dynamic_prio_.begin(); it != dynamic_prio_.end(); ++it) { - if (*it == "artist") { + for (const auto& item : dynamic_prio_) { + if (item == "artist") { if (totalLen + artistLen > dynamicLen) { showArtist = false; } else if (showArtist) { totalLen += artistLen; } - } else if (*it == "album") { + } else if (item == "album") { if (totalLen + albumLen > dynamicLen) { showAlbum = false; } else if (showAlbum) { totalLen += albumLen; } - } else if (*it == "title") { + } else if (item == "title") { if (totalLen + titleLen > dynamicLen) { showTitle = false; } else if (showTitle) { totalLen += titleLen; } - } else if (*it == "length") { + } else if (item == "length") { if (totalLen + lengthLen > dynamicLen) { showLength = false; } else if (showLength) { totalLen += lengthLen; posLen = std::max((size_t)2, posLen) - 2; } - } else if (*it == "position") { + } else if (item == "position") { if (totalLen + posLen > dynamicLen) { showPos = false; } else if (showPos) { @@ -406,7 +402,7 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: name-appeared callback: {}", player_name->name); @@ -415,7 +411,7 @@ auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlaye return; } - mpris->player = playerctl_player_new_from_name(player_name, NULL); + mpris->player = playerctl_player_new_from_name(player_name, nullptr); g_object_connect(mpris->player, "signal::play", G_CALLBACK(onPlayerPlay), mpris, "signal::pause", G_CALLBACK(onPlayerPause), mpris, "signal::stop", G_CALLBACK(onPlayerStop), mpris, "signal::stop", G_CALLBACK(onPlayerStop), mpris, "signal::metadata", @@ -426,7 +422,7 @@ auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlaye auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: player-vanished callback: {}", player_name->name); @@ -439,7 +435,7 @@ auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlaye } auto Mpris::onPlayerPlay(PlayerctlPlayer* player, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: player-play callback"); @@ -448,7 +444,7 @@ auto Mpris::onPlayerPlay(PlayerctlPlayer* player, gpointer data) -> void { } auto Mpris::onPlayerPause(PlayerctlPlayer* player, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: player-pause callback"); @@ -457,7 +453,7 @@ auto Mpris::onPlayerPause(PlayerctlPlayer* player, gpointer data) -> void { } auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: player-stop callback"); @@ -469,7 +465,7 @@ auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void { } auto Mpris::onPlayerMetadata(PlayerctlPlayer* player, GVariant* metadata, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: player-metadata callback"); @@ -524,30 +520,30 @@ auto Mpris::getPlayerInfo() -> std::optional { .length = std::nullopt, }; - if (auto artist_ = playerctl_player_get_artist(player, &error)) { + if (auto* artist_ = playerctl_player_get_artist(player, &error)) { spdlog::debug("mpris[{}]: artist = {}", info.name, artist_); info.artist = artist_; g_free(artist_); } if (error) goto errorexit; - if (auto album_ = playerctl_player_get_album(player, &error)) { + if (auto* album_ = playerctl_player_get_album(player, &error)) { spdlog::debug("mpris[{}]: album = {}", info.name, album_); info.album = album_; g_free(album_); } if (error) goto errorexit; - if (auto title_ = playerctl_player_get_title(player, &error)) { + if (auto* title_ = playerctl_player_get_title(player, &error)) { spdlog::debug("mpris[{}]: title = {}", info.name, title_); info.title = title_; g_free(title_); } if (error) goto errorexit; - if (auto length_ = playerctl_player_print_metadata_prop(player, "mpris:length", &error)) { + if (auto* length_ = playerctl_player_print_metadata_prop(player, "mpris:length", &error)) { spdlog::debug("mpris[{}]: mpris:length = {}", info.name, length_); - std::chrono::microseconds len = std::chrono::microseconds(std::strtol(length_, nullptr, 10)); + auto len = std::chrono::microseconds(std::strtol(length_, nullptr, 10)); auto len_h = std::chrono::duration_cast(len); auto len_m = std::chrono::duration_cast(len - len_h); auto len_s = std::chrono::duration_cast(len - len_h - len_m); @@ -564,7 +560,7 @@ auto Mpris::getPlayerInfo() -> std::optional { error = nullptr; } else { spdlog::debug("mpris[{}]: position = {}", info.name, position_); - std::chrono::microseconds len = std::chrono::microseconds(position_); + auto len = std::chrono::microseconds(position_); auto len_h = std::chrono::duration_cast(len); auto len_m = std::chrono::duration_cast(len - len_h); auto len_s = std::chrono::duration_cast(len - len_h - len_m); From 1cd013a09b1034733056d3775c4a73a870ab6d91 Mon Sep 17 00:00:00 2001 From: giskard Date: Mon, 10 Jun 2024 17:30:33 +0800 Subject: [PATCH 286/407] clock: respect tooltip option --- src/modules/clock.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 9f279711f..55d3395bb 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -12,7 +12,8 @@ #ifdef HAVE_LANGINFO_1STDAY #include -#include + +#include #endif namespace fmt_lib = waybar::util::date::format; @@ -126,8 +127,10 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } } - label_.set_has_tooltip(true); - label_.signal_query_tooltip().connect(sigc::mem_fun(*this, &Clock::query_tlp_cb)); + if (tooltipEnabled()) { + label_.set_has_tooltip(true); + label_.signal_query_tooltip().connect(sigc::mem_fun(*this, &Clock::query_tlp_cb)); + } thread_ = [this] { dp.emit(); @@ -194,8 +197,8 @@ const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) { return 2u + ceil((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count(); } -auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line) - -> const year_month_weekday { +auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, + const unsigned line) -> const year_month_weekday { unsigned index{line - 2}; if (weekday{ym / 1} == firstdow) ++index; return ym / firstdow[index]; From 892042eb92f9b95736930298bf07ff02ebfaded9 Mon Sep 17 00:00:00 2001 From: Oliver Locke Date: Wed, 12 Jun 2024 17:03:39 +1000 Subject: [PATCH 287/407] Support muted icons for pulseaudio devices/ports --- src/modules/pulseaudio.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index d7dc80d36..3efd9d232 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -42,15 +42,27 @@ static const std::array ports = { }; const std::vector waybar::modules::Pulseaudio::getPulseIcon() const { - std::vector res = {backend->getCurrentSinkName(), backend->getDefaultSourceName()}; + std::vector res; + auto sink_muted = backend->getSinkMuted(); + if (sink_muted) { + res.emplace_back(backend->getCurrentSinkName() + "-muted"); + } + res.push_back(backend->getCurrentSinkName()); + res.push_back(backend->getDefaultSourceName()); std::string nameLC = backend->getSinkPortName() + backend->getFormFactor(); std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { if (nameLC.find(port) != std::string::npos) { + if (sink_muted) { + res.emplace_back(port + "-muted"); + } res.push_back(port); - return res; + break; } } + if (sink_muted) { + res.emplace_back("default-muted"); + } return res; } From 0bc43c1aa74cebb688e16f8d00d15d71ac68067a Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 12 Jun 2024 23:08:27 +0200 Subject: [PATCH 288/407] fix: lint --- include/AModule.hpp | 2 +- include/modules/clock.hpp | 4 ++-- include/util/clara.hpp | 12 ++++++------ include/util/format.hpp | 2 +- src/modules/bluetooth.cpp | 17 ++++++++--------- src/modules/cpu_usage/linux.cpp | 3 +-- src/modules/dwl/tags.cpp | 4 ++-- src/modules/hyprland/workspaces.cpp | 4 ++-- src/modules/memory/bsd.cpp | 8 ++++---- src/modules/wlr/workspace_manager.cpp | 4 ++-- test/utils/SafeSignal.cpp | 2 +- 11 files changed, 30 insertions(+), 32 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 58076655b..8a5a665b0 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -15,7 +15,7 @@ class AModule : public IModule { virtual ~AModule(); auto update() -> void override; - virtual auto refresh(int) -> void{}; + virtual auto refresh(int) -> void {}; operator Gtk::Widget &() override; auto doAction(const std::string &name) -> void override; diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index c212ec8b9..0c62b6767 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -51,8 +51,8 @@ class Clock final : public ALabel { day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight) std::string cldText_{""}; // calendar text to print CldMode cldMode_{CldMode::MONTH}; - auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) - -> const std::string; + auto get_calendar(const year_month_day& today, const year_month_day& ymd, + const time_zone* tz) -> const std::string; // get local time zone auto local_zone() -> const time_zone*; diff --git a/include/util/clara.hpp b/include/util/clara.hpp index 73fa5415d..da7151fe4 100644 --- a/include/util/clara.hpp +++ b/include/util/clara.hpp @@ -622,8 +622,8 @@ inline auto convertInto(std::string const &source, bool &target) -> ParserResult } #ifdef CLARA_CONFIG_OPTIONAL_TYPE template -inline auto convertInto(std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE &target) - -> ParserResult { +inline auto convertInto(std::string const &source, + CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult { T temp; auto result = convertInto(source, temp); if (result) target = std::move(temp); @@ -751,8 +751,8 @@ class ParserBase { public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse(std::string const &exeName, TokenStream const &tokens) const - -> InternalParseResult = 0; + virtual auto parse(std::string const &exeName, + TokenStream const &tokens) const -> InternalParseResult = 0; virtual auto cardinality() const -> size_t { return 1; } auto parse(Args const &args) const -> InternalParseResult { @@ -1098,8 +1098,8 @@ struct Parser : ParserBase { using ParserBase::parse; - auto parse(std::string const &exeName, TokenStream const &tokens) const - -> InternalParseResult override { + auto parse(std::string const &exeName, + TokenStream const &tokens) const -> InternalParseResult override { struct ParserInfo { ParserBase const *parser = nullptr; size_t count = 0; diff --git a/include/util/format.hpp b/include/util/format.hpp index 069d8897a..df39bb1bb 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -6,7 +6,7 @@ class pow_format { public: pow_format(long long val, std::string&& unit, bool binary = false) - : val_(val), unit_(unit), binary_(binary){}; + : val_(val), unit_(unit), binary_(binary) {}; long long val_; std::string unit_; diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 80e4731b1..3436ae299 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -49,8 +49,8 @@ auto getBoolProperty(GDBusProxy* proxy, const char* property_name) -> bool { return false; } -auto getOptionalStringProperty(GDBusProxy* proxy, const char* property_name) - -> std::optional { +auto getOptionalStringProperty(GDBusProxy* proxy, + const char* property_name) -> std::optional { auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name); if (gvar) { std::string property_value = g_variant_get_string(gvar, NULL); @@ -303,8 +303,8 @@ auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* m auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged( GDBusObjectManagerClient* manager, GDBusObjectProxy* object_proxy, GDBusProxy* interface_proxy, - GVariant* changed_properties, const gchar* const* invalidated_properties, gpointer user_data) - -> void { + GVariant* changed_properties, const gchar* const* invalidated_properties, + gpointer user_data) -> void { std::string interface_name = g_dbus_proxy_get_interface_name(interface_proxy); std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy)); @@ -348,8 +348,8 @@ auto waybar::modules::Bluetooth::getDeviceBatteryPercentage(GDBusObject* object) return std::nullopt; } -auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, DeviceInfo& device_info) - -> bool { +auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, + DeviceInfo& device_info) -> bool { GDBusProxy* proxy_device = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Device1")); if (proxy_device != NULL) { @@ -415,9 +415,8 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional& connected_devices) - -> void { +auto waybar::modules::Bluetooth::findConnectedDevices( + const std::string& cur_controller_path, std::vector& connected_devices) -> void { GList* objects = g_dbus_object_manager_get_objects(manager_.get()); for (GList* l = objects; l != NULL; l = l->next) { GDBusObject* object = G_DBUS_OBJECT(l->data); diff --git a/src/modules/cpu_usage/linux.cpp b/src/modules/cpu_usage/linux.cpp index bcd9594eb..a47123504 100644 --- a/src/modules/cpu_usage/linux.cpp +++ b/src/modules/cpu_usage/linux.cpp @@ -16,8 +16,7 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( } std::stringstream sline(line.substr(5)); std::vector times; - for (size_t time = 0; sline >> time; times.push_back(time)) - ; + for (size_t time = 0; sline >> time; times.push_back(time)); size_t idle_time = 0; size_t total_time = 0; diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index 085b82246..f8b250c8c 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -53,8 +53,8 @@ static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t // Intentionally empty } -static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid){ - // Intentionally empty +static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid) { + // Intentionally empty }; static const zdwl_ipc_output_v2_listener output_status_listener_impl{ diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3209243cb..e5bee553f 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -591,8 +591,8 @@ auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { m_iconsMap.emplace("", ""); } -auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) - -> void { +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, + bool &member) -> void { auto configValue = config[key]; if (configValue.isBool()) { member = configValue.asBool(); diff --git a/src/modules/memory/bsd.cpp b/src/modules/memory/bsd.cpp index 67f9fed7a..1d970e8aa 100644 --- a/src/modules/memory/bsd.cpp +++ b/src/modules/memory/bsd.cpp @@ -21,13 +21,13 @@ static uint64_t get_total_memory() { u_long physmem; #endif int mib[] = { - CTL_HW, + CTL_HW, #if defined(HW_MEMSIZE) - HW_MEMSIZE, + HW_MEMSIZE, #elif defined(HW_PHYSMEM64) - HW_PHYSMEM64, + HW_PHYSMEM64, #else - HW_PHYSMEM, + HW_PHYSMEM, #endif }; u_int miblen = sizeof(mib) / sizeof(mib[0]); diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index f556a161e..3c630d814 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -118,8 +118,8 @@ auto WorkspaceManager::sort_workspaces() -> void { } } -auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) - -> void { +auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, + uint32_t version) -> void { if (workspace_manager_) { spdlog::warn("Register workspace manager again although already registered!"); return; diff --git a/test/utils/SafeSignal.cpp b/test/utils/SafeSignal.cpp index 341e8e2ea..e7e096b09 100644 --- a/test/utils/SafeSignal.cpp +++ b/test/utils/SafeSignal.cpp @@ -71,7 +71,7 @@ struct TestObject { unsigned copied = 0; unsigned moved = 0; - TestObject(const T& v) : value(v){}; + TestObject(const T& v) : value(v) {}; ~TestObject() = default; TestObject(const TestObject& other) From 01438f71a489f5a755f215c203cd73c0c7fe20d3 Mon Sep 17 00:00:00 2001 From: Oliver Locke Date: Thu, 13 Jun 2024 15:59:42 +1000 Subject: [PATCH 289/407] Added muted icons usage to waybar-pulseaudio man page --- man/waybar-pulseaudio.5.scd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index 4bc75258f..6a29e8fcf 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -142,6 +142,8 @@ If they are found in the current PulseAudio port name, the corresponding icons w - *hifi* - *phone* +Additionally, suffixing a device name or port with *-muted* will cause the icon +to be selected when the corresponding audio device is muted. This applies to *default* as well. # EXAMPLES @@ -152,10 +154,12 @@ If they are found in the current PulseAudio port name, the corresponding icons w "format-muted": "", "format-icons": { "alsa_output.pci-0000_00_1f.3.analog-stereo": "", + "alsa_output.pci-0000_00_1f.3.analog-stereo-muted": "", "headphones": "", "handsfree": "", "headset": "", "phone": "", + "phone-muted": "", "portable": "", "car": "", "default": ["", ""] From ab91d0bac3b16948536b40a233acea446591be99 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Fri, 14 Jun 2024 02:24:24 +0200 Subject: [PATCH 290/407] Add hotplug detection of bluetooth controllers --- include/modules/bluetooth.hpp | 3 ++ src/modules/bluetooth.cpp | 79 +++++++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 18 deletions(-) diff --git a/include/modules/bluetooth.hpp b/include/modules/bluetooth.hpp index 89658dcf9..b89383a04 100644 --- a/include/modules/bluetooth.hpp +++ b/include/modules/bluetooth.hpp @@ -49,6 +49,9 @@ class Bluetooth : public ALabel { auto update() -> void override; private: + static auto onObjectAdded(GDBusObjectManager*, GDBusObject*, gpointer) -> void; + static auto onObjectRemoved(GDBusObjectManager*, GDBusObject*, gpointer) -> void; + static auto onInterfaceAddedOrRemoved(GDBusObjectManager*, GDBusObject*, GDBusInterface*, gpointer) -> void; static auto onInterfaceProxyPropertiesChanged(GDBusObjectManagerClient*, GDBusObjectProxy*, diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 3436ae299..478d00735 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -98,30 +98,31 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& std::back_inserter(device_preference_), [](auto x) { return x.asString(); }); } - // NOTE: assumption made that the controller that is selected stays unchanged - // for duration of the module if (cur_controller_ = findCurController(); !cur_controller_) { if (config_["controller-alias"].isString()) { - spdlog::error("findCurController() failed: no bluetooth controller found with alias '{}'", + spdlog::warn("no bluetooth controller found with alias '{}'", config_["controller-alias"].asString()); } else { - spdlog::error("findCurController() failed: no bluetooth controller found"); + spdlog::warn("no bluetooth controller found"); } update(); } else { - // These calls only make sense if a controller could be found + // This call only make sense if a controller could be found findConnectedDevices(cur_controller_->path, connected_devices_); - g_signal_connect(manager_.get(), "interface-proxy-properties-changed", - G_CALLBACK(onInterfaceProxyPropertiesChanged), this); - g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), - this); - g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved), - this); + } + + g_signal_connect(manager_.get(), "object-added", G_CALLBACK(onObjectAdded), this); + g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this); + g_signal_connect(manager_.get(), "interface-proxy-properties-changed", + G_CALLBACK(onInterfaceProxyPropertiesChanged), this); + g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), + this); + g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved), + this); #ifdef WANT_RFKILL - rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update))); + rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update))); #endif - } dp.emit(); } @@ -282,6 +283,41 @@ auto waybar::modules::Bluetooth::update() -> void { ALabel::update(); } +auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, + GDBusObject* object, + gpointer user_data) -> void { + ControllerInfo info; + Bluetooth* bt = static_cast(user_data); + + if (!bt->cur_controller_.has_value() && + bt->getControllerProperties(object, info) && + (!bt->config_["controller-alias"].isString() || + bt->config_["controller-alias"].asString() == info.alias)) { + bt->cur_controller_ = std::move(info); + bt->dp.emit(); + } +} + +auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, + GDBusObject* object, + gpointer user_data) -> void { + GDBusProxy* proxy_controller = + G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1")); + + if (proxy_controller != NULL) { + + std::string object_path = g_dbus_object_get_object_path(object); + Bluetooth* bt = static_cast(user_data); + + if (object_path == bt->cur_controller_->path) { + bt->cur_controller_ = bt->findCurController(); + bt->dp.emit(); + } + + g_object_unref(proxy_controller); + } +} + // NOTE: only for when the org.bluez.Battery1 interface is added/removed after/before a device is // connected/disconnected auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* manager, @@ -292,11 +328,13 @@ auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* m std::string object_path = g_dbus_proxy_get_object_path(G_DBUS_PROXY(interface)); if (interface_name == "org.bluez.Battery1") { Bluetooth* bt = static_cast(user_data); - auto device = std::find_if(bt->connected_devices_.begin(), bt->connected_devices_.end(), - [object_path](auto d) { return d.path == object_path; }); - if (device != bt->connected_devices_.end()) { - device->battery_percentage = bt->getDeviceBatteryPercentage(object); - bt->dp.emit(); + if (bt->cur_controller_.has_value()) { + auto device = std::find_if(bt->connected_devices_.begin(), bt->connected_devices_.end(), + [object_path](auto d) { return d.path == object_path; }); + if (device != bt->connected_devices_.end()) { + device->battery_percentage = bt->getDeviceBatteryPercentage(object); + bt->dp.emit(); + } } } } @@ -309,6 +347,11 @@ auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged( std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy)); Bluetooth* bt = static_cast(user_data); + + if (!bt->cur_controller_.has_value()) { + return; + } + if (interface_name == "org.bluez.Adapter1") { if (object_path == bt->cur_controller_->path) { bt->getControllerProperties(G_DBUS_OBJECT(object_proxy), *bt->cur_controller_); From 0df3c84c0f66e9ca156859fe2b4ad1a832e9b6a6 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Fri, 14 Jun 2024 14:00:55 +0200 Subject: [PATCH 291/407] Fix device list not being updated on selecting new bluetooth controller --- src/modules/bluetooth.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 478d00735..c3c8c2c36 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -301,16 +301,25 @@ auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, GDBusObject* object, gpointer user_data) -> void { - GDBusProxy* proxy_controller = - G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1")); + Bluetooth* bt = static_cast(user_data); + GDBusProxy* proxy_controller; + + if (!bt->cur_controller_.has_value()) { + return; + } + + proxy_controller = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1")); if (proxy_controller != NULL) { std::string object_path = g_dbus_object_get_object_path(object); - Bluetooth* bt = static_cast(user_data); if (object_path == bt->cur_controller_->path) { bt->cur_controller_ = bt->findCurController(); + if (bt->cur_controller_.has_value()) { + bt->connected_devices_.clear(); + bt->findConnectedDevices(bt->cur_controller_->path, bt->connected_devices_); + } bt->dp.emit(); } From bac4d038131ed9ab5a4f4440f102a455cdbe7cc2 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 15 Jun 2024 18:34:45 -0500 Subject: [PATCH 292/407] modules/hyprland/workspaces: remove deprecated property --- include/modules/hyprland/workspaces.hpp | 1 - src/modules/hyprland/workspaces.cpp | 15 +++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 6c6e09329..df7292b1c 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -66,7 +66,6 @@ class Workspaces : public AModule, public EventHandler { auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) -> void; auto populateSortByConfig(const Json::Value& config) -> void; auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; - auto populatePersistentWorkspacesConfig(const Json::Value& config) -> void; auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; auto populateWindowRewriteConfig(const Json::Value& config) -> void; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index e5bee553f..52239b7b6 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -577,9 +577,9 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { populateBoolConfig(config, "active-only", m_activeOnly); populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); + m_persistentWorkspaceConfig = config.get("persistent-workspaces", Json::Value()); populateSortByConfig(config); populateIgnoreWorkspacesConfig(config); - populatePersistentWorkspacesConfig(config); populateFormatWindowSeparatorConfig(config); populateWindowRewriteConfig(config); } @@ -591,8 +591,8 @@ auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { m_iconsMap.emplace("", ""); } -auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, - bool &member) -> void { +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) + -> void { auto configValue = config[key]; if (configValue.isBool()) { member = configValue.asBool(); @@ -632,15 +632,6 @@ auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> vo } } -auto Workspaces::populatePersistentWorkspacesConfig(const Json::Value &config) -> void { - if (config.isMember("persistent-workspaces") || config.isMember("persistent_workspaces")) { - spdlog::warn( - "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); - m_persistentWorkspaceConfig = - config.get("persistent-workspaces", config.get("persistent_workspaces", Json::Value())); - } -} - auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void { auto formatWindowSeparator = config["format-window-separator"]; m_formatWindowSeparator = From f9e693b2a2f5c627c64725487c6c085803a98d93 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 15 Jun 2024 18:36:59 -0500 Subject: [PATCH 293/407] modules/hyprland/backend: remove testing log warn --- src/modules/hyprland/backend.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index a39fcd694..6b36b04e9 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -20,7 +20,6 @@ std::filesystem::path IPC::socketFolder_; std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { // socket path, specified by EventManager of Hyprland if (!socketFolder_.empty()) { - spdlog::warn("socketFolder already set, using {}", socketFolder_.c_str()); return socketFolder_; } From b114b1155c2a9fe4c888087533cacac1d0c3438e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 15 Jun 2024 18:44:46 -0500 Subject: [PATCH 294/407] treewide: clang-format --- include/AModule.hpp | 2 +- include/modules/clock.hpp | 4 ++-- include/util/clara.hpp | 12 +++++----- include/util/format.hpp | 2 +- src/modules/bluetooth.cpp | 32 ++++++++++++--------------- src/modules/clock.cpp | 4 ++-- src/modules/cpu_usage/linux.cpp | 3 ++- src/modules/dwl/tags.cpp | 4 ++-- src/modules/memory/bsd.cpp | 8 +++---- src/modules/wlr/workspace_manager.cpp | 4 ++-- test/utils/SafeSignal.cpp | 2 +- 11 files changed, 37 insertions(+), 40 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 8a5a665b0..58076655b 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -15,7 +15,7 @@ class AModule : public IModule { virtual ~AModule(); auto update() -> void override; - virtual auto refresh(int) -> void {}; + virtual auto refresh(int) -> void{}; operator Gtk::Widget &() override; auto doAction(const std::string &name) -> void override; diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 0c62b6767..c212ec8b9 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -51,8 +51,8 @@ class Clock final : public ALabel { day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight) std::string cldText_{""}; // calendar text to print CldMode cldMode_{CldMode::MONTH}; - auto get_calendar(const year_month_day& today, const year_month_day& ymd, - const time_zone* tz) -> const std::string; + auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) + -> const std::string; // get local time zone auto local_zone() -> const time_zone*; diff --git a/include/util/clara.hpp b/include/util/clara.hpp index da7151fe4..73fa5415d 100644 --- a/include/util/clara.hpp +++ b/include/util/clara.hpp @@ -622,8 +622,8 @@ inline auto convertInto(std::string const &source, bool &target) -> ParserResult } #ifdef CLARA_CONFIG_OPTIONAL_TYPE template -inline auto convertInto(std::string const &source, - CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult { +inline auto convertInto(std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE &target) + -> ParserResult { T temp; auto result = convertInto(source, temp); if (result) target = std::move(temp); @@ -751,8 +751,8 @@ class ParserBase { public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse(std::string const &exeName, - TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto parse(std::string const &exeName, TokenStream const &tokens) const + -> InternalParseResult = 0; virtual auto cardinality() const -> size_t { return 1; } auto parse(Args const &args) const -> InternalParseResult { @@ -1098,8 +1098,8 @@ struct Parser : ParserBase { using ParserBase::parse; - auto parse(std::string const &exeName, - TokenStream const &tokens) const -> InternalParseResult override { + auto parse(std::string const &exeName, TokenStream const &tokens) const + -> InternalParseResult override { struct ParserInfo { ParserBase const *parser = nullptr; size_t count = 0; diff --git a/include/util/format.hpp b/include/util/format.hpp index df39bb1bb..069d8897a 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -6,7 +6,7 @@ class pow_format { public: pow_format(long long val, std::string&& unit, bool binary = false) - : val_(val), unit_(unit), binary_(binary) {}; + : val_(val), unit_(unit), binary_(binary){}; long long val_; std::string unit_; diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index c3c8c2c36..06475a2e5 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -49,8 +49,8 @@ auto getBoolProperty(GDBusProxy* proxy, const char* property_name) -> bool { return false; } -auto getOptionalStringProperty(GDBusProxy* proxy, - const char* property_name) -> std::optional { +auto getOptionalStringProperty(GDBusProxy* proxy, const char* property_name) + -> std::optional { auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name); if (gvar) { std::string property_value = g_variant_get_string(gvar, NULL); @@ -101,7 +101,7 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& if (cur_controller_ = findCurController(); !cur_controller_) { if (config_["controller-alias"].isString()) { spdlog::warn("no bluetooth controller found with alias '{}'", - config_["controller-alias"].asString()); + config_["controller-alias"].asString()); } else { spdlog::warn("no bluetooth controller found"); } @@ -115,8 +115,7 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this); g_signal_connect(manager_.get(), "interface-proxy-properties-changed", G_CALLBACK(onInterfaceProxyPropertiesChanged), this); - g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), - this); + g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), this); g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved), this); @@ -283,14 +282,12 @@ auto waybar::modules::Bluetooth::update() -> void { ALabel::update(); } -auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, - GDBusObject* object, +auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, GDBusObject* object, gpointer user_data) -> void { ControllerInfo info; Bluetooth* bt = static_cast(user_data); - if (!bt->cur_controller_.has_value() && - bt->getControllerProperties(object, info) && + if (!bt->cur_controller_.has_value() && bt->getControllerProperties(object, info) && (!bt->config_["controller-alias"].isString() || bt->config_["controller-alias"].asString() == info.alias)) { bt->cur_controller_ = std::move(info); @@ -298,8 +295,7 @@ auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, } } -auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, - GDBusObject* object, +auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, GDBusObject* object, gpointer user_data) -> void { Bluetooth* bt = static_cast(user_data); GDBusProxy* proxy_controller; @@ -311,7 +307,6 @@ auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, proxy_controller = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1")); if (proxy_controller != NULL) { - std::string object_path = g_dbus_object_get_object_path(object); if (object_path == bt->cur_controller_->path) { @@ -350,8 +345,8 @@ auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* m auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged( GDBusObjectManagerClient* manager, GDBusObjectProxy* object_proxy, GDBusProxy* interface_proxy, - GVariant* changed_properties, const gchar* const* invalidated_properties, - gpointer user_data) -> void { + GVariant* changed_properties, const gchar* const* invalidated_properties, gpointer user_data) + -> void { std::string interface_name = g_dbus_proxy_get_interface_name(interface_proxy); std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy)); @@ -400,8 +395,8 @@ auto waybar::modules::Bluetooth::getDeviceBatteryPercentage(GDBusObject* object) return std::nullopt; } -auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, - DeviceInfo& device_info) -> bool { +auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, DeviceInfo& device_info) + -> bool { GDBusProxy* proxy_device = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Device1")); if (proxy_device != NULL) { @@ -467,8 +462,9 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional& connected_devices) -> void { +auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path, + std::vector& connected_devices) + -> void { GList* objects = g_dbus_object_manager_get_objects(manager_.get()); for (GList* l = objects; l != NULL; l = l->next) { GDBusObject* object = G_DBUS_OBJECT(l->data); diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 55d3395bb..fe2c4c8fb 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -197,8 +197,8 @@ const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) { return 2u + ceil((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count(); } -auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, - const unsigned line) -> const year_month_weekday { +auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line) + -> const year_month_weekday { unsigned index{line - 2}; if (weekday{ym / 1} == firstdow) ++index; return ym / firstdow[index]; diff --git a/src/modules/cpu_usage/linux.cpp b/src/modules/cpu_usage/linux.cpp index a47123504..bcd9594eb 100644 --- a/src/modules/cpu_usage/linux.cpp +++ b/src/modules/cpu_usage/linux.cpp @@ -16,7 +16,8 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( } std::stringstream sline(line.substr(5)); std::vector times; - for (size_t time = 0; sline >> time; times.push_back(time)); + for (size_t time = 0; sline >> time; times.push_back(time)) + ; size_t idle_time = 0; size_t total_time = 0; diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index f8b250c8c..085b82246 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -53,8 +53,8 @@ static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t // Intentionally empty } -static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid) { - // Intentionally empty +static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid){ + // Intentionally empty }; static const zdwl_ipc_output_v2_listener output_status_listener_impl{ diff --git a/src/modules/memory/bsd.cpp b/src/modules/memory/bsd.cpp index 1d970e8aa..67f9fed7a 100644 --- a/src/modules/memory/bsd.cpp +++ b/src/modules/memory/bsd.cpp @@ -21,13 +21,13 @@ static uint64_t get_total_memory() { u_long physmem; #endif int mib[] = { - CTL_HW, + CTL_HW, #if defined(HW_MEMSIZE) - HW_MEMSIZE, + HW_MEMSIZE, #elif defined(HW_PHYSMEM64) - HW_PHYSMEM64, + HW_PHYSMEM64, #else - HW_PHYSMEM, + HW_PHYSMEM, #endif }; u_int miblen = sizeof(mib) / sizeof(mib[0]); diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 3c630d814..f556a161e 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -118,8 +118,8 @@ auto WorkspaceManager::sort_workspaces() -> void { } } -auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, - uint32_t version) -> void { +auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) + -> void { if (workspace_manager_) { spdlog::warn("Register workspace manager again although already registered!"); return; diff --git a/test/utils/SafeSignal.cpp b/test/utils/SafeSignal.cpp index e7e096b09..341e8e2ea 100644 --- a/test/utils/SafeSignal.cpp +++ b/test/utils/SafeSignal.cpp @@ -71,7 +71,7 @@ struct TestObject { unsigned copied = 0; unsigned moved = 0; - TestObject(const T& v) : value(v) {}; + TestObject(const T& v) : value(v){}; ~TestObject() = default; TestObject(const TestObject& other) From c4d769a586d9f72ff4d209c51c44e23591965df9 Mon Sep 17 00:00:00 2001 From: Felix Glinka Date: Fri, 21 Jun 2024 15:30:12 +0200 Subject: [PATCH 295/407] Add explicit constructor to struct Profile Not adding the constructor causes a compilation error on Ubuntu 22.04 with both clang 14 and gcc 11: /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/alloc_traits.h:518:4: error: no matching function for call to 'construct_at' std::construct_at(__p, std::forward<_Args>(__args)...); ^~~~~~~~~~~~~~~~~ /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/vector.tcc:117:21: note: in instantiation of function template specialization 'std::allocator_traits>::construct' requested here _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, ^ ../src/modules/power_profiles_daemon.cpp:106:26: note: in instantiation of function template specialization 'std::vector::emplace_back' requested here availableProfiles_.emplace_back(std::move(name), std::move(driver)); ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_construct.h:94:5: note: candidate template ignored: substitution failure [with _Tp = waybar::modules::Profile, _Args = ]: no matching constructor for initialization of 'waybar::modules::Profile' construct_at(_Tp* __location, _Args&&... __args) ^ --- include/modules/power_profiles_daemon.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index edd9fe006..8bc250b88 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -10,6 +10,10 @@ namespace waybar::modules { struct Profile { std::string name; std::string driver; + + Profile(std::string n, std::string d) + : name(std::move(n)), driver(std::move(d)){ + } }; class PowerProfilesDaemon : public ALabel { From 136b207a12b8c4de9666150f31b7c9d688fc9c55 Mon Sep 17 00:00:00 2001 From: Felix Glinka Date: Fri, 21 Jun 2024 16:43:21 +0200 Subject: [PATCH 296/407] Add suggestion by clang-format --- include/modules/power_profiles_daemon.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index 8bc250b88..a2bd38587 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -11,9 +11,7 @@ struct Profile { std::string name; std::string driver; - Profile(std::string n, std::string d) - : name(std::move(n)), driver(std::move(d)){ - } + Profile(std::string n, std::string d) : name(std::move(n)), driver(std::move(d)) {} }; class PowerProfilesDaemon : public ALabel { From 4126502fe86a17ba3b68bdfed87055c947c65ee0 Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Sat, 22 Jun 2024 17:58:22 +0300 Subject: [PATCH 297/407] Add debug information for keyboard layout selection --- src/modules/hyprland/language.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 549faf738..db7435aa6 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -36,6 +36,11 @@ Language::~Language() { auto Language::update() -> void { std::lock_guard lg(mutex_); + spdlog::debug("hyprland language update with full name {}", layout_.full_name); + spdlog::debug("hyprland language update with short name {}", layout_.short_name); + spdlog::debug("hyprland language update with short description {}", layout_.short_description); + spdlog::debug("hyprland language update with variant {}", layout_.variant); + std::string layoutName = std::string{}; if (config_.isMember("format-" + layout_.short_description + "-" + layout_.variant)) { const auto propName = "format-" + layout_.short_description + "-" + layout_.variant; @@ -50,6 +55,8 @@ auto Language::update() -> void { fmt::arg("variant", layout_.variant))); } + spdlog::debug("hyprland language formatted layout name {}", layoutName); + if (!format_.empty()) { label_.show(); label_.set_markup(layoutName); From d68bcbd292e63fd0f7d1a968cf640c4f601b1259 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Sun, 23 Jun 2024 14:35:48 +0100 Subject: [PATCH 298/407] modules/battery: Deprioritize `capacity` /sys value --- src/modules/battery.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 327420ae5..d87cc6129 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -272,13 +272,6 @@ waybar::modules::Battery::getInfos() { // Some battery will report current and charge in μA/μAh. // Scale these by the voltage to get μW/μWh. - uint32_t capacity = 0; - bool capacity_exists = false; - if (fs::exists(bat / "capacity")) { - capacity_exists = true; - std::ifstream(bat / "capacity") >> capacity; - } - uint32_t current_now = 0; bool current_now_exists = false; if (fs::exists(bat / "current_now")) { @@ -382,6 +375,19 @@ waybar::modules::Battery::getInfos() { } } + uint32_t capacity = 0; + bool capacity_exists = false; + if (charge_now_exists && charge_full_exists && charge_full != 0) { + capacity_exists = true; + capacity = 100 * (uint64_t)charge_now / (uint64_t)charge_full; + } else if (energy_now_exists && energy_full_exists && energy_full != 0) { + capacity_exists = true; + capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full; + } else if (fs::exists(bat / "capacity")) { + capacity_exists = true; + std::ifstream(bat / "capacity") >> capacity; + } + if (!voltage_now_exists) { if (power_now_exists && current_now_exists && current_now != 0) { voltage_now_exists = true; @@ -422,13 +428,7 @@ waybar::modules::Battery::getInfos() { } if (!capacity_exists) { - if (charge_now_exists && charge_full_exists && charge_full != 0) { - capacity_exists = true; - capacity = 100 * (uint64_t)charge_now / (uint64_t)charge_full; - } else if (energy_now_exists && energy_full_exists && energy_full != 0) { - capacity_exists = true; - capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full; - } else if (charge_now_exists && energy_full_exists && voltage_now_exists) { + if (charge_now_exists && energy_full_exists && voltage_now_exists) { if (!charge_full_exists && voltage_now != 0) { charge_full_exists = true; charge_full = 1000000 * (uint64_t)energy_full / (uint64_t)voltage_now; From ccc3c132124623bde5127937fe4fc9aa45a9d35d Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Mon, 24 Jun 2024 08:58:29 +0200 Subject: [PATCH 299/407] Update archlinux --- Dockerfiles/archlinux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index 73f190674..d4274a465 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,5 +3,5 @@ FROM archlinux:base-devel RUN pacman -Syu --noconfirm && \ - pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw && \ + pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols glib2-devel pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw && \ sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen From f6482c36dc1f92f3230761ae65f43eb43ce8177f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 22 Jun 2024 23:19:48 -0500 Subject: [PATCH 300/407] hyprland: clangd cleanup --- include/modules/hyprland/workspaces.hpp | 15 +++++++-------- src/modules/hyprland/backend.cpp | 2 +- src/modules/hyprland/submap.cpp | 4 ++-- src/modules/hyprland/windowcreationpayload.cpp | 3 --- src/modules/hyprland/workspace.cpp | 3 --- src/modules/hyprland/workspaces.cpp | 7 +++---- 6 files changed, 13 insertions(+), 21 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index df7292b1c..f5c20f69c 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -4,14 +4,11 @@ #include #include -#include #include #include #include -#include #include #include -#include #include #include "AModule.hpp" @@ -53,17 +50,19 @@ class Workspaces : public AModule, public EventHandler { void onEvent(const std::string& e) override; void updateWindowCount(); void sortWorkspaces(); - void createWorkspace(Json::Value const& workspaceData, - Json::Value const& clientsData = Json::Value::nullRef); + void createWorkspace(Json::Value const& workspace_data, + Json::Value const& clients_data = Json::Value::nullRef); - Json::Value createMonitorWorkspaceData(std::string const& name, std::string const& monitor); + static Json::Value createMonitorWorkspaceData(std::string const& name, + std::string const& monitor); void removeWorkspace(std::string const& name); void setUrgentWorkspace(std::string const& windowaddress); // Config void parseConfig(const Json::Value& config); auto populateIconsMap(const Json::Value& formatIcons) -> void; - auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) -> void; + static auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) + -> void; auto populateSortByConfig(const Json::Value& config) -> void; auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; @@ -98,7 +97,7 @@ class Workspaces : public AModule, public EventHandler { void doUpdate(); void removeWorkspacesToRemove(); void createWorkspacesToCreate(); - std::vector getVisibleWorkspaces(); + static std::vector getVisibleWorkspaces(); void updateWorkspaceStates(); bool updateWindowsToCreate(); diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 6b36b04e9..b3fb36d47 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -26,7 +26,7 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { const char* xdgRuntimeDirEnv = std::getenv("XDG_RUNTIME_DIR"); std::filesystem::path xdgRuntimeDir; // Only set path if env variable is set - if (xdgRuntimeDirEnv) { + if (xdgRuntimeDirEnv != nullptr) { xdgRuntimeDir = std::filesystem::path(xdgRuntimeDirEnv); } diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 3d8ed6992..96677d127 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -38,12 +38,12 @@ Submap::~Submap() { } auto Submap::parseConfig(const Json::Value& config) -> void { - auto const alwaysOn = config["always-on"]; + auto const& alwaysOn = config["always-on"]; if (alwaysOn.isBool()) { always_on_ = alwaysOn.asBool(); } - auto const defaultSubmap = config["default-submap"]; + auto const& defaultSubmap = config["default-submap"]; if (defaultSubmap.isString()) { default_submap_ = defaultSubmap.asString(); } diff --git a/src/modules/hyprland/windowcreationpayload.cpp b/src/modules/hyprland/windowcreationpayload.cpp index 22febc598..df7fe784e 100644 --- a/src/modules/hyprland/windowcreationpayload.cpp +++ b/src/modules/hyprland/windowcreationpayload.cpp @@ -3,14 +3,11 @@ #include #include -#include -#include #include #include #include #include "modules/hyprland/workspaces.hpp" -#include "util/regex_collection.hpp" namespace waybar::modules::hyprland { diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index bf0a33681..eac21b7ed 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -1,14 +1,11 @@ #include #include -#include #include #include #include -#include #include "modules/hyprland/workspaces.hpp" -#include "util/regex_collection.hpp" namespace waybar::modules::hyprland { diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 52239b7b6..047703cc9 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "util/regex_collection.hpp" @@ -593,14 +592,14 @@ auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) -> void { - auto configValue = config[key]; + const auto &configValue = config[key]; if (configValue.isBool()) { member = configValue.asBool(); } } auto Workspaces::populateSortByConfig(const Json::Value &config) -> void { - auto configSortBy = config["sort-by"]; + const auto &configSortBy = config["sort-by"]; if (configSortBy.isString()) { auto sortByStr = configSortBy.asString(); try { @@ -633,7 +632,7 @@ auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> vo } auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void { - auto formatWindowSeparator = config["format-window-separator"]; + const auto &formatWindowSeparator = config["format-window-separator"]; m_formatWindowSeparator = formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " "; } From c08660d837f9896a538e1b6dce541eccc2b1feb7 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 28 Jun 2024 13:11:50 -0500 Subject: [PATCH 301/407] modules/hyprland/backend: handle empty json responses Fixes https://github.com/Alexays/Waybar/issues/3388 --- src/modules/hyprland/backend.cpp | 8 +++++++- test/hyprland/backend.cpp | 10 ++++++++-- test/hyprland/fixtures/IPCTestFixture.hpp | 6 ++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index b3fb36d47..8ec6eddac 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -218,7 +218,13 @@ std::string IPC::getSocket1Reply(const std::string& rq) { } Json::Value IPC::getSocket1JsonReply(const std::string& rq) { - return parser_.parse(getSocket1Reply("j/" + rq)); + std::string reply = getSocket1Reply("j/" + rq); + + if (reply.empty()) { + return {}; + } + + return parser_.parse(reply); } } // namespace waybar::modules::hyprland diff --git a/test/hyprland/backend.cpp b/test/hyprland/backend.cpp index e6b209da4..dcae05090 100644 --- a/test/hyprland/backend.cpp +++ b/test/hyprland/backend.cpp @@ -1,4 +1,3 @@ -#include #if __has_include() #include #else @@ -6,7 +5,6 @@ #endif #include "fixtures/IPCTestFixture.hpp" -#include "modules/hyprland/backend.hpp" namespace fs = std::filesystem; namespace hyprland = waybar::modules::hyprland; @@ -53,3 +51,11 @@ TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirExistsNoHyprDir", "[getSocketFold // Assert expected result REQUIRE(actualPath == expectedPath); } + +TEST_CASE_METHOD(IPCMock, "getSocket1JsonReply handles empty response", "[getSocket1JsonReply]") { + std::string request = "test_request"; + + Json::Value jsonResponse = getSocket1JsonReply(request); + + REQUIRE(jsonResponse.isNull()); +} diff --git a/test/hyprland/fixtures/IPCTestFixture.hpp b/test/hyprland/fixtures/IPCTestFixture.hpp index 3b04b3b0e..f6fa335f7 100644 --- a/test/hyprland/fixtures/IPCTestFixture.hpp +++ b/test/hyprland/fixtures/IPCTestFixture.hpp @@ -14,3 +14,9 @@ class IPCTestFixture : public hyprland::IPC { private: }; + +class IPCMock : public IPCTestFixture { + public: + // Mock getSocket1Reply to return an empty string + static std::string getSocket1Reply(const std::string& rq) { return ""; } +}; From 64a31330833d008e568adaaead65458c17226cb7 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 30 Jun 2024 14:52:49 +0200 Subject: [PATCH 302/407] workaround for icons not rendered for apps existing before waybar launch --- src/modules/wlr/taskbar.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 41d467482..e6c8e536c 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -788,6 +788,10 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu } icon_themes_.push_back(Gtk::IconTheme::get_default()); + + for (auto &t : tasks_) { + t->handle_app_id(t->app_id().c_str()); + } } Taskbar::~Taskbar() { From fb24e8cb1f9cd7ece865b9f150f2d23101be624d Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 30 Jun 2024 21:16:52 +0200 Subject: [PATCH 303/407] add hide-empty-text option to hide module whenever output is empty but format is not --- src/modules/custom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 45e849cc0..20d8d9348 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -162,7 +162,7 @@ auto waybar::modules::Custom::update() -> void { auto str = fmt::format(fmt::runtime(format_), text_, fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); - if (str.empty()) { + if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { event_box_.hide(); } else { label_.set_markup(str); From 8eee5687316af4a6c32752e79210f58fee52c499 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 30 Jun 2024 21:23:54 +0200 Subject: [PATCH 304/407] manpage for PR #3395 --- man/waybar-custom.5.scd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index a62c93126..1c09eac9b 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -21,6 +21,10 @@ Addressed by *custom/* The path to a script, which determines if the script in *exec* should be executed. ++ *exec* will be executed if the exit code of *exec-if* equals 0. +*hide-empty-text*: ++ + typeof: bool ++ + Disables the module when output is empty, but format might contain additional static content. + *exec-on-event*: ++ typeof: bool ++ default: true ++ From f609042ecebbf8911cdc6598d61807778c5b758f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 1 Jul 2024 00:09:58 +0000 Subject: [PATCH 305/407] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/ad57eef4ef0659193044870c731987a6df5cf56b?narHash=sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs%3D' (2024-05-29) → 'github:NixOS/nixpkgs/b2852eb9365c6de48ffb0dc2c9562591f652242a?narHash=sha256-C8e9S7RzshSdHB7L%2Bv9I51af1gDM5unhJ2xO1ywxNH8%3D' (2024-06-27) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index f35322ece..3f0deffe9 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1716948383, - "narHash": "sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs=", + "lastModified": 1719506693, + "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ad57eef4ef0659193044870c731987a6df5cf56b", + "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a", "type": "github" }, "original": { From 8f64caceb5cf3cfe08de82b07af0fb4da97dd6d9 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Mon, 1 Jul 2024 18:30:58 +0200 Subject: [PATCH 306/407] fix example in manpage for pulseaudio/slider --- man/waybar-pulseaudio-slider.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-pulseaudio-slider.5.scd b/man/waybar-pulseaudio-slider.5.scd index fc1da1c4a..cf07fed18 100644 --- a/man/waybar-pulseaudio-slider.5.scd +++ b/man/waybar-pulseaudio-slider.5.scd @@ -31,7 +31,7 @@ The volume can be controlled by dragging the slider across the bar or clicking o ``` "modules-right": [ - "pulseaudio-slider", + "pulseaudio/slider", ], "pulseaudio/slider": { "min": 0, From 14c3235c12eae0615dda9b1e71c6b8fd6cb20cd3 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 2 Jul 2024 10:12:41 -0500 Subject: [PATCH 307/407] src: clang-tidy --- include/AModule.hpp | 8 ++++---- src/AAppIconLabel.cpp | 8 ++++---- src/ALabel.cpp | 8 ++++---- src/AModule.cpp | 14 +++++++------- src/bar.cpp | 27 +++++++++++++-------------- src/client.cpp | 33 +++++++++++++++++---------------- src/config.cpp | 12 +++++++----- src/group.cpp | 4 ++-- src/main.cpp | 7 ++++--- 9 files changed, 62 insertions(+), 59 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 90f34187d..facb3130f 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -14,9 +14,9 @@ class AModule : public IModule { public: static constexpr const char *MODULE_CLASS = "module"; - virtual ~AModule(); + ~AModule() override; auto update() -> void override; - virtual auto refresh(int) -> void{}; + virtual auto refresh(int shouldRefresh) -> void{}; operator Gtk::Widget &() override; auto doAction(const std::string &name) -> void override; @@ -32,13 +32,13 @@ class AModule : public IModule { enum SCROLL_DIR { NONE, UP, DOWN, LEFT, RIGHT }; SCROLL_DIR getScrollDir(GdkEventScroll *e); - bool tooltipEnabled(); + bool tooltipEnabled() const; const std::string name_; const Json::Value &config_; Gtk::EventBox event_box_; - virtual void setCursor(Gdk::CursorType const c); + virtual void setCursor(Gdk::CursorType const &c); virtual bool handleToggle(GdkEventButton *const &ev); virtual bool handleMouseEnter(GdkEventCrossing *const &ev); diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index e64e6daa5..fda5f9fd1 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -96,14 +96,14 @@ std::optional getIconName(const std::string& app_identifier, return app_identifier; } - const auto app_identifier_desktop = app_identifier + "-desktop"; + auto app_identifier_desktop = app_identifier + "-desktop"; if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) { return app_identifier_desktop; } - const auto first_space = app_identifier.find_first_of(' '); + auto first_space = app_identifier.find_first_of(' '); if (first_space != std::string::npos) { - const auto first_word = toLowerCase(app_identifier.substr(0, first_space)); + auto first_word = toLowerCase(app_identifier.substr(0, first_space)); if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { return first_word; } @@ -111,7 +111,7 @@ std::optional getIconName(const std::string& app_identifier, const auto first_dash = app_identifier.find_first_of('-'); if (first_dash != std::string::npos) { - const auto first_word = toLowerCase(app_identifier.substr(0, first_dash)); + auto first_word = toLowerCase(app_identifier.substr(0, first_dash)); if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { return first_word; } diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 5497c62ac..da2991a35 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -71,12 +71,12 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st GtkBuilder* builder = gtk_builder_new(); // Make the GtkBuilder and check for errors in his parsing - if (!gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr)) { + if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) { throw std::runtime_error("Error found in the file " + menuFile); } menu_ = gtk_builder_get_object(builder, "menu"); - if (!menu_) { + if (menu_ == nullptr) { throw std::runtime_error("Failed to get 'menu' object from GtkBuilder"); } submenus_ = std::map(); @@ -121,7 +121,7 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_ } if (format_icons.isArray()) { auto size = format_icons.size(); - if (size) { + if (size != 0U) { auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); format_icons = format_icons[idx]; } @@ -147,7 +147,7 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector& } if (format_icons.isArray()) { auto size = format_icons.size(); - if (size) { + if (size != 0U) { auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); format_icons = format_icons[idx]; } diff --git a/src/AModule.cpp b/src/AModule.cpp index 9948edabc..c40e3a56b 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -8,8 +8,8 @@ namespace waybar { AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id, bool enable_click, bool enable_scroll) - : name_(std::move(name)), - config_(std::move(config)), + : name_(name), + config_(config), isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true}, distance_scrolled_y_(0.0), distance_scrolled_x_(0.0) { @@ -18,12 +18,12 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: for (Json::Value::const_iterator it = actions.begin(); it != actions.end(); ++it) { if (it.key().isString() && it->isString()) - if (eventActionMap_.count(it.key().asString()) == 0) { + if (!eventActionMap_.contains(it.key().asString())) { eventActionMap_.insert({it.key().asString(), it->asString()}); enable_click = true; enable_scroll = true; } else - spdlog::warn("Dublicate action is ignored: {0}", it.key().asString()); + spdlog::warn("Duplicate action is ignored: {0}", it.key().asString()); else spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); } @@ -90,7 +90,7 @@ auto AModule::doAction(const std::string& name) -> void { } } -void AModule::setCursor(Gdk::CursorType c) { +void AModule::setCursor(Gdk::CursorType const& c) { auto cursor = Gdk::Cursor::create(c); auto gdk_window = event_box_.get_window(); gdk_window->set_cursor(cursor); @@ -164,7 +164,7 @@ AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { // ignore reverse-scrolling if event comes from a mouse wheel GdkDevice* device = gdk_event_get_source_device((GdkEvent*)e); - if (device != NULL && gdk_device_get_source(device) == GDK_SOURCE_MOUSE) { + if (device != nullptr && gdk_device_get_source(device) == GDK_SOURCE_MOUSE) { reverse = reverse_mouse; } @@ -242,7 +242,7 @@ bool AModule::handleScroll(GdkEventScroll* e) { return true; } -bool AModule::tooltipEnabled() { return isTooltip; } +bool AModule::tooltipEnabled() const { return isTooltip; } AModule::operator Gtk::Widget&() { return event_box_; } diff --git a/src/bar.cpp b/src/bar.cpp index 5efe58897..8c75c2c20 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -72,16 +72,16 @@ void from_json(const Json::Value& j, std::optional& l) { /* Deserializer for struct bar_mode */ void from_json(const Json::Value& j, bar_mode& m) { if (j.isObject()) { - if (auto v = j["layer"]; v.isString()) { + if (const auto& v = j["layer"]; v.isString()) { from_json(v, m.layer); } - if (auto v = j["exclusive"]; v.isBool()) { + if (const auto& v = j["exclusive"]; v.isBool()) { m.exclusive = v.asBool(); } - if (auto v = j["passthrough"]; v.isBool()) { + if (const auto& v = j["passthrough"]; v.isBool()) { m.passthrough = v.asBool(); } - if (auto v = j["visible"]; v.isBool()) { + if (const auto& v = j["visible"]; v.isBool()) { m.visible = v.asBool(); } } @@ -118,7 +118,7 @@ Glib::ustring to_string(Gtk::PositionType pos) { * Assumes that all the values in the object are deserializable to the same type. */ template ::value>> + typename = std::enable_if_t>> void from_json(const Json::Value& j, std::map& m) { if (j.isObject()) { for (auto it = j.begin(); it != j.end(); ++it) { @@ -393,19 +393,18 @@ void waybar::Bar::setPosition(Gtk::PositionType position) { } } -void waybar::Bar::onMap(GdkEventAny*) { +void waybar::Bar::onMap(GdkEventAny* /*unused*/) { /* * Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor). */ - auto gdk_window = window.get_window()->gobj(); + auto* gdk_window = window.get_window()->gobj(); surface = gdk_wayland_window_get_wl_surface(gdk_window); configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window)); setPassThrough(passthrough_); } -void waybar::Bar::setVisible(bool value) { - visible = value; +void waybar::Bar::setVisible(bool visible) { if (auto mode = config.get("mode", {}); mode.isString()) { setMode(visible ? config["mode"].asString() : MODE_INVISIBLE); } else { @@ -473,7 +472,7 @@ void waybar::Bar::handleSignal(int signal) { void waybar::Bar::getModules(const Factory& factory, const std::string& pos, waybar::Group* group = nullptr) { - auto module_list = group ? config[pos]["modules"] : config[pos]; + auto module_list = group != nullptr ? config[pos]["modules"] : config[pos]; if (module_list.isArray()) { for (const auto& name : module_list) { try { @@ -485,10 +484,10 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, auto id_name = ref.substr(6, hash_pos - 6); auto class_name = hash_pos != std::string::npos ? ref.substr(hash_pos + 1) : ""; - auto vertical = (group ? group->getBox().get_orientation() : box_.get_orientation()) == - Gtk::ORIENTATION_VERTICAL; + auto vertical = (group != nullptr ? group->getBox().get_orientation() + : box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL; - auto group_module = new waybar::Group(id_name, class_name, config[ref], vertical); + auto* group_module = new waybar::Group(id_name, class_name, config[ref], vertical); getModules(factory, ref, group_module); module = group_module; } else { @@ -497,7 +496,7 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, std::shared_ptr module_sp(module); modules_all_.emplace_back(module_sp); - if (group) { + if (group != nullptr) { group->addWidget(*module); } else { if (pos == "modules-left") { diff --git a/src/client.cpp b/src/client.cpp index 7c59dd5ee..5768b1b03 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "gtkmm/icontheme.h" #include "idle-inhibit-unstable-v1-client-protocol.h" @@ -11,13 +12,13 @@ #include "util/format.hpp" waybar::Client *waybar::Client::inst() { - static auto c = new Client(); + static auto *c = new Client(); return c; } void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - auto client = static_cast(data); + auto *client = static_cast(data); if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { client->xdg_output_manager = static_cast(wl_registry_bind( @@ -42,7 +43,7 @@ void waybar::Client::handleOutput(struct waybar_output &output) { .description = &handleOutputDescription, }; // owned by output->monitor; no need to destroy - auto wl_output = gdk_wayland_monitor_get_wl_output(output.monitor->gobj()); + auto *wl_output = gdk_wayland_monitor_get_wl_output(output.monitor->gobj()); output.xdg_output.reset(zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, wl_output)); zxdg_output_v1_add_listener(output.xdg_output.get(), &xdgOutputListener, &output); } @@ -61,7 +62,7 @@ std::vector waybar::Client::getOutputConfigs(struct waybar_output & } void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_output*/) { - auto client = waybar::Client::inst(); + auto *client = waybar::Client::inst(); try { auto &output = client->getOutput(data); /** @@ -85,24 +86,24 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_ } } } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; + std::cerr << e.what() << '\n'; } } void waybar::Client::handleOutputName(void *data, struct zxdg_output_v1 * /*xdg_output*/, const char *name) { - auto client = waybar::Client::inst(); + auto *client = waybar::Client::inst(); try { auto &output = client->getOutput(data); output.name = name; } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; + std::cerr << e.what() << '\n'; } } void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * /*xdg_output*/, const char *description) { - auto client = waybar::Client::inst(); + auto *client = waybar::Client::inst(); try { auto &output = client->getOutput(data); const char *open_paren = strrchr(description, '('); @@ -111,13 +112,13 @@ void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * size_t identifier_length = open_paren - description; output.identifier = std::string(description, identifier_length - 1); } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; + std::cerr << e.what() << '\n'; } } void waybar::Client::handleMonitorAdded(Glib::RefPtr monitor) { auto &output = outputs_.emplace_back(); - output.monitor = monitor; + output.monitor = std::move(monitor); handleOutput(output); } @@ -154,15 +155,15 @@ const std::string waybar::Client::getStyle(const std::string &style, std::vector search_files; switch (appearance.value_or(portal->getAppearance())) { case waybar::Appearance::LIGHT: - search_files.push_back("style-light.css"); + search_files.emplace_back("style-light.css"); break; case waybar::Appearance::DARK: - search_files.push_back("style-dark.css"); + search_files.emplace_back("style-dark.css"); break; case waybar::Appearance::UNKNOWN: break; } - search_files.push_back("style.css"); + search_files.emplace_back("style.css"); css_file = Config::findConfigPath(search_files); } else { css_file = style; @@ -196,7 +197,7 @@ void waybar::Client::bindInterfaces() { wl_registry_add_listener(registry, ®istry_listener, this); wl_display_roundtrip(wl_display); - if (!gtk_layer_is_supported()) { + if (gtk_layer_is_supported() == 0) { throw std::runtime_error("The Wayland compositor does not support wlr-layer-shell protocol"); } @@ -233,11 +234,11 @@ int waybar::Client::main(int argc, char *argv[]) { return 1; } if (show_help) { - std::cout << cli << std::endl; + std::cout << cli << '\n'; return 0; } if (show_version) { - std::cout << "Waybar v" << VERSION << std::endl; + std::cout << "Waybar v" << VERSION << '\n'; return 0; } if (!log_level.empty()) { diff --git a/src/config.cpp b/src/config.cpp index c43e5a632..b78af56c3 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -21,10 +21,10 @@ const std::vector Config::CONFIG_DIRS = { const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR"; -std::optional tryExpandPath(const std::string base, const std::string filename) { +std::optional tryExpandPath(const std::string &base, const std::string &filename) { fs::path path; - if (filename != "") { + if (!filename.empty()) { path = fs::path(base) / fs::path(filename); } else { path = fs::path(base); @@ -129,9 +129,9 @@ bool isValidOutput(const Json::Value &config, const std::string &name, if (config_output.substr(0, 1) == "!") { if (config_output.substr(1) == name || config_output.substr(1) == identifier) { return false; - } else { - continue; } + + continue; } if (config_output == name || config_output == identifier) { return true; @@ -142,7 +142,9 @@ bool isValidOutput(const Json::Value &config, const std::string &name, } } return false; - } else if (config["output"].isString()) { + } + + if (config["output"].isString()) { auto config_output = config["output"].asString(); if (!config_output.empty()) { if (config_output.substr(0, 1) == "!") { diff --git a/src/group.cpp b/src/group.cpp index 9f707dc94..c77f2d311 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -19,9 +19,9 @@ const Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) { if (is_vertical) { return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_UP; - } else { - return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_LEFT; } + + return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_LEFT; } Group::Group(const std::string& name, const std::string& id, const Json::Value& config, diff --git a/src/main.cpp b/src/main.cpp index ff446ffce..679c66d65 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,8 @@ std::list reap; volatile bool reload; void* signalThread(void* args) { - int err, signum; + int err; + int signum; sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); @@ -46,7 +47,7 @@ void* signalThread(void* args) { } } -void startSignalThread(void) { +void startSignalThread() { int err; sigset_t mask; sigemptyset(&mask); @@ -71,7 +72,7 @@ void startSignalThread(void) { int main(int argc, char* argv[]) { try { - auto client = waybar::Client::inst(); + auto* client = waybar::Client::inst(); std::signal(SIGUSR1, [](int /*signal*/) { for (auto& bar : waybar::Client::inst()->bars) { From d66685a3aa85d02f1a9498cf9d59afef94c820de Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 2 Jul 2024 10:30:04 -0500 Subject: [PATCH 308/407] util: clang-tidy --- include/util/backlight_backend.hpp | 8 +++---- include/util/regex_collection.hpp | 8 ++++--- src/util/audio_backend.cpp | 37 +++++++++++++++--------------- src/util/backlight_backend.cpp | 18 ++++++++------- src/util/portal.cpp | 4 +++- src/util/prepare_for_sleep.cpp | 12 +++++----- src/util/regex_collection.cpp | 6 +++-- src/util/sanitize_str.cpp | 3 +-- src/util/ustring_clen.cpp | 6 ++--- 9 files changed, 54 insertions(+), 48 deletions(-) diff --git a/include/util/backlight_backend.hpp b/include/util/backlight_backend.hpp index 20925b524..eb42d3ccf 100644 --- a/include/util/backlight_backend.hpp +++ b/include/util/backlight_backend.hpp @@ -56,10 +56,10 @@ class BacklightBackend { void set_previous_best_device(const BacklightDevice *device); - void set_brightness(std::string preferred_device, ChangeType change_type, double step); + void set_brightness(const std::string &preferred_device, ChangeType change_type, double step); - void set_scaled_brightness(std::string preferred_device, int brightness); - int get_scaled_brightness(std::string preferred_device); + void set_scaled_brightness(const std::string &preferred_device, int brightness); + int get_scaled_brightness(const std::string &preferred_device); bool is_login_proxy_initialized() const { return static_cast(login_proxy_); } @@ -70,7 +70,7 @@ class BacklightBackend { std::mutex udev_thread_mutex_; private: - void set_brightness_internal(std::string device_name, int brightness, int max_brightness); + void set_brightness_internal(const std::string &device_name, int brightness, int max_brightness); std::function on_updated_cb_; std::chrono::milliseconds polling_interval_; diff --git a/include/util/regex_collection.hpp b/include/util/regex_collection.hpp index fe958461d..30d26d4a8 100644 --- a/include/util/regex_collection.hpp +++ b/include/util/regex_collection.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace waybar::util { @@ -17,7 +18,7 @@ struct Rule { // See https://en.cppreference.com/w/cpp/compiler_support/20 "Parenthesized initialization of // aggregates" Rule(std::regex rule, std::string repr, int priority) - : rule(rule), repr(repr), priority(priority) {} + : rule(std::move(rule)), repr(std::move(repr)), priority(priority) {} }; int default_priority_function(std::string& key); @@ -40,8 +41,9 @@ class RegexCollection { public: RegexCollection() = default; - RegexCollection(const Json::Value& map, std::string default_repr = "", - std::function priority_function = default_priority_function); + RegexCollection( + const Json::Value& map, std::string default_repr = "", + const std::function& priority_function = default_priority_function); ~RegexCollection() = default; std::string& get(std::string& value, bool& matched_any); diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index f4dd72c4a..e634784bb 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace waybar::util { @@ -15,13 +16,11 @@ AudioBackend::AudioBackend(std::function on_updated_cb, private_construc : mainloop_(nullptr), mainloop_api_(nullptr), context_(nullptr), - sink_idx_(0), volume_(0), muted_(false), - source_idx_(0), source_volume_(0), source_muted_(false), - on_updated_cb_(on_updated_cb) { + on_updated_cb_(std::move(on_updated_cb)) { mainloop_ = pa_threaded_mainloop_new(); if (mainloop_ == nullptr) { throw std::runtime_error("pa_mainloop_new() failed."); @@ -66,7 +65,7 @@ void AudioBackend::connectContext() { } void AudioBackend::contextStateCb(pa_context *c, void *data) { - auto backend = static_cast(data); + auto *backend = static_cast(data); switch (pa_context_get_state(c)) { case PA_CONTEXT_TERMINATED: backend->mainloop_api_->quit(backend->mainloop_api_, 0); @@ -127,7 +126,7 @@ void AudioBackend::subscribeCb(pa_context *context, pa_subscription_event_type_t * Called in response to a volume change request */ void AudioBackend::volumeModifyCb(pa_context *c, int success, void *data) { - auto backend = static_cast(data); + auto *backend = static_cast(data); if (success != 0) { pa_context_get_sink_info_by_index(backend->context_, backend->sink_idx_, sinkInfoCb, data); } @@ -140,7 +139,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i void *data) { if (i == nullptr) return; - auto backend = static_cast(data); + auto *backend = static_cast(data); if (!backend->ignored_sinks_.empty()) { for (const auto &ignored_sink : backend->ignored_sinks_) { @@ -151,11 +150,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i } if (backend->current_sink_name_ == i->name) { - if (i->state != PA_SINK_RUNNING) { - backend->current_sink_running_ = false; - } else { - backend->current_sink_running_ = true; - } + backend->current_sink_running_ = i->state == PA_SINK_RUNNING; } if (!backend->current_sink_running_ && i->state == PA_SINK_RUNNING) { @@ -173,7 +168,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i backend->desc_ = i->description; backend->monitor_ = i->monitor_source_name; backend->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; - if (auto ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) { + if (const auto *ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) { backend->form_factor_ = ff; } else { backend->form_factor_ = ""; @@ -187,7 +182,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i */ void AudioBackend::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i, int /*eol*/, void *data) { - auto backend = static_cast(data); + auto *backend = static_cast(data); if (i != nullptr && backend->default_source_name_ == i->name) { auto source_volume = static_cast(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM}; backend->source_volume_ = std::round(source_volume * 100.0F); @@ -204,7 +199,7 @@ void AudioBackend::sourceInfoCb(pa_context * /*context*/, const pa_source_info * * used to find the default PulseAudio sink. */ void AudioBackend::serverInfoCb(pa_context *context, const pa_server_info *i, void *data) { - auto backend = static_cast(data); + auto *backend = static_cast(data); backend->current_sink_name_ = i->default_sink_name; backend->default_source_name_ = i->default_source_name; @@ -253,22 +248,26 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma void AudioBackend::toggleSinkMute() { muted_ = !muted_; - pa_context_set_sink_mute_by_index(context_, sink_idx_, muted_, nullptr, nullptr); + pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast(muted_), nullptr, + nullptr); } void AudioBackend::toggleSinkMute(bool mute) { muted_ = mute; - pa_context_set_sink_mute_by_index(context_, sink_idx_, muted_, nullptr, nullptr); + pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast(muted_), nullptr, + nullptr); } void AudioBackend::toggleSourceMute() { source_muted_ = !muted_; - pa_context_set_source_mute_by_index(context_, source_idx_, source_muted_, nullptr, nullptr); + pa_context_set_source_mute_by_index(context_, source_idx_, static_cast(source_muted_), + nullptr, nullptr); } void AudioBackend::toggleSourceMute(bool mute) { source_muted_ = mute; - pa_context_set_source_mute_by_index(context_, source_idx_, source_muted_, nullptr, nullptr); + pa_context_set_source_mute_by_index(context_, source_idx_, static_cast(source_muted_), + nullptr, nullptr); } bool AudioBackend::isBluetooth() { @@ -287,4 +286,4 @@ void AudioBackend::setIgnoredSinks(const Json::Value &config) { } } -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index 60d5ca3a6..bb102cd93 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -4,7 +4,9 @@ #include #include +#include #include +#include namespace { class FileDescriptor { @@ -122,7 +124,7 @@ static void enumerate_devices(std::vector &devices, udev *udev) } BacklightDevice::BacklightDevice(std::string name, int actual, int max, bool powered) - : name_(name), actual_(actual), max_(max), powered_(powered) {} + : name_(std::move(name)), actual_(actual), max_(max), powered_(powered) {} std::string BacklightDevice::name() const { return name_; } @@ -140,7 +142,7 @@ void BacklightDevice::set_powered(bool powered) { powered_ = powered; } BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, std::function on_updated_cb) - : on_updated_cb_(on_updated_cb), polling_interval_(interval), previous_best_({}) { + : on_updated_cb_(std::move(on_updated_cb)), polling_interval_(interval), previous_best_({}) { std::unique_ptr udev_check{udev_new()}; check_nn(udev_check.get(), "Udev check new failed"); enumerate_devices(devices_, udev_check.get()); @@ -236,24 +238,24 @@ void BacklightBackend::set_previous_best_device(const BacklightDevice *device) { } } -void BacklightBackend::set_scaled_brightness(std::string preferred_device, int brightness) { +void BacklightBackend::set_scaled_brightness(const std::string &preferred_device, int brightness) { GET_BEST_DEVICE(best, (*this), preferred_device); if (best != nullptr) { const auto max = best->get_max(); - const auto abs_val = static_cast(round(brightness * max / 100.0f)); + const auto abs_val = static_cast(std::round(brightness * max / 100.0F)); set_brightness_internal(best->name(), abs_val, best->get_max()); } } -void BacklightBackend::set_brightness(std::string preferred_device, ChangeType change_type, +void BacklightBackend::set_brightness(const std::string &preferred_device, ChangeType change_type, double step) { GET_BEST_DEVICE(best, (*this), preferred_device); if (best != nullptr) { const auto max = best->get_max(); - const auto abs_step = static_cast(round(step * max / 100.0f)); + const auto abs_step = static_cast(round(step * max / 100.0F)); const int new_brightness = change_type == ChangeType::Increase ? best->get_actual() + abs_step : best->get_actual() - abs_step; @@ -261,7 +263,7 @@ void BacklightBackend::set_brightness(std::string preferred_device, ChangeType c } } -void BacklightBackend::set_brightness_internal(std::string device_name, int brightness, +void BacklightBackend::set_brightness_internal(const std::string &device_name, int brightness, int max_brightness) { brightness = std::clamp(brightness, 0, max_brightness); @@ -271,7 +273,7 @@ void BacklightBackend::set_brightness_internal(std::string device_name, int brig login_proxy_->call_sync("SetBrightness", call_args); } -int BacklightBackend::get_scaled_brightness(std::string preferred_device) { +int BacklightBackend::get_scaled_brightness(const std::string &preferred_device) { GET_BEST_DEVICE(best, (*this), preferred_device); if (best != nullptr) { diff --git a/src/util/portal.cpp b/src/util/portal.cpp index 50c646c54..5874871b9 100644 --- a/src/util/portal.cpp +++ b/src/util/portal.cpp @@ -85,7 +85,9 @@ void waybar::Portal::on_signal(const Glib::ustring& sender_name, const Glib::ust if (signal_name != "SettingChanged" || parameters.get_n_children() != 3) { return; } - Glib::VariantBase nspcv, keyv, valuev; + Glib::VariantBase nspcv; + Glib::VariantBase keyv; + Glib::VariantBase valuev; parameters.get_child(nspcv, 0); parameters.get_child(keyv, 1); parameters.get_child(valuev, 2); diff --git a/src/util/prepare_for_sleep.cpp b/src/util/prepare_for_sleep.cpp index 661285a21..3adcdf672 100644 --- a/src/util/prepare_for_sleep.cpp +++ b/src/util/prepare_for_sleep.cpp @@ -7,14 +7,14 @@ namespace { class PrepareForSleep { private: PrepareForSleep() { - login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); - if (!login1_connection) { + login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr); + if (login1_connection == nullptr) { spdlog::warn("Unable to connect to the SYSTEM Bus!..."); } else { login1_id = g_dbus_connection_signal_subscribe( login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager", - "PrepareForSleep", "/org/freedesktop/login1", NULL, G_DBUS_SIGNAL_FLAGS_NONE, - prepareForSleep_cb, this, NULL); + "PrepareForSleep", "/org/freedesktop/login1", nullptr, G_DBUS_SIGNAL_FLAGS_NONE, + prepareForSleep_cb, this, nullptr); } } @@ -22,11 +22,11 @@ class PrepareForSleep { const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { - if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)"))) { + if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)")) != 0) { gboolean sleeping; g_variant_get(parameters, "(b)", &sleeping); - PrepareForSleep *self = static_cast(user_data); + auto *self = static_cast(user_data); self->signal.emit(sleeping); } } diff --git a/src/util/regex_collection.cpp b/src/util/regex_collection.cpp index db2f30ea1..929e67cd6 100644 --- a/src/util/regex_collection.cpp +++ b/src/util/regex_collection.cpp @@ -3,13 +3,15 @@ #include #include +#include + namespace waybar::util { int default_priority_function(std::string& key) { return 0; } RegexCollection::RegexCollection(const Json::Value& map, std::string default_repr, - std::function priority_function) - : default_repr(default_repr) { + const std::function& priority_function) + : default_repr(std::move(default_repr)) { if (!map.isObject()) { spdlog::warn("Mapping is not an object"); return; diff --git a/src/util/sanitize_str.cpp b/src/util/sanitize_str.cpp index 131b9f28e..ae9a9e37e 100644 --- a/src/util/sanitize_str.cpp +++ b/src/util/sanitize_str.cpp @@ -10,9 +10,8 @@ std::string sanitize_string(std::string str) { const std::pair replacement_table[] = { {'&', "&"}, {'<', "<"}, {'>', ">"}, {'"', """}, {'\'', "'"}}; size_t startpoint; - for (size_t i = 0; i < (sizeof(replacement_table) / sizeof(replacement_table[0])); ++i) { + for (const auto& pair : replacement_table) { startpoint = 0; - std::pair pair = replacement_table[i]; while ((startpoint = str.find(pair.first, startpoint)) != std::string::npos) { str.replace(startpoint, 1, pair.second); startpoint += pair.second.length(); diff --git a/src/util/ustring_clen.cpp b/src/util/ustring_clen.cpp index 374df0d62..a8b9c9af6 100644 --- a/src/util/ustring_clen.cpp +++ b/src/util/ustring_clen.cpp @@ -2,8 +2,8 @@ int ustring_clen(const Glib::ustring &str) { int total = 0; - for (auto i = str.begin(); i != str.end(); ++i) { - total += g_unichar_iswide(*i) + 1; + for (unsigned int i : str) { + total += g_unichar_iswide(i) + 1; } return total; -} \ No newline at end of file +} From 702e10649e9f6ddcb1c9972c034775b63b724bb8 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 3 Jul 2024 00:19:54 -0500 Subject: [PATCH 309/407] modules/hyprland/workspace: ignore empty window-rewrite I'd like to ignore some windows from having icons or empty space taken on the bar. By filtering out empty repr we can supply rewrite rules that will ignore them from being processed and showing an empty space or default icon. --- man/waybar-hyprland-workspaces.5.scd | 1 + src/modules/hyprland/workspace.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 686f8aa75..c71168d46 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -26,6 +26,7 @@ Addressed by *hyprland/workspaces* Regex rules to map window class to an icon or preferred method of representation for a workspace's window. Keys are the rules, while the values are the methods of representation. Values may use the placeholders {class} and {title} to use the window's original class and/or title respectively. Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching. + You may assign an empty value to a rule to have it ignored from generating any representation in workspaces. *window-rewrite-default*: typeof: string ++ diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index eac21b7ed..e575d1c4d 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -92,7 +92,11 @@ void Workspace::initializeWindowMap(const Json::Value &clients_data) { void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { if (!create_window_paylod.isEmpty(m_workspaceManager)) { - m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager); + auto repr = create_window_paylod.repr(m_workspaceManager); + + if (!repr.empty()) { + m_windowMap[create_window_paylod.getAddress()] = repr; + } } }; From 7e2d8ab2a37302d09727b29d600f7768d1a2efb4 Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" Date: Wed, 15 May 2024 20:07:28 +0200 Subject: [PATCH 310/407] fix(#3239): hide cursor type change behind config flag also, statically configure the cursor type --- man/waybar-styles.5.scd.in | 32 ++++++++++++++++++++++++++++++++ src/AModule.cpp | 37 +++++++++++++++++++++++++++---------- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/man/waybar-styles.5.scd.in b/man/waybar-styles.5.scd.in index 0af393ef4..c1bc25e3e 100644 --- a/man/waybar-styles.5.scd.in +++ b/man/waybar-styles.5.scd.in @@ -39,6 +39,38 @@ You can apply special styling to any module for when the cursor hovers it. } ``` +## Setting cursor style + +Most, if not all, module types support setting the `cursor` option. This is +configured in your `config.jsonc`. If set to `true`, when hovering the module a +"pointer"(as commonly known from web CSS styling `cursor: pointer`) style cursor +will be shown. +There are more cursor types to choose from by setting the `cursor` option to +a number, see Gdk3 official docs for all possible cursor types: +https://docs.gtk.org/gdk3/enum.CursorType.html. +However, note that not all cursor options listed may be available on +your system. If you attempt to use a cursor which is not available, the +application will crash. + +Example of enabling pointer(`Gdk::Hand2`) cursor type on a custom module: + +``` +"custom/my-custom-module": { + ... + "cursor": true, +} +``` + +Example of setting cursor type to `Gdk::Boat`(according to +https://docs.gtk.org/gdk3/enum.CursorType.html#boat): + +``` +"custom/my-custom-module": { + ... + "cursor": 8, +} +``` + # SEE ALSO - *waybar(5)* diff --git a/src/AModule.cpp b/src/AModule.cpp index c40e3a56b..887b0de09 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -1,9 +1,13 @@ #include "AModule.hpp" #include +#include #include +#include "gdk/gdk.h" +#include "gdkmm/cursor.h" + namespace waybar { AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id, @@ -64,6 +68,16 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll)); } + + if (config_.isMember("cursor")) { + if (config_["cursor"].isBool() && config_["cursor"].asBool()) { + setCursor(Gdk::HAND2); + } else if (config_["cursor"].isInt()) { + setCursor(Gdk::CursorType(config_["cursor"].asInt())); + } else { + spdlog::warn("unknown cursor option configured on module {}", name_); + } + } } AModule::~AModule() { @@ -91,19 +105,26 @@ auto AModule::doAction(const std::string& name) -> void { } void AModule::setCursor(Gdk::CursorType const& c) { - auto cursor = Gdk::Cursor::create(c); auto gdk_window = event_box_.get_window(); - gdk_window->set_cursor(cursor); + if (gdk_window) { + auto cursor = Gdk::Cursor::create(c); + gdk_window->set_cursor(cursor); + } else { + // window may not be accessible yet, in this case, + // schedule another call for setting the cursor in 1 sec + Glib::signal_timeout().connect_seconds( + [this, c]() { + setCursor(c); + return false; + }, + 1); + } } bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - - if (hasUserEvents_) { - setCursor(Gdk::HAND2); - } return false; } @@ -111,10 +132,6 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - - if (hasUserEvents_) { - setCursor(Gdk::ARROW); - } return false; } From f78f29ee66f5d67579791380098759768e3682e8 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 2 Jul 2024 18:13:53 -0500 Subject: [PATCH 311/407] AModule: retain existing default behavior when unconfigured --- man/waybar-styles.5.scd.in | 10 ++++++---- src/AModule.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/man/waybar-styles.5.scd.in b/man/waybar-styles.5.scd.in index c1bc25e3e..b11e15bd1 100644 --- a/man/waybar-styles.5.scd.in +++ b/man/waybar-styles.5.scd.in @@ -42,9 +42,11 @@ You can apply special styling to any module for when the cursor hovers it. ## Setting cursor style Most, if not all, module types support setting the `cursor` option. This is -configured in your `config.jsonc`. If set to `true`, when hovering the module a +configured in your `config.jsonc`. If set to `false`, when hovering the module a "pointer"(as commonly known from web CSS styling `cursor: pointer`) style cursor -will be shown. +will not be shown. Default behavior is to indicate an interaction event is +available. + There are more cursor types to choose from by setting the `cursor` option to a number, see Gdk3 official docs for all possible cursor types: https://docs.gtk.org/gdk3/enum.CursorType.html. @@ -52,12 +54,12 @@ However, note that not all cursor options listed may be available on your system. If you attempt to use a cursor which is not available, the application will crash. -Example of enabling pointer(`Gdk::Hand2`) cursor type on a custom module: +Example of disabling pointer(`Gdk::Hand2`) cursor type on a custom module: ``` "custom/my-custom-module": { ... - "cursor": true, + "cursor": false, } ``` diff --git a/src/AModule.cpp b/src/AModule.cpp index 887b0de09..c180b4801 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -69,6 +69,7 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll)); } + // Respect user configuration of cursor if (config_.isMember("cursor")) { if (config_["cursor"].isBool() && config_["cursor"].asBool()) { setCursor(Gdk::HAND2); @@ -125,6 +126,12 @@ bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } + + // Default behavior indicating event availability + if (hasUserEvents_ && !config_.isMember("cursor")) { + setCursor(Gdk::HAND2); + } + return false; } @@ -132,6 +139,12 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } + + // Default behavior indicating event availability + if (hasUserEvents_ && !config_.isMember("cursor")) { + setCursor(Gdk::ARROW); + } + return false; } From 23274a9d570f3821c20dcc004da5e6e0afa122bd Mon Sep 17 00:00:00 2001 From: Lauri Niskanen Date: Sat, 6 Jul 2024 01:15:16 +0300 Subject: [PATCH 312/407] pulseaudio: Consider ignored sinks never running If the current sink happens to be ignored it is never considered running so it will be replaced with another sink. --- src/util/audio_backend.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index e634784bb..3d90b6d5a 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -144,6 +144,12 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i if (!backend->ignored_sinks_.empty()) { for (const auto &ignored_sink : backend->ignored_sinks_) { if (ignored_sink == i->description) { + if (i->name == backend->current_sink_name_) { + // If the current sink happens to be ignored it is never considered running + // so it will be replaced with another sink. + backend->current_sink_running_ = false; + } + return; } } From 21d42baa8ef13d98f29ac74e28565dfefe1e2217 Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Sat, 6 Jul 2024 08:16:45 +0800 Subject: [PATCH 313/407] (temperature) fix clang-tidy lint . --- src/modules/temperature.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 889109826..302877639 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -80,10 +80,10 @@ auto waybar::modules::Temperature::update() -> void { if (format.empty()) { event_box_.hide(); return; - } else { - event_box_.show(); } + event_box_.show(); + auto max_temp = config_["critical-threshold"].isInt() ? config_["critical-threshold"].asInt() : 0; label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("temperatureC", temperature_c), fmt::arg("temperatureF", temperature_f), From 7725f6ed5aca20eff825666937e76ae80ab7ea7d Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Mon, 8 Jul 2024 20:28:26 +0000 Subject: [PATCH 314/407] Fix build with fmt11 Since fmt 11.0.0, formatter:format() is required to be const.Mark affected functions as const to stay compatible with fmt 11. Signed-off-by: Yao Zi --- include/util/date.hpp | 2 +- include/util/format.hpp | 2 +- src/client.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/util/date.hpp b/include/util/date.hpp index 2431b766a..a467cc564 100644 --- a/include/util/date.hpp +++ b/include/util/date.hpp @@ -64,7 +64,7 @@ struct fmt::formatter> { } template - auto format(const date::zoned_time& ztime, FormatContext& ctx) { + auto format(const date::zoned_time& ztime, FormatContext& ctx) const { if (ctx.locale()) { const auto loc = ctx.locale().template get(); return fmt::format_to(ctx.out(), "{}", date::format(loc, fmt::to_string(specs), ztime)); diff --git a/include/util/format.hpp b/include/util/format.hpp index 069d8897a..a5630cf47 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -45,7 +45,7 @@ struct formatter { } template - auto format(const pow_format& s, FormatContext& ctx) -> decltype(ctx.out()) { + auto format(const pow_format& s, FormatContext& ctx) const -> decltype(ctx.out()) { const char* units[] = {"", "k", "M", "G", "T", "P", nullptr}; auto base = s.binary_ ? 1024ull : 1000ll; diff --git a/src/client.cpp b/src/client.cpp index 5768b1b03..cac1ffe85 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -123,7 +123,7 @@ void waybar::Client::handleMonitorAdded(Glib::RefPtr monitor) { } void waybar::Client::handleMonitorRemoved(Glib::RefPtr monitor) { - spdlog::debug("Output removed: {} {}", monitor->get_manufacturer(), monitor->get_model()); + spdlog::debug("Output removed: {} {}", monitor->get_manufacturer().c_str(), monitor->get_model().c_str()); /* This event can be triggered from wl_display_roundtrip called by GTK or our code. * Defer destruction of bars for the output to the next iteration of the event loop to avoid * deleting objects referenced by currently executed code. From e2e5d4d447fceca6d3185ddd4b7171a751412920 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 7 Jul 2024 22:08:45 +0200 Subject: [PATCH 315/407] feat/issue 3256: Toggle drawer state --- include/group.hpp | 4 ++++ man/waybar.5.scd.in | 5 +++++ src/group.cpp | 31 ++++++++++++++++++++++++++++--- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index 564d2eb52..b10402c66 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -25,9 +25,13 @@ class Group : public AModule { Gtk::Revealer revealer; bool is_first_widget = true; bool is_drawer = false; + bool click_to_reveal = false; std::string add_class_to_drawer_children; bool handleMouseEnter(GdkEventCrossing *const &ev) override; bool handleMouseLeave(GdkEventCrossing *const &ev) override; + bool handleToggle(GdkEventButton *const &ev) override; + void show_group(); + void hide_group(); }; } // namespace waybar diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 53613e4ab..db546e178 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -278,6 +278,11 @@ A group may hide all but one element, showing them only on mouse hover. In order default: "hidden" ++ Defines the CSS class to be applied to the hidden elements. +*click-to-reveal*: ++ + typeof: bool ++ + default: false ++ + Whether left click should reveal the content rather than mouse over. Note that grouped modules may still process their own on-click events. + *transition-left-to-right*: ++ typeof: bool ++ default: true ++ diff --git a/src/group.cpp b/src/group.cpp index c77f2d311..deeecc757 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -62,6 +62,7 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& const bool left_to_right = (drawer_config["transition-left-to-right"].isBool() ? drawer_config["transition-left-to-right"].asBool() : true); + click_to_reveal = drawer_config["click-to-reveal"].asBool(); auto transition_type = getPreferredTransitionType(vertical); @@ -83,18 +84,42 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& event_box_.add(box); } -bool Group::handleMouseEnter(GdkEventCrossing* const& e) { +void Group::show_group() { box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); revealer.set_reveal_child(true); - return false; } -bool Group::handleMouseLeave(GdkEventCrossing* const& e) { +void Group::hide_group() { box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); revealer.set_reveal_child(false); +} + +bool Group::handleMouseEnter(GdkEventCrossing* const& e) { + if (!click_to_reveal) { + show_group(); + } return false; } +bool Group::handleMouseLeave(GdkEventCrossing* const& e) { + if (!click_to_reveal) { + hide_group(); + } + return false; +} + +bool Group::handleToggle(GdkEventButton* const& e) { + if (!click_to_reveal || e->button != 1) { + return false; + } + if (box.get_state_flags() & Gtk::StateFlags::STATE_FLAG_PRELIGHT) { + hide_group(); + } else { + show_group(); + } + return true; +} + auto Group::update() -> void { // noop } From 0dd6af5a7e374161e2831a6f1cd052aeb0edbe25 Mon Sep 17 00:00:00 2001 From: Matt Provost Date: Thu, 11 Jul 2024 08:01:54 -0500 Subject: [PATCH 316/407] chore: update meson build command Signed-off-by: Matt Provost --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5781ea3a4..a019eb6fb 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ An Ubuntu PPA with more recent versions is available ```bash $ git clone https://github.com/Alexays/Waybar $ cd Waybar -$ meson build +$ meson setup build $ ninja -C build $ ./build/waybar # If you want to install it From e117bd7cb6da994fbe5bd3cb69ccecdd86664d09 Mon Sep 17 00:00:00 2001 From: Siddhant Kameswar <115331356+grimsteel@users.noreply.github.com> Date: Fri, 12 Jul 2024 20:46:26 -0500 Subject: [PATCH 317/407] network: add bssid format replacement --- include/modules/network.hpp | 2 ++ man/waybar-network.5.scd | 2 ++ src/modules/network.cpp | 23 ++++++++++++++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 47701b4ee..4a84b02f9 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -40,6 +40,7 @@ class Network : public ALabel { void parseEssid(struct nlattr**); void parseSignal(struct nlattr**); void parseFreq(struct nlattr**); + void parseBssid(struct nlattr**); bool associatedOrJoined(struct nlattr**); bool checkInterface(std::string name); auto getInfo() -> void; @@ -69,6 +70,7 @@ class Network : public ALabel { std::string state_; std::string essid_; + std::string bssid_; bool carrier_; std::string ifname_; std::string ipaddr_; diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index cc0b470b1..bd546916e 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -156,6 +156,8 @@ Addressed by *network* *{essid}*: Name (SSID) of the wireless network. +*{bssid}*: MAC address (BSSID) of the wireless access point. + *{signalStrength}*: Signal strength of the wireless network. *{signaldBm}*: Signal strength of the wireless network in dBm. diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 654afbe84..0e49177af 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -332,8 +332,8 @@ auto waybar::modules::Network::update() -> void { getState(signal_strength_); auto text = fmt::format( - fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), - fmt::arg("signalStrength", signal_strength_), + fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_), + fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), @@ -364,7 +364,7 @@ auto waybar::modules::Network::update() -> void { } if (!tooltip_format.empty()) { auto tooltip_text = fmt::format( - fmt::runtime(tooltip_format), fmt::arg("essid", essid_), + fmt::runtime(tooltip_format), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_), @@ -407,6 +407,7 @@ void waybar::modules::Network::clearIface() { ifid_ = -1; ifname_.clear(); essid_.clear(); + bssid_.clear(); ipaddr_.clear(); gwaddr_.clear(); netmask_.clear(); @@ -481,6 +482,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } else { // clear state related to WiFi connection net->essid_.clear(); + net->bssid_.clear(); net->signal_strength_dbm_ = 0; net->signal_strength_ = 0; net->signal_strength_app_.clear(); @@ -772,6 +774,7 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) { net->parseEssid(bss); net->parseSignal(bss); net->parseFreq(bss); + net->parseBssid(bss); return NL_OK; } @@ -837,6 +840,20 @@ void waybar::modules::Network::parseFreq(struct nlattr **bss) { } } +void waybar::modules::Network::parseBssid(struct nlattr **bss) { + if (bss[NL80211_BSS_BSSID] != nullptr) { + auto bssid = static_cast(nla_data(bss[NL80211_BSS_BSSID])); + auto bssid_len = nla_len(bss[NL80211_BSS_BSSID]); + if (bssid_len == 6) { + bssid_ = std::format( + "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", + bssid[0], bssid[1], bssid[2], + bssid[3], bssid[4], bssid[5] + ); + } + } +} + bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) { if (bss[NL80211_BSS_STATUS] == nullptr) { return false; From 0a78da0315a96e31365f49076021356cd278e5fa Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 15 Jul 2024 08:55:30 -0500 Subject: [PATCH 318/407] flake.lock: update --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 3f0deffe9..0d945dbe8 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1719506693, - "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=", + "lastModified": 1720957393, + "narHash": "sha256-oedh2RwpjEa+TNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a", + "rev": "693bc46d169f5af9c992095736e82c3488bf7dbb", "type": "github" }, "original": { From b41fcdedff884f25e96011278dcdb38788a291fe Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 15 Jul 2024 08:44:26 -0500 Subject: [PATCH 319/407] hyprland/window: fix crash when no return from socket Gracefully handle lack of response from the IPC. If socket isn't available, we already log the IPC isn't running. We dont need to crash program just because we couldn't get responses. We can just return an empty object. --- src/modules/hyprland/window.cpp | 144 +++++++++++++++++--------------- 1 file changed, 77 insertions(+), 67 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index ec151a7bc..b5ed8f021 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -92,30 +92,39 @@ auto Window::update() -> void { auto Window::getActiveWorkspace() -> Workspace { const auto workspace = gIPC->getSocket1JsonReply("activeworkspace"); - assert(workspace.isObject()); - return Workspace::parse(workspace); + + if (workspace.isObject()) { + return Workspace::parse(workspace); + } + + return {}; } auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { const auto monitors = gIPC->getSocket1JsonReply("monitors"); - assert(monitors.isArray()); - auto monitor = std::find_if(monitors.begin(), monitors.end(), - [&](Json::Value monitor) { return monitor["name"] == monitorName; }); - if (monitor == std::end(monitors)) { - spdlog::warn("Monitor not found: {}", monitorName); - return Workspace{-1, 0, "", ""}; - } - const int id = (*monitor)["activeWorkspace"]["id"].asInt(); - - const auto workspaces = gIPC->getSocket1JsonReply("workspaces"); - assert(workspaces.isArray()); - auto workspace = std::find_if(workspaces.begin(), workspaces.end(), - [&](Json::Value workspace) { return workspace["id"] == id; }); - if (workspace == std::end(workspaces)) { - spdlog::warn("No workspace with id {}", id); - return Workspace{-1, 0, "", ""}; - } - return Workspace::parse(*workspace); + if (monitors.isArray()) { + auto monitor = std::find_if(monitors.begin(), monitors.end(), [&](Json::Value monitor) { + return monitor["name"] == monitorName; + }); + if (monitor == std::end(monitors)) { + spdlog::warn("Monitor not found: {}", monitorName); + return Workspace{-1, 0, "", ""}; + } + const int id = (*monitor)["activeWorkspace"]["id"].asInt(); + + const auto workspaces = gIPC->getSocket1JsonReply("workspaces"); + if (workspaces.isArray()) { + auto workspace = std::find_if(workspaces.begin(), workspaces.end(), + [&](Json::Value workspace) { return workspace["id"] == id; }); + if (workspace == std::end(workspaces)) { + spdlog::warn("No workspace with id {}", id); + return Workspace{-1, 0, "", ""}; + } + return Workspace::parse(*workspace); + }; + }; + + return {}; } auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { @@ -146,53 +155,54 @@ void Window::queryActiveWorkspace() { focused_ = true; if (workspace_.windows > 0) { const auto clients = gIPC->getSocket1JsonReply("clients"); - assert(clients.isArray()); - auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { - return window["address"] == workspace_.last_window; - }); - - if (activeWindow == std::end(clients)) { - focused_ = false; - return; - } - - windowData_ = WindowData::parse(*activeWindow); - updateAppIconName(windowData_.class_name, windowData_.initial_class_name); - std::vector workspaceWindows; - std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows), - [&](Json::Value window) { - return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); - }); - swallowing_ = - std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) { - return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; - }); - std::vector visibleWindows; - std::copy_if(workspaceWindows.begin(), workspaceWindows.end(), - std::back_inserter(visibleWindows), - [&](Json::Value window) { return !window["hidden"].asBool(); }); - solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), - [&](Json::Value window) { return !window["floating"].asBool(); }); - allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(), - [&](Json::Value window) { return window["floating"].asBool(); }); - fullscreen_ = windowData_.fullscreen; - - // Fullscreen windows look like they are solo - if (fullscreen_) { - solo_ = true; - } - - // Grouped windows have a tab bar and therefore don't look fullscreen or solo - if (windowData_.grouped) { - fullscreen_ = false; - solo_ = false; - } - - if (solo_) { - soloClass_ = windowData_.class_name; - } else { - soloClass_ = ""; - } + if (clients.isArray()) { + auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { + return window["address"] == workspace_.last_window; + }); + + if (activeWindow == std::end(clients)) { + focused_ = false; + return; + } + + windowData_ = WindowData::parse(*activeWindow); + updateAppIconName(windowData_.class_name, windowData_.initial_class_name); + std::vector workspaceWindows; + std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows), + [&](Json::Value window) { + return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); + }); + swallowing_ = + std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) { + return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; + }); + std::vector visibleWindows; + std::copy_if(workspaceWindows.begin(), workspaceWindows.end(), + std::back_inserter(visibleWindows), + [&](Json::Value window) { return !window["hidden"].asBool(); }); + solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), + [&](Json::Value window) { return !window["floating"].asBool(); }); + allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(), + [&](Json::Value window) { return window["floating"].asBool(); }); + fullscreen_ = windowData_.fullscreen; + + // Fullscreen windows look like they are solo + if (fullscreen_) { + solo_ = true; + } + + // Grouped windows have a tab bar and therefore don't look fullscreen or solo + if (windowData_.grouped) { + fullscreen_ = false; + solo_ = false; + } + + if (solo_) { + soloClass_ = windowData_.class_name; + } else { + soloClass_ = ""; + } + }; } else { focused_ = false; windowData_ = WindowData{}; From b19890c0b1e9de87bb5ada10e2d6fa3bf5ad518b Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 15 Jul 2024 08:48:01 -0500 Subject: [PATCH 320/407] network: clang-format --- src/modules/network.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 0e49177af..e84b0d905 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -845,11 +845,8 @@ void waybar::modules::Network::parseBssid(struct nlattr **bss) { auto bssid = static_cast(nla_data(bss[NL80211_BSS_BSSID])); auto bssid_len = nla_len(bss[NL80211_BSS_BSSID]); if (bssid_len == 6) { - bssid_ = std::format( - "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", - bssid[0], bssid[1], bssid[2], - bssid[3], bssid[4], bssid[5] - ); + bssid_ = std::format("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", bssid[0], bssid[1], bssid[2], bssid[3], + bssid[4], bssid[5]); } } } From 47d7324a19647996dda96aaae1a7ee3956900440 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 15 Jul 2024 08:48:08 -0500 Subject: [PATCH 321/407] client: clang-format --- src/client.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client.cpp b/src/client.cpp index cac1ffe85..63a9276a6 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -123,7 +123,8 @@ void waybar::Client::handleMonitorAdded(Glib::RefPtr monitor) { } void waybar::Client::handleMonitorRemoved(Glib::RefPtr monitor) { - spdlog::debug("Output removed: {} {}", monitor->get_manufacturer().c_str(), monitor->get_model().c_str()); + spdlog::debug("Output removed: {} {}", monitor->get_manufacturer().c_str(), + monitor->get_model().c_str()); /* This event can be triggered from wl_display_roundtrip called by GTK or our code. * Defer destruction of bars for the output to the next iteration of the event loop to avoid * deleting objects referenced by currently executed code. From 895c870d02758b8698cdce859fc4c359d255c5a9 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 15 Jul 2024 09:44:39 -0500 Subject: [PATCH 322/407] network: use fmt for format Fixes the gentoo build --- 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 e84b0d905..0bbea6315 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -845,7 +845,7 @@ void waybar::modules::Network::parseBssid(struct nlattr **bss) { auto bssid = static_cast(nla_data(bss[NL80211_BSS_BSSID])); auto bssid_len = nla_len(bss[NL80211_BSS_BSSID]); if (bssid_len == 6) { - bssid_ = std::format("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", bssid[0], bssid[1], bssid[2], bssid[3], + bssid_ = fmt::format("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); } } From b71dfce1f7eefcd6c0dc99f162899b053d7fe082 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Tue, 16 Jul 2024 06:39:45 +0800 Subject: [PATCH 323/407] Fix build with fmt11 Since fmt 11.0.0, formatter:format() is required to be const. Mark all of the specializations as const to be compatible with fmt 11. This change is implemented in the same spirit of 7725f6ed5a. Signed-off-by: Kefu Chai --- include/util/format.hpp | 2 +- src/modules/sni/item.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/util/format.hpp b/include/util/format.hpp index a5630cf47..cf8d706b2 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -92,7 +92,7 @@ struct formatter { template <> struct formatter : formatter { template - auto format(const Glib::ustring& value, FormatContext& ctx) { + auto format(const Glib::ustring& value, FormatContext& ctx) const { return formatter::format(static_cast(value), ctx); } }; diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index b5c0dd85f..6c4ec8c06 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -14,14 +14,14 @@ template <> struct fmt::formatter : formatter { - bool is_printable(const Glib::VariantBase& value) { + bool is_printable(const Glib::VariantBase& value) const { auto type = value.get_type_string(); /* Print only primitive (single character excluding 'v') and short complex types */ return (type.length() == 1 && islower(type[0]) && type[0] != 'v') || value.get_size() <= 32; } template - auto format(const Glib::VariantBase& value, FormatContext& ctx) { + auto format(const Glib::VariantBase& value, FormatContext& ctx) const { if (is_printable(value)) { return formatter::format(static_cast(value.print()), ctx); } else { From b65ca334a8c9218a9ee1f42c411ef81be5014f38 Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Tue, 16 Jul 2024 09:07:39 +0800 Subject: [PATCH 324/407] fix #3442 --- src/modules/sni/watcher.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/modules/sni/watcher.cpp b/src/modules/sni/watcher.cpp index 8c035ae1d..324bd9f5e 100644 --- a/src/modules/sni/watcher.cpp +++ b/src/modules/sni/watcher.cpp @@ -67,10 +67,9 @@ gboolean Watcher::handleRegisterHost(Watcher* obj, GDBusMethodInvocation* invoca } auto watch = gfWatchFind(obj->hosts_, bus_name, object_path); if (watch != nullptr) { - g_dbus_method_invocation_return_error( - invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, - "Status Notifier Host with bus name '%s' and object path '%s' is already registered", - bus_name, object_path); + g_warning("Status Notifier Host with bus name '%s' and object path '%s' is already registered", + bus_name, object_path); + sn_watcher_complete_register_item(obj->watcher_, invocation); return TRUE; } watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj); From 17132b250d4a61e0366742fb9d86210762d5a5a2 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 16 Jul 2024 18:24:40 -0500 Subject: [PATCH 325/407] sway/workspaces: remove deprecated field Was deprecated a long time ago, we removed the Hyprland version. Removing this, as well. --- src/modules/sway/workspaces.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 2adde69ca..f58517375 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -125,18 +125,10 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { std::copy(output["floating_nodes"].begin(), output["floating_nodes"].end(), std::back_inserter(workspaces_)); } - if (config_["persistent_workspaces"].isObject()) { - spdlog::warn( - "persistent_workspaces is deprecated. Please change config to use " - "persistent-workspaces."); - } // adding persistent workspaces (as per the config file) - if (config_["persistent-workspaces"].isObject() || - config_["persistent_workspaces"].isObject()) { - const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject() - ? config_["persistent-workspaces"] - : config_["persistent_workspaces"]; + if (config_["persistent-workspaces"].isObject()) { + const Json::Value &p_workspaces = config_["persistent-workspaces"]; const std::vector p_workspaces_names = p_workspaces.getMemberNames(); for (const std::string &p_w_name : p_workspaces_names) { From 9c40137d05cfac4f41a49e354dd77d008a96386e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 16 Jul 2024 18:26:28 -0500 Subject: [PATCH 326/407] sway/workspaces: clang-tidy --- include/modules/sway/workspaces.hpp | 8 ++++---- src/modules/sway/workspaces.cpp | 16 +++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 4258252a2..97f4e9503 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -19,7 +19,7 @@ namespace waybar::modules::sway { class Workspaces : public AModule, public sigc::trackable { public: Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); - virtual ~Workspaces() = default; + ~Workspaces() override = default; auto update() -> void override; private: @@ -38,10 +38,10 @@ class Workspaces : public AModule, public sigc::trackable { Gtk::Button& addButton(const Json::Value&); void onButtonReady(const Json::Value&, Gtk::Button&); std::string getIcon(const std::string&, const Json::Value&); - const std::string getCycleWorkspace(std::vector::iterator, bool prev) const; + std::string getCycleWorkspace(std::vector::iterator, bool prev) const; uint16_t getWorkspaceIndex(const std::string& name) const; - std::string trimWorkspaceName(std::string); - bool handleScroll(GdkEventScroll*) override; + static std::string trimWorkspaceName(std::string); + bool handleScroll(GdkEventScroll* /*unused*/) override; const Bar& bar_; std::vector workspaces_; diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index f58517375..0ca41d1c3 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -11,15 +11,14 @@ namespace waybar::modules::sway { // Helper function to assign a number to a workspace, just like sway. In fact // this is taken quite verbatim from `sway/ipc-json.c`. int Workspaces::convertWorkspaceNameToNum(std::string name) { - if (isdigit(name[0])) { + if (isdigit(name[0]) != 0) { errno = 0; - char *endptr = NULL; + char *endptr = nullptr; long long parsed_num = strtoll(name.c_str(), &endptr, 10); if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == name.c_str()) { return -1; - } else { - return (int)parsed_num; } + return (int)parsed_num; } return -1; } @@ -47,7 +46,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value bar_(bar), box_(bar.orientation, 0) { if (config["format-icons"]["high-priority-named"].isArray()) { - for (auto &it : config["format-icons"]["high-priority-named"]) { + for (const auto &it : config["format-icons"]["high-priority-named"]) { high_priority_named_.push_back(it.asString()); } } @@ -70,7 +69,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value m_windowRewriteRules = waybar::util::RegexCollection( windowRewrite, m_windowRewriteDefault, - [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); + [](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); ipc_.subscribe(R"(["workspace"])"); ipc_.subscribe(R"(["window"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); @@ -414,7 +413,7 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node } bool Workspaces::handleScroll(GdkEventScroll *e) { - if (gdk_event_get_pointer_emulated((GdkEvent *)e)) { + if (gdk_event_get_pointer_emulated((GdkEvent *)e) != 0) { /** * Ignore emulated scroll events on window */ @@ -464,8 +463,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { return true; } -const std::string Workspaces::getCycleWorkspace(std::vector::iterator it, - bool prev) const { +std::string Workspaces::getCycleWorkspace(std::vector::iterator it, bool prev) const { if (prev && it == workspaces_.begin() && !config_["disable-scroll-wraparound"].asBool()) { return (*(--workspaces_.end()))["name"].asString(); } From 4295faa7c4b23b7f6e86669d1fe8c93562e10241 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 16 Jul 2024 14:53:54 -0500 Subject: [PATCH 327/407] hyprland/backend: throw runtime_error instead of log Allows us to disable modules entirely when socket connection isn't working. This is similar to how sway handles their socket connections disabling modules. This supports a single waybar config for multiple IPCs. --- src/modules/hyprland/backend.cpp | 16 ++++++---------- test/hyprland/backend.cpp | 6 ++---- test/hyprland/fixtures/IPCTestFixture.hpp | 3 +++ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 8ec6eddac..60453dcb1 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -153,8 +153,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) { const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0); if (serverSocket < 0) { - spdlog::error("Hyprland IPC: Couldn't open a socket (1)"); - return ""; + throw std::runtime_error("Hyprland IPC: Couldn't open a socket (1)"); } memset(&aiHints, 0, sizeof(struct addrinfo)); @@ -162,16 +161,15 @@ std::string IPC::getSocket1Reply(const std::string& rq) { aiHints.ai_socktype = SOCK_STREAM; if (getaddrinfo("localhost", nullptr, &aiHints, &aiRes) != 0) { - spdlog::error("Hyprland IPC: Couldn't get host (2)"); - return ""; + throw std::runtime_error("Hyprland IPC: Couldn't get host (2)"); } // get the instance signature auto* instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE"); if (instanceSig == nullptr) { - spdlog::error("Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"); - return ""; + throw std::runtime_error( + "Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"); } sockaddr_un serverAddress = {0}; @@ -182,14 +180,12 @@ std::string IPC::getSocket1Reply(const std::string& rq) { // Use snprintf to copy the socketPath string into serverAddress.sun_path if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < 0) { - spdlog::error("Hyprland IPC: Couldn't copy socket path (6)"); - return ""; + throw std::runtime_error("Hyprland IPC: Couldn't copy socket path (6)"); } if (connect(serverSocket, reinterpret_cast(&serverAddress), sizeof(serverAddress)) < 0) { - spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); - return ""; + throw std::runtime_error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); } auto sizeWritten = write(serverSocket, rq.c_str(), rq.length()); diff --git a/test/hyprland/backend.cpp b/test/hyprland/backend.cpp index dcae05090..b83b839cb 100644 --- a/test/hyprland/backend.cpp +++ b/test/hyprland/backend.cpp @@ -52,10 +52,8 @@ TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirExistsNoHyprDir", "[getSocketFold REQUIRE(actualPath == expectedPath); } -TEST_CASE_METHOD(IPCMock, "getSocket1JsonReply handles empty response", "[getSocket1JsonReply]") { +TEST_CASE_METHOD(IPCTestFixture, "getSocket1Reply throws on no socket", "[getSocket1Reply]") { std::string request = "test_request"; - Json::Value jsonResponse = getSocket1JsonReply(request); - - REQUIRE(jsonResponse.isNull()); + CHECK_THROWS(getSocket1Reply(request)); } diff --git a/test/hyprland/fixtures/IPCTestFixture.hpp b/test/hyprland/fixtures/IPCTestFixture.hpp index f6fa335f7..caa92975c 100644 --- a/test/hyprland/fixtures/IPCTestFixture.hpp +++ b/test/hyprland/fixtures/IPCTestFixture.hpp @@ -19,4 +19,7 @@ class IPCMock : public IPCTestFixture { public: // Mock getSocket1Reply to return an empty string static std::string getSocket1Reply(const std::string& rq) { return ""; } + + protected: + const char* instanceSig = "instance_sig"; }; From 90ac7d5d2c0fd5728647dd63fe0069170bfabe16 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 16 Jul 2024 22:48:25 -0500 Subject: [PATCH 328/407] sway/workspaces: support ignore window-rewrite Similar to hyprland implementation to ignore "" empty rules --- man/waybar-sway-workspaces.5.scd | 1 + src/modules/sway/workspaces.cpp | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index a65a999ba..fc73a85af 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -87,6 +87,7 @@ warp-on-scroll: ++ Regex rules to map window class to an icon or preferred method of representation for a workspace's window. Keys are the rules, while the values are the methods of representation. Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching. + You may assign an empty value to a rule to have it ignored from generating any representation in workspaces. *window-rewrite-default*: typeof: string ++ diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 0ca41d1c3..8f273300e 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -261,13 +261,17 @@ void Workspaces::updateWindows(const Json::Value &node, std::string &windows) { node["name"].isString()) { std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1); std::string windowClass = node["app_id"].asString(); - std::string windowReprKey = fmt::format("class<{}> title<{}>", windowClass, title); - std::string window = m_windowRewriteRules.get(windowReprKey); - // allow result to have formatting - window = - fmt::format(fmt::runtime(window), fmt::arg("name", title), fmt::arg("class", windowClass)); - windows.append(window); - windows.append(m_formatWindowSeperator); + + // Only add window rewrites that can be looked up + if (!windowClass.empty()) { + std::string windowReprKey = fmt::format("class<{}> title<{}>", windowClass, title); + std::string window = m_windowRewriteRules.get(windowReprKey); + // allow result to have formatting + window = fmt::format(fmt::runtime(window), fmt::arg("name", title), + fmt::arg("class", windowClass)); + windows.append(window); + windows.append(m_formatWindowSeperator); + } } for (const Json::Value &child : node["nodes"]) { updateWindows(child, windows); From ed0ed398b74cdd3bbf6bfcc751255507c56062e2 Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Wed, 17 Jul 2024 22:46:58 +0200 Subject: [PATCH 329/407] Update freebsd.yml --- .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 7effb4840..242633f57 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Test in FreeBSD VM - uses: cross-platform-actions/action@v0.23.0 + uses: cross-platform-actions/action@v0.25.0 timeout-minutes: 180 env: CPPFLAGS: '-isystem/usr/local/include' From dcbcf90aef9665b179cf8c021dfd1c008e06ee10 Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Wed, 17 Jul 2024 22:52:39 +0200 Subject: [PATCH 330/407] Update freebsd.yml --- .github/workflows/freebsd.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 242633f57..bbb971987 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -21,11 +21,10 @@ jobs: LDFLAGS: '-L/usr/local/lib' with: operating_system: freebsd - version: "13.2" + version: "14.1" environment_variables: CPPFLAGS LDFLAGS sync_files: runner-to-vm run: | - sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf sudo pkg install -y git # subprojects/date sudo pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \ libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ From 15e1547661bfc5fe9b3d45bb0d9cea11cf07db7f Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 17 Jul 2024 23:04:05 +0200 Subject: [PATCH 331/407] chore: 0.10.4 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a154a51b7..8daa6c9cf 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.10.3', + version: '0.10.4', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From ee0912a254326ecbc0686fdd6c571f63bac73d95 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sat, 20 Jul 2024 09:00:59 +0200 Subject: [PATCH 332/407] Issue #3414/clock: Shift ONLY calendar Right now, for the tooltip, all times are shifted if shift-down/shift-up actions are used. But it really only makes sense for this to apply to the {calendar} replacement, so use shiftedNow there and now for all the rest. --- src/modules/clock.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index fe2c4c8fb..7a4cb9c27 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -163,15 +163,16 @@ auto waybar::modules::Clock::update() -> void { // std::vformat doesn't support named arguments. m_tlpText_ = std::regex_replace(m_tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_); - m_tlpText_ = - std::regex_replace(m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); + m_tlpText_ = std::regex_replace( + m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), + fmt_lib::vformat(m_locale_, cldText_, fmt_lib::make_format_args(shiftedNow))); m_tlpText_ = std::regex_replace(m_tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); } else { m_tlpText_ = m_tlpFmt_; } - m_tlpText_ = fmt_lib::vformat(m_locale_, m_tlpText_, fmt_lib::make_format_args(shiftedNow)); + m_tlpText_ = fmt_lib::vformat(m_locale_, m_tlpText_, fmt_lib::make_format_args(now)); m_tooltip_->set_markup(m_tlpText_); label_.trigger_tooltip_query(); } From a544f4b2cdcf632f1a4424b89f6e3d85ef5aaa85 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 20 Jul 2024 09:33:13 -0500 Subject: [PATCH 333/407] bar: fix setVisible Accidentally removed updating the visible variable --- include/bar.hpp | 2 +- src/bar.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 6900da479..43756bfdc 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -66,7 +66,7 @@ class Bar { ~Bar(); void setMode(const std::string &mode); - void setVisible(bool visible); + void setVisible(bool value); void toggle(); void handleSignal(int); diff --git a/src/bar.cpp b/src/bar.cpp index 8c75c2c20..8a245ad16 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -404,7 +404,8 @@ void waybar::Bar::onMap(GdkEventAny* /*unused*/) { setPassThrough(passthrough_); } -void waybar::Bar::setVisible(bool visible) { +void waybar::Bar::setVisible(bool value) { + visible = value; if (auto mode = config.get("mode", {}); mode.isString()) { setMode(visible ? config["mode"].asString() : MODE_INVISIBLE); } else { From 58e21e876e3b4184f197ed8c8f48a081130ab3a4 Mon Sep 17 00:00:00 2001 From: DomCristaldi Date: Sat, 20 Jul 2024 22:58:03 -0400 Subject: [PATCH 334/407] walk up symlink tree "reload_style_on_change" would check if the target file is a symlink, but only resolves the first link. If the symlink is acutally a chain of symlink, such as what happens with NixOS's mkOutOfStoreSymlink, we will not find the actual file style file. Update the symlink resolution logic to walk down the symlink chain until it finds a non-symlink. Also check against a the original filename (which may be a symlink) to guard against infinitely looping on a circular symlink chain. --- src/util/css_reload_helper.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/util/css_reload_helper.cpp b/src/util/css_reload_helper.cpp index 45fd801a7..e440c3c11 100644 --- a/src/util/css_reload_helper.cpp +++ b/src/util/css_reload_helper.cpp @@ -43,8 +43,14 @@ std::string waybar::CssReloadHelper::findPath(const std::string& filename) { } // File monitor does not work with symlinks, so resolve them - if (std::filesystem::is_symlink(result)) { + std::string original = result; + while(std::filesystem::is_symlink(result)) { result = std::filesystem::read_symlink(result); + + // prevent infinite cycle + if (result == original) { + break; + } } return result; From 7e1fffc455cb9930982e65d29e1b8bfd7d2c90d9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 Aug 2024 00:09:59 +0000 Subject: [PATCH 335/407] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/693bc46d169f5af9c992095736e82c3488bf7dbb?narHash=sha256-oedh2RwpjEa%2BTNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA%3D' (2024-07-14) → 'github:NixOS/nixpkgs/52ec9ac3b12395ad677e8b62106f0b98c1f8569d?narHash=sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k%3D' (2024-07-28) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 0d945dbe8..b8f68f4ba 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1720957393, - "narHash": "sha256-oedh2RwpjEa+TNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA=", + "lastModified": 1722185531, + "narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "693bc46d169f5af9c992095736e82c3488bf7dbb", + "rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d", "type": "github" }, "original": { From 7ec1343ad5012e5ada25e76aefc227d35e4ce7f7 Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Thu, 1 Aug 2024 17:47:10 +0800 Subject: [PATCH 336/407] fix #3490: expand menu file before opening it --- include/config.hpp | 3 +++ src/ALabel.cpp | 12 +++++++++++- src/config.cpp | 3 ++- src/util/css_reload_helper.cpp | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/config.hpp b/include/config.hpp index 669455420..18a1daed7 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -20,6 +20,9 @@ class Config { static std::optional findConfigPath( const std::vector &names, const std::vector &dirs = CONFIG_DIRS); + static std::optional tryExpandPath(const std::string &base, + const std::string &filename); + Config() = default; void load(const std::string &config); diff --git a/src/ALabel.cpp b/src/ALabel.cpp index da2991a35..ecb1b7ced 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -6,6 +6,8 @@ #include #include +#include "config.hpp" + namespace waybar { ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id, @@ -61,6 +63,14 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st try { // Check that the file exists std::string menuFile = config_["menu-file"].asString(); + + // there might be "~" or "$HOME" in original path, try to expand it. + auto result = Config::tryExpandPath(menuFile, ""); + if (!result.has_value()) { + throw std::runtime_error("Failed to expand file: " + menuFile); + } + + menuFile = result.value(); // Read the menu descriptor file std::ifstream file(menuFile); if (!file.is_open()) { @@ -170,7 +180,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { return AModule::handleToggle(e); } -void ALabel::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) { +void ALabel::handleGtkMenuEvent(GtkMenuItem* /*menuitem*/, gpointer data) { waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu"); } diff --git a/src/config.cpp b/src/config.cpp index b78af56c3..375dc4cb5 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -21,7 +21,8 @@ const std::vector Config::CONFIG_DIRS = { const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR"; -std::optional tryExpandPath(const std::string &base, const std::string &filename) { +std::optional Config::tryExpandPath(const std::string &base, + const std::string &filename) { fs::path path; if (!filename.empty()) { diff --git a/src/util/css_reload_helper.cpp b/src/util/css_reload_helper.cpp index e440c3c11..274bdeedd 100644 --- a/src/util/css_reload_helper.cpp +++ b/src/util/css_reload_helper.cpp @@ -44,7 +44,7 @@ std::string waybar::CssReloadHelper::findPath(const std::string& filename) { // File monitor does not work with symlinks, so resolve them std::string original = result; - while(std::filesystem::is_symlink(result)) { + while (std::filesystem::is_symlink(result)) { result = std::filesystem::read_symlink(result); // prevent infinite cycle From 24a9886952297a3be27c26195b924c5bf975f260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20S=C3=A1lyi?= Date: Fri, 2 Aug 2024 15:21:01 +0200 Subject: [PATCH 337/407] Handle offline CPUs and CPU hotplug First of all in case when the number CPUs change prevent out-of-bound index access in waybar::modules::CpuUsage::getCpuUsage() Secondly on Linux when updating CPU usage read /sys/devices/system/cpu/present and use it to detect the offline CPUs missing from /proc/stat For offline CPUs report 0 usage and "offline" in the tooltip Fixes issue #3498 On Linux one can test this functionality with: echo 0 > /sys/devices/system/cpu/cpu1/online echo 1 > /sys/devices/system/cpu/cpu1/online On non-Linux OSes I'm not sure how to detect offline CPUs, so I didn't add the offline CPU detection there but at least CPU number change should not cause a crash there anymore or cause memory safety issues after this fix --- src/modules/cpu_usage/common.cpp | 27 +++++++++++++++++++++++ src/modules/cpu_usage/linux.cpp | 38 ++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/modules/cpu_usage/common.cpp b/src/modules/cpu_usage/common.cpp index 4e36f48ed..e39479678 100644 --- a/src/modules/cpu_usage/common.cpp +++ b/src/modules/cpu_usage/common.cpp @@ -61,9 +61,36 @@ std::tuple, std::string> waybar::modules::CpuUsage::getCpu std::vector> curr_times = CpuUsage::parseCpuinfo(); std::string tooltip; std::vector usage; + + if (curr_times.size() != prev_times.size()) { + // The number of CPUs has changed, eg. due to CPU hotplug + // We don't know which CPU came up or went down + // so only give total usage (if we can) + if (!curr_times.empty() && !prev_times.empty()) { + auto [curr_idle, curr_total] = curr_times[0]; + auto [prev_idle, prev_total] = prev_times[0]; + const float delta_idle = curr_idle - prev_idle; + const float delta_total = curr_total - prev_total; + uint16_t tmp = 100 * (1 - delta_idle / delta_total); + tooltip = fmt::format("Total: {}%\nCores: (pending)", tmp); + usage.push_back(tmp); + } else { + tooltip = "(pending)"; + usage.push_back(0); + } + prev_times = curr_times; + return {usage, tooltip}; + } + for (size_t i = 0; i < curr_times.size(); ++i) { auto [curr_idle, curr_total] = curr_times[i]; auto [prev_idle, prev_total] = prev_times[i]; + if (i > 0 && (curr_total == 0 || prev_total == 0)) { + // This CPU is offline + tooltip = tooltip + fmt::format("\nCore{}: offline", i - 1); + usage.push_back(0); + continue; + } const float delta_idle = curr_idle - prev_idle; const float delta_total = curr_total - prev_total; uint16_t tmp = 100 * (1 - delta_idle / delta_total); diff --git a/src/modules/cpu_usage/linux.cpp b/src/modules/cpu_usage/linux.cpp index bcd9594eb..6fbd659b1 100644 --- a/src/modules/cpu_usage/linux.cpp +++ b/src/modules/cpu_usage/linux.cpp @@ -3,6 +3,23 @@ #include "modules/cpu_usage.hpp" std::vector> waybar::modules::CpuUsage::parseCpuinfo() { + // Get the "existing CPU count" from /sys/devices/system/cpu/present + // Probably this is what the user wants the offline CPUs accounted from + // For further details see: + // https://www.kernel.org/doc/html/latest/core-api/cpu_hotplug.html + const std::string sys_cpu_present_path = "/sys/devices/system/cpu/present"; + size_t cpu_present_last = 0; + std::ifstream cpu_present_file(sys_cpu_present_path); + std::string cpu_present_text; + if (cpu_present_file.is_open()) { + getline(cpu_present_file, cpu_present_text); + // This is a comma-separated list of ranges, eg. 0,2-4,7 + size_t last_separator = cpu_present_text.find_last_of("-,"); + if (last_separator < cpu_present_text.size()) { + std::stringstream(cpu_present_text.substr(last_separator + 1)) >> cpu_present_last; + } + } + const std::string data_dir_ = "/proc/stat"; std::ifstream info(data_dir_); if (!info.is_open()) { @@ -10,14 +27,23 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( } std::vector> cpuinfo; std::string line; + size_t current_cpu_number = -1; // First line is total, second line is cpu 0 while (getline(info, line)) { if (line.substr(0, 3).compare("cpu") != 0) { break; } + size_t line_cpu_number; + if (current_cpu_number >= 0) { + std::stringstream(line.substr(3)) >> line_cpu_number; + while (line_cpu_number > current_cpu_number) { + // Fill in 0 for offline CPUs missing inside the lines of /proc/stat + cpuinfo.emplace_back(0, 0); + current_cpu_number++; + } + } std::stringstream sline(line.substr(5)); std::vector times; - for (size_t time = 0; sline >> time; times.push_back(time)) - ; + for (size_t time = 0; sline >> time; times.push_back(time)); size_t idle_time = 0; size_t total_time = 0; @@ -27,6 +53,14 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( total_time = std::accumulate(times.begin(), times.end(), 0); } cpuinfo.emplace_back(idle_time, total_time); + current_cpu_number++; } + + while (cpu_present_last >= current_cpu_number) { + // Fill in 0 for offline CPUs missing after the lines of /proc/stat + cpuinfo.emplace_back(0, 0); + current_cpu_number++; + } + return cpuinfo; } From 4efa1231835f87b55852cdf9e27b96d0cdb3d60c Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 2 Aug 2024 22:30:56 -0500 Subject: [PATCH 338/407] group: clang-tidy --- include/group.hpp | 2 +- src/group.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index b10402c66..5ce331a8d 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -12,7 +12,7 @@ namespace waybar { class Group : public AModule { public: Group(const std::string &, const std::string &, const Json::Value &, bool); - virtual ~Group() = default; + ~Group() override = default; auto update() -> void override; operator Gtk::Widget &() override; diff --git a/src/group.cpp b/src/group.cpp index deeecc757..2660868a6 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -9,7 +9,7 @@ namespace waybar { -const Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) { +Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) { /* The transition direction of a drawer is not actually determined by the transition type, * but rather by the order of 'box' and 'revealer_box': * 'REVEALER_TRANSITION_TYPE_SLIDE_LEFT' and 'REVEALER_TRANSITION_TYPE_SLIDE_RIGHT' @@ -112,7 +112,7 @@ bool Group::handleToggle(GdkEventButton* const& e) { if (!click_to_reveal || e->button != 1) { return false; } - if (box.get_state_flags() & Gtk::StateFlags::STATE_FLAG_PRELIGHT) { + if ((box.get_state_flags() & Gtk::StateFlags::STATE_FLAG_PRELIGHT) != 0U) { hide_group(); } else { show_group(); From 3ae81d62bc300ece26c23e4f01c44180d6cc3edc Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 2 Aug 2024 22:32:22 -0500 Subject: [PATCH 339/407] group: fix hover regression We aren't including the hover detection on the revealer, so when the animation fires we fire the leave event which starts an infinite loop of enter/leave while we watch boxes move back and forth. --- include/group.hpp | 1 + src/group.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/group.hpp b/include/group.hpp index 5ce331a8d..f5c6864b8 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -30,6 +30,7 @@ class Group : public AModule { bool handleMouseEnter(GdkEventCrossing *const &ev) override; bool handleMouseLeave(GdkEventCrossing *const &ev) override; bool handleToggle(GdkEventButton *const &ev) override; + void addHoverHandlerTo(Gtk::Widget &widget); void show_group(); void hide_group(); }; diff --git a/src/group.cpp b/src/group.cpp index 2660868a6..9b7ac2d5c 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -81,9 +81,16 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& } } + addHoverHandlerTo(revealer); event_box_.add(box); } +void Group::addHoverHandlerTo(Gtk::Widget& widget) { + widget.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK); + widget.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseEnter)); + widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseLeave)); +} + void Group::show_group() { box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); revealer.set_reveal_child(true); From 05d69ae82244cb28d4ce22009dc2bc486d278574 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 2 Aug 2024 22:37:06 -0500 Subject: [PATCH 340/407] src/util/css_reload_helper: clang-format --- src/util/css_reload_helper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/css_reload_helper.cpp b/src/util/css_reload_helper.cpp index e440c3c11..274bdeedd 100644 --- a/src/util/css_reload_helper.cpp +++ b/src/util/css_reload_helper.cpp @@ -44,7 +44,7 @@ std::string waybar::CssReloadHelper::findPath(const std::string& filename) { // File monitor does not work with symlinks, so resolve them std::string original = result; - while(std::filesystem::is_symlink(result)) { + while (std::filesystem::is_symlink(result)) { result = std::filesystem::read_symlink(result); // prevent infinite cycle From 17f07b24522da93da28f3e9083d25cd7126489cf Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 2 Aug 2024 23:37:52 -0500 Subject: [PATCH 341/407] group: proper fix of enter/leave Ignore mouse leave event when we are still within the parent element --- include/group.hpp | 1 - src/group.cpp | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index f5c6864b8..5ce331a8d 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -30,7 +30,6 @@ class Group : public AModule { bool handleMouseEnter(GdkEventCrossing *const &ev) override; bool handleMouseLeave(GdkEventCrossing *const &ev) override; bool handleToggle(GdkEventButton *const &ev) override; - void addHoverHandlerTo(Gtk::Widget &widget); void show_group(); void hide_group(); }; diff --git a/src/group.cpp b/src/group.cpp index 9b7ac2d5c..50841efd6 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -81,16 +81,9 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& } } - addHoverHandlerTo(revealer); event_box_.add(box); } -void Group::addHoverHandlerTo(Gtk::Widget& widget) { - widget.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK); - widget.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseEnter)); - widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseLeave)); -} - void Group::show_group() { box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); revealer.set_reveal_child(true); @@ -109,7 +102,7 @@ bool Group::handleMouseEnter(GdkEventCrossing* const& e) { } bool Group::handleMouseLeave(GdkEventCrossing* const& e) { - if (!click_to_reveal) { + if (!click_to_reveal && e->detail != GDK_NOTIFY_INFERIOR) { hide_group(); } return false; From fdc8431709447bbfaf124115e2cffa1b54b391cb Mon Sep 17 00:00:00 2001 From: Scott Moreau Date: Sun, 4 Aug 2024 22:49:51 -0600 Subject: [PATCH 342/407] taskbar: Send minimize geometry hints This allows compositors to know the minimize widget geometry so that minimize animations work properly. --- include/modules/wlr/taskbar.hpp | 7 +++++++ src/modules/wlr/taskbar.cpp | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 4465dd06e..026f364ad 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -24,6 +24,10 @@ namespace waybar::modules::wlr { +struct widget_geometry { + int x, y, w, h; +}; + class Taskbar; class Task { @@ -42,6 +46,7 @@ class Task { }; // made public so TaskBar can reorder based on configuration. Gtk::Button button; + struct widget_geometry minimize_hint; private: static uint32_t global_id; @@ -82,6 +87,8 @@ class Task { private: std::string repr() const; std::string state_string(bool = false) const; + void set_minimize_hint(); + void on_button_size_allocated(Gtk::Allocation &alloc); void set_app_info_from_app_id_list(const std::string &app_id_list); bool image_load_icon(Gtk::Image &image, const Glib::RefPtr &icon_theme, Glib::RefPtr app_info, int size); diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index e6c8e536c..7ff11baf7 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -387,6 +387,10 @@ void Task::handle_title(const char *title) { hide_if_ignored(); } +void Task::set_minimize_hint() { + zwlr_foreign_toplevel_handle_v1_set_rectangle(handle_, bar_.surface, minimize_hint.x, minimize_hint.y, minimize_hint.w, minimize_hint.h); +} + void Task::hide_if_ignored() { if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) { ignored_ = true; @@ -447,6 +451,12 @@ void Task::handle_app_id(const char *app_id) { spdlog::debug("Couldn't find icon for {}", app_id_); } +void Task::on_button_size_allocated(Gtk::Allocation &alloc) { + gtk_widget_translate_coordinates(GTK_WIDGET(button.gobj()), GTK_WIDGET(bar_.window.gobj()), 0, 0, &minimize_hint.x, &minimize_hint.y); + minimize_hint.w = button.get_width(); + minimize_hint.h = button.get_height(); +} + void Task::handle_output_enter(struct wl_output *output) { if (ignored_) { spdlog::debug("{} is ignored", repr()); @@ -457,6 +467,7 @@ void Task::handle_output_enter(struct wl_output *output) { if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) { /* The task entered the output of the current bar make the button visible */ + button.signal_size_allocate().connect_notify(sigc::mem_fun(this, &Task::on_button_size_allocated)); tbar_->add_button(button); button.show(); button_visible_ = true; @@ -553,9 +564,11 @@ bool Task::handle_clicked(GdkEventButton *bt) { return true; else if (action == "activate") activate(); - else if (action == "minimize") + else if (action == "minimize") { + set_minimize_hint(); minimize(!minimized()); - else if (action == "minimize-raise") { + } else if (action == "minimize-raise") { + set_minimize_hint(); if (minimized()) minimize(false); else if (active()) From c468119f5220b61045f6fcc4588f2bba7528b3a1 Mon Sep 17 00:00:00 2001 From: hacrvlq Date: Tue, 6 Aug 2024 18:01:37 +0200 Subject: [PATCH 343/407] fix(wireplumber): Handle changes to the default node ID --- src/modules/wireplumber.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index bd019b623..eddc3e6be 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -163,7 +163,8 @@ void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wir "[{}]: (onDefaultNodesApiChanged) - got the following default node: Node(name: {}, id: {})", self->name_, defaultNodeName, defaultNodeId); - if (g_strcmp0(self->default_node_name_, defaultNodeName) == 0) { + if (g_strcmp0(self->default_node_name_, defaultNodeName) == 0 && + self->node_id_ == defaultNodeId) { spdlog::debug( "[{}]: (onDefaultNodesApiChanged) - Default node has not changed. Node(name: {}, id: {}). " "Ignoring.", From 62cb61c670876f192976a366b3fc4175cbe72406 Mon Sep 17 00:00:00 2001 From: Sonter Date: Wed, 14 Aug 2024 11:34:28 +0300 Subject: [PATCH 344/407] Add format_silent to cava module --- include/modules/cava.hpp | 1 + src/modules/cava.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/modules/cava.hpp b/include/modules/cava.hpp index 430c71b78..219d9302a 100644 --- a/include/modules/cava.hpp +++ b/include/modules/cava.hpp @@ -39,6 +39,7 @@ class Cava final : public ALabel { std::chrono::seconds suspend_silence_delay_{0}; bool silence_{false}; bool hide_on_silence_{false}; + std::string format_silent_{""}; int sleep_counter_{0}; // Cava method void pause_resume(); diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 431ce5f15..1841e2763 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -59,6 +59,7 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) if (config_["input_delay"].isInt()) fetch_input_delay_ = std::chrono::seconds(config_["input_delay"].asInt()); if (config_["hide_on_silence"].isBool()) hide_on_silence_ = config_["hide_on_silence"].asBool(); + if (config_["format_silent"].isString()) format_silent_ = config_["format_silent"].asString(); // Make cava parameters configuration plan_ = new cava::cava_plan{}; @@ -176,6 +177,7 @@ auto waybar::modules::Cava::update() -> void { } else { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); if (hide_on_silence_) label_.hide(); + else if (config_["format_silent"].isString()) label_.set_markup(format_silent_); } } From ed40168d89085d30538422252016018f152a2d06 Mon Sep 17 00:00:00 2001 From: Sonter <108224581+S0nter@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:43:17 +0000 Subject: [PATCH 345/407] Add cava.silence to css --- src/modules/cava.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 1841e2763..26ad4fd14 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -174,10 +174,14 @@ auto waybar::modules::Cava::update() -> void { label_.show(); ALabel::update(); } + + label_.get_style_context()->remove_class("silence"); } else { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); if (hide_on_silence_) label_.hide(); else if (config_["format_silent"].isString()) label_.set_markup(format_silent_); + + label_.get_style_context()->add_class("silence"); } } From 367f156eb0f956b848739c94139ea0311e8f1890 Mon Sep 17 00:00:00 2001 From: Sonter Date: Wed, 14 Aug 2024 19:25:07 +0300 Subject: [PATCH 346/407] Add cava.update to css --- src/modules/cava.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 26ad4fd14..1e6a97f7e 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -173,6 +173,7 @@ auto waybar::modules::Cava::update() -> void { label_.set_markup(text_); label_.show(); ALabel::update(); + label_.get_style_context()->add_class("update"); } label_.get_style_context()->remove_class("silence"); @@ -182,6 +183,7 @@ auto waybar::modules::Cava::update() -> void { else if (config_["format_silent"].isString()) label_.set_markup(format_silent_); label_.get_style_context()->add_class("silence"); + label_.get_style_context()->remove_class("update"); } } From 36497cd41a599d8c5f9da8f11a8dd4ea5efe169e Mon Sep 17 00:00:00 2001 From: Sonter Date: Wed, 14 Aug 2024 19:27:12 +0300 Subject: [PATCH 347/407] Rename cava css values --- src/modules/cava.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 1e6a97f7e..f81bf7991 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -173,17 +173,17 @@ auto waybar::modules::Cava::update() -> void { label_.set_markup(text_); label_.show(); ALabel::update(); - label_.get_style_context()->add_class("update"); + label_.get_style_context()->add_class("updated"); } - label_.get_style_context()->remove_class("silence"); + label_.get_style_context()->remove_class("silent"); } else { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); if (hide_on_silence_) label_.hide(); else if (config_["format_silent"].isString()) label_.set_markup(format_silent_); - label_.get_style_context()->add_class("silence"); - label_.get_style_context()->remove_class("update"); + label_.get_style_context()->add_class("silent"); + label_.get_style_context()->remove_class("updated"); } } From 1f23b30b560b4577bf65adc6f77bb5595abaccb0 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Aug 2024 22:24:15 -0700 Subject: [PATCH 348/407] hyprland/backend: drop unnecessary getaddrinfo call Hyprland hasn't been using TCP sockets for IPC since the first release, so this getaddrinfo call and its result was never needed. Additionally, it leaks the `aiRes`, causing test failure under ASan. --- src/modules/hyprland/backend.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 60453dcb1..77f534e0d 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -148,22 +148,12 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { std::string IPC::getSocket1Reply(const std::string& rq) { // basically hyprctl - struct addrinfo aiHints; - struct addrinfo* aiRes = nullptr; const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0); if (serverSocket < 0) { throw std::runtime_error("Hyprland IPC: Couldn't open a socket (1)"); } - memset(&aiHints, 0, sizeof(struct addrinfo)); - aiHints.ai_family = AF_UNSPEC; - aiHints.ai_socktype = SOCK_STREAM; - - if (getaddrinfo("localhost", nullptr, &aiHints, &aiRes) != 0) { - throw std::runtime_error("Hyprland IPC: Couldn't get host (2)"); - } - // get the instance signature auto* instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE"); From fd478bf2ab3d00be7889054b4c517463e48df7ca Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Mon, 19 Aug 2024 12:35:52 +0800 Subject: [PATCH 349/407] fix crash caused by use bar instance after it is freed (use-after-free) --- include/bar.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bar.hpp b/include/bar.hpp index 43756bfdc..936bc749c 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -54,7 +54,7 @@ class BarIpcClient; } #endif // HAVE_SWAY -class Bar { +class Bar : public sigc::trackable { public: using bar_mode_map = std::map; static const bar_mode_map PRESET_MODES; From 0fb1957daedee6316932be438fbbcf6140003849 Mon Sep 17 00:00:00 2001 From: Andrea Scarpino Date: Tue, 20 Aug 2024 13:57:29 +0200 Subject: [PATCH 350/407] fix: check format-source before use --- 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 3efd9d232..255ca571f 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -106,7 +106,7 @@ auto waybar::modules::Pulseaudio::update() -> void { } } else { label_.get_style_context()->remove_class("source-muted"); - if (config_["format-source-muted"].isString()) { + if (config_["format-source"].isString()) { format_source = config_["format-source"].asString(); } } From 4d89c64bed8b5f5963615e65850238f1a4ee9cc6 Mon Sep 17 00:00:00 2001 From: Scott Moreau Date: Mon, 26 Aug 2024 04:44:22 -0600 Subject: [PATCH 351/407] taskbar: Fixup whitespace --- include/modules/wlr/taskbar.hpp | 2 +- src/modules/wlr/taskbar.cpp | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 026f364ad..07110ddee 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -25,7 +25,7 @@ namespace waybar::modules::wlr { struct widget_geometry { - int x, y, w, h; + int x, y, w, h; }; class Taskbar; diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 7ff11baf7..30e4ee488 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -388,7 +388,8 @@ void Task::handle_title(const char *title) { } void Task::set_minimize_hint() { - zwlr_foreign_toplevel_handle_v1_set_rectangle(handle_, bar_.surface, minimize_hint.x, minimize_hint.y, minimize_hint.w, minimize_hint.h); + zwlr_foreign_toplevel_handle_v1_set_rectangle(handle_, bar_.surface, minimize_hint.x, + minimize_hint.y, minimize_hint.w, minimize_hint.h); } void Task::hide_if_ignored() { @@ -452,9 +453,10 @@ void Task::handle_app_id(const char *app_id) { } void Task::on_button_size_allocated(Gtk::Allocation &alloc) { - gtk_widget_translate_coordinates(GTK_WIDGET(button.gobj()), GTK_WIDGET(bar_.window.gobj()), 0, 0, &minimize_hint.x, &minimize_hint.y); - minimize_hint.w = button.get_width(); - minimize_hint.h = button.get_height(); + gtk_widget_translate_coordinates(GTK_WIDGET(button.gobj()), GTK_WIDGET(bar_.window.gobj()), 0, 0, + &minimize_hint.x, &minimize_hint.y); + minimize_hint.w = button.get_width(); + minimize_hint.h = button.get_height(); } void Task::handle_output_enter(struct wl_output *output) { @@ -467,7 +469,8 @@ void Task::handle_output_enter(struct wl_output *output) { if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) { /* The task entered the output of the current bar make the button visible */ - button.signal_size_allocate().connect_notify(sigc::mem_fun(this, &Task::on_button_size_allocated)); + button.signal_size_allocate().connect_notify( + sigc::mem_fun(this, &Task::on_button_size_allocated)); tbar_->add_button(button); button.show(); button_visible_ = true; From 0ee519753cf832c723623fbe8ba307acb1707503 Mon Sep 17 00:00:00 2001 From: Antoine Bolvy Date: Tue, 27 Aug 2024 15:43:19 +0200 Subject: [PATCH 352/407] feat: hidpi support for image module --- src/modules/image.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 8274d323e..5e6c1493f 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -42,7 +42,6 @@ void waybar::modules::Image::refresh(int sig) { } auto waybar::modules::Image::update() -> void { - Glib::RefPtr pixbuf; if (config_["path"].isString()) { path_ = config_["path"].asString(); } else if (config_["exec"].isString()) { @@ -51,19 +50,24 @@ auto waybar::modules::Image::update() -> void { } else { path_ = ""; } - if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) - pixbuf = Gdk::Pixbuf::create_from_file(path_, size_, size_); - else - pixbuf = {}; - if (pixbuf) { + if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) { + Glib::RefPtr pixbuf; + + int scaled_icon_size = size_ * image_.get_scale_factor(); + pixbuf = Gdk::Pixbuf::create_from_file(path_, scaled_icon_size, scaled_icon_size); + + auto surface = + Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), image_.get_window()); + image_.set(surface); + image_.show(); + if (tooltipEnabled() && !tooltip_.empty()) { if (box_.get_tooltip_markup() != tooltip_) { box_.set_tooltip_markup(tooltip_); } } - image_.set(pixbuf); - image_.show(); + box_.get_style_context()->remove_class("empty"); } else { image_.clear(); From 9b5c2dc7ed059ca5304fcbe8b23b9697cc853491 Mon Sep 17 00:00:00 2001 From: Leonard Cohnen Date: Sat, 31 Aug 2024 20:33:52 +0200 Subject: [PATCH 353/407] fix: upower module selection with multiple devices While looping over all the upower devices, the currently set device that will be rendered in the waybar, is overridden. Since the loop doesn't end when the device is found, the upDevice_ is overridden with NULL in the iteration for the next device. Now we only override upDevice_ if the current device matches the constraints. Fixes d2a719d67c5427dffc3e431c41b96b60399576e6 ("Redo to minimize code duplication.") Fixes #3267 --- src/modules/upower.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 552495f85..5ee6d64c5 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -384,10 +384,11 @@ void UPower::setDisplayDevice() { displayDevice = upDevice; } } - // Unref current upDevice - if (displayDevice.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); - // Reassign new upDevice - thisPtr->upDevice_ = displayDevice; + // Unref current upDevice if it exists + if (displayDevice.upDevice != NULL) { + if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); + thisPtr->upDevice_ = displayDevice; + } }, this); } From 7a6960842aac3cabc8256489631fe36e219f2576 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 1 Sep 2024 00:11:30 +0000 Subject: [PATCH 354/407] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/52ec9ac3b12395ad677e8b62106f0b98c1f8569d?narHash=sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k%3D' (2024-07-28) → 'github:NixOS/nixpkgs/71e91c409d1e654808b2621f28a327acfdad8dc2?narHash=sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w%3D' (2024-08-28) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index b8f68f4ba..9bd73acca 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1722185531, - "narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=", + "lastModified": 1724819573, + "narHash": "sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d", + "rev": "71e91c409d1e654808b2621f28a327acfdad8dc2", "type": "github" }, "original": { From 1fa8019ad5de59f7a9077a0d928618576581a9b8 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Sun, 8 Sep 2024 13:33:52 +0200 Subject: [PATCH 355/407] man: Clarify rotate option fixes: #3576 --- man/waybar-backlight.5.scd | 2 +- man/waybar-battery.5.scd | 2 +- man/waybar-bluetooth.5.scd | 2 +- man/waybar-clock.5.scd | 2 +- man/waybar-cpu.5.scd | 2 +- man/waybar-custom.5.scd | 2 +- man/waybar-disk.5.scd | 2 +- man/waybar-dwl-window.5.scd | 2 +- man/waybar-hyprland-submap.5.scd | 2 +- man/waybar-idle-inhibitor.5.scd | 2 +- man/waybar-inhibitor.5.scd | 2 +- man/waybar-jack.5.scd | 2 +- man/waybar-memory.5.scd | 2 +- man/waybar-mpd.5.scd | 2 +- man/waybar-mpris.5.scd | 2 +- man/waybar-network.5.scd | 2 +- man/waybar-pulseaudio.5.scd | 2 +- man/waybar-river-layout.5.scd | 2 +- man/waybar-river-mode.5.scd | 2 +- man/waybar-river-window.5.scd | 2 +- man/waybar-sndio.5.scd | 2 +- man/waybar-sway-mode.5.scd | 2 +- man/waybar-sway-window.5.scd | 2 +- man/waybar-temperature.5.scd | 2 +- man/waybar-wireplumber.5.scd | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index 1f674fc00..5286c2ed3 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -38,7 +38,7 @@ The *backlight* module displays the current backlight level. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 4fe9650a7..29cb7d6d8 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -69,7 +69,7 @@ The *battery* module displays the current capacity and state (eg. charging) of y *rotate*: ++ typeof: integer++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *on-click*: ++ typeof: string ++ diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 1783dab32..bd64f4577 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -54,7 +54,7 @@ Addressed by *bluetooth* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 40aedd152..3947266d0 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -51,7 +51,7 @@ $XDG_CONFIG_HOME/waybar/config ++ |[ *rotate* :[ integer :[ -:[ Positive value to rotate the text label +:[ Positive value to rotate the text label (in 90 degree increments) |[ *on-click* :[ string :[ diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index fcbd12653..6b13a5632 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -43,7 +43,7 @@ The *cpu* module displays the current CPU utilization. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 4cf3c33dc..aba1c18f2 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -64,7 +64,7 @@ Addressed by *custom/* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index df9ca4e5a..1699a511f 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -29,7 +29,7 @@ Addressed by *disk* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ diff --git a/man/waybar-dwl-window.5.scd b/man/waybar-dwl-window.5.scd index c2f5b93eb..f185c82c8 100644 --- a/man/waybar-dwl-window.5.scd +++ b/man/waybar-dwl-window.5.scd @@ -19,7 +19,7 @@ Addressed by *dwl/window* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index 64398e614..e27138e75 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -19,7 +19,7 @@ Addressed by *hyprland/submap* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index f7677634c..81a097a71 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -21,7 +21,7 @@ screensaver, also known as "presentation mode". *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-inhibitor.5.scd b/man/waybar-inhibitor.5.scd index 679a5c4b0..5513cc496 100644 --- a/man/waybar-inhibitor.5.scd +++ b/man/waybar-inhibitor.5.scd @@ -25,7 +25,7 @@ See *systemd-inhibit*(1) for more information. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-jack.5.scd b/man/waybar-jack.5.scd index 573b36c27..180143b7c 100644 --- a/man/waybar-jack.5.scd +++ b/man/waybar-jack.5.scd @@ -51,7 +51,7 @@ Addressed by *jack* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index 7738c576d..5c368ae89 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -29,7 +29,7 @@ Addressed by *memory* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index 2f1bdf208..c576a5c03 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -91,7 +91,7 @@ Addressed by *mpd* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 455fcb177..380a1a199 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -107,7 +107,7 @@ The *mpris* module displays currently playing media via libplayerctl. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index bd546916e..e1cf810d8 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -58,7 +58,7 @@ Addressed by *network* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index 232e84a00..5b38e8b7e 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -40,7 +40,7 @@ Additionally, you can control the volume by scrolling *up* or *down* while the c *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ diff --git a/man/waybar-river-layout.5.scd b/man/waybar-river-layout.5.scd index 4fb23085b..1368bda92 100644 --- a/man/waybar-river-layout.5.scd +++ b/man/waybar-river-layout.5.scd @@ -21,7 +21,7 @@ Addressed by *river/layout* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-river-mode.5.scd b/man/waybar-river-mode.5.scd index 5769a9a2c..b992fdaff 100644 --- a/man/waybar-river-mode.5.scd +++ b/man/waybar-river-mode.5.scd @@ -19,7 +19,7 @@ Addressed by *river/mode* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-river-window.5.scd b/man/waybar-river-window.5.scd index 7e661f438..6db9a2fa1 100644 --- a/man/waybar-river-window.5.scd +++ b/man/waybar-river-window.5.scd @@ -19,7 +19,7 @@ Addressed by *river/window* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd index f8d1615d4..03dfe0afa 100644 --- a/man/waybar-sndio.5.scd +++ b/man/waybar-sndio.5.scd @@ -20,7 +20,7 @@ cursor is over the module, and clicking on the module toggles mute. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index 1fcf3cf82..8d5d7c2c1 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -19,7 +19,7 @@ Addressed by *sway/mode* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 037e6b55c..6d1e31960 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -19,7 +19,7 @@ Addressed by *sway/window* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index eab4cbb3b..541bf3af6 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -60,7 +60,7 @@ Addressed by *temperature* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-wireplumber.5.scd b/man/waybar-wireplumber.5.scd index 770ff0d5c..9c26ebaf4 100644 --- a/man/waybar-wireplumber.5.scd +++ b/man/waybar-wireplumber.5.scd @@ -31,7 +31,7 @@ The *wireplumber* module displays the current volume reported by WirePlumber. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ From 5b1826d2f66b836e73643998cc1d82af125c218f Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Sun, 8 Sep 2024 13:54:50 +0200 Subject: [PATCH 356/407] label: Add warning for invalid rotate property --- src/ALabel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index ecb1b7ced..3cb2c590c 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -45,6 +45,8 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st if (config_["rotate"].isUInt()) { rotate = config["rotate"].asUInt(); + if (not (rotate == 0 || rotate == 90 || rotate == 180 || rotate == 270)) + spdlog::warn("'rotate' is only supported in 90 degree increments {} is not valid.", rotate); label_.set_angle(rotate); } From 70f3c1d9e95e1886181604298e71993888844ff3 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 10 Sep 2024 01:16:42 +0900 Subject: [PATCH 357/407] chore: update power_profiles_daemon.cpp minor fix --- src/modules/power_profiles_daemon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index eaa470232..3ae3ae830 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -204,7 +204,7 @@ void PowerProfilesDaemon::setPropCb(Glib::RefPtr& r) { auto _ = powerProfilesProxy_->call_finish(r); dp.emit(); } catch (const std::exception& e) { - spdlog::error("Failed to set the the active power profile: {}", e.what()); + spdlog::error("Failed to set the active power profile: {}", e.what()); } catch (const Glib::Error& e) { spdlog::error("Failed to set the active power profile: {}", std::string(e.what())); } From c2f1a7894b0b99779a482fcc85d30861f461e21a Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 08:40:19 +0200 Subject: [PATCH 358/407] chore: update deps --- subprojects/catch2.wrap | 12 ++++++------ subprojects/fmt.wrap | 18 +++++++++--------- subprojects/spdlog.wrap | 18 +++++++++--------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap index f2dfd57c2..489db6c6f 100644 --- a/subprojects/catch2.wrap +++ b/subprojects/catch2.wrap @@ -1,10 +1,10 @@ [wrap-file] -directory = Catch2-3.5.1 -source_url = https://github.com/catchorg/Catch2/archive/v3.5.1.tar.gz -source_filename = Catch2-3.5.1.tar.gz -source_hash = 49c3ca7a68f1c8ec71307736bc6ed14fec21631707e1be9af45daf4037e75a08 -# source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.4.0-1/Catch2-3.4.0.tar.gz -# wrapdb_version = 3.4.0-1 +directory = Catch2-3.7.0 +source_url = https://github.com/catchorg/Catch2/archive/v3.7.0.tar.gz +source_filename = Catch2-3.7.0.tar.gz +source_hash = 5b10cd536fa3818112a82820ce0787bd9f2a906c618429e7c4dea639983c8e88 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.7.0-1/Catch2-3.7.0.tar.gz +wrapdb_version = 3.7.0-1 [provide] catch2 = catch2_dep diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap index 9efe101e8..42b615963 100644 --- a/subprojects/fmt.wrap +++ b/subprojects/fmt.wrap @@ -1,13 +1,13 @@ [wrap-file] -directory = fmt-9.1.0 -source_url = https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz -source_filename = fmt-9.1.0.tar.gz -source_hash = 5dea48d1fcddc3ec571ce2058e13910a0d4a6bab4cc09a809d8b1dd1c88ae6f2 -patch_filename = fmt_9.1.0-2_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/fmt_9.1.0-2/get_patch -patch_hash = 23e8c4829f3e63f509b5643fe6bb87cbed39eae9594c451b338475d14d051967 -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_9.1.0-2/fmt-9.1.0.tar.gz -wrapdb_version = 9.1.0-2 +directory = fmt-11.0.1 +source_url = https://github.com/fmtlib/fmt/archive/11.0.1.tar.gz +source_filename = fmt-11.0.1.tar.gz +source_hash = 7d009f7f89ac84c0a83f79ed602463d092fbf66763766a907c97fd02b100f5e9 +patch_filename = fmt_11.0.1-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/fmt_11.0.1-1/get_patch +patch_hash = 0a8b93d1ee6d84a82d3872a9bfb4c3977d8a53f7f484d42d1f7ed63ed496d549 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_11.0.1-1/fmt-11.0.1.tar.gz +wrapdb_version = 11.0.1-1 [provide] fmt = fmt_dep diff --git a/subprojects/spdlog.wrap b/subprojects/spdlog.wrap index 08004c901..af00d5a79 100644 --- a/subprojects/spdlog.wrap +++ b/subprojects/spdlog.wrap @@ -1,13 +1,13 @@ [wrap-file] -directory = spdlog-1.12.0 -source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.12.0.tar.gz -source_filename = spdlog-1.12.0.tar.gz -source_hash = 4dccf2d10f410c1e2feaff89966bfc49a1abb29ef6f08246335b110e001e09a9 -patch_filename = spdlog_1.12.0-2_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.12.0-2/get_patch -patch_hash = 9596972d1eb2e0a69cea4a53273ca7bbbcb9b2fa872cd734864fc7232dc2d573 -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.12.0-2/spdlog-1.12.0.tar.gz -wrapdb_version = 1.12.0-2 +directory = spdlog-1.14.1 +source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.14.1.tar.gz +source_filename = spdlog-1.14.1.tar.gz +source_hash = 1586508029a7d0670dfcb2d97575dcdc242d3868a259742b69f100801ab4e16b +patch_filename = spdlog_1.14.1-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.14.1-1/get_patch +patch_hash = ae878e732330ea1048f90d7e117c40c0cd2a6fb8ae5492c7955818ce3aaade6c +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.14.1-1/spdlog-1.14.1.tar.gz +wrapdb_version = 1.14.1-1 [provide] spdlog = spdlog_dep From 64d99a588406e5bba88472f25f814e58406fc836 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 08:50:07 +0200 Subject: [PATCH 359/407] chore(fmt): std format --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 8daa6c9cf..834ceb750 100644 --- a/meson.build +++ b/meson.build @@ -69,7 +69,7 @@ is_openbsd = host_machine.system() == 'openbsd' thread_dep = dependency('threads') fmt = dependency('fmt', version : ['>=8.1.1'], fallback : ['fmt', 'fmt_dep']) -spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled']) +spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled', 'std_format=enabled']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') From 6417782af6a88a7d2dd8f355d10d755f32e1d402 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 08:55:14 +0200 Subject: [PATCH 360/407] chore: lint --- .github/workflows/clang-format.yml | 1 + include/AModule.hpp | 2 +- include/modules/clock.hpp | 4 ++-- include/modules/hyprland/workspaces.hpp | 4 ++-- include/util/clara.hpp | 12 ++++++------ include/util/format.hpp | 2 +- src/modules/bluetooth.cpp | 17 ++++++++--------- src/modules/clock.cpp | 4 ++-- src/modules/dwl/tags.cpp | 4 ++-- src/modules/hyprland/workspaces.cpp | 4 ++-- src/modules/memory/bsd.cpp | 8 ++++---- src/modules/wlr/workspace_manager.cpp | 4 ++-- test/utils/SafeSignal.cpp | 2 +- 13 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 40fd3126e..a7b5d8969 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -16,4 +16,5 @@ jobs: with: source: "." extensions: "hpp,h,cpp,c" + style: "file:.clang-format" clangFormatVersion: 16 diff --git a/include/AModule.hpp b/include/AModule.hpp index facb3130f..94a883718 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -16,7 +16,7 @@ class AModule : public IModule { ~AModule() override; auto update() -> void override; - virtual auto refresh(int shouldRefresh) -> void{}; + virtual auto refresh(int shouldRefresh) -> void {}; operator Gtk::Widget &() override; auto doAction(const std::string &name) -> void override; diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index c212ec8b9..0c62b6767 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -51,8 +51,8 @@ class Clock final : public ALabel { day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight) std::string cldText_{""}; // calendar text to print CldMode cldMode_{CldMode::MONTH}; - auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) - -> const std::string; + auto get_calendar(const year_month_day& today, const year_month_day& ymd, + const time_zone* tz) -> const std::string; // get local time zone auto local_zone() -> const time_zone*; diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index f5c20f69c..a9d56b79f 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -61,8 +61,8 @@ class Workspaces : public AModule, public EventHandler { // Config void parseConfig(const Json::Value& config); auto populateIconsMap(const Json::Value& formatIcons) -> void; - static auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) - -> void; + static auto populateBoolConfig(const Json::Value& config, const std::string& key, + bool& member) -> void; auto populateSortByConfig(const Json::Value& config) -> void; auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; diff --git a/include/util/clara.hpp b/include/util/clara.hpp index 73fa5415d..da7151fe4 100644 --- a/include/util/clara.hpp +++ b/include/util/clara.hpp @@ -622,8 +622,8 @@ inline auto convertInto(std::string const &source, bool &target) -> ParserResult } #ifdef CLARA_CONFIG_OPTIONAL_TYPE template -inline auto convertInto(std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE &target) - -> ParserResult { +inline auto convertInto(std::string const &source, + CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult { T temp; auto result = convertInto(source, temp); if (result) target = std::move(temp); @@ -751,8 +751,8 @@ class ParserBase { public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse(std::string const &exeName, TokenStream const &tokens) const - -> InternalParseResult = 0; + virtual auto parse(std::string const &exeName, + TokenStream const &tokens) const -> InternalParseResult = 0; virtual auto cardinality() const -> size_t { return 1; } auto parse(Args const &args) const -> InternalParseResult { @@ -1098,8 +1098,8 @@ struct Parser : ParserBase { using ParserBase::parse; - auto parse(std::string const &exeName, TokenStream const &tokens) const - -> InternalParseResult override { + auto parse(std::string const &exeName, + TokenStream const &tokens) const -> InternalParseResult override { struct ParserInfo { ParserBase const *parser = nullptr; size_t count = 0; diff --git a/include/util/format.hpp b/include/util/format.hpp index cf8d706b2..c8ed837a5 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -6,7 +6,7 @@ class pow_format { public: pow_format(long long val, std::string&& unit, bool binary = false) - : val_(val), unit_(unit), binary_(binary){}; + : val_(val), unit_(unit), binary_(binary) {}; long long val_; std::string unit_; diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 06475a2e5..c8f1f9966 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -49,8 +49,8 @@ auto getBoolProperty(GDBusProxy* proxy, const char* property_name) -> bool { return false; } -auto getOptionalStringProperty(GDBusProxy* proxy, const char* property_name) - -> std::optional { +auto getOptionalStringProperty(GDBusProxy* proxy, + const char* property_name) -> std::optional { auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name); if (gvar) { std::string property_value = g_variant_get_string(gvar, NULL); @@ -345,8 +345,8 @@ auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* m auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged( GDBusObjectManagerClient* manager, GDBusObjectProxy* object_proxy, GDBusProxy* interface_proxy, - GVariant* changed_properties, const gchar* const* invalidated_properties, gpointer user_data) - -> void { + GVariant* changed_properties, const gchar* const* invalidated_properties, + gpointer user_data) -> void { std::string interface_name = g_dbus_proxy_get_interface_name(interface_proxy); std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy)); @@ -395,8 +395,8 @@ auto waybar::modules::Bluetooth::getDeviceBatteryPercentage(GDBusObject* object) return std::nullopt; } -auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, DeviceInfo& device_info) - -> bool { +auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, + DeviceInfo& device_info) -> bool { GDBusProxy* proxy_device = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Device1")); if (proxy_device != NULL) { @@ -462,9 +462,8 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional& connected_devices) - -> void { +auto waybar::modules::Bluetooth::findConnectedDevices( + const std::string& cur_controller_path, std::vector& connected_devices) -> void { GList* objects = g_dbus_object_manager_get_objects(manager_.get()); for (GList* l = objects; l != NULL; l = l->next) { GDBusObject* object = G_DBUS_OBJECT(l->data); diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 7a4cb9c27..db2979ebc 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -198,8 +198,8 @@ const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) { return 2u + ceil((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count(); } -auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line) - -> const year_month_weekday { +auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, + const unsigned line) -> const year_month_weekday { unsigned index{line - 2}; if (weekday{ym / 1} == firstdow) ++index; return ym / firstdow[index]; diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index 085b82246..f8b250c8c 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -53,8 +53,8 @@ static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t // Intentionally empty } -static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid){ - // Intentionally empty +static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid) { + // Intentionally empty }; static const zdwl_ipc_output_v2_listener output_status_listener_impl{ diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 047703cc9..13364f3f2 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -590,8 +590,8 @@ auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { m_iconsMap.emplace("", ""); } -auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) - -> void { +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, + bool &member) -> void { const auto &configValue = config[key]; if (configValue.isBool()) { member = configValue.asBool(); diff --git a/src/modules/memory/bsd.cpp b/src/modules/memory/bsd.cpp index 67f9fed7a..1d970e8aa 100644 --- a/src/modules/memory/bsd.cpp +++ b/src/modules/memory/bsd.cpp @@ -21,13 +21,13 @@ static uint64_t get_total_memory() { u_long physmem; #endif int mib[] = { - CTL_HW, + CTL_HW, #if defined(HW_MEMSIZE) - HW_MEMSIZE, + HW_MEMSIZE, #elif defined(HW_PHYSMEM64) - HW_PHYSMEM64, + HW_PHYSMEM64, #else - HW_PHYSMEM, + HW_PHYSMEM, #endif }; u_int miblen = sizeof(mib) / sizeof(mib[0]); diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index f556a161e..3c630d814 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -118,8 +118,8 @@ auto WorkspaceManager::sort_workspaces() -> void { } } -auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) - -> void { +auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, + uint32_t version) -> void { if (workspace_manager_) { spdlog::warn("Register workspace manager again although already registered!"); return; diff --git a/test/utils/SafeSignal.cpp b/test/utils/SafeSignal.cpp index 341e8e2ea..e7e096b09 100644 --- a/test/utils/SafeSignal.cpp +++ b/test/utils/SafeSignal.cpp @@ -71,7 +71,7 @@ struct TestObject { unsigned copied = 0; unsigned moved = 0; - TestObject(const T& v) : value(v){}; + TestObject(const T& v) : value(v) {}; ~TestObject() = default; TestObject(const TestObject& other) From 4354da284969097f5d7ab2382cb9fe60a7f1a016 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 08:59:36 +0200 Subject: [PATCH 361/407] chore: disable fmt tests --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 834ceb750..ed5ec7b8b 100644 --- a/meson.build +++ b/meson.build @@ -69,7 +69,7 @@ is_openbsd = host_machine.system() == 'openbsd' thread_dep = dependency('threads') fmt = dependency('fmt', version : ['>=8.1.1'], fallback : ['fmt', 'fmt_dep']) -spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled', 'std_format=enabled']) +spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled', 'std_format=disabled', 'tests=disabled']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') From 3ade275d100a4e53e6ac75912ea299d2965df250 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:02:23 +0200 Subject: [PATCH 362/407] fix: version --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index a7b5d8969..4a774dbdd 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -17,4 +17,4 @@ jobs: source: "." extensions: "hpp,h,cpp,c" style: "file:.clang-format" - clangFormatVersion: 16 + clangFormatVersion: 18 From d623a89cd17b89d8e69d8185170c9a05413fac30 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:33:40 +0200 Subject: [PATCH 363/407] fix: sni item fmt --- src/modules/image.cpp | 4 ++-- src/modules/sni/item.cpp | 21 +++++++++++++-------- subprojects/gtk-layer-shell.wrap | 8 ++++---- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 5e6c1493f..71e93b946 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -57,8 +57,8 @@ auto waybar::modules::Image::update() -> void { int scaled_icon_size = size_ * image_.get_scale_factor(); pixbuf = Gdk::Pixbuf::create_from_file(path_, scaled_icon_size, scaled_icon_size); - auto surface = - Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), image_.get_window()); + auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), + image_.get_window()); image_.set(surface); image_.show(); diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 6c4ec8c06..126545b1f 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -104,9 +104,11 @@ void Item::proxyReady(Glib::RefPtr& result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, + std::string(err.what())); } catch (const std::exception& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, + std::string(err.what())); } } @@ -124,14 +126,15 @@ ToolTip get_variant(const Glib::VariantBase& value) { 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); + result.text = fmt::format("{}\n{}", std::string(result.text), std::string(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); + spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, + std::string(name), get_variant(value)); if (name == "Category") { category = get_variant(value); @@ -176,10 +179,12 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } } catch (const Glib::Error& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, name, value, err.what()); + id.empty() ? bus_name : id, std::string(name), get_variant(value), + std::string(err.what())); } catch (const std::exception& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, name, value, err.what()); + id.empty() ? bus_name : id, std::string(name), get_variant(value), + std::string(err.what())); } } @@ -221,9 +226,9 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::warn("Failed to update properties: {}", err.what()); + spdlog::warn("Failed to update properties: {}", std::string(err.what())); } catch (const std::exception& err) { - spdlog::warn("Failed to update properties: {}", err.what()); + spdlog::warn("Failed to update properties: {}", std::string(err.what())); } update_pending_.clear(); } diff --git a/subprojects/gtk-layer-shell.wrap b/subprojects/gtk-layer-shell.wrap index cb7303455..fc0ddf743 100644 --- a/subprojects/gtk-layer-shell.wrap +++ b/subprojects/gtk-layer-shell.wrap @@ -1,5 +1,5 @@ [wrap-file] -directory = gtk-layer-shell-0.8.2 -source_filename = gtk-layer-shell-0.8.2.tar.gz -source_hash = 254dd246303127c5d5236ea640f01a82e35d2d652a48d139dd669c832a0f0dce -source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.8.2/gtk-layer-shell-0.8.2.tar.gz +directory = gtk-layer-shell-0.9.0 +source_filename = gtk-layer-shell-0.9.0.tar.gz +source_hash = 3809e5565d9ed02e44bb73787ff218523e8760fef65830afe60ea7322e22da1c +source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.9.0/gtk-layer-shell-0.9.0.tar.gz From 46e7ed35de6d105d7f65b5172a69250a57af4462 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Thu, 29 Aug 2024 11:45:17 +0300 Subject: [PATCH 364/407] Add niri/workspaces, niri/window, niri/language --- README.md | 1 + include/modules/niri/backend.hpp | 52 ++++++ include/modules/niri/language.hpp | 38 +++++ include/modules/niri/window.hpp | 28 ++++ include/modules/niri/workspaces.hpp | 30 ++++ man/waybar-niri-language.5.scd | 58 +++++++ man/waybar-niri-window.5.scd | 81 +++++++++ man/waybar-niri-workspaces.5.scd | 97 +++++++++++ man/waybar.5.scd.in | 3 + meson.build | 15 ++ src/factory.cpp | 16 ++ src/modules/niri/backend.cpp | 249 ++++++++++++++++++++++++++++ src/modules/niri/language.cpp | 138 +++++++++++++++ src/modules/niri/window.cpp | 121 ++++++++++++++ src/modules/niri/workspaces.cpp | 204 +++++++++++++++++++++++ 15 files changed, 1131 insertions(+) create mode 100644 include/modules/niri/backend.hpp create mode 100644 include/modules/niri/language.hpp create mode 100644 include/modules/niri/window.hpp create mode 100644 include/modules/niri/workspaces.hpp create mode 100644 man/waybar-niri-language.5.scd create mode 100644 man/waybar-niri-window.5.scd create mode 100644 man/waybar-niri-workspaces.5.scd create mode 100644 src/modules/niri/backend.cpp create mode 100644 src/modules/niri/language.cpp create mode 100644 src/modules/niri/window.cpp create mode 100644 src/modules/niri/workspaces.cpp diff --git a/README.md b/README.md index a019eb6fb..55a6c7d9e 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ - Sway (Workspaces, Binding mode, Focused window name) - River (Mapping mode, Tags, Focused window name) - Hyprland (Window Icons, Workspaces, Focused window name) +- Niri (Workspaces, Focused window name, Language) - DWL (Tags, Focused window name) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc) - Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Local time diff --git a/include/modules/niri/backend.hpp b/include/modules/niri/backend.hpp new file mode 100644 index 000000000..01af50174 --- /dev/null +++ b/include/modules/niri/backend.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +#include "util/json.hpp" + +namespace waybar::modules::niri { + +class EventHandler { + public: + virtual void onEvent(const Json::Value& ev) = 0; + virtual ~EventHandler() = default; +}; + +class IPC { + public: + IPC() { startIPC(); } + + void registerForIPC(const std::string& ev, EventHandler* ev_handler); + void unregisterForIPC(EventHandler* handler); + + static Json::Value send(const Json::Value& request); + + // The data members are only safe to access while dataMutex_ is locked. + std::lock_guard lockData() { return std::lock_guard(dataMutex_); } + const std::vector &workspaces() const { return workspaces_; } + const std::vector &windows() const { return windows_; } + const std::vector &keyboardLayoutNames() const { return keyboardLayoutNames_; } + unsigned keyboardLayoutCurrent() const { return keyboardLayoutCurrent_; } + + private: + void startIPC(); + static int connectToSocket(); + void parseIPC(const std::string&); + + std::mutex dataMutex_; + std::vector workspaces_; + std::vector windows_; + std::vector keyboardLayoutNames_; + unsigned keyboardLayoutCurrent_; + + util::JsonParser parser_; + std::mutex callbackMutex_; + std::list> callbacks_; +}; + +inline std::unique_ptr gIPC; + +}; // namespace waybar::modules::niri diff --git a/include/modules/niri/language.hpp b/include/modules/niri/language.hpp new file mode 100644 index 000000000..1cecd206c --- /dev/null +++ b/include/modules/niri/language.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "ALabel.hpp" +#include "bar.hpp" +#include "modules/niri/backend.hpp" + +namespace waybar::modules::niri { + +class Language : public ALabel, public EventHandler { + public: + Language(const std::string&, const Bar&, const Json::Value&); + ~Language() override; + void update() override; + + private: + void updateFromIPC(); + void onEvent(const Json::Value &ev) override; + void doUpdate(); + + struct Layout { + std::string full_name; + std::string short_name; + std::string variant; + std::string short_description; + }; + + static Layout getLayout(const std::string &fullName); + + std::mutex mutex_; + const Bar &bar_; + + std::vector layouts_; + unsigned current_idx_; +}; + +} // namespace waybar::modules::niri diff --git a/include/modules/niri/window.hpp b/include/modules/niri/window.hpp new file mode 100644 index 000000000..909ae6f0e --- /dev/null +++ b/include/modules/niri/window.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "AAppIconLabel.hpp" +#include "bar.hpp" +#include "modules/niri/backend.hpp" + +namespace waybar::modules::niri { + +class Window : public AAppIconLabel, public EventHandler { + public: + Window(const std::string &, const Bar &, const Json::Value &); + ~Window() override; + void update() override; + + private: + void onEvent(const Json::Value &ev) override; + void doUpdate(); + void setClass(const std::string &className, bool enable); + + const Bar &bar_; + + std::string oldAppId_; +}; + +} // namespace waybar::modules::niri diff --git a/include/modules/niri/workspaces.hpp b/include/modules/niri/workspaces.hpp new file mode 100644 index 000000000..a6850ed10 --- /dev/null +++ b/include/modules/niri/workspaces.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "modules/niri/backend.hpp" + +namespace waybar::modules::niri { + +class Workspaces : public AModule, public EventHandler { + public: + Workspaces(const std::string &, const Bar &, const Json::Value &); + ~Workspaces() override; + void update() override; + + private: + void onEvent(const Json::Value &ev) override; + void doUpdate(); + Gtk::Button &addButton(const Json::Value &ws); + std::string getIcon(const std::string &value, const Json::Value &ws); + + const Bar &bar_; + Gtk::Box box_; + // Map from niri workspace id to button. + std::unordered_map buttons_; +}; + +} // namespace waybar::modules::niri diff --git a/man/waybar-niri-language.5.scd b/man/waybar-niri-language.5.scd new file mode 100644 index 000000000..6895d25c0 --- /dev/null +++ b/man/waybar-niri-language.5.scd @@ -0,0 +1,58 @@ +waybar-niri-language(5) + +# NAME + +waybar - niri language module + +# DESCRIPTION + +The *language* module displays the currently selected language in niri. + +# CONFIGURATION + +Addressed by *niri/language* + +*format*: ++ + typeof: string ++ + default: {} ++ + The format, how information should be displayed. + +*format-* ++ + typeof: string++ + Provide an alternative name to display per language where is the language of your choosing. Can be passed multiple times with multiple languages as shown by the example below. + +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + +# FORMAT REPLACEMENTS + +*{short}*: Short name of layout (e.g. "us"). Equals to {}. + +*{shortDescription}*: Short description of layout (e.g. "en"). + +*{long}*: Long name of layout (e.g. "English (Dvorak)"). + +*{variant}*: Variant of layout (e.g. "dvorak"). + +# EXAMPLES + +``` +"niri/language": { + "format": "Lang: {long}" + "format-en": "AMERICA, HELL YEAH!" + "format-tr": "As bayrakları" +} +``` + +# STYLE + +- *#language* diff --git a/man/waybar-niri-window.5.scd b/man/waybar-niri-window.5.scd new file mode 100644 index 000000000..9e2e9f632 --- /dev/null +++ b/man/waybar-niri-window.5.scd @@ -0,0 +1,81 @@ +waybar-niri-window(5) + +# NAME + +waybar - niri window module + +# DESCRIPTION + +The *window* module displays the title of the currently focused window in niri. + +# CONFIGURATION + +Addressed by *niri/window* + +*format*: ++ + typeof: string ++ + default: {title} ++ + The format, how information should be displayed. On {} the current window title is displayed. + +*rewrite*: ++ + typeof: object ++ + Rules to rewrite window title. See *rewrite rules*. + +*separate-outputs*: ++ + typeof: bool ++ + Show the active window of the monitor the bar belongs to, instead of the focused window. + +*icon*: ++ + typeof: bool ++ + default: false ++ + Option to hide the application icon. + +*icon-size*: ++ + typeof: integer ++ + default: 24 ++ + Option to change the size of the application icon. + +# FORMAT REPLACEMENTS + +See the output of "niri msg windows" for examples + +*{title}*: The current title of the focused window. + +*{app_id}*: The current app ID of the focused window. + +# REWRITE RULES + +*rewrite* is an object where keys are regular expressions and values are +rewrite rules if the expression matches. Rules may contain references to +captures of the expression. + +Regular expression and replacement follow ECMA-script rules. + +If no expression matches, the title is left unchanged. + +Invalid expressions (e.g., mismatched parentheses) are skipped. + +# EXAMPLES + +``` +"niri/window": { + "format": "{}", + "rewrite": { + "(.*) - Mozilla Firefox": "🌎 $1", + "(.*) - zsh": "> [$1]" + } +} +``` + +# STYLE + +- *#window* +- *window#waybar.empty #window* When no windows are on the workspace + +The following classes are applied to the entire Waybar rather than just the +window widget: + +- *window#waybar.empty* When no windows are in the workspace +- *window#waybar.solo* When only one window is on the workspace +- *window#waybar.* Where *app-id* is the app ID of the only window on + the workspace diff --git a/man/waybar-niri-workspaces.5.scd b/man/waybar-niri-workspaces.5.scd new file mode 100644 index 000000000..50e497cdf --- /dev/null +++ b/man/waybar-niri-workspaces.5.scd @@ -0,0 +1,97 @@ +waybar-niri-workspaces(5) + +# NAME + +waybar - niri workspaces module + +# DESCRIPTION + +The *workspaces* module displays the currently used workspaces in niri. + +# CONFIGURATION + +Addressed by *niri/workspaces* + +*all-outputs*: ++ + typeof: bool ++ + default: false ++ + If set to false, workspaces will only be shown on the output they are on. If set to true all workspaces will be shown on every output. + +*format*: ++ + typeof: string ++ + default: {value} ++ + The format, how information should be displayed. + +*format-icons*: ++ + typeof: array ++ + Based on the workspace name, index and state, the corresponding icon gets selected. See *icons*. + +*disable-click*: ++ + typeof: bool ++ + default: false ++ + If set to false, you can click to change workspace. If set to true this behaviour is disabled. + +*disable-markup*: ++ + typeof: bool ++ + default: false ++ + If set to true, button label will escape pango markup. + +*current-only*: ++ + typeof: bool ++ + default: false ++ + If set to true, only the active or focused workspace will be shown. + +*on-update*: ++ + typeof: string ++ + Command to execute when the module is updated. + +# FORMAT REPLACEMENTS + +*{value}*: Name of the workspace, or index for unnamed workspaces, +as defined by niri. + +*{name}*: Name of the workspace for named workspaces. + +*{icon}*: Icon, as defined in *format-icons*. + +*{index}*: Index of the workspace on its output. + +*{output}*: Output where the workspace is located. + +# ICONS + +Additional to workspace name matching, the following *format-icons* can be set. + +- *default*: Will be shown, when no string matches are found. +- *focused*: Will be shown, when workspace is focused. +- *active*: Will be shown, when workspace is active on its output. + +# EXAMPLES + +``` +"niri/workspaces": { + "format": "{icon}", + "format-icons": { + // Named workspaces + // (you need to configure them in niri) + "browser": "", + "discord": "", + "chat": "", + + // Icons by state + "active": "", + "default": "" + } +} +``` + +# Style + +- *#workspaces button* +- *#workspaces button.focused*: The single focused workspace. +- *#workspaces button.active*: The workspace is active (visible) on its output. +- *#workspaces button.empty*: The workspace is empty. +- *#workspaces button.current_output*: The workspace is from the same output as + the bar that it is displayed on. +- *#workspaces button#niri-workspace-*: Workspaces named this, or index + for unnamed workspaces. diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index db546e178..f3a896565 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -323,6 +323,9 @@ A group may hide all but one element, showing them only on mouse hover. In order - *waybar-hyprland-submap(5)* - *waybar-hyprland-window(5)* - *waybar-hyprland-workspaces(5)* +- *waybar-niri-language(5)* +- *waybar-niri-window(5)* +- *waybar-niri-workspaces(5)* - *waybar-idle-inhibitor(5)* - *waybar-image(5)* - *waybar-inhibitor(5)* diff --git a/meson.build b/meson.build index ed5ec7b8b..7097a9fbf 100644 --- a/meson.build +++ b/meson.build @@ -318,6 +318,21 @@ if true ) endif +if true + add_project_arguments('-DHAVE_NIRI', language: 'cpp') + src_files += files( + 'src/modules/niri/backend.cpp', + 'src/modules/niri/language.cpp', + 'src/modules/niri/window.cpp', + 'src/modules/niri/workspaces.cpp', + ) + man_files += files( + 'man/waybar-niri-language.5.scd', + 'man/waybar-niri-window.5.scd', + 'man/waybar-niri-workspaces.5.scd', + ) +endif + if libnl.found() and libnlgen.found() add_project_arguments('-DHAVE_LIBNL', language: 'cpp') src_files += files('src/modules/network.cpp') diff --git a/src/factory.cpp b/src/factory.cpp index ca10ef956..6c2313e38 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -36,6 +36,11 @@ #include "modules/hyprland/window.hpp" #include "modules/hyprland/workspaces.hpp" #endif +#ifdef HAVE_NIRI +#include "modules/niri/language.hpp" +#include "modules/niri/window.hpp" +#include "modules/niri/workspaces.hpp" +#endif #if defined(__FreeBSD__) || defined(__linux__) #include "modules/battery.hpp" #endif @@ -205,6 +210,17 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "hyprland/workspaces") { return new waybar::modules::hyprland::Workspaces(id, bar_, config_[name]); } +#endif +#ifdef HAVE_NIRI + if (ref == "niri/language") { + return new waybar::modules::niri::Language(id, bar_, config_[name]); + } + if (ref == "niri/window") { + return new waybar::modules::niri::Window(id, bar_, config_[name]); + } + if (ref == "niri/workspaces") { + return new waybar::modules::niri::Workspaces(id, bar_, config_[name]); + } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); diff --git a/src/modules/niri/backend.cpp b/src/modules/niri/backend.cpp new file mode 100644 index 000000000..ef23c8817 --- /dev/null +++ b/src/modules/niri/backend.cpp @@ -0,0 +1,249 @@ +#include "modules/niri/backend.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace waybar::modules::niri { + +int IPC::connectToSocket() { + const char* socket_path = getenv("NIRI_SOCKET"); + + if (socket_path == nullptr) { + spdlog::warn("Niri is not running, niri IPC will not be available."); + return -1; + } + + struct sockaddr_un addr; + int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (socketfd == -1) { + throw std::runtime_error("socketfd failed"); + } + + addr.sun_family = AF_UNIX; + + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + + addr.sun_path[sizeof(addr.sun_path) - 1] = 0; + + int l = sizeof(struct sockaddr_un); + + if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) { + close(socketfd); + throw std::runtime_error("unable to connect"); + } + + return socketfd; +} + +void IPC::startIPC() { + // will start IPC and relay events to parseIPC + + std::thread([&]() { + int socketfd; + try { + socketfd = connectToSocket(); + } catch (std::exception &e) { + spdlog::error("Niri IPC: failed to start, reason: {}", e.what()); + return; + } + if (socketfd == -1) + return; + + spdlog::info("Niri IPC starting"); + + __gnu_cxx::stdio_filebuf filebuf(socketfd, std::ios::in | std::ios::out); + std::iostream fs(&filebuf); + fs << R"("EventStream")" << std::endl; + + std::string line; + std::getline(fs, line); + if (line != R"({"Ok":"Handled"})") { + spdlog::error("Niri IPC: failed to start event stream"); + return; + } + + while (std::getline(fs, line)) { + spdlog::debug("Niri IPC: received {}", line); + + try { + parseIPC(line); + } catch (std::exception& e) { + spdlog::warn("Failed to parse IPC message: {}, reason: {}", line, e.what()); + } catch (...) { + throw; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + }).detach(); +} + +void IPC::parseIPC(const std::string& line) { + const auto ev = parser_.parse(line); + const auto members = ev.getMemberNames(); + if (members.size() != 1) + throw std::runtime_error("Event must have a single member"); + + { + auto lock = lockData(); + + if (const auto &payload = ev["WorkspacesChanged"]) { + workspaces_.clear(); + const auto &values = payload["workspaces"]; + std::copy(values.begin(), values.end(), std::back_inserter(workspaces_)); + + std::sort(workspaces_.begin(), workspaces_.end(), + [](const auto &a, const auto &b) { + const auto &aOutput = a["output"].asString(); + const auto &bOutput = b["output"].asString(); + const auto aIdx = a["idx"].asUInt(); + const auto bIdx = b["idx"].asUInt(); + if (aOutput == bOutput) + return aIdx < bIdx; + return aOutput < bOutput; + }); + } else if (const auto& payload = ev["WorkspaceActivated"]) { + const auto id = payload["id"].asUInt64(); + const auto focused = payload["focused"].asBool(); + auto it = std::find_if(workspaces_.begin(), workspaces_.end(), + [id](const auto &ws) { return ws["id"].asUInt64() == id; }); + if (it != workspaces_.end()) { + const auto &ws = *it; + const auto &output = ws["output"].asString(); + for (auto &ws : workspaces_) { + const auto got_activated = (ws["id"].asUInt64() == id); + if (ws["output"] == output) + ws["is_active"] = got_activated; + + if (focused) + ws["is_focused"] = got_activated; + } + } else { + spdlog::error("Activated unknown workspace"); + } + } else if (const auto& payload = ev["WorkspaceActiveWindowChanged"]) { + const auto workspaceId = payload["workspace_id"].asUInt64(); + auto it = std::find_if(workspaces_.begin(), workspaces_.end(), + [workspaceId](const auto &ws) { return ws["id"].asUInt64() == workspaceId; }); + if (it != workspaces_.end()) { + auto &ws = *it; + ws["active_window_id"] = payload["active_window_id"]; + } else { + spdlog::error("Active window changed on unknown workspace"); + } + } else if (const auto &payload = ev["KeyboardLayoutsChanged"]) { + const auto &layouts = payload["keyboard_layouts"]; + const auto &names = layouts["names"]; + keyboardLayoutCurrent_ = layouts["current_idx"].asUInt(); + + keyboardLayoutNames_.clear(); + for (const auto &fullName : names) + keyboardLayoutNames_.push_back(fullName.asString()); + } else if (const auto& payload = ev["KeyboardLayoutSwitched"]) { + keyboardLayoutCurrent_ = payload["idx"].asUInt(); + } else if (const auto &payload = ev["WindowsChanged"]) { + windows_.clear(); + const auto &values = payload["windows"]; + std::copy(values.begin(), values.end(), std::back_inserter(windows_)); + } else if (const auto &payload = ev["WindowOpenedOrChanged"]) { + const auto &window = payload["window"]; + const auto id = window["id"].asUInt64(); + auto it = std::find_if(windows_.begin(), windows_.end(), + [id](const auto &win) { return win["id"].asUInt64() == id; }); + if (it == windows_.end()) { + windows_.push_back(window); + + if (window["is_focused"].asBool()) { + for (auto &win : windows_) { + win["is_focused"] = win["id"].asUInt64() == id; + } + } + } else { + *it = window; + } + } else if (const auto &payload = ev["WindowClosed"]) { + const auto id = payload["id"].asUInt64(); + auto it = std::find_if(windows_.begin(), windows_.end(), + [id](const auto &win) { return win["id"].asUInt64() == id; }); + if (it != windows_.end()) { + windows_.erase(it); + } else { + spdlog::error("Unknown window closed"); + } + } else if (const auto &payload = ev["WindowFocusChanged"]) { + const auto focused = !payload["id"].isNull(); + const auto id = payload["id"].asUInt64(); + for (auto &win : windows_) { + win["is_focused"] = focused && win["id"].asUInt64() == id; + } + } + } + + std::unique_lock lock(callbackMutex_); + + for (auto& [eventname, handler] : callbacks_) { + if (eventname == members[0]) { + handler->onEvent(ev); + } + } +} + +void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) { + if (ev_handler == nullptr) { + return; + } + + std::unique_lock lock(callbackMutex_); + callbacks_.emplace_back(ev, ev_handler); +} + +void IPC::unregisterForIPC(EventHandler* ev_handler) { + if (ev_handler == nullptr) { + return; + } + + std::unique_lock lock(callbackMutex_); + + for (auto it = callbacks_.begin(); it != callbacks_.end();) { + auto& [eventname, handler] = *it; + if (handler == ev_handler) { + it = callbacks_.erase(it); + } else { + ++it; + } + } +} + +Json::Value IPC::send(const Json::Value& request) { + int socketfd = connectToSocket(); + if (socketfd == -1) + throw std::runtime_error("Niri is not running"); + + __gnu_cxx::stdio_filebuf filebuf(socketfd, std::ios::in | std::ios::out); + std::iostream fs(&filebuf); + + // Niri needs the request on a single line. + Json::StreamWriterBuilder builder; + builder["indentation"] = ""; + std::unique_ptr writer(builder.newStreamWriter()); + writer->write(request, &fs); + fs << std::endl; + + Json::Value response; + fs >> response; + return response; +} + +} // namespace waybar::modules::hyprland diff --git a/src/modules/niri/language.cpp b/src/modules/niri/language.cpp new file mode 100644 index 000000000..f124d4dd9 --- /dev/null +++ b/src/modules/niri/language.cpp @@ -0,0 +1,138 @@ +#include "modules/niri/language.hpp" + +#include +#include +#include + +#include "util/string.hpp" + +namespace waybar::modules::niri { + +Language::Language(const std::string &id, const Bar &bar, const Json::Value &config) + : ALabel(config, "language", id, "{}", 0, true), bar_(bar) { + label_.hide(); + + if (!gIPC) + gIPC = std::make_unique(); + + gIPC->registerForIPC("KeyboardLayoutsChanged", this); + gIPC->registerForIPC("KeyboardLayoutSwitched", this); + + updateFromIPC(); + dp.emit(); +} + +Language::~Language() { + gIPC->unregisterForIPC(this); + // wait for possible event handler to finish + std::lock_guard lock(mutex_); +} + +void Language::updateFromIPC() { + std::lock_guard lock(mutex_); + auto ipcLock = gIPC->lockData(); + + layouts_.clear(); + for (const auto &fullName : gIPC->keyboardLayoutNames()) + layouts_.push_back(getLayout(fullName)); + + current_idx_ = gIPC->keyboardLayoutCurrent(); +} + +/** + * Language::doUpdate - update workspaces in UI thread. + * + * Note: some member fields are modified by both UI thread and event listener thread, use mutex_ to + * protect these member fields, and lock should released before calling ALabel::update(). + */ +void Language::doUpdate() { + std::lock_guard lock(mutex_); + + if (layouts_.size() <= current_idx_) { + spdlog::error("niri language layout index out of bounds"); + label_.hide(); + return; + } + const auto &layout = layouts_[current_idx_]; + + spdlog::debug("niri language update with full name {}", layout.full_name); + spdlog::debug("niri language update with short name {}", layout.short_name); + spdlog::debug("niri language update with short description {}", layout.short_description); + spdlog::debug("niri language update with variant {}", layout.variant); + + std::string layoutName = std::string{}; + if (config_.isMember("format-" + layout.short_description + "-" + layout.variant)) { + const auto propName = "format-" + layout.short_description + "-" + layout.variant; + layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); + } else if (config_.isMember("format-" + layout.short_description)) { + const auto propName = "format-" + layout.short_description; + layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); + } else { + layoutName = trim(fmt::format(fmt::runtime(format_), fmt::arg("long", layout.full_name), + fmt::arg("short", layout.short_name), + fmt::arg("shortDescription", layout.short_description), + fmt::arg("variant", layout.variant))); + } + + spdlog::debug("niri language formatted layout name {}", layoutName); + + if (!format_.empty()) { + label_.show(); + label_.set_markup(layoutName); + } else { + label_.hide(); + } +} + +void Language::update() { + doUpdate(); + ALabel::update(); +} + +void Language::onEvent(const Json::Value& ev) { + if (ev["KeyboardLayoutsChanged"]) { + updateFromIPC(); + } else if (ev["KeyboardLayoutSwitched"]) { + std::lock_guard lock(mutex_); + auto ipcLock = gIPC->lockData(); + current_idx_ = gIPC->keyboardLayoutCurrent(); + } + + dp.emit(); +} + +Language::Layout Language::getLayout(const std::string &fullName) { + auto* const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES); + rxkb_context_parse_default_ruleset(context); + + rxkb_layout* layout = rxkb_layout_first(context); + while (layout != nullptr) { + std::string nameOfLayout = rxkb_layout_get_description(layout); + + if (nameOfLayout != fullName) { + layout = rxkb_layout_next(layout); + continue; + } + + auto name = std::string(rxkb_layout_get_name(layout)); + const auto* variantPtr = rxkb_layout_get_variant(layout); + std::string variant = variantPtr == nullptr ? "" : std::string(variantPtr); + + const auto* descriptionPtr = rxkb_layout_get_brief(layout); + std::string description = descriptionPtr == nullptr ? "" : std::string(descriptionPtr); + + Layout info = Layout{nameOfLayout, name, variant, description}; + + rxkb_context_unref(context); + + return info; + } + + rxkb_context_unref(context); + + spdlog::debug("niri language didn't find matching layout for {}", fullName); + + return Layout{"", "", "", ""}; +} + +} // namespace waybar::modules::niri diff --git a/src/modules/niri/window.cpp b/src/modules/niri/window.cpp new file mode 100644 index 000000000..b2405435c --- /dev/null +++ b/src/modules/niri/window.cpp @@ -0,0 +1,121 @@ +#include "modules/niri/window.hpp" + +#include +#include +#include + +#include "util/rewrite_string.hpp" +#include "util/sanitize_str.hpp" + +namespace waybar::modules::niri { + +Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) + : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { + if (!gIPC) + gIPC = std::make_unique(); + + gIPC->registerForIPC("WindowsChanged", this); + gIPC->registerForIPC("WindowOpenedOrChanged", this); + gIPC->registerForIPC("WindowClosed", this); + gIPC->registerForIPC("WindowFocusChanged", this); + + dp.emit(); +} + +Window::~Window() { + gIPC->unregisterForIPC(this); +} + +void Window::onEvent(const Json::Value &ev) { + dp.emit(); +} + +void Window::doUpdate() { + auto ipcLock = gIPC->lockData(); + + const auto &windows = gIPC->windows(); + const auto &workspaces = gIPC->workspaces(); + + const auto separateOutputs = config_["separate-outputs"].asBool(); + const auto ws_it = std::find_if(workspaces.cbegin(), workspaces.cend(), + [&](const auto &ws) { + if (separateOutputs) { + return ws["is_active"].asBool() && ws["output"].asString() == bar_.output->name; + } + + return ws["is_focused"].asBool(); + }); + + std::vector::const_iterator it; + if (ws_it == workspaces.cend() || (*ws_it)["active_window_id"].isNull()) { + it = windows.cend(); + } else { + const auto id = (*ws_it)["active_window_id"].asUInt64(); + it = std::find_if(windows.cbegin(), windows.cend(), + [id](const auto &win) { return win["id"].asUInt64() == id; }); + } + + setClass("empty", ws_it == workspaces.cend() || (*ws_it)["active_window_id"].isNull()); + + if (it != windows.cend()) { + const auto &window = *it; + + const auto title = window["title"].asString(); + const auto appId = window["app_id"].asString(); + const auto sanitizedTitle = waybar::util::sanitize_string(title); + const auto sanitizedAppId = waybar::util::sanitize_string(appId); + + label_.show(); + label_.set_markup(waybar::util::rewriteString( + fmt::format(fmt::runtime(format_), + fmt::arg("title", sanitizedTitle), + fmt::arg("app_id", sanitizedAppId)), + config_["rewrite"])); + + updateAppIconName(appId, ""); + + if (tooltipEnabled()) + label_.set_tooltip_text(title); + + const auto id = window["id"].asUInt64(); + const auto workspaceId = window["workspace_id"].asUInt64(); + const auto isSolo = std::none_of(windows.cbegin(), windows.cend(), + [&](const auto &win) { + return win["id"].asUInt64() != id && win["workspace_id"].asUInt64() == workspaceId; + }); + setClass("solo", isSolo); + if (!appId.empty()) + setClass(appId, isSolo); + + if (oldAppId_ != appId) { + if (!oldAppId_.empty()) + setClass(oldAppId_, false); + oldAppId_ = appId; + } + } else { + label_.hide(); + updateAppIconName("", ""); + setClass("solo", false); + if (!oldAppId_.empty()) + setClass(oldAppId_, false); + oldAppId_.clear(); + } +} + +void Window::update() { + doUpdate(); + AAppIconLabel::update(); +} + +void Window::setClass(const std::string &className, bool enable) { + auto styleContext = bar_.window.get_style_context(); + if (enable) { + if (!styleContext->has_class(className)) { + styleContext->add_class(className); + } + } else { + styleContext->remove_class(className); + } +} + +} // namespace waybar::modules::niri diff --git a/src/modules/niri/workspaces.cpp b/src/modules/niri/workspaces.cpp new file mode 100644 index 000000000..2ecfa1ba3 --- /dev/null +++ b/src/modules/niri/workspaces.cpp @@ -0,0 +1,204 @@ +#include "modules/niri/workspaces.hpp" + +#include +#include +#include + +namespace waybar::modules::niri { + +Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) + : AModule(config, "workspaces", id, false, false), + bar_(bar), + box_(bar.orientation, 0) { + box_.set_name("workspaces"); + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + box_.get_style_context()->add_class(MODULE_CLASS); + event_box_.add(box_); + + if (!gIPC) + gIPC = std::make_unique(); + + gIPC->registerForIPC("WorkspacesChanged", this); + gIPC->registerForIPC("WorkspaceActivated", this); + gIPC->registerForIPC("WorkspaceActiveWindowChanged", this); + + dp.emit(); +} + +Workspaces::~Workspaces() { + gIPC->unregisterForIPC(this); +} + +void Workspaces::onEvent(const Json::Value &ev) { + dp.emit(); +} + +void Workspaces::doUpdate() { + auto ipcLock = gIPC->lockData(); + + const auto alloutputs = config_["all-outputs"].asBool(); + std::vector my_workspaces; + const auto &workspaces = gIPC->workspaces(); + std::copy_if(workspaces.cbegin(), workspaces.cend(), std::back_inserter(my_workspaces), + [&](const auto &ws) { + if (alloutputs) + return true; + return ws["output"].asString() == bar_.output->name; + }); + + // Remove buttons for removed workspaces. + for (auto it = buttons_.begin(); it != buttons_.end(); ) { + auto ws = std::find_if(my_workspaces.begin(), my_workspaces.end(), + [it](const auto &ws) { return ws["id"].asUInt64() == it->first; }); + if (ws == my_workspaces.end()) { + it = buttons_.erase(it); + } else { + ++it; + } + } + + // Add buttons for new workspaces, update existing ones. + for (const auto &ws : my_workspaces) { + auto bit = buttons_.find(ws["id"].asUInt64()); + auto &button = bit == buttons_.end() ? addButton(ws) : bit->second; + auto style_context = button.get_style_context(); + + if (ws["is_focused"].asBool()) + style_context->add_class("focused"); + else + style_context->remove_class("focused"); + + if (ws["is_active"].asBool()) + style_context->add_class("active"); + else + style_context->remove_class("active"); + + if (ws["output"]) { + if (ws["output"].asString() == bar_.output->name) + style_context->add_class("current_output"); + else + style_context->remove_class("current_output"); + } else { + style_context->remove_class("current_output"); + } + + if (ws["active_window_id"].isNull()) + style_context->add_class("empty"); + else + style_context->remove_class("empty"); + + std::string name; + if (ws["name"]) { + name = ws["name"].asString(); + } else { + name = std::to_string(ws["idx"].asUInt()); + } + button.set_name("niri-workspace-" + name); + + if (config_["format"].isString()) { + auto format = config_["format"].asString(); + name = fmt::format( + fmt::runtime(format), + fmt::arg("icon", getIcon(name, ws)), + fmt::arg("value", name), + fmt::arg("name", ws["name"].asString()), + fmt::arg("index", ws["idx"].asUInt()), + fmt::arg("output", ws["output"].asString())); + } + if (!config_["disable-markup"].asBool()) { + static_cast(button.get_children()[0])->set_markup(name); + } else { + button.set_label(name); + } + + if (config_["current-only"].asBool()) { + const auto *property = alloutputs ? "is_focused" : "is_active"; + if (ws[property].asBool()) + button.show(); + else + button.hide(); + } else { + button.show(); + } + } + + // Refresh the button order. + for (auto it = my_workspaces.cbegin(); it != my_workspaces.cend(); ++it) { + const auto &ws = *it; + + auto pos = ws["idx"].asUInt() - 1; + if (alloutputs) + pos = it - my_workspaces.cbegin(); + + auto &button = buttons_[ws["id"].asUInt64()]; + box_.reorder_child(button, pos); + } +} + +void Workspaces::update() { + doUpdate(); + AModule::update(); +} + +Gtk::Button &Workspaces::addButton(const Json::Value &ws) { + std::string name; + if (ws["name"]) { + name = ws["name"].asString(); + } else { + name = std::to_string(ws["idx"].asUInt()); + } + + auto pair = buttons_.emplace(ws["id"].asUInt64(), name); + auto &&button = pair.first->second; + box_.pack_start(button, false, false, 0); + button.set_relief(Gtk::RELIEF_NONE); + if (!config_["disable-click"].asBool()) { + const auto id = ws["id"].asUInt64(); + button.signal_pressed().connect([=] { + try { + // {"Action":{"FocusWorkspace":{"reference":{"Id":1}}}} + Json::Value request(Json::objectValue); + auto &action = (request["Action"] = Json::Value(Json::objectValue)); + auto &focusWorkspace = (action["FocusWorkspace"] = Json::Value(Json::objectValue)); + auto &reference = (focusWorkspace["reference"] = Json::Value(Json::objectValue)); + reference["Id"] = id; + + IPC::send(request); + } catch (const std::exception &e) { + spdlog::error("Error switching workspace: {}", e.what()); + } + }); + } + return button; +} + +std::string Workspaces::getIcon(const std::string &value, const Json::Value &ws) { + const auto &icons = config_["format-icons"]; + if (!icons) + return value; + + if (ws["is_focused"].asBool() && icons["focused"]) + return icons["focused"].asString(); + + if (ws["is_active"].asBool() && icons["active"]) + return icons["active"].asString(); + + if (ws["name"]) { + const auto &name = ws["name"].asString(); + if (icons[name]) + return icons[name].asString(); + } + + const auto idx = ws["idx"].asString(); + if (icons[idx]) + return icons[idx].asString(); + + if (icons["default"]) + return icons["default"].asString(); + + return value; +} + +} // namespace waybar::modules::niri From fef0bb995c0300f796856460173ce731c075a0ab Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Fri, 13 Sep 2024 10:33:08 +0300 Subject: [PATCH 365/407] niri: Replace gnu extension with GDataInputStream --- src/modules/niri/backend.cpp | 43 ++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/modules/niri/backend.cpp b/src/modules/niri/backend.cpp index ef23c8817..6e251d31f 100644 --- a/src/modules/niri/backend.cpp +++ b/src/modules/niri/backend.cpp @@ -8,8 +8,11 @@ #include #include #include +#include "giomm/datainputstream.h" +#include "giomm/dataoutputstream.h" +#include "giomm/unixinputstream.h" +#include "giomm/unixoutputstream.h" -#include #include #include #include @@ -63,18 +66,23 @@ void IPC::startIPC() { spdlog::info("Niri IPC starting"); - __gnu_cxx::stdio_filebuf filebuf(socketfd, std::ios::in | std::ios::out); - std::iostream fs(&filebuf); - fs << R"("EventStream")" << std::endl; + auto unix_istream = Gio::UnixInputStream::create(socketfd, true); + auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false); + auto istream = Gio::DataInputStream::create(unix_istream); + auto ostream = Gio::DataOutputStream::create(unix_ostream); + + if (!ostream->put_string("\"EventStream\"\n") || !ostream->flush()) { + spdlog::error("Niri IPC: failed to start event stream"); + return; + } std::string line; - std::getline(fs, line); - if (line != R"({"Ok":"Handled"})") { + if (!istream->read_line(line) || line != R"({"Ok":"Handled"})") { spdlog::error("Niri IPC: failed to start event stream"); return; } - while (std::getline(fs, line)) { + while (istream->read_line(line)) { spdlog::debug("Niri IPC: received {}", line); try { @@ -231,18 +239,29 @@ Json::Value IPC::send(const Json::Value& request) { if (socketfd == -1) throw std::runtime_error("Niri is not running"); - __gnu_cxx::stdio_filebuf filebuf(socketfd, std::ios::in | std::ios::out); - std::iostream fs(&filebuf); + auto unix_istream = Gio::UnixInputStream::create(socketfd, true); + auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false); + auto istream = Gio::DataInputStream::create(unix_istream); + auto ostream = Gio::DataOutputStream::create(unix_ostream); // Niri needs the request on a single line. Json::StreamWriterBuilder builder; builder["indentation"] = ""; std::unique_ptr writer(builder.newStreamWriter()); - writer->write(request, &fs); - fs << std::endl; + std::ostringstream oss; + writer->write(request, &oss); + oss << '\n'; + + if (!ostream->put_string(oss.str()) || !ostream->flush()) + throw std::runtime_error("error writing to niri socket"); + + std::string line; + if (!istream->read_line(line)) + throw std::runtime_error("error reading from niri socket"); + std::istringstream iss(std::move(line)); Json::Value response; - fs >> response; + iss >> response; return response; } From 34bfefcd2e6f6916a1b6b6b29c5836b5aeaf70b4 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Fri, 13 Sep 2024 10:33:50 +0300 Subject: [PATCH 366/407] niri: Gate behind a meson option --- meson.build | 2 +- meson_options.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 7097a9fbf..666c0dd05 100644 --- a/meson.build +++ b/meson.build @@ -318,7 +318,7 @@ if true ) endif -if true +if get_option('niri') add_project_arguments('-DHAVE_NIRI', language: 'cpp') src_files += files( 'src/modules/niri/backend.cpp', diff --git a/meson_options.txt b/meson_options.txt index fef508397..303ef038e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -19,3 +19,4 @@ option('experimental', type : 'boolean', value : false, description: 'Enable exp option('jack', type: 'feature', value: 'auto', description: 'Enable support for JACK') option('wireplumber', type: 'feature', value: 'auto', description: 'Enable support for WirePlumber') option('cava', type: 'feature', value: 'auto', description: 'Enable support for Cava') +option('niri', type: 'boolean', description: 'Enable support for niri') From a4d31ab10d1630cb9104c695d7b777ca12468904 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:40:35 +0200 Subject: [PATCH 367/407] fix: sni item fmt --- src/modules/image.cpp | 4 ++-- src/modules/sni/item.cpp | 23 ++++++++++++++--------- subprojects/gtk-layer-shell.wrap | 8 ++++---- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 5e6c1493f..71e93b946 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -57,8 +57,8 @@ auto waybar::modules::Image::update() -> void { int scaled_icon_size = size_ * image_.get_scale_factor(); pixbuf = Gdk::Pixbuf::create_from_file(path_, scaled_icon_size, scaled_icon_size); - auto surface = - Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), image_.get_window()); + auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), + image_.get_window()); image_.set(surface); image_.show(); diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 6c4ec8c06..8afb39fb3 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -104,9 +104,11 @@ void Item::proxyReady(Glib::RefPtr& result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, + std::string(err.what())); } catch (const std::exception& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, + std::string(err.what())); } } @@ -124,14 +126,15 @@ ToolTip get_variant(const Glib::VariantBase& value) { 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); + result.text = fmt::format("{}\n{}", std::string(result.text), std::string(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); + spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, + std::string(name), get_variant(value)); if (name == "Category") { category = get_variant(value); @@ -176,10 +179,12 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } } catch (const Glib::Error& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, name, value, err.what()); + id.empty() ? bus_name : id, std::string(name), get_variant(value), + std::string(err.what())); } catch (const std::exception& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, name, value, err.what()); + id.empty() ? bus_name : id, std::string(name), get_variant(value), + std::string(err.what())); } } @@ -221,9 +226,9 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::warn("Failed to update properties: {}", err.what()); + spdlog::warn("Failed to update properties: {}", std::string(err.what())); } catch (const std::exception& err) { - spdlog::warn("Failed to update properties: {}", err.what()); + spdlog::warn("Failed to update properties: {}", std::string(err.what())); } update_pending_.clear(); } @@ -245,7 +250,7 @@ static const std::map> signal2props 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); + spdlog::trace("Tray item '{}' got signal {}", id, std::string(signal_name)); auto changed = signal2props.find(signal_name.raw()); if (changed != signal2props.end()) { if (update_pending_.empty()) { diff --git a/subprojects/gtk-layer-shell.wrap b/subprojects/gtk-layer-shell.wrap index cb7303455..fc0ddf743 100644 --- a/subprojects/gtk-layer-shell.wrap +++ b/subprojects/gtk-layer-shell.wrap @@ -1,5 +1,5 @@ [wrap-file] -directory = gtk-layer-shell-0.8.2 -source_filename = gtk-layer-shell-0.8.2.tar.gz -source_hash = 254dd246303127c5d5236ea640f01a82e35d2d652a48d139dd669c832a0f0dce -source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.8.2/gtk-layer-shell-0.8.2.tar.gz +directory = gtk-layer-shell-0.9.0 +source_filename = gtk-layer-shell-0.9.0.tar.gz +source_hash = 3809e5565d9ed02e44bb73787ff218523e8760fef65830afe60ea7322e22da1c +source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.9.0/gtk-layer-shell-0.9.0.tar.gz From 1142979581de1b6487e0ca5c65d887f4fb71eba0 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:43:19 +0200 Subject: [PATCH 368/407] chore: lint --- include/modules/niri/backend.hpp | 6 +- include/modules/niri/language.hpp | 2 +- src/modules/niri/backend.cpp | 113 ++++++++++++++---------------- src/modules/niri/language.cpp | 16 ++--- src/modules/niri/window.cpp | 46 +++++------- src/modules/niri/workspaces.cpp | 52 +++++--------- 6 files changed, 98 insertions(+), 137 deletions(-) diff --git a/include/modules/niri/backend.hpp b/include/modules/niri/backend.hpp index 01af50174..42b9ff7f8 100644 --- a/include/modules/niri/backend.hpp +++ b/include/modules/niri/backend.hpp @@ -26,9 +26,9 @@ class IPC { // The data members are only safe to access while dataMutex_ is locked. std::lock_guard lockData() { return std::lock_guard(dataMutex_); } - const std::vector &workspaces() const { return workspaces_; } - const std::vector &windows() const { return windows_; } - const std::vector &keyboardLayoutNames() const { return keyboardLayoutNames_; } + const std::vector& workspaces() const { return workspaces_; } + const std::vector& windows() const { return windows_; } + const std::vector& keyboardLayoutNames() const { return keyboardLayoutNames_; } unsigned keyboardLayoutCurrent() const { return keyboardLayoutCurrent_; } private: diff --git a/include/modules/niri/language.hpp b/include/modules/niri/language.hpp index 1cecd206c..42b90ac48 100644 --- a/include/modules/niri/language.hpp +++ b/include/modules/niri/language.hpp @@ -10,7 +10,7 @@ namespace waybar::modules::niri { class Language : public ALabel, public EventHandler { public: - Language(const std::string&, const Bar&, const Json::Value&); + Language(const std::string &, const Bar &, const Json::Value &); ~Language() override; void update() override; diff --git a/src/modules/niri/backend.cpp b/src/modules/niri/backend.cpp index 6e251d31f..383bf1136 100644 --- a/src/modules/niri/backend.cpp +++ b/src/modules/niri/backend.cpp @@ -8,46 +8,47 @@ #include #include #include -#include "giomm/datainputstream.h" -#include "giomm/dataoutputstream.h" -#include "giomm/unixinputstream.h" -#include "giomm/unixoutputstream.h" #include #include #include +#include "giomm/datainputstream.h" +#include "giomm/dataoutputstream.h" +#include "giomm/unixinputstream.h" +#include "giomm/unixoutputstream.h" + namespace waybar::modules::niri { int IPC::connectToSocket() { - const char* socket_path = getenv("NIRI_SOCKET"); + const char *socket_path = getenv("NIRI_SOCKET"); - if (socket_path == nullptr) { - spdlog::warn("Niri is not running, niri IPC will not be available."); - return -1; - } + if (socket_path == nullptr) { + spdlog::warn("Niri is not running, niri IPC will not be available."); + return -1; + } - struct sockaddr_un addr; - int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); + struct sockaddr_un addr; + int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (socketfd == -1) { - throw std::runtime_error("socketfd failed"); - } + if (socketfd == -1) { + throw std::runtime_error("socketfd failed"); + } - addr.sun_family = AF_UNIX; + addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); - addr.sun_path[sizeof(addr.sun_path) - 1] = 0; + addr.sun_path[sizeof(addr.sun_path) - 1] = 0; - int l = sizeof(struct sockaddr_un); + int l = sizeof(struct sockaddr_un); - if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) { - close(socketfd); - throw std::runtime_error("unable to connect"); - } + if (connect(socketfd, (struct sockaddr *)&addr, l) == -1) { + close(socketfd); + throw std::runtime_error("unable to connect"); + } - return socketfd; + return socketfd; } void IPC::startIPC() { @@ -61,8 +62,7 @@ void IPC::startIPC() { spdlog::error("Niri IPC: failed to start, reason: {}", e.what()); return; } - if (socketfd == -1) - return; + if (socketfd == -1) return; spdlog::info("Niri IPC starting"); @@ -87,7 +87,7 @@ void IPC::startIPC() { try { parseIPC(line); - } catch (std::exception& e) { + } catch (std::exception &e) { spdlog::warn("Failed to parse IPC message: {}, reason: {}", line, e.what()); } catch (...) { throw; @@ -98,11 +98,10 @@ void IPC::startIPC() { }).detach(); } -void IPC::parseIPC(const std::string& line) { +void IPC::parseIPC(const std::string &line) { const auto ev = parser_.parse(line); const auto members = ev.getMemberNames(); - if (members.size() != 1) - throw std::runtime_error("Event must have a single member"); + if (members.size() != 1) throw std::runtime_error("Event must have a single member"); { auto lock = lockData(); @@ -112,17 +111,15 @@ void IPC::parseIPC(const std::string& line) { const auto &values = payload["workspaces"]; std::copy(values.begin(), values.end(), std::back_inserter(workspaces_)); - std::sort(workspaces_.begin(), workspaces_.end(), - [](const auto &a, const auto &b) { - const auto &aOutput = a["output"].asString(); - const auto &bOutput = b["output"].asString(); - const auto aIdx = a["idx"].asUInt(); - const auto bIdx = b["idx"].asUInt(); - if (aOutput == bOutput) - return aIdx < bIdx; - return aOutput < bOutput; - }); - } else if (const auto& payload = ev["WorkspaceActivated"]) { + std::sort(workspaces_.begin(), workspaces_.end(), [](const auto &a, const auto &b) { + const auto &aOutput = a["output"].asString(); + const auto &bOutput = b["output"].asString(); + const auto aIdx = a["idx"].asUInt(); + const auto bIdx = b["idx"].asUInt(); + if (aOutput == bOutput) return aIdx < bIdx; + return aOutput < bOutput; + }); + } else if (const auto &payload = ev["WorkspaceActivated"]) { const auto id = payload["id"].asUInt64(); const auto focused = payload["focused"].asBool(); auto it = std::find_if(workspaces_.begin(), workspaces_.end(), @@ -132,19 +129,18 @@ void IPC::parseIPC(const std::string& line) { const auto &output = ws["output"].asString(); for (auto &ws : workspaces_) { const auto got_activated = (ws["id"].asUInt64() == id); - if (ws["output"] == output) - ws["is_active"] = got_activated; + if (ws["output"] == output) ws["is_active"] = got_activated; - if (focused) - ws["is_focused"] = got_activated; + if (focused) ws["is_focused"] = got_activated; } } else { spdlog::error("Activated unknown workspace"); } - } else if (const auto& payload = ev["WorkspaceActiveWindowChanged"]) { + } else if (const auto &payload = ev["WorkspaceActiveWindowChanged"]) { const auto workspaceId = payload["workspace_id"].asUInt64(); - auto it = std::find_if(workspaces_.begin(), workspaces_.end(), - [workspaceId](const auto &ws) { return ws["id"].asUInt64() == workspaceId; }); + auto it = std::find_if(workspaces_.begin(), workspaces_.end(), [workspaceId](const auto &ws) { + return ws["id"].asUInt64() == workspaceId; + }); if (it != workspaces_.end()) { auto &ws = *it; ws["active_window_id"] = payload["active_window_id"]; @@ -157,9 +153,8 @@ void IPC::parseIPC(const std::string& line) { keyboardLayoutCurrent_ = layouts["current_idx"].asUInt(); keyboardLayoutNames_.clear(); - for (const auto &fullName : names) - keyboardLayoutNames_.push_back(fullName.asString()); - } else if (const auto& payload = ev["KeyboardLayoutSwitched"]) { + for (const auto &fullName : names) keyboardLayoutNames_.push_back(fullName.asString()); + } else if (const auto &payload = ev["KeyboardLayoutSwitched"]) { keyboardLayoutCurrent_ = payload["idx"].asUInt(); } else if (const auto &payload = ev["WindowsChanged"]) { windows_.clear(); @@ -201,14 +196,14 @@ void IPC::parseIPC(const std::string& line) { std::unique_lock lock(callbackMutex_); - for (auto& [eventname, handler] : callbacks_) { + for (auto &[eventname, handler] : callbacks_) { if (eventname == members[0]) { handler->onEvent(ev); } } } -void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) { +void IPC::registerForIPC(const std::string &ev, EventHandler *ev_handler) { if (ev_handler == nullptr) { return; } @@ -217,7 +212,7 @@ void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) { callbacks_.emplace_back(ev, ev_handler); } -void IPC::unregisterForIPC(EventHandler* ev_handler) { +void IPC::unregisterForIPC(EventHandler *ev_handler) { if (ev_handler == nullptr) { return; } @@ -225,7 +220,7 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { std::unique_lock lock(callbackMutex_); for (auto it = callbacks_.begin(); it != callbacks_.end();) { - auto& [eventname, handler] = *it; + auto &[eventname, handler] = *it; if (handler == ev_handler) { it = callbacks_.erase(it); } else { @@ -234,10 +229,9 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { } } -Json::Value IPC::send(const Json::Value& request) { +Json::Value IPC::send(const Json::Value &request) { int socketfd = connectToSocket(); - if (socketfd == -1) - throw std::runtime_error("Niri is not running"); + if (socketfd == -1) throw std::runtime_error("Niri is not running"); auto unix_istream = Gio::UnixInputStream::create(socketfd, true); auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false); @@ -256,8 +250,7 @@ Json::Value IPC::send(const Json::Value& request) { throw std::runtime_error("error writing to niri socket"); std::string line; - if (!istream->read_line(line)) - throw std::runtime_error("error reading from niri socket"); + if (!istream->read_line(line)) throw std::runtime_error("error reading from niri socket"); std::istringstream iss(std::move(line)); Json::Value response; @@ -265,4 +258,4 @@ Json::Value IPC::send(const Json::Value& request) { return response; } -} // namespace waybar::modules::hyprland +} // namespace waybar::modules::niri diff --git a/src/modules/niri/language.cpp b/src/modules/niri/language.cpp index f124d4dd9..1e4d6d105 100644 --- a/src/modules/niri/language.cpp +++ b/src/modules/niri/language.cpp @@ -12,8 +12,7 @@ Language::Language(const std::string &id, const Bar &bar, const Json::Value &con : ALabel(config, "language", id, "{}", 0, true), bar_(bar) { label_.hide(); - if (!gIPC) - gIPC = std::make_unique(); + if (!gIPC) gIPC = std::make_unique(); gIPC->registerForIPC("KeyboardLayoutsChanged", this); gIPC->registerForIPC("KeyboardLayoutSwitched", this); @@ -33,8 +32,7 @@ void Language::updateFromIPC() { auto ipcLock = gIPC->lockData(); layouts_.clear(); - for (const auto &fullName : gIPC->keyboardLayoutNames()) - layouts_.push_back(getLayout(fullName)); + for (const auto &fullName : gIPC->keyboardLayoutNames()) layouts_.push_back(getLayout(fullName)); current_idx_ = gIPC->keyboardLayoutCurrent(); } @@ -89,7 +87,7 @@ void Language::update() { ALabel::update(); } -void Language::onEvent(const Json::Value& ev) { +void Language::onEvent(const Json::Value &ev) { if (ev["KeyboardLayoutsChanged"]) { updateFromIPC(); } else if (ev["KeyboardLayoutSwitched"]) { @@ -102,10 +100,10 @@ void Language::onEvent(const Json::Value& ev) { } Language::Layout Language::getLayout(const std::string &fullName) { - auto* const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES); + auto *const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES); rxkb_context_parse_default_ruleset(context); - rxkb_layout* layout = rxkb_layout_first(context); + rxkb_layout *layout = rxkb_layout_first(context); while (layout != nullptr) { std::string nameOfLayout = rxkb_layout_get_description(layout); @@ -115,10 +113,10 @@ Language::Layout Language::getLayout(const std::string &fullName) { } auto name = std::string(rxkb_layout_get_name(layout)); - const auto* variantPtr = rxkb_layout_get_variant(layout); + const auto *variantPtr = rxkb_layout_get_variant(layout); std::string variant = variantPtr == nullptr ? "" : std::string(variantPtr); - const auto* descriptionPtr = rxkb_layout_get_brief(layout); + const auto *descriptionPtr = rxkb_layout_get_brief(layout); std::string description = descriptionPtr == nullptr ? "" : std::string(descriptionPtr); Layout info = Layout{nameOfLayout, name, variant, description}; diff --git a/src/modules/niri/window.cpp b/src/modules/niri/window.cpp index b2405435c..6e6fd36ff 100644 --- a/src/modules/niri/window.cpp +++ b/src/modules/niri/window.cpp @@ -11,8 +11,7 @@ namespace waybar::modules::niri { Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { - if (!gIPC) - gIPC = std::make_unique(); + if (!gIPC) gIPC = std::make_unique(); gIPC->registerForIPC("WindowsChanged", this); gIPC->registerForIPC("WindowOpenedOrChanged", this); @@ -22,13 +21,9 @@ Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) dp.emit(); } -Window::~Window() { - gIPC->unregisterForIPC(this); -} +Window::~Window() { gIPC->unregisterForIPC(this); } -void Window::onEvent(const Json::Value &ev) { - dp.emit(); -} +void Window::onEvent(const Json::Value &ev) { dp.emit(); } void Window::doUpdate() { auto ipcLock = gIPC->lockData(); @@ -37,14 +32,13 @@ void Window::doUpdate() { const auto &workspaces = gIPC->workspaces(); const auto separateOutputs = config_["separate-outputs"].asBool(); - const auto ws_it = std::find_if(workspaces.cbegin(), workspaces.cend(), - [&](const auto &ws) { - if (separateOutputs) { - return ws["is_active"].asBool() && ws["output"].asString() == bar_.output->name; - } + const auto ws_it = std::find_if(workspaces.cbegin(), workspaces.cend(), [&](const auto &ws) { + if (separateOutputs) { + return ws["is_active"].asBool() && ws["output"].asString() == bar_.output->name; + } - return ws["is_focused"].asBool(); - }); + return ws["is_focused"].asBool(); + }); std::vector::const_iterator it; if (ws_it == workspaces.cend() || (*ws_it)["active_window_id"].isNull()) { @@ -67,37 +61,31 @@ void Window::doUpdate() { label_.show(); label_.set_markup(waybar::util::rewriteString( - fmt::format(fmt::runtime(format_), - fmt::arg("title", sanitizedTitle), + fmt::format(fmt::runtime(format_), fmt::arg("title", sanitizedTitle), fmt::arg("app_id", sanitizedAppId)), config_["rewrite"])); updateAppIconName(appId, ""); - if (tooltipEnabled()) - label_.set_tooltip_text(title); + if (tooltipEnabled()) label_.set_tooltip_text(title); const auto id = window["id"].asUInt64(); const auto workspaceId = window["workspace_id"].asUInt64(); - const auto isSolo = std::none_of(windows.cbegin(), windows.cend(), - [&](const auto &win) { - return win["id"].asUInt64() != id && win["workspace_id"].asUInt64() == workspaceId; - }); + const auto isSolo = std::none_of(windows.cbegin(), windows.cend(), [&](const auto &win) { + return win["id"].asUInt64() != id && win["workspace_id"].asUInt64() == workspaceId; + }); setClass("solo", isSolo); - if (!appId.empty()) - setClass(appId, isSolo); + if (!appId.empty()) setClass(appId, isSolo); if (oldAppId_ != appId) { - if (!oldAppId_.empty()) - setClass(oldAppId_, false); + if (!oldAppId_.empty()) setClass(oldAppId_, false); oldAppId_ = appId; } } else { label_.hide(); updateAppIconName("", ""); setClass("solo", false); - if (!oldAppId_.empty()) - setClass(oldAppId_, false); + if (!oldAppId_.empty()) setClass(oldAppId_, false); oldAppId_.clear(); } } diff --git a/src/modules/niri/workspaces.cpp b/src/modules/niri/workspaces.cpp index 2ecfa1ba3..d2fcad5d9 100644 --- a/src/modules/niri/workspaces.cpp +++ b/src/modules/niri/workspaces.cpp @@ -7,9 +7,7 @@ namespace waybar::modules::niri { Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) - : AModule(config, "workspaces", id, false, false), - bar_(bar), - box_(bar.orientation, 0) { + : AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.orientation, 0) { box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -17,8 +15,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); - if (!gIPC) - gIPC = std::make_unique(); + if (!gIPC) gIPC = std::make_unique(); gIPC->registerForIPC("WorkspacesChanged", this); gIPC->registerForIPC("WorkspaceActivated", this); @@ -27,13 +24,9 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value dp.emit(); } -Workspaces::~Workspaces() { - gIPC->unregisterForIPC(this); -} +Workspaces::~Workspaces() { gIPC->unregisterForIPC(this); } -void Workspaces::onEvent(const Json::Value &ev) { - dp.emit(); -} +void Workspaces::onEvent(const Json::Value &ev) { dp.emit(); } void Workspaces::doUpdate() { auto ipcLock = gIPC->lockData(); @@ -43,13 +36,12 @@ void Workspaces::doUpdate() { const auto &workspaces = gIPC->workspaces(); std::copy_if(workspaces.cbegin(), workspaces.cend(), std::back_inserter(my_workspaces), [&](const auto &ws) { - if (alloutputs) - return true; + if (alloutputs) return true; return ws["output"].asString() == bar_.output->name; }); // Remove buttons for removed workspaces. - for (auto it = buttons_.begin(); it != buttons_.end(); ) { + for (auto it = buttons_.begin(); it != buttons_.end();) { auto ws = std::find_if(my_workspaces.begin(), my_workspaces.end(), [it](const auto &ws) { return ws["id"].asUInt64() == it->first; }); if (ws == my_workspaces.end()) { @@ -99,13 +91,10 @@ void Workspaces::doUpdate() { if (config_["format"].isString()) { auto format = config_["format"].asString(); - name = fmt::format( - fmt::runtime(format), - fmt::arg("icon", getIcon(name, ws)), - fmt::arg("value", name), - fmt::arg("name", ws["name"].asString()), - fmt::arg("index", ws["idx"].asUInt()), - fmt::arg("output", ws["output"].asString())); + name = fmt::format(fmt::runtime(format), fmt::arg("icon", getIcon(name, ws)), + fmt::arg("value", name), fmt::arg("name", ws["name"].asString()), + fmt::arg("index", ws["idx"].asUInt()), + fmt::arg("output", ws["output"].asString())); } if (!config_["disable-markup"].asBool()) { static_cast(button.get_children()[0])->set_markup(name); @@ -129,8 +118,7 @@ void Workspaces::doUpdate() { const auto &ws = *it; auto pos = ws["idx"].asUInt() - 1; - if (alloutputs) - pos = it - my_workspaces.cbegin(); + if (alloutputs) pos = it - my_workspaces.cbegin(); auto &button = buttons_[ws["id"].asUInt64()]; box_.reorder_child(button, pos); @@ -176,27 +164,21 @@ Gtk::Button &Workspaces::addButton(const Json::Value &ws) { std::string Workspaces::getIcon(const std::string &value, const Json::Value &ws) { const auto &icons = config_["format-icons"]; - if (!icons) - return value; + if (!icons) return value; - if (ws["is_focused"].asBool() && icons["focused"]) - return icons["focused"].asString(); + if (ws["is_focused"].asBool() && icons["focused"]) return icons["focused"].asString(); - if (ws["is_active"].asBool() && icons["active"]) - return icons["active"].asString(); + if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString(); if (ws["name"]) { const auto &name = ws["name"].asString(); - if (icons[name]) - return icons[name].asString(); + if (icons[name]) return icons[name].asString(); } const auto idx = ws["idx"].asString(); - if (icons[idx]) - return icons[idx].asString(); + if (icons[idx]) return icons[idx].asString(); - if (icons["default"]) - return icons["default"].asString(); + if (icons["default"]) return icons["default"].asString(); return value; } From d56dd6ee7fdf8c5ba4e90790af62b7f7829d3a47 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:51:11 +0200 Subject: [PATCH 369/407] chore: v0.11.0 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 666c0dd05..42f9da920 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.10.4', + version: '0.11.0', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From d177969f51b3435308a520c9c0385ae80579b255 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:53:15 +0200 Subject: [PATCH 370/407] chore: lint --- src/ALabel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 3cb2c590c..467572f18 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -45,7 +45,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st if (config_["rotate"].isUInt()) { rotate = config["rotate"].asUInt(); - if (not (rotate == 0 || rotate == 90 || rotate == 180 || rotate == 270)) + if (not(rotate == 0 || rotate == 90 || rotate == 180 || rotate == 270)) spdlog::warn("'rotate' is only supported in 90 degree increments {} is not valid.", rotate); label_.set_angle(rotate); } From 4a6af0da99306fdf155c5739cea9e7b9e54b6a34 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Aug 2024 15:30:24 -0700 Subject: [PATCH 371/407] fix(bar): use overlay layer for `hide` and `overlay` modes This fixes a major inconsistency with the swaybar implementation of these modes[^1]. `overlay` layer no longer has security implications due to a wide adoption of `ext-session-lock`, so it's safe to use. Following config will restore the previous behavior: ```json "modes": { "hide": { "layer": "top" }, "overlay": { "layer": "top" } }, ``` [^1]: https://github.com/swaywm/sway/commit/2f7247e08a16610228067c9ec34d2b6d897e15fa --- src/bar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index 8a245ad16..8bb214c0a 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -37,7 +37,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // .visible = true}}, {"hide", {// - .layer = bar_layer::TOP, + .layer = bar_layer::OVERLAY, .exclusive = false, .passthrough = false, .visible = true}}, @@ -49,7 +49,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // .visible = false}}, {"overlay", {// - .layer = bar_layer::TOP, + .layer = bar_layer::OVERLAY, .exclusive = false, .passthrough = true, .visible = true}}}; From f60c291b82181b6e871b4807625fbcc7818d3c36 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 14 Sep 2024 07:36:23 -0700 Subject: [PATCH 372/407] chore: update fmt wrap to 11.0.2 --- subprojects/fmt.wrap | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap index 42b615963..fd508477f 100644 --- a/subprojects/fmt.wrap +++ b/subprojects/fmt.wrap @@ -1,13 +1,13 @@ [wrap-file] -directory = fmt-11.0.1 -source_url = https://github.com/fmtlib/fmt/archive/11.0.1.tar.gz -source_filename = fmt-11.0.1.tar.gz -source_hash = 7d009f7f89ac84c0a83f79ed602463d092fbf66763766a907c97fd02b100f5e9 -patch_filename = fmt_11.0.1-1_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/fmt_11.0.1-1/get_patch -patch_hash = 0a8b93d1ee6d84a82d3872a9bfb4c3977d8a53f7f484d42d1f7ed63ed496d549 -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_11.0.1-1/fmt-11.0.1.tar.gz -wrapdb_version = 11.0.1-1 +directory = fmt-11.0.2 +source_url = https://github.com/fmtlib/fmt/archive/11.0.2.tar.gz +source_filename = fmt-11.0.2.tar.gz +source_hash = 6cb1e6d37bdcb756dbbe59be438790db409cdb4868c66e888d5df9f13f7c027f +patch_filename = fmt_11.0.2-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/fmt_11.0.2-1/get_patch +patch_hash = 90c9e3b8e8f29713d40ca949f6f93ad115d78d7fb921064112bc6179e6427c5e +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_11.0.2-1/fmt-11.0.2.tar.gz +wrapdb_version = 11.0.2-1 [provide] fmt = fmt_dep From 0006e4713ae19776528038b3242ded05db884ba5 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 14 Sep 2024 07:37:37 -0700 Subject: [PATCH 373/407] fix(tray): revert ustring formatting changes This reverts commit a4d31ab10d1630cb9104c695d7b777ca12468904. --- src/modules/sni/item.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 8afb39fb3..6c4ec8c06 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -104,11 +104,9 @@ void Item::proxyReady(Glib::RefPtr& result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, - std::string(err.what())); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); } catch (const std::exception& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, - std::string(err.what())); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); } } @@ -126,15 +124,14 @@ ToolTip get_variant(const Glib::VariantBase& value) { result.text = get_variant(container.get_child(2)); auto description = get_variant(container.get_child(3)); if (!description.empty()) { - result.text = fmt::format("{}\n{}", std::string(result.text), std::string(description)); + 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, - std::string(name), get_variant(value)); + spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value); if (name == "Category") { category = get_variant(value); @@ -179,12 +176,10 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } } catch (const Glib::Error& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, std::string(name), get_variant(value), - std::string(err.what())); + id.empty() ? bus_name : id, name, value, err.what()); } catch (const std::exception& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, std::string(name), get_variant(value), - std::string(err.what())); + id.empty() ? bus_name : id, name, value, err.what()); } } @@ -226,9 +221,9 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::warn("Failed to update properties: {}", std::string(err.what())); + spdlog::warn("Failed to update properties: {}", err.what()); } catch (const std::exception& err) { - spdlog::warn("Failed to update properties: {}", std::string(err.what())); + spdlog::warn("Failed to update properties: {}", err.what()); } update_pending_.clear(); } @@ -250,7 +245,7 @@ static const std::map> signal2props void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, const Glib::VariantContainerBase& arguments) { - spdlog::trace("Tray item '{}' got signal {}", id, std::string(signal_name)); + spdlog::trace("Tray item '{}' got signal {}", id, signal_name); auto changed = signal2props.find(signal_name.raw()); if (changed != signal2props.end()) { if (update_pending_.empty()) { From 7b23d586846e9cdb10f203f25a472bf945f4ed68 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Aug 2024 21:55:28 -0700 Subject: [PATCH 374/407] fix(bar): force commit for occluded surfaces All the mode or visibility changes require `wl_surface_commit` to be applied. gtk-layer-shell will attempt to force GTK to commit, but may fail if the surface has stopped receiving frame callbacks[^1]. Thus, we could get stuck in a state where the bar is hidden and unable to regain visibility. To address this, a new API has been added to gtk-layer-shell, `gtk_layer_try_force_commit`, which does `wl_surface_commit` with the necessary safety checks to avoid corrupting GTK internal state. Note: this change bumps gtk-layer-shell requirement to 0.9.0. [^1]: https://github.com/wmww/gtk-layer-shell/issues/185 --- meson.build | 2 +- src/bar.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 42f9da920..f4941c8fa 100644 --- a/meson.build +++ b/meson.build @@ -106,7 +106,7 @@ if libsndio.found() endif endif -gtk_layer_shell = dependency('gtk-layer-shell-0', version: ['>=0.6.0'], +gtk_layer_shell = dependency('gtk-layer-shell-0', version: ['>=0.9.0'], default_options: ['introspection=false', 'vapi=false'], fallback: ['gtk-layer-shell', 'gtk_layer_shell']) systemd = dependency('systemd', required: get_option('systemd')) diff --git a/src/bar.cpp b/src/bar.cpp index 8bb214c0a..ebe23367c 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -132,6 +132,7 @@ void from_json(const Json::Value& j, std::map& m) { waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) : output(w_output), config(w_config), + surface(nullptr), window{Gtk::WindowType::WINDOW_TOPLEVEL}, x_global(0), y_global(0), @@ -339,6 +340,13 @@ void waybar::Bar::setMode(const struct bar_mode& mode) { window.get_style_context()->add_class("hidden"); window.set_opacity(0); } + /* + * All the changes above require `wl_surface_commit`. + * gtk-layer-shell schedules a commit on the next frame event in GTK, but this could fail in + * certain scenarios, such as fully occluded bar. + */ + gtk_layer_try_force_commit(gtk_window); + wl_display_flush(Client::inst()->wl_display); } void waybar::Bar::setPassThrough(bool passthrough) { From 45fec7bcbbb475e6ab9afa32bc57b8409dc4ed86 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Aug 2024 22:40:56 -0700 Subject: [PATCH 375/407] Revert "change layer for mode invisible to nullopt" Previous commit should have a better workaround for #3211. This reverts commit b61ea62732a51e564ded6e7d4d37cd4796b014f2. --- include/bar.hpp | 2 +- src/bar.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 936bc749c..9b407abf4 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -42,7 +42,7 @@ struct bar_margins { }; struct bar_mode { - std::optional layer; + bar_layer layer; bool exclusive; bool passthrough; bool visible; diff --git a/src/bar.cpp b/src/bar.cpp index ebe23367c..5068e90d0 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -43,7 +43,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // .visible = true}}, {"invisible", {// - .layer = std::nullopt, + .layer = bar_layer::BOTTOM, .exclusive = false, .passthrough = true, .visible = false}}, @@ -59,7 +59,7 @@ const std::string Bar::MODE_INVISIBLE = "invisible"; const std::string_view DEFAULT_BAR_ID = "bar-0"; /* Deserializer for enum bar_layer */ -void from_json(const Json::Value& j, std::optional& l) { +void from_json(const Json::Value& j, bar_layer& l) { if (j == "bottom") { l = bar_layer::BOTTOM; } else if (j == "top") { @@ -317,13 +317,13 @@ void waybar::Bar::setMode(const std::string& mode) { void waybar::Bar::setMode(const struct bar_mode& mode) { auto* gtk_window = window.gobj(); - if (mode.layer == bar_layer::BOTTOM) { - gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_BOTTOM); - } else if (mode.layer == bar_layer::TOP) { - gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_TOP); + auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM; + if (mode.layer == bar_layer::TOP) { + layer = GTK_LAYER_SHELL_LAYER_TOP; } else if (mode.layer == bar_layer::OVERLAY) { - gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_OVERLAY); + layer = GTK_LAYER_SHELL_LAYER_OVERLAY; } + gtk_layer_set_layer(gtk_window, layer); if (mode.exclusive) { gtk_layer_auto_exclusive_zone_enable(gtk_window); From e0be3ac178300982f1507218026fe450f898abf6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 15 Sep 2024 05:59:33 +0000 Subject: [PATCH 376/407] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/71e91c409d1e654808b2621f28a327acfdad8dc2?narHash=sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w%3D' (2024-08-28) → 'github:NixOS/nixpkgs/4f807e8940284ad7925ebd0a0993d2a1791acb2f?narHash=sha256-IiA3jfbR7K/B5%2B9byVi9BZGWTD4VSbWe8VLpp9B/iYk%3D' (2024-09-11) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 9bd73acca..15bf9f093 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1724819573, - "narHash": "sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w=", + "lastModified": 1726062873, + "narHash": "sha256-IiA3jfbR7K/B5+9byVi9BZGWTD4VSbWe8VLpp9B/iYk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "71e91c409d1e654808b2621f28a327acfdad8dc2", + "rev": "4f807e8940284ad7925ebd0a0993d2a1791acb2f", "type": "github" }, "original": { From 085a1ede97bef27f08adb17788be901a8e29a60f Mon Sep 17 00:00:00 2001 From: Kainoa Kanter Date: Sun, 15 Sep 2024 21:28:15 -0300 Subject: [PATCH 377/407] fix: use app_identifier itself in AAppIconLabel if it's an absolute path --- src/AAppIconLabel.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index fda5f9fd1..dc22f20e0 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -154,6 +154,16 @@ void AAppIconLabel::updateAppIcon() { update_app_icon_ = false; if (app_icon_name_.empty()) { image_.set_visible(false); + } + else if (app_icon_name_.front() == '/') { + auto pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_); + int scaled_icon_size = app_icon_size_ * image_.get_scale_factor(); + pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size); + + auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), + image_.get_window()); + image_.set(surface); + image_.set_visible(true); } else { image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); image_.set_visible(true); From ff66b5dd57408158f2eea84eba64269d90b92c5b Mon Sep 17 00:00:00 2001 From: Sonter <108224581+S0nter@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:47:10 +0000 Subject: [PATCH 378/407] Update waybar-cava man page --- man/waybar-cava.5.scd | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/man/waybar-cava.5.scd b/man/waybar-cava.5.scd index 2a7e8f678..7825c38a2 100644 --- a/man/waybar-cava.5.scd +++ b/man/waybar-cava.5.scd @@ -64,6 +64,10 @@ libcava lives in: :[ bool :[ false :[ Hides the widget if no input (after sleep_timer elapsed) +|[ *format_silent* +:[ string +:[ +:[ Widget's text after sleep_timer elapsed (hide_on_silence has to be false) |[ *method* :[ string :[ pulse @@ -196,3 +200,8 @@ In case when cava releases new version and you're wanna get it, it should be rai } }, ``` +# STYLE + +- *#cava* +- *#cava.silent* Applied after no sound has been detected for sleep_timer seconds +- *#cava.updated* Applied when a new frame is shown From 3bb3c2d23fdecebdaa2da80cbb17fcc3beec32da Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Tue, 17 Sep 2024 00:13:23 +0200 Subject: [PATCH 379/407] fix(custom): stop mixing manual and automatic arg indexing The current documentation for the custom module suggests mixing manual (`{icon}`) and automatic (`{}`) indexing of format args. Newer versions of the fmt library seem to not support this anymore (see issue #3605). This commit introduces a name for the `text` output of the script, so that `{text}` can now be used instead of `{}` in the configuration. --- src/modules/custom.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 20d8d9348..bc5df76fa 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -159,7 +159,9 @@ auto waybar::modules::Custom::update() -> void { parseOutputRaw(); } - auto str = fmt::format(fmt::runtime(format_), text_, fmt::arg("alt", alt_), + auto str = fmt::format(fmt::runtime(format_), + fmt::arg("text", text_), + fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { @@ -169,7 +171,9 @@ auto waybar::modules::Custom::update() -> void { if (tooltipEnabled()) { if (tooltip_format_enabled_) { auto tooltip = config_["tooltip-format"].asString(); - tooltip = fmt::format(fmt::runtime(tooltip), text_, fmt::arg("alt", alt_), + tooltip = fmt::format(fmt::runtime(tooltip), + fmt::arg("text", text_), + fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); label_.set_tooltip_markup(tooltip); From 83992d29a063361ed89e41f100d8ddbbd5456e27 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Tue, 17 Sep 2024 00:39:33 +0200 Subject: [PATCH 380/407] Fix formatting --- src/modules/custom.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index bc5df76fa..90c541282 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -159,9 +159,7 @@ auto waybar::modules::Custom::update() -> void { parseOutputRaw(); } - auto str = fmt::format(fmt::runtime(format_), - fmt::arg("text", text_), - fmt::arg("alt", alt_), + auto str = fmt::format(fmt::runtime(format_), fmt::arg("text", text_), fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { @@ -171,10 +169,8 @@ auto waybar::modules::Custom::update() -> void { if (tooltipEnabled()) { if (tooltip_format_enabled_) { auto tooltip = config_["tooltip-format"].asString(); - tooltip = fmt::format(fmt::runtime(tooltip), - fmt::arg("text", text_), - fmt::arg("alt", alt_), - fmt::arg("icon", getIcon(percentage_, alt_)), + tooltip = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_), + fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); label_.set_tooltip_markup(tooltip); } else if (text_ == tooltip_) { From de170fa57989a11867b179cce222a3a01b9bbaa8 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Tue, 17 Sep 2024 02:56:38 +0200 Subject: [PATCH 381/407] Update documentation --- man/waybar-custom.5.scd | 16 ++++++++-------- resources/config.jsonc | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index aba1c18f2..6b96d2a44 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -55,8 +55,8 @@ Addressed by *custom/* *format*: ++ typeof: string ++ - default: {} ++ - The format, how information should be displayed. On {} data gets inserted. + default: {text} ++ + The format, how information should be displayed. On {text} data gets inserted. *format-icons*: ++ typeof: array ++ @@ -160,7 +160,7 @@ $text\\n$tooltip\\n$class* # FORMAT REPLACEMENTS -*{}*: Output of the script. +*{text}*: Output of the script. *{percentage}* Percentage which can be set via a json return type. @@ -172,7 +172,7 @@ $text\\n$tooltip\\n$class* ``` "custom/spotify": { - "format": " {}", + "format": " {text}", "max-length": 40, "interval": 30, // Remove this if your script is endless and write in loop "exec": "$HOME/.config/waybar/mediaplayer.sh 2> /dev/null", // Script in resources folder @@ -185,7 +185,7 @@ $text\\n$tooltip\\n$class* ``` "custom/mpd": { - "format": "♪ {}", + "format": "♪ {text}", //"max-length": 15, "interval": 10, "exec": "mpc current", @@ -199,7 +199,7 @@ $text\\n$tooltip\\n$class* ``` "custom/cmus": { - "format": "♪ {}", + "format": "♪ {text}", //"max-length": 15, "interval": 10, "exec": "cmus-remote -C \"format_print '%a - %t'\"", // artist - title @@ -214,7 +214,7 @@ $text\\n$tooltip\\n$class* ``` "custom/pacman": { - "format": "{} ", + "format": "{text} ", "interval": "once", "exec": "pacman_packages", "on-click": "update-system", @@ -226,7 +226,7 @@ $text\\n$tooltip\\n$class* ``` "custom/pacman": { - "format": "{} ", + "format": "{text} ", "interval": 3600, // every hour "exec": "checkupdates | wc -l", // # of updates "exec-if": "exit 0", // always run; consider advanced run conditions diff --git a/resources/config.jsonc b/resources/config.jsonc index 7e0771f51..6ac1aa506 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -189,7 +189,7 @@ "on-click": "pavucontrol" }, "custom/media": { - "format": "{icon} {}", + "format": "{icon} {text}", "return-type": "json", "max-length": 40, "format-icons": { From 254111ff91c68816b35d426ab6977630e26a5a65 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Wed, 18 Sep 2024 17:28:58 +0200 Subject: [PATCH 382/407] Improve error message for mixed arg indexing in format string --- src/modules/custom.cpp | 76 +++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 90c541282..a42555212 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -159,43 +159,51 @@ auto waybar::modules::Custom::update() -> void { parseOutputRaw(); } - auto str = fmt::format(fmt::runtime(format_), fmt::arg("text", text_), fmt::arg("alt", alt_), - fmt::arg("icon", getIcon(percentage_, alt_)), - fmt::arg("percentage", percentage_)); - if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { - event_box_.hide(); - } else { - label_.set_markup(str); - if (tooltipEnabled()) { - if (tooltip_format_enabled_) { - auto tooltip = config_["tooltip-format"].asString(); - tooltip = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_), - fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), - fmt::arg("percentage", percentage_)); - label_.set_tooltip_markup(tooltip); - } else if (text_ == tooltip_) { - if (label_.get_tooltip_markup() != str) { - label_.set_tooltip_markup(str); - } - } else { - if (label_.get_tooltip_markup() != tooltip_) { - label_.set_tooltip_markup(tooltip_); + try { + auto str = fmt::format(fmt::runtime(format_), fmt::arg("text", text_), fmt::arg("alt", alt_), + fmt::arg("icon", getIcon(percentage_, alt_)), + fmt::arg("percentage", percentage_)); + if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { + event_box_.hide(); + } else { + label_.set_markup(str); + if (tooltipEnabled()) { + if (tooltip_format_enabled_) { + auto tooltip = config_["tooltip-format"].asString(); + tooltip = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_), + fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), + fmt::arg("percentage", percentage_)); + label_.set_tooltip_markup(tooltip); + } else if (text_ == tooltip_) { + if (label_.get_tooltip_markup() != str) { + label_.set_tooltip_markup(str); + } + } else { + if (label_.get_tooltip_markup() != tooltip_) { + label_.set_tooltip_markup(tooltip_); + } } } + auto style = label_.get_style_context(); + auto classes = style->list_classes(); + for (auto const& c : classes) { + if (c == id_) continue; + style->remove_class(c); + } + for (auto const& c : class_) { + style->add_class(c); + } + style->add_class("flat"); + style->add_class("text-button"); + style->add_class(MODULE_CLASS); + event_box_.show(); } - auto style = label_.get_style_context(); - auto classes = style->list_classes(); - for (auto const& c : classes) { - if (c == id_) continue; - style->remove_class(c); - } - for (auto const& c : class_) { - style->add_class(c); - } - style->add_class("flat"); - style->add_class("text-button"); - style->add_class(MODULE_CLASS); - event_box_.show(); + } catch (const fmt::format_error& e) { + if (std::strcmp(e.what(), "cannot switch from manual to automatic argument indexing") != 0) + throw; + + throw fmt::format_error("mixing manual and automatic argument indexing is no longer supported; " + "try replacing \"{}\" with \"{text}\" in your format specifier"); } } // Call parent update From a3e7031fe25421c1f64d4b88ce2298bbd6dd2324 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Wed, 18 Sep 2024 17:30:55 +0200 Subject: [PATCH 383/407] Fix formatting --- src/modules/custom.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index a42555212..e023aaf6e 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -170,9 +170,9 @@ auto waybar::modules::Custom::update() -> void { if (tooltipEnabled()) { if (tooltip_format_enabled_) { auto tooltip = config_["tooltip-format"].asString(); - tooltip = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_), - fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), - fmt::arg("percentage", percentage_)); + tooltip = fmt::format( + fmt::runtime(tooltip), fmt::arg("text", text_), fmt::arg("alt", alt_), + fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); label_.set_tooltip_markup(tooltip); } else if (text_ == tooltip_) { if (label_.get_tooltip_markup() != str) { @@ -202,8 +202,9 @@ auto waybar::modules::Custom::update() -> void { if (std::strcmp(e.what(), "cannot switch from manual to automatic argument indexing") != 0) throw; - throw fmt::format_error("mixing manual and automatic argument indexing is no longer supported; " - "try replacing \"{}\" with \"{text}\" in your format specifier"); + throw fmt::format_error( + "mixing manual and automatic argument indexing is no longer supported; " + "try replacing \"{}\" with \"{text}\" in your format specifier"); } } // Call parent update From 21af48fdc95b21ad067e627abb96655757c48b36 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 19 Sep 2024 17:31:07 +0200 Subject: [PATCH 384/407] chore: lint --- src/AAppIconLabel.cpp | 3 +-- src/modules/cava.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index dc22f20e0..3f47eff18 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -154,8 +154,7 @@ void AAppIconLabel::updateAppIcon() { update_app_icon_ = false; if (app_icon_name_.empty()) { image_.set_visible(false); - } - else if (app_icon_name_.front() == '/') { + } else if (app_icon_name_.front() == '/') { auto pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_); int scaled_icon_size = app_icon_size_ * image_.get_scale_factor(); pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size); diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index f81bf7991..f16d3f637 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -175,12 +175,14 @@ auto waybar::modules::Cava::update() -> void { ALabel::update(); label_.get_style_context()->add_class("updated"); } - + label_.get_style_context()->remove_class("silent"); } else { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); - if (hide_on_silence_) label_.hide(); - else if (config_["format_silent"].isString()) label_.set_markup(format_silent_); + if (hide_on_silence_) + label_.hide(); + else if (config_["format_silent"].isString()) + label_.set_markup(format_silent_); label_.get_style_context()->add_class("silent"); label_.get_style_context()->remove_class("updated"); From dedee8cd1456616e145d6ceb31788b28e4863918 Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Mon, 22 Jul 2024 12:17:21 +0200 Subject: [PATCH 385/407] pulseaudio: show correct sink volume on default output changes on sinkInfo callbacks, the default sink now has highest priority. That fixes an issue that the volume indicator is not updated when the changes the default output to another devices. added PA_SINK_IDLE as valid state. PA_SINK_RUNNING is only true if any sound output is happening on sink switch. Indicator should also update when no sound is being played. --- include/util/audio_backend.hpp | 2 ++ src/modules/pulseaudio.cpp | 7 +++++++ src/util/audio_backend.cpp | 26 ++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/util/audio_backend.hpp b/include/util/audio_backend.hpp index 2f53103e5..3737ae264 100644 --- a/include/util/audio_backend.hpp +++ b/include/util/audio_backend.hpp @@ -38,6 +38,8 @@ class AudioBackend { std::string desc_; std::string monitor_; std::string current_sink_name_; + std::string default_sink_name; + bool default_sink_running_; bool current_sink_running_; // SOURCE uint32_t source_idx_{0}; diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 255ca571f..9d3f1862a 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -1,4 +1,6 @@ #include "modules/pulseaudio.hpp" +#include +#include waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config) : ALabel(config, "pulseaudio", id, "{volume}%") { @@ -52,6 +54,7 @@ const std::vector waybar::modules::Pulseaudio::getPulseIcon() const std::string nameLC = backend->getSinkPortName() + backend->getFormFactor(); std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { + spdlog::trace("Port: {}", nameLC); if (nameLC.find(port) != std::string::npos) { if (sink_muted) { res.emplace_back(port + "-muted"); @@ -63,6 +66,10 @@ const std::vector waybar::modules::Pulseaudio::getPulseIcon() const if (sink_muted) { res.emplace_back("default-muted"); } + spdlog::trace("Ports:"); + for (auto const &item : res) { + spdlog::trace(" {}", item); + } return res; } diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 3d90b6d5a..cfff18189 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -1,14 +1,18 @@ #include "util/audio_backend.hpp" #include +#include #include +#include #include #include #include #include +#include #include #include +#include namespace waybar::util { @@ -132,6 +136,7 @@ void AudioBackend::volumeModifyCb(pa_context *c, int success, void *data) { } } + /* * Called when the requested sink information is ready. */ @@ -139,6 +144,11 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i void *data) { if (i == nullptr) return; + spdlog::trace("Callback start"); + auto running = i->state == PA_SINK_RUNNING; + auto idle = i->state == PA_SINK_IDLE; + spdlog::trace("Sink name {} Running:[{}] Idle:[{}]", i->name, running,idle ); + auto *backend = static_cast(data); if (!backend->ignored_sinks_.empty()) { @@ -155,11 +165,22 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i } } + backend->default_sink_running_ = + backend->default_sink_name == i->name; + + if ( i->name != backend->default_sink_name) { + return; + } + if (backend->current_sink_name_ == i->name) { - backend->current_sink_running_ = i->state == PA_SINK_RUNNING; + backend->current_sink_running_ = + (i->state == PA_SINK_RUNNING || + i->state == PA_SINK_IDLE); } - if (!backend->current_sink_running_ && i->state == PA_SINK_RUNNING) { + if (!backend->current_sink_running_ && ( + i->state == PA_SINK_RUNNING || + i->state == PA_SINK_IDLE)) { backend->current_sink_name_ = i->name; backend->current_sink_running_ = true; } @@ -207,6 +228,7 @@ void AudioBackend::sourceInfoCb(pa_context * /*context*/, const pa_source_info * void AudioBackend::serverInfoCb(pa_context *context, const pa_server_info *i, void *data) { auto *backend = static_cast(data); backend->current_sink_name_ = i->default_sink_name; + backend->default_sink_name = i->default_sink_name; backend->default_source_name_ = i->default_source_name; pa_context_get_sink_info_list(context, sinkInfoCb, data); From 9c47b2e9dddcb5fe58825cce69ff3fe785ad857f Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Mon, 22 Jul 2024 12:37:17 +0200 Subject: [PATCH 386/407] removed debug logging --- src/modules/pulseaudio.cpp | 7 ------- src/util/audio_backend.cpp | 3 --- 2 files changed, 10 deletions(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 9d3f1862a..255ca571f 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -1,6 +1,4 @@ #include "modules/pulseaudio.hpp" -#include -#include waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config) : ALabel(config, "pulseaudio", id, "{volume}%") { @@ -54,7 +52,6 @@ const std::vector waybar::modules::Pulseaudio::getPulseIcon() const std::string nameLC = backend->getSinkPortName() + backend->getFormFactor(); std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { - spdlog::trace("Port: {}", nameLC); if (nameLC.find(port) != std::string::npos) { if (sink_muted) { res.emplace_back(port + "-muted"); @@ -66,10 +63,6 @@ const std::vector waybar::modules::Pulseaudio::getPulseIcon() const if (sink_muted) { res.emplace_back("default-muted"); } - spdlog::trace("Ports:"); - for (auto const &item : res) { - spdlog::trace(" {}", item); - } return res; } diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index cfff18189..d084f94a5 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -136,7 +135,6 @@ void AudioBackend::volumeModifyCb(pa_context *c, int success, void *data) { } } - /* * Called when the requested sink information is ready. */ @@ -144,7 +142,6 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i void *data) { if (i == nullptr) return; - spdlog::trace("Callback start"); auto running = i->state == PA_SINK_RUNNING; auto idle = i->state == PA_SINK_IDLE; spdlog::trace("Sink name {} Running:[{}] Idle:[{}]", i->name, running,idle ); From 8b1d73690d025379937bb63c063ecf088848f800 Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Mon, 22 Jul 2024 12:40:30 +0200 Subject: [PATCH 387/407] added running check to default sink return condition --- src/util/audio_backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index d084f94a5..5f1cf2f27 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -165,7 +165,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i backend->default_sink_running_ = backend->default_sink_name == i->name; - if ( i->name != backend->default_sink_name) { + if ( i->name != backend->default_sink_name && !backend->default_sink_running_) { return; } From d6bfeb5a44996fe21fafbc93cf450e5a119a9644 Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Mon, 22 Jul 2024 13:05:25 +0200 Subject: [PATCH 388/407] added is running condition to default_sink_is_running check --- src/util/audio_backend.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 5f1cf2f27..d2c2928dc 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -162,8 +162,9 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i } } - backend->default_sink_running_ = - backend->default_sink_name == i->name; + backend->default_sink_running_ = backend->default_sink_name == i->name + && (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE); + if ( i->name != backend->default_sink_name && !backend->default_sink_running_) { return; From 951b89ffcb1cdb0d8ccc34fbe13b7bd42971e7ad Mon Sep 17 00:00:00 2001 From: Findus Date: Tue, 23 Jul 2024 15:57:32 +0200 Subject: [PATCH 389/407] Update clang-format.yml workflow dispatch to debug failing workflow manually --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 4a774dbdd..89793f5ee 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,6 +1,6 @@ name: clang-format -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] concurrency: group: ${{ github.workflow }}-format-${{ github.event.pull_request.number || github.ref }} From e3095c6d1d1647d46b730007ca34c8f8b2ebd452 Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Tue, 23 Jul 2024 16:01:24 +0200 Subject: [PATCH 390/407] clang-format --- src/util/audio_backend.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index d2c2928dc..73aac148b 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -6,12 +6,12 @@ #include #include #include +#include #include #include #include #include -#include namespace waybar::util { @@ -144,7 +144,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i auto running = i->state == PA_SINK_RUNNING; auto idle = i->state == PA_SINK_IDLE; - spdlog::trace("Sink name {} Running:[{}] Idle:[{}]", i->name, running,idle ); + spdlog::trace("Sink name {} Running:[{}] Idle:[{}]", i->name, running, idle); auto *backend = static_cast(data); @@ -162,23 +162,19 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i } } - backend->default_sink_running_ = backend->default_sink_name == i->name - && (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE); - + backend->default_sink_running_ = backend->default_sink_name == i->name && + (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE); - if ( i->name != backend->default_sink_name && !backend->default_sink_running_) { + if (i->name != backend->default_sink_name && !backend->default_sink_running_) { return; } if (backend->current_sink_name_ == i->name) { - backend->current_sink_running_ = - (i->state == PA_SINK_RUNNING || - i->state == PA_SINK_IDLE); + backend->current_sink_running_ = (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE); } - if (!backend->current_sink_running_ && ( - i->state == PA_SINK_RUNNING || - i->state == PA_SINK_IDLE)) { + if (!backend->current_sink_running_ && + (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE)) { backend->current_sink_name_ = i->name; backend->current_sink_running_ = true; } From 57156bce7e1239349661cbcf6c96a73a670d36a3 Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Tue, 23 Jul 2024 16:06:01 +0200 Subject: [PATCH 391/407] removed manual flag from clang format again --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 89793f5ee..4a774dbdd 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,6 +1,6 @@ name: clang-format -on: [push, pull_request, workflow_dispatch] +on: [push, pull_request] concurrency: group: ${{ github.workflow }}-format-${{ github.event.pull_request.number || github.ref }} From 773b1d4806b01e426516e70f673b949175159d95 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Mon, 23 Sep 2024 15:51:01 +0300 Subject: [PATCH 392/407] Default value for cldYearShift_ = 1900/01/01 Signed-off-by: Viktar Lukashonak --- src/modules/clock.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index db2979ebc..0accdd0c7 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -24,6 +24,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) m_tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""}, m_tooltip_{new Gtk::Label()}, cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, + cldYearShift_{1900y / 1 / 1}, tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, tzCurrIdx_{0}, ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { From b4e97eb2f49ef2ca111896bf43846b5b24be8361 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Mon, 23 Sep 2024 16:06:50 +0300 Subject: [PATCH 393/407] FreeBSD format fix Signed-off-by: Viktar Lukashonak --- src/modules/clock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 0accdd0c7..7f5a4d558 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -24,7 +24,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) m_tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""}, m_tooltip_{new Gtk::Label()}, cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, - cldYearShift_{1900y / 1 / 1}, + cldYearShift_{January / 1 / 1900}, tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, tzCurrIdx_{0}, ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { From c88a86f510820005426d998bdc0d8d5ac20ba304 Mon Sep 17 00:00:00 2001 From: mslxl Date: Wed, 25 Sep 2024 09:56:42 +0800 Subject: [PATCH 394/407] nix: remove patches from downstream The patches is the modification of downstream, it should not affect upstream. Any changes of upstream would caused patch fail. --- nix/default.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nix/default.nix b/nix/default.nix index 9ce39a9b5..e7f079297 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -25,6 +25,9 @@ in mesonFlags = lib.remove "-Dgtk-layer-shell=enabled" oldAttrs.mesonFlags; + # downstream patch should not affect upstream + patches = []; + buildInputs = (builtins.filter (p: p.pname != "wireplumber") oldAttrs.buildInputs) ++ [ pkgs.wireplumber ]; From 04bda9f443a2ab8ac21ce3efe762bd1a907dc248 Mon Sep 17 00:00:00 2001 From: Lars Niesen Date: Tue, 24 Sep 2024 21:41:54 +0200 Subject: [PATCH 395/407] Backlight: Add minimum brightness As currently it is possible to turn the brightness to zero which may not be desirable, this patch add a configurable brightness check. --- src/modules/backlight.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 4ae511eb9..6bfe8ceea 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -112,6 +112,14 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { step = config_["scroll-step"].asDouble(); } + double min_brightness = 10; + if (config_["min-brightness"].isDouble()) { + min_brightness = config_["min-brightness"].asDouble(); + } + if (backend.get_scaled_brightness(preferred_device_) <= min_brightness && + ct == util::ChangeType::Decrease) { + return true; + } backend.set_brightness(preferred_device_, ct, step); return true; From 47f767b0ee32de13a2ff893a9146f59b47681601 Mon Sep 17 00:00:00 2001 From: Lars Niesen Date: Wed, 25 Sep 2024 06:24:24 +0200 Subject: [PATCH 396/407] Backlight: Add documentation for min-brightness --- man/waybar-backlight.5.scd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index 5286c2ed3..a57b8278c 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -81,6 +81,11 @@ The *backlight* module displays the current backlight level. default: 1.0 ++ The speed at which to change the brightness when scrolling. +*min-brightness*: ++ + typeof: double ++ + default: 10.0 ++ + The minimum brightness of the backlight. + *menu*: ++ typeof: string ++ Action that popups the menu. From d684a6de21ee78d58f819eb759b905a9091b3dd4 Mon Sep 17 00:00:00 2001 From: Lars Niesen Date: Wed, 25 Sep 2024 16:37:21 +0200 Subject: [PATCH 397/407] Backlight: Set default to 0 to prevent breaking existing setups --- man/waybar-backlight.5.scd | 2 +- src/modules/backlight.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index a57b8278c..e1a688dba 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -83,7 +83,7 @@ The *backlight* module displays the current backlight level. *min-brightness*: ++ typeof: double ++ - default: 10.0 ++ + default: 0.0 ++ The minimum brightness of the backlight. *menu*: ++ diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 6bfe8ceea..ff58951cd 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -112,7 +112,7 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { step = config_["scroll-step"].asDouble(); } - double min_brightness = 10; + double min_brightness = 0; if (config_["min-brightness"].isDouble()) { min_brightness = config_["min-brightness"].asDouble(); } From e46a1c6bfc1de05cb267eb80f6ccd2f67f90edd9 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 28 Sep 2024 00:57:02 +0300 Subject: [PATCH 398/407] cava bump Signed-off-by: Viktar Lukashonak --- meson.build | 2 +- subprojects/cava.wrap | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index f4941c8fa..726d492bb 100644 --- a/meson.build +++ b/meson.build @@ -482,7 +482,7 @@ if get_option('experimental') endif cava = dependency('cava', - version : '>=0.10.2', + version : '>=0.10.3', required: get_option('cava'), fallback : ['cava', 'cava_dep'], not_found_message: 'cava is not found. Building waybar without cava') diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap index 275ba114b..f0309bf5a 100644 --- a/subprojects/cava.wrap +++ b/subprojects/cava.wrap @@ -1,7 +1,7 @@ [wrap-file] -directory = cava-0.10.2 -source_url = https://github.com/LukashonakV/cava/archive/0.10.2.tar.gz -source_filename = cava-0.10.2.tar.gz -source_hash = dff78c4787c9843583086408a0a6e5bde7a5dee1fa17ae526847366846cb19c3 +directory = cava-0.10.3 +source_url = https://github.com/LukashonakV/cava/archive/0.10.3.tar.gz +source_filename = cava-0.10.3.tar.gz +source_hash = aab0a4ed3f999e8461ad9de63ef8a77f28b6b2011f7dd0c69ba81819d442f6f9 [provide] cava = cava_dep From edab49f2912d29a89325a359261b8b1de878aebb Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 28 Sep 2024 12:41:10 -0500 Subject: [PATCH 399/407] nix/default: cava bump --- nix/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/default.nix b/nix/default.nix index e7f079297..a9ff180b0 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -5,12 +5,12 @@ }: let libcava = rec { - version = "0.10.2"; + version = "0.10.3"; src = pkgs.fetchFromGitHub { owner = "LukashonakV"; repo = "cava"; rev = version; - hash = "sha256-jU7RQV2txruu/nUUl0TzjK4nai7G38J1rcTjO7UXumY="; + hash = "sha256-ZDFbI69ECsUTjbhlw2kHRufZbQMu+FQSMmncCJ5pagg="; }; }; in From e394485857d64d63b76a7555c26456051c65d3bf Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 28 Sep 2024 12:51:08 -0500 Subject: [PATCH 400/407] .github/workflows: don't run on forks --- .github/workflows/docker.yml | 1 + .github/workflows/nix-update-flake-lock.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d9fc5d3e2..66c465ba1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -8,6 +8,7 @@ on: jobs: build-and-push: runs-on: ubuntu-latest + if: github.repository == 'Alexays/Waybar' strategy: fail-fast: false # don't fail the other jobs if one of the images fails to build matrix: diff --git a/.github/workflows/nix-update-flake-lock.yml b/.github/workflows/nix-update-flake-lock.yml index 2b65c329d..6ba189045 100644 --- a/.github/workflows/nix-update-flake-lock.yml +++ b/.github/workflows/nix-update-flake-lock.yml @@ -9,6 +9,7 @@ on: jobs: lockfile: runs-on: ubuntu-latest + if: github.repository == 'Alexays/Waybar' steps: - name: Checkout repository uses: actions/checkout@v4 From e53497bab6f826f7eace501f21f5e6f178253059 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 28 Sep 2024 13:21:55 -0500 Subject: [PATCH 401/407] .github/workflows: allow forks to manually run flake lock update --- .github/workflows/nix-update-flake-lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nix-update-flake-lock.yml b/.github/workflows/nix-update-flake-lock.yml index 6ba189045..a1679eada 100644 --- a/.github/workflows/nix-update-flake-lock.yml +++ b/.github/workflows/nix-update-flake-lock.yml @@ -9,7 +9,7 @@ on: jobs: lockfile: runs-on: ubuntu-latest - if: github.repository == 'Alexays/Waybar' + if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar' steps: - name: Checkout repository uses: actions/checkout@v4 From 95eaffcfb1af0e826a7e93ecd3da7c3be56c02e2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 1 Oct 2024 00:11:21 +0000 Subject: [PATCH 402/407] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/4f807e8940284ad7925ebd0a0993d2a1791acb2f?narHash=sha256-IiA3jfbR7K/B5%2B9byVi9BZGWTD4VSbWe8VLpp9B/iYk%3D' (2024-09-11) → 'github:NixOS/nixpkgs/06cf0e1da4208d3766d898b7fdab6513366d45b9?narHash=sha256-S5kVU7U82LfpEukbn/ihcyNt2%2BEvG7Z5unsKW9H/yFA%3D' (2024-09-29) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 15bf9f093..4b3e7212a 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1726062873, - "narHash": "sha256-IiA3jfbR7K/B5+9byVi9BZGWTD4VSbWe8VLpp9B/iYk=", + "lastModified": 1727634051, + "narHash": "sha256-S5kVU7U82LfpEukbn/ihcyNt2+EvG7Z5unsKW9H/yFA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4f807e8940284ad7925ebd0a0993d2a1791acb2f", + "rev": "06cf0e1da4208d3766d898b7fdab6513366d45b9", "type": "github" }, "original": { From 6df26ccba761af1c41bf0494320f98d6b93b6e09 Mon Sep 17 00:00:00 2001 From: PassiHD Date: Wed, 9 Oct 2024 20:22:58 +0200 Subject: [PATCH 403/407] feat: add warning threshold to temperature module Signed-off-by: PassiHD --- include/modules/temperature.hpp | 1 + src/modules/temperature.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/modules/temperature.hpp b/include/modules/temperature.hpp index 5440df77b..918281be5 100644 --- a/include/modules/temperature.hpp +++ b/include/modules/temperature.hpp @@ -18,6 +18,7 @@ class Temperature : public ALabel { private: float getTemperature(); bool isCritical(uint16_t); + bool isWarning(uint16_t); std::string file_path_; util::SleeperThread thread_; diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 302877639..356536f94 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -69,12 +69,17 @@ auto waybar::modules::Temperature::update() -> void { uint16_t temperature_f = std::round(temperature * 1.8 + 32); uint16_t temperature_k = std::round(temperature + 273.15); auto critical = isCritical(temperature_c); + auto warning = isWarning(temperature_c); auto format = format_; if (critical) { format = config_["format-critical"].isString() ? config_["format-critical"].asString() : format; label_.get_style_context()->add_class("critical"); - } else { + } else if (warning) { + format = config_["format-warning"].isString() ? config_["format-warning"].asString() : format; + label_.get_style_context()->add_class("warning"); + } else { label_.get_style_context()->remove_class("critical"); + label_.get_style_context()->remove_class("warning"); } if (format.empty()) { @@ -135,7 +140,12 @@ float waybar::modules::Temperature::getTemperature() { #endif } +bool waybar::modules::Temperature::isWarning(uint16_t temperature_c) { + return config_["warning-threshold"].isInt() && + temperature_c >= config_["warning-threshold"].asInt(); +} + bool waybar::modules::Temperature::isCritical(uint16_t temperature_c) { return config_["critical-threshold"].isInt() && temperature_c >= config_["critical-threshold"].asInt(); -} +} \ No newline at end of file From 0e03c7a811ef477eae4465ffb3ff443e1b6e1e87 Mon Sep 17 00:00:00 2001 From: Christian Fillion Date: Wed, 16 Oct 2024 02:12:06 -0400 Subject: [PATCH 404/407] fix a segfault on signals received after main returns The waybar process does not exit instantaneously. Signals may be recevied after main has started freeing resources. When a worker thread is in `fgets` this time window can last forever. An easy way to duplicate the crash is pressing ^C twice with a Hyprland module. Thread 1 "waybar" received signal SIGSEGV, Segmentation fault. spdlog::sinks::sink::should_log (this=0x5f620b542ca5, msg_level=spdlog::level::info) at /usr/src/debug/spdlog/spdlog-1.14.1/include/spdlog/sinks/sink-inl.h:13 13 return msg_level >= level_.load(std::memory_order_relaxed); (gdb) p $_siginfo._sifields._sigfault.si_addr $1 = (void *) 0x5f620b542cad --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 679c66d65..442c530cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -107,6 +107,10 @@ int main(int argc, char* argv[]) { ret = client->main(argc, argv); } while (reload); + std::signal(SIGUSR1, SIG_IGN); + std::signal(SIGUSR2, SIG_IGN); + std::signal(SIGINT, SIG_IGN); + delete client; return ret; } catch (const std::exception& e) { From 92242f0b9d7f37f4ccf87f03b0c0ce30255d2d54 Mon Sep 17 00:00:00 2001 From: Christian Fillion Date: Wed, 16 Oct 2024 09:56:05 -0400 Subject: [PATCH 405/407] hyprland: fix a data race at startup between sockets 1 and 2 `Workspaces::*` and `IPC::startIPC` may both call `getSocketFolder` at the same time. This randomly causes crashes and/or corruption of the socket path. Typical crash A: [2024-10-16 07:42:09.987] [info] Hyprland IPC starting malloc(): unaligned tcache chunk detected [2024-10-16 07:42:09.987] [error] Hyprland IPC: Unable to connect? Thread 1 "waybar" received signal SIGABRT, Aborted. (gdb) bt #0 __pthread_kill_implementation (threadid=, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44 (omitted for brievety) #9 0x00007ffff64ae745 in operator new (sz=sz@entry=296) at /usr/src/debug/gcc/gcc/libstdc++-v3/libsupc++/new_op.cc:50 #10 0x00007ffff65ab1f1 in std::filesystem::__cxx11::path::_List::_Impl::copy (this=0x555555a23350) at /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++17/fs_path.cc:249 #11 0x00007ffff65ab3bd in std::filesystem::__cxx11::path::_List::_List (this=0x7fffffff9d30, other=) at /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/unique_ptr.h:454 #12 0x00005555556f4ab1 in waybar::modules::hyprland::IPC::getSocket1Reply(std::__cxx11::basic_string, std::allocator > const&) () #13 0x00005555556f5e3d in waybar::modules::hyprland::IPC::getSocket1JsonReply(std::__cxx11::basic_string, std::allocator > const&) () #14 0x000055555571289c in waybar::modules::hyprland::Workspaces::setCurrentMonitorId() () Typical crash B: [2024-10-16 10:01:15.859] [info] Hyprland IPC starting [2024-10-16 10:01:15.859] [info] Loading persistent workspaces from Hyprland workspace rules Thread 8 "waybar" received signal SIGSEGV, Segmentation fault. (gdb) bt #0 std::__cxx11::basic_string, std::allocator >::_S_copy (__d=0x5555558fbca8 "/", __s=0x2973961a26d35726 , __n=1) at /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:433 (omitted for brievety) #15 waybar::modules::hyprland::IPC::getSocketFolder[abi:cxx11](char const*) (instanceSig=0x7fffffffe604 "4520b30d498daca8079365bdb909a8dea38e8d55_1729051218_1982280648") at ../src/modules/hyprland/backend.cpp:41 #16 0x000055555564230f in waybar::modules::hyprland::IPC::startIPC()::{lambda()#1}::operator()() const () at ../src/modules/hyprland/backend.cpp:70 #17 0x00007ffff64e1c34 in std::execute_native_thread_routine (__p=0x5555558119c0) at /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++11/thread.cc:104 #18 0x00007ffff62a339d in start_thread (arg=) at pthread_create.c:447 --- src/modules/hyprland/backend.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 77f534e0d..39341a14b 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -18,6 +18,9 @@ namespace waybar::modules::hyprland { std::filesystem::path IPC::socketFolder_; std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { + static std::mutex folderMutex; + std::unique_lock lock(folderMutex); + // socket path, specified by EventManager of Hyprland if (!socketFolder_.empty()) { return socketFolder_; From bb40e169fd3cb77a46a623ff44db496c9b161749 Mon Sep 17 00:00:00 2001 From: Blexyel Date: Tue, 22 Oct 2024 10:56:26 +0200 Subject: [PATCH 406/407] feat: update man page --- man/waybar-temperature.5.scd | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 541bf3af6..bf41ecc87 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -31,6 +31,10 @@ Addressed by *temperature* typeof: string ++ The temperature filename of your *hwmon-path-abs*, e.g. *temp1_input* +*warning-threshold*: ++ + typeof: integer ++ + The threshold before it is considered warning (Celsius). + *critical-threshold*: ++ typeof: integer ++ The threshold before it is considered critical (Celsius). @@ -40,6 +44,10 @@ Addressed by *temperature* default: 10 ++ The interval in which the information gets polled. +*format-warning*: ++ + typeof: string ++ + The format to use when temperature is considered warning + *format-critical*: ++ typeof: string ++ The format to use when temperature is considered critical From 654a3268983c05740d6f5dbde1f117a1b731ccc8 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 1 Nov 2024 23:44:13 +0300 Subject: [PATCH 407/407] GTK4 migration Signed-off-by: Viktar Lukashonak --- include/AAppIconLabel.hpp | 6 +- include/AIconLabel.hpp | 1 + include/ALabel.hpp | 20 +- include/AModule.hpp | 119 ++++--- include/ASlider.hpp | 7 +- include/bar.hpp | 25 +- include/client.hpp | 7 +- include/factory.hpp | 2 - include/group.hpp | 30 +- include/modules/backlight.hpp | 20 +- include/modules/backlight_slider.hpp | 6 +- include/modules/battery.hpp | 15 +- include/modules/bluetooth.hpp | 7 +- include/modules/cffi.hpp | 17 +- include/modules/cpu.hpp | 11 +- include/modules/cpu_frequency.hpp | 9 - include/modules/custom.hpp | 11 +- include/modules/disk.hpp | 8 +- include/modules/dwl/tags.hpp | 13 +- include/modules/dwl/window.hpp | 2 - include/modules/gamemode.hpp | 19 +- include/modules/hyprland/backend.hpp | 7 +- include/modules/hyprland/window.hpp | 5 - .../hyprland/windowcreationpayload.hpp | 16 - include/modules/hyprland/workspace.hpp | 22 +- include/modules/hyprland/workspaces.hpp | 12 - include/modules/idle_inhibitor.hpp | 6 +- include/modules/image.hpp | 12 +- include/modules/inhibitor.hpp | 17 +- include/modules/keyboard_state.hpp | 5 +- include/modules/load.hpp | 9 - include/modules/memory.hpp | 5 +- include/modules/mpd/mpd.hpp | 4 +- include/modules/{mpris => }/mpris.hpp | 4 +- include/modules/network.hpp | 9 +- include/modules/niri/workspaces.hpp | 1 + include/modules/power_profiles_daemon.hpp | 2 +- include/modules/privacy/privacy.hpp | 9 +- include/modules/privacy/privacy_item.hpp | 6 +- include/modules/pulseaudio.hpp | 12 +- include/modules/pulseaudio_slider.hpp | 8 +- include/modules/river/tags.hpp | 5 +- include/modules/sndio.hpp | 9 +- include/modules/sway/bar.hpp | 2 - include/modules/sway/ipc/client.hpp | 16 +- include/modules/sway/language.hpp | 8 +- include/modules/sway/mode.hpp | 6 +- include/modules/sway/scratchpad.hpp | 7 +- include/modules/sway/window.hpp | 7 +- include/modules/sway/workspaces.hpp | 11 +- include/modules/systemd_failed_units.hpp | 2 +- include/modules/temperature.hpp | 6 +- include/modules/ui.hpp | 17 + include/modules/upower.hpp | 6 +- include/modules/user.hpp | 9 +- include/modules/wireplumber.hpp | 4 +- include/modules/wlr/taskbar.hpp | 47 +-- include/modules/wlr/workspace_manager.hpp | 12 +- include/util/SafeSignal.hpp | 17 +- include/util/audio_backend.hpp | 6 +- include/util/backend_common.hpp | 4 +- include/util/backlight_backend.hpp | 16 +- include/util/css_reload_helper.hpp | 10 +- include/util/format.hpp | 22 +- include/util/gtk_icon.hpp | 14 - include/util/pipewire/pipewire_backend.hpp | 5 +- include/util/pipewire/privacy_node_info.hpp | 32 +- include/util/portal.hpp | 2 +- include/util/regex_collection.hpp | 1 - include/util/rfkill.hpp | 8 +- meson.build | 261 +++++++-------- resources/custom_modules/cffi_example/main.c | 17 +- .../custom_modules/cffi_example/meson.build | 4 +- .../cffi_example/waybar_cffi_module.h | 2 +- resources/ui/ui-power.xml | 43 +++ src/AAppIconLabel.cpp | 60 ++-- src/AIconLabel.cpp | 13 +- src/ALabel.cpp | 119 ++----- src/AModule.cpp | 302 ++++++++++-------- src/ASlider.cpp | 10 +- src/bar.cpp | 159 +++++---- src/client.cpp | 55 ++-- src/factory.cpp | 13 +- src/group.cpp | 100 +++--- src/main.cpp | 8 +- src/modules/backlight.cpp | 27 +- src/modules/backlight_slider.cpp | 2 +- src/modules/battery.cpp | 32 +- src/modules/bluetooth.cpp | 8 +- src/modules/cffi.cpp | 15 +- src/modules/clock.cpp | 13 +- src/modules/cpu.cpp | 4 +- src/modules/cpu_frequency/common.cpp | 8 +- src/modules/cpu_frequency/linux.cpp | 1 + src/modules/cpu_usage/common.cpp | 4 +- src/modules/custom.cpp | 23 +- src/modules/disk.cpp | 13 +- src/modules/dwl/tags.cpp | 58 ++-- src/modules/dwl/window.cpp | 4 +- src/modules/gamemode.cpp | 62 ++-- src/modules/hyprland/backend.cpp | 11 - src/modules/hyprland/submap.cpp | 4 +- src/modules/hyprland/window.cpp | 19 +- src/modules/hyprland/workspace.cpp | 61 ++-- src/modules/hyprland/workspaces.cpp | 10 +- src/modules/idle_inhibitor.cpp | 41 ++- src/modules/image.cpp | 35 +- src/modules/inhibitor.cpp | 33 +- src/modules/keyboard_state.cpp | 145 ++++----- src/modules/load.cpp | 6 +- src/modules/memory/common.cpp | 8 +- src/modules/memory/linux.cpp | 2 + src/modules/mpd/mpd.cpp | 17 +- src/modules/mpd/state.cpp | 13 +- src/modules/{mpris => }/mpris.cpp | 28 +- src/modules/network.cpp | 198 ++++++------ src/modules/niri/window.cpp | 6 +- src/modules/niri/workspaces.cpp | 13 +- src/modules/power_profiles_daemon.cpp | 16 +- src/modules/privacy/privacy.cpp | 29 +- src/modules/privacy/privacy_item.cpp | 60 ++-- src/modules/pulseaudio.cpp | 56 ++-- src/modules/pulseaudio_slider.cpp | 8 +- src/modules/river/layout.cpp | 1 + src/modules/river/mode.cpp | 1 + src/modules/river/tags.cpp | 33 +- src/modules/river/window.cpp | 1 + src/modules/sndio.cpp | 57 ++-- src/modules/sway/bar.cpp | 37 +-- src/modules/sway/ipc/client.cpp | 30 +- src/modules/sway/language.cpp | 66 ++-- src/modules/sway/mode.cpp | 9 +- src/modules/sway/scratchpad.cpp | 24 +- src/modules/sway/window.cpp | 93 +++--- src/modules/sway/workspaces.cpp | 133 ++++---- src/modules/systemd_failed_units.cpp | 18 +- src/modules/temperature.cpp | 11 +- src/modules/ui.cpp | 40 +++ src/modules/upower.cpp | 74 +++-- src/modules/user.cpp | 20 +- src/modules/wireplumber.cpp | 9 +- src/modules/wlr/taskbar.cpp | 284 +++++++--------- src/modules/wlr/workspace_manager.cpp | 62 ++-- src/util/audio_backend.cpp | 44 ++- src/util/backlight_backend.cpp | 9 +- src/util/css_reload_helper.cpp | 4 +- src/util/gtk_icon.cpp | 25 -- src/util/pipewire/pipewire_backend.cpp | 2 - src/util/pipewire/privacy_node_info.cpp | 4 +- src/util/portal.cpp | 2 +- src/util/regex_collection.cpp | 2 - src/util/rfkill.cpp | 13 +- subprojects/gtk-layer-shell.wrap | 5 - subprojects/gtk4-layer-shell.wrap | 8 + 154 files changed, 1921 insertions(+), 2296 deletions(-) rename include/modules/{mpris => }/mpris.hpp (96%) create mode 100644 include/modules/ui.hpp delete mode 100644 include/util/gtk_icon.hpp create mode 100644 resources/ui/ui-power.xml rename src/modules/{mpris => }/mpris.cpp (98%) create mode 100644 src/modules/ui.cpp delete mode 100644 src/util/gtk_icon.cpp delete mode 100644 subprojects/gtk-layer-shell.wrap create mode 100644 subprojects/gtk4-layer-shell.wrap diff --git a/include/AAppIconLabel.hpp b/include/AAppIconLabel.hpp index d09ab14a6..70d99edd5 100644 --- a/include/AAppIconLabel.hpp +++ b/include/AAppIconLabel.hpp @@ -1,7 +1,6 @@ #pragma once -#include -#include +#include #include "AIconLabel.hpp" @@ -19,9 +18,12 @@ class AAppIconLabel : public AIconLabel { void updateAppIconName(const std::string &app_identifier, const std::string &alternative_app_identifier); void updateAppIcon(); + + private: unsigned app_icon_size_{24}; bool update_app_icon_{true}; std::string app_icon_name_; + Glib::RefPtr gtkTheme_; }; } // namespace waybar diff --git a/include/AIconLabel.hpp b/include/AIconLabel.hpp index 054d031a3..e3ef39169 100644 --- a/include/AIconLabel.hpp +++ b/include/AIconLabel.hpp @@ -14,6 +14,7 @@ class AIconLabel : public ALabel { bool enable_click = false, bool enable_scroll = false); virtual ~AIconLabel() = default; auto update() -> void override; + operator Gtk::Widget &() override; protected: Gtk::Image image_; diff --git a/include/ALabel.hpp b/include/ALabel.hpp index a1aae9da9..2bbd63c76 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -1,8 +1,8 @@ #pragma once -#include #include -#include + +#include #include "AModule.hpp" @@ -10,27 +10,25 @@ namespace waybar { class ALabel : public AModule { public: - ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format, - uint16_t interval = 0, bool ellipsize = false, bool enable_click = false, - bool enable_scroll = false); virtual ~ALabel() = default; auto update() -> void override; virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0); virtual std::string getIcon(uint16_t, const std::vector &alts, uint16_t max = 0); + operator Gtk::Widget &() override; protected: - Gtk::Label label_; + ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format, + uint16_t interval = 0, bool ellipsize = false, bool enable_click = false, + bool enable_scroll = false); + std::string format_; + Gtk::Label label_; const std::chrono::seconds interval_; bool alt_ = false; std::string default_format_; - bool handleToggle(GdkEventButton *const &e) override; + void handleToggle(int n_press, double dx, double dy) override; virtual std::string getState(uint8_t value, bool lesser = false); - - std::map submenus_; - std::map menuActionsMap_; - static void handleGtkMenuEvent(GtkMenuItem *menuitem, gpointer data); }; } // namespace waybar diff --git a/include/AModule.hpp b/include/AModule.hpp index 94a883718..a9e45eb86 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -1,18 +1,17 @@ #pragma once #include -#include -#include -#include +#include +#include +#include #include #include "IModule.hpp" - namespace waybar { class AModule : public IModule { public: - static constexpr const char *MODULE_CLASS = "module"; + static constexpr const char *MODULE_CLASS{"module"}; ~AModule() override; auto update() -> void override; @@ -28,54 +27,88 @@ class AModule : public IModule { // Derived classes are able to use it AModule(const Json::Value &, const std::string &, const std::string &, bool enable_click = false, bool enable_scroll = false); - + const std::string name_; + const Json::Value &config_; + Glib::RefPtr controllClick_; + Glib::RefPtr controllScroll_; + Glib::RefPtr controllMotion_; enum SCROLL_DIR { NONE, UP, DOWN, LEFT, RIGHT }; - SCROLL_DIR getScrollDir(GdkEventScroll *e); + void bindEvents(Gtk::Widget &wg); + void unBindEvents(); bool tooltipEnabled() const; - const std::string name_; - const Json::Value &config_; - Gtk::EventBox event_box_; - - virtual void setCursor(Gdk::CursorType const &c); + virtual void setCursor(const Glib::RefPtr &cur); + virtual void setCursor(const Glib::ustring &name); - virtual bool handleToggle(GdkEventButton *const &ev); - virtual bool handleMouseEnter(GdkEventCrossing *const &ev); - virtual bool handleMouseLeave(GdkEventCrossing *const &ev); - virtual bool handleScroll(GdkEventScroll *); - virtual bool handleRelease(GdkEventButton *const &ev); - GObject *menu_; + virtual void handleToggle(int n_press, double dx, double dy); + virtual void handleRelease(int n_press, double dx, double dy); + virtual bool handleScroll(double dx, double dy); + virtual void handleMouseEnter(double x, double y); + virtual void handleMouseLeave(); + const SCROLL_DIR getScrollDir(Glib::RefPtr e); private: - bool handleUserEvent(GdkEventButton *const &ev); const bool isTooltip; - bool hasUserEvents_; + const bool isAfter{true}; + bool enableClick_{false}; + bool enableScroll_{false}; + bool hasPressEvents_{false}; + bool hasReleaseEvents_{false}; std::vector pid_; - gdouble distance_scrolled_y_; - gdouble distance_scrolled_x_; + double distance_scrolled_x_{0.0}; + double distance_scrolled_y_{0.0}; + const Glib::RefPtr curDefault; + const Glib::RefPtr curPoint; + Glib::RefPtr currEvent_; std::map eventActionMap_; - static const inline std::map, std::string> eventMap_{ - {std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"}, - {std::make_pair(1, GdkEventType::GDK_BUTTON_RELEASE), "on-click-release"}, - {std::make_pair(1, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click"}, - {std::make_pair(1, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click"}, - {std::make_pair(2, GdkEventType::GDK_BUTTON_PRESS), "on-click-middle"}, - {std::make_pair(2, GdkEventType::GDK_BUTTON_RELEASE), "on-click-middle-release"}, - {std::make_pair(2, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-middle"}, - {std::make_pair(2, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-middle"}, - {std::make_pair(3, GdkEventType::GDK_BUTTON_PRESS), "on-click-right"}, - {std::make_pair(3, GdkEventType::GDK_BUTTON_RELEASE), "on-click-right-release"}, - {std::make_pair(3, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-right"}, - {std::make_pair(3, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-right"}, - {std::make_pair(8, GdkEventType::GDK_BUTTON_PRESS), "on-click-backward"}, - {std::make_pair(8, GdkEventType::GDK_BUTTON_RELEASE), "on-click-backward-release"}, - {std::make_pair(8, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-backward"}, - {std::make_pair(8, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-backward"}, - {std::make_pair(9, GdkEventType::GDK_BUTTON_PRESS), "on-click-forward"}, - {std::make_pair(9, GdkEventType::GDK_BUTTON_RELEASE), "on-click-forward-release"}, - {std::make_pair(9, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-forward"}, - {std::make_pair(9, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-forward"}}; + static const inline std::map, Gdk::Event::Type>, std::string> + eventMap_{ + {std::make_pair(std::make_pair(1u, 1), Gdk::Event::Type::BUTTON_PRESS), "on-click"}, + {std::make_pair(std::make_pair(1u, 1), Gdk::Event::Type::BUTTON_RELEASE), + "on-click-release"}, + {std::make_pair(std::make_pair(1u, 2), Gdk::Event::Type::BUTTON_PRESS), + "on-double-click"}, + {std::make_pair(std::make_pair(1u, 3), Gdk::Event::Type::BUTTON_PRESS), + "on-triple-click"}, + {std::make_pair(std::make_pair(2u, 1), Gdk::Event::Type::BUTTON_PRESS), + "on-click-middle"}, + {std::make_pair(std::make_pair(2u, 1), Gdk::Event::Type::BUTTON_RELEASE), + "on-click-middle-release"}, + {std::make_pair(std::make_pair(2u, 2), Gdk::Event::Type::BUTTON_PRESS), + "on-double-click-middle"}, + {std::make_pair(std::make_pair(2u, 3), Gdk::Event::Type::BUTTON_PRESS), + "on-triple-click-middle"}, + {std::make_pair(std::make_pair(3u, 1), Gdk::Event::Type::BUTTON_PRESS), "on-click-right"}, + {std::make_pair(std::make_pair(3u, 1), Gdk::Event::Type::BUTTON_RELEASE), + "on-click-right-release"}, + {std::make_pair(std::make_pair(3u, 2), Gdk::Event::Type::BUTTON_PRESS), + "on-double-click-right"}, + {std::make_pair(std::make_pair(3u, 3), Gdk::Event::Type::BUTTON_PRESS), + "on-triple-click-right"}, + {std::make_pair(std::make_pair(8u, 1), Gdk::Event::Type::BUTTON_PRESS), + "on-click-backward"}, + {std::make_pair(std::make_pair(8u, 1), Gdk::Event::Type::BUTTON_RELEASE), + "on-click-backward-release"}, + {std::make_pair(std::make_pair(8u, 2), Gdk::Event::Type::BUTTON_PRESS), + "on-double-click-backward"}, + {std::make_pair(std::make_pair(8u, 3), Gdk::Event::Type::BUTTON_PRESS), + "on-triple-click-backward"}, + {std::make_pair(std::make_pair(9u, 1), Gdk::Event::Type::BUTTON_PRESS), + "on-click-forward"}, + {std::make_pair(std::make_pair(9u, 1), Gdk::Event::Type::BUTTON_RELEASE), + "on-click-forward-release"}, + {std::make_pair(std::make_pair(9u, 2), Gdk::Event::Type::BUTTON_PRESS), + "on-double-click-forward"}, + {std::make_pair(std::make_pair(9u, 3), Gdk::Event::Type::BUTTON_PRESS), + "on-triple-click-forward"}}; + void handleClickEvent(uint n_button, int n_press, Gdk::Event::Type n_evtype); + void makeControllClick(); + void makeControllScroll(); + void makeControllMotion(); + void removeControllClick(); + void removeControllScroll(); + void removeControllMotion(); }; } // namespace waybar diff --git a/include/ASlider.hpp b/include/ASlider.hpp index 44cde5077..b612a1301 100644 --- a/include/ASlider.hpp +++ b/include/ASlider.hpp @@ -9,11 +9,12 @@ class ASlider : public AModule { public: ASlider(const Json::Value& config, const std::string& name, const std::string& id); virtual void onValueChanged(); + operator Gtk::Widget&() override; protected: - bool vertical_ = false; - int min_ = 0, max_ = 100, curr_ = 50; + bool vertical_{false}; + int min_{0}, max_{100}, curr_{50}; Gtk::Scale scale_; }; -} // namespace waybar \ No newline at end of file +} // namespace waybar diff --git a/include/bar.hpp b/include/bar.hpp index 9b407abf4..91ba614c5 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -1,16 +1,10 @@ #pragma once #include -#include #include +#include #include -#include #include -#include - -#include -#include -#include #include "AModule.hpp" #include "group.hpp" @@ -75,18 +69,15 @@ class Bar : public sigc::trackable { struct wl_surface *surface; bool visible = true; Gtk::Window window; - Gtk::Orientation orientation = Gtk::ORIENTATION_HORIZONTAL; - Gtk::PositionType position = Gtk::POS_TOP; - - int x_global; - int y_global; + Gtk::Orientation orientation{Gtk::Orientation::HORIZONTAL}; + Gtk::PositionType position{Gtk::PositionType::TOP}; #ifdef HAVE_SWAY std::string bar_id; #endif private: - void onMap(GdkEventAny *); + void onMap(); auto setupWidgets() -> void; void getModules(const Factory &, const std::string &, waybar::Group *); void setupAltFormatKeyForModule(const std::string &module_name); @@ -94,10 +85,14 @@ class Bar : public sigc::trackable { void setMode(const bar_mode &); void setPassThrough(bool passthrough); void setPosition(Gtk::PositionType position); - void onConfigure(GdkEventConfigure *ev); + void onConfigure(int width, int height); void configureGlobalOffset(int width, int height); void onOutputGeometryChanged(); + Glib::RefPtr gdk_surface_; + int x_global; + int y_global; + /* Copy initial set of modes to allow customization */ bar_mode_map configured_modes = PRESET_MODES; std::string last_mode_{MODE_DEFAULT}; @@ -109,7 +104,7 @@ class Bar : public sigc::trackable { Gtk::Box left_; Gtk::Box center_; Gtk::Box right_; - Gtk::Box box_; + Gtk::CenterBox box_; std::vector> modules_left_; std::vector> modules_center_; std::vector> modules_right_; diff --git a/include/client.hpp b/include/client.hpp index 0e68f0025..307b241ec 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -1,9 +1,6 @@ #pragma once -#include -#include -#include -#include +#include #include "bar.hpp" #include "config.hpp" @@ -33,6 +30,7 @@ class Client { private: Client() = default; + Glib::RefPtr monitors_; const std::string getStyle(const std::string &style, std::optional appearance); void bindInterfaces(); void handleOutput(struct waybar_output &output); @@ -50,7 +48,6 @@ class Client { void handleMonitorRemoved(Glib::RefPtr monitor); void handleDeferredMonitorRemoval(Glib::RefPtr monitor); - Glib::RefPtr style_context_; Glib::RefPtr css_provider_; std::unique_ptr portal; std::list outputs_; diff --git a/include/factory.hpp b/include/factory.hpp index f805aab5e..81530db09 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -1,7 +1,5 @@ #pragma once -#include - #include namespace waybar { diff --git a/include/group.hpp b/include/group.hpp index 5ce331a8d..8dffa148a 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -1,35 +1,33 @@ #pragma once #include -#include -#include +#include #include "AModule.hpp" -#include "gtkmm/revealer.h" namespace waybar { -class Group : public AModule { +class Group final : public AModule { public: Group(const std::string &, const std::string &, const Json::Value &, bool); ~Group() override = default; auto update() -> void override; - operator Gtk::Widget &() override; virtual Gtk::Box &getBox(); void addWidget(Gtk::Widget &widget); - protected: - Gtk::Box box; - Gtk::Box revealer_box; - Gtk::Revealer revealer; - bool is_first_widget = true; - bool is_drawer = false; - bool click_to_reveal = false; - std::string add_class_to_drawer_children; - bool handleMouseEnter(GdkEventCrossing *const &ev) override; - bool handleMouseLeave(GdkEventCrossing *const &ev) override; - bool handleToggle(GdkEventButton *const &ev) override; + private: + Gtk::Box box_; + Gtk::Box revealer_box_; + Gtk::Revealer revealer_; + bool is_first_widget_{true}; + bool is_drawer_{false}; + bool click_to_reveal_{false}; + std::string add_class_to_drawer_children_; + + void handleMouseEnter(double x, double y) override; + void handleMouseLeave() override; + void handleToggle(int n_press, double dx, double dy) override; void show_group(); void hide_group(); }; diff --git a/include/modules/backlight.hpp b/include/modules/backlight.hpp index 110cd434b..12361294c 100644 --- a/include/modules/backlight.hpp +++ b/include/modules/backlight.hpp @@ -1,32 +1,22 @@ #pragma once -#include -#include -#include -#include -#include - #include "ALabel.hpp" #include "util/backlight_backend.hpp" -#include "util/json.hpp" - -struct udev; -struct udev_device; namespace waybar::modules { -class Backlight : public ALabel { +class Backlight final : public ALabel { public: Backlight(const std::string &, const Json::Value &); virtual ~Backlight() = default; auto update() -> void override; - bool handleScroll(GdkEventScroll *e) override; - + private: const std::string preferred_device_; - std::string previous_format_; - util::BacklightBackend backend; + + bool handleScroll(double dx, double dy) override; }; + } // namespace waybar::modules diff --git a/include/modules/backlight_slider.hpp b/include/modules/backlight_slider.hpp index 437c53c45..30fad3fca 100644 --- a/include/modules/backlight_slider.hpp +++ b/include/modules/backlight_slider.hpp @@ -1,13 +1,11 @@ #pragma once -#include - #include "ASlider.hpp" #include "util/backlight_backend.hpp" namespace waybar::modules { -class BacklightSlider : public ASlider { +class BacklightSlider final : public ASlider { public: BacklightSlider(const std::string&, const Json::Value&); virtual ~BacklightSlider() = default; @@ -21,4 +19,4 @@ class BacklightSlider : public ASlider { util::BacklightBackend backend; }; -} // namespace waybar::modules \ No newline at end of file +} // namespace waybar::modules diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 8e1a2ad2b..29828efd5 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -1,33 +1,25 @@ #pragma once -#include - #include #if defined(__linux__) #include #endif -#include -#include -#include -#include - #include "ALabel.hpp" -#include "bar.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { namespace fs = std::filesystem; -class Battery : public ALabel { +class Battery final : public ALabel { public: - Battery(const std::string&, const waybar::Bar&, const Json::Value&); + Battery(const std::string&, const Json::Value&); virtual ~Battery(); auto update() -> void override; private: - static inline const fs::path data_dir_ = "/sys/class/power_supply/"; + static inline const fs::path data_dir_{"/sys/class/power_supply/"}; void refreshBatteries(); void worker(); @@ -44,7 +36,6 @@ class Battery : public ALabel { std::mutex battery_list_mutex_; std::string old_status_; bool warnFirstTime_{true}; - const Bar& bar_; util::SleeperThread thread_; util::SleeperThread thread_battery_update_; diff --git a/include/modules/bluetooth.hpp b/include/modules/bluetooth.hpp index b89383a04..7e0770fa0 100644 --- a/include/modules/bluetooth.hpp +++ b/include/modules/bluetooth.hpp @@ -4,15 +4,10 @@ #ifdef WANT_RFKILL #include "util/rfkill.hpp" #endif -#include - -#include -#include -#include namespace waybar::modules { -class Bluetooth : public ALabel { +class Bluetooth final : public ALabel { struct ControllerInfo { std::string path; std::string address; diff --git a/include/modules/cffi.hpp b/include/modules/cffi.hpp index 85f129896..782533eea 100644 --- a/include/modules/cffi.hpp +++ b/include/modules/cffi.hpp @@ -1,24 +1,21 @@ #pragma once -#include +#include #include "AModule.hpp" -#include "util/command.hpp" -#include "util/json.hpp" -#include "util/sleeper_thread.hpp" namespace waybar::modules { namespace ffi { extern "C" { -typedef struct wbcffi_module wbcffi_module; +using wbcffi_module = struct wbcffi_module; -typedef struct { +using wbcffi_init_info = struct { wbcffi_module* obj; const char* waybar_version; - GtkContainer* (*get_root_widget)(wbcffi_module*); + GtkWidget* (*get_root_widget)(wbcffi_module*); void (*queue_update)(wbcffi_module*); -} wbcffi_init_info; +}; struct wbcffi_config_entry { const char* key; @@ -27,7 +24,7 @@ struct wbcffi_config_entry { } } // namespace ffi -class CFFI : public AModule { +class CFFI final : public AModule { public: CFFI(const std::string&, const std::string&, const Json::Value&); virtual ~CFFI(); @@ -35,10 +32,12 @@ class CFFI : public AModule { virtual auto refresh(int signal) -> void override; virtual auto doAction(const std::string& name) -> void override; virtual auto update() -> void override; + operator Gtk::Widget&() override; private: /// void* cffi_instance_ = nullptr; + Glib::RefPtr const box_; typedef void*(InitFn)(const ffi::wbcffi_init_info* init_info, const ffi::wbcffi_config_entry* config_entries, size_t config_entries_len); diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 7f78c1650..739461846 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -1,20 +1,11 @@ #pragma once -#include - -#include -#include -#include -#include -#include -#include - #include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { -class Cpu : public ALabel { +class Cpu final : public ALabel { public: Cpu(const std::string&, const Json::Value&); virtual ~Cpu() = default; diff --git a/include/modules/cpu_frequency.hpp b/include/modules/cpu_frequency.hpp index 49ca1b86a..fc4ad13fc 100644 --- a/include/modules/cpu_frequency.hpp +++ b/include/modules/cpu_frequency.hpp @@ -1,14 +1,5 @@ #pragma once -#include - -#include -#include -#include -#include -#include -#include - #include "ALabel.hpp" #include "util/sleeper_thread.hpp" diff --git a/include/modules/custom.hpp b/include/modules/custom.hpp index 6c17c6e45..faf728bc0 100644 --- a/include/modules/custom.hpp +++ b/include/modules/custom.hpp @@ -1,10 +1,5 @@ #pragma once -#include - -#include -#include - #include "ALabel.hpp" #include "util/command.hpp" #include "util/json.hpp" @@ -12,7 +7,7 @@ namespace waybar::modules { -class Custom : public ALabel { +class Custom final : public ALabel { public: Custom(const std::string&, const std::string&, const Json::Value&, const std::string&); virtual ~Custom(); @@ -26,8 +21,8 @@ class Custom : public ALabel { void parseOutputRaw(); void parseOutputJson(); void handleEvent(); - bool handleScroll(GdkEventScroll* e) override; - bool handleToggle(GdkEventButton* const& e) override; + bool handleScroll(double dx, double dy); + void handleToggle(int n_press, double dx, double dy); const std::string name_; const std::string output_name_; diff --git a/include/modules/disk.hpp b/include/modules/disk.hpp index 1b4f31761..d0f31c5fd 100644 --- a/include/modules/disk.hpp +++ b/include/modules/disk.hpp @@ -1,17 +1,11 @@ #pragma once -#include -#include - -#include - #include "ALabel.hpp" -#include "util/format.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { -class Disk : public ALabel { +class Disk final : public ALabel { public: Disk(const std::string&, const Json::Value&); virtual ~Disk() = default; diff --git a/include/modules/dwl/tags.hpp b/include/modules/dwl/tags.hpp index 53dff9899..02758c185 100644 --- a/include/modules/dwl/tags.hpp +++ b/include/modules/dwl/tags.hpp @@ -1,26 +1,22 @@ #pragma once #include -#include #include "AModule.hpp" #include "bar.hpp" #include "dwl-ipc-unstable-v2-client-protocol.h" -#include "xdg-output-unstable-v1-client-protocol.h" namespace waybar::modules::dwl { -class Tags : public waybar::AModule { +class Tags final : public waybar::AModule { public: Tags(const std::string &, const waybar::Bar &, const Json::Value &); virtual ~Tags(); + operator Gtk::Widget &() override; // Handlers for wayland events void handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused); - void handle_primary_clicked(uint32_t tag); - bool handle_button_press(GdkEventButton *event_button, uint32_t tag); - struct zdwl_ipc_manager_v2 *status_manager_; struct wl_seat *seat_; @@ -28,7 +24,12 @@ class Tags : public waybar::AModule { const waybar::Bar &bar_; Gtk::Box box_; std::vector buttons_; + std::vector> clickControls_; struct zdwl_ipc_output_v2 *output_status_; + + void handle_primary_clicked(int n_press, double dx, double dy, uint32_t tag); + void handle_button_press(int n_press, double dx, double dy, uint32_t tag, + Glib::RefPtr controlClick); }; } /* namespace waybar::modules::dwl */ diff --git a/include/modules/dwl/window.hpp b/include/modules/dwl/window.hpp index 435863999..2c01be9a5 100644 --- a/include/modules/dwl/window.hpp +++ b/include/modules/dwl/window.hpp @@ -7,7 +7,6 @@ #include "AAppIconLabel.hpp" #include "bar.hpp" #include "dwl-ipc-unstable-v2-client-protocol.h" -#include "util/json.hpp" namespace waybar::modules::dwl { @@ -26,7 +25,6 @@ class Window : public AAppIconLabel, public sigc::trackable { private: const Bar &bar_; - std::string title_; std::string appid_; std::string layout_symbol_; diff --git a/include/modules/gamemode.hpp b/include/modules/gamemode.hpp index 69c0c3aeb..1d763ee9d 100644 --- a/include/modules/gamemode.hpp +++ b/include/modules/gamemode.hpp @@ -1,21 +1,15 @@ #pragma once -#include -#include -#include +#include +#include +#include +#include #include "ALabel.hpp" -#include "giomm/dbusconnection.h" -#include "giomm/dbusproxy.h" -#include "glibconfig.h" -#include "gtkmm/box.h" -#include "gtkmm/image.h" -#include "gtkmm/label.h" -#include "gtkmm/overlay.h" namespace waybar::modules { -class Gamemode : public AModule { +class Gamemode final : public ALabel { public: Gamemode(const std::string &, const Json::Value &); virtual ~Gamemode(); @@ -39,7 +33,7 @@ class Gamemode : public AModule { const Glib::VariantContainerBase &arguments); void getData(); - bool handleToggle(GdkEventButton *const &) override; + void handleToggle(int n_press, double dx, double dy) override; // Config std::string format = DEFAULT_FORMAT; @@ -70,6 +64,7 @@ class Gamemode : public AModule { guint login1_id; Glib::RefPtr gamemode_proxy; Glib::RefPtr system_connection; + Glib::RefPtr gtkTheme_; bool gamemodeRunning; guint gamemodeWatcher_id; }; diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index 11e73d8f6..f805c2bb9 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -1,11 +1,10 @@ #pragma once +#include +#include + #include #include -#include -#include -#include -#include #include "util/json.hpp" diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index f2c266bd2..393ccc3f2 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -1,13 +1,8 @@ #pragma once -#include - -#include - #include "AAppIconLabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" -#include "util/json.hpp" namespace waybar::modules::hyprland { diff --git a/include/modules/hyprland/windowcreationpayload.hpp b/include/modules/hyprland/windowcreationpayload.hpp index 45229ed42..52d3ba0ec 100644 --- a/include/modules/hyprland/windowcreationpayload.hpp +++ b/include/modules/hyprland/windowcreationpayload.hpp @@ -1,24 +1,8 @@ #pragma once -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include -#include - -#include "AModule.hpp" -#include "bar.hpp" -#include "modules/hyprland/backend.hpp" -#include "util/enum.hpp" -#include "util/regex_collection.hpp" using WindowAddress = std::string; diff --git a/include/modules/hyprland/workspace.hpp b/include/modules/hyprland/workspace.hpp index f1fea4e8c..6610f1f6a 100644 --- a/include/modules/hyprland/workspace.hpp +++ b/include/modules/hyprland/workspace.hpp @@ -2,24 +2,8 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "AModule.hpp" -#include "bar.hpp" -#include "modules/hyprland/backend.hpp" -#include "modules/hyprland/windowcreationpayload.hpp" -#include "util/enum.hpp" -#include "util/regex_collection.hpp" +#include "modules/hyprland/workspaces.hpp" using WindowAddress = std::string; @@ -44,8 +28,7 @@ class Workspace { bool isVisible() const { return m_isVisible; }; bool isEmpty() const { return m_windows == 0; }; bool isUrgent() const { return m_isUrgent; }; - - bool handleClicked(GdkEventButton* bt) const; + void handleToggle(int n_press, double dx, double dy); void setActive(bool value = true) { m_isActive = value; }; void setPersistentRule(bool value = true) { m_isPersistentRule = value; }; void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; }; @@ -83,6 +66,7 @@ class Workspace { Gtk::Button m_button; Gtk::Box m_content; Gtk::Label m_label; + Glib::RefPtr const controllClick_; }; } // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index a9d56b79f..2baaf53ab 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -1,17 +1,7 @@ #pragma once -#include -#include #include -#include -#include -#include -#include -#include -#include - -#include "AModule.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" #include "modules/hyprland/windowcreationpayload.hpp" @@ -23,8 +13,6 @@ using WindowAddress = std::string; namespace waybar::modules::hyprland { -class Workspaces; - class Workspaces : public AModule, public EventHandler { public: Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); diff --git a/include/modules/idle_inhibitor.hpp b/include/modules/idle_inhibitor.hpp index 22bd808fc..26f701269 100644 --- a/include/modules/idle_inhibitor.hpp +++ b/include/modules/idle_inhibitor.hpp @@ -1,14 +1,12 @@ #pragma once -#include - #include "ALabel.hpp" #include "bar.hpp" #include "client.hpp" namespace waybar::modules { -class IdleInhibitor : public ALabel { +class IdleInhibitor final : public ALabel { sigc::connection timeout_; public: @@ -19,7 +17,7 @@ class IdleInhibitor : public ALabel { static bool status; private: - bool handleToggle(GdkEventButton* const& e) override; + void handleToggle(int n_press, double dx, double dy); void toggleStatus(); const Bar& bar_; diff --git a/include/modules/image.hpp b/include/modules/image.hpp index 7c0d014fb..266b6f295 100644 --- a/include/modules/image.hpp +++ b/include/modules/image.hpp @@ -1,25 +1,21 @@ #pragma once -#include #include -#include -#include - -#include "ALabel.hpp" +#include "AModule.hpp" #include "gtkmm/box.h" #include "util/command.hpp" -#include "util/json.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { -class Image : public AModule { +class Image final : public AModule { public: Image(const std::string&, const Json::Value&); virtual ~Image() = default; auto update() -> void override; void refresh(int /*signal*/) override; + operator Gtk::Widget&() override; private: void delayWorker(); @@ -29,7 +25,7 @@ class Image : public AModule { Gtk::Box box_; Gtk::Image image_; std::string path_; - std::string tooltip_; + Glib::ustring tooltip_; int size_; int interval_; util::command::res output_; diff --git a/include/modules/inhibitor.hpp b/include/modules/inhibitor.hpp index 43cb6cab1..5da8e6bb5 100644 --- a/include/modules/inhibitor.hpp +++ b/include/modules/inhibitor.hpp @@ -1,27 +1,26 @@ #pragma once -#include - -#include - #include "ALabel.hpp" -#include "bar.hpp" namespace waybar::modules { -class Inhibitor : public ALabel { +class Inhibitor final : public ALabel { public: - Inhibitor(const std::string&, const waybar::Bar&, const Json::Value&); + Inhibitor(const std::string&, const Json::Value&); virtual ~Inhibitor(); auto update() -> void override; + auto doAction(const std::string& name) -> void override; auto activated() -> bool; private: - auto handleToggle(::GdkEventButton* const& e) -> bool override; - const std::unique_ptr<::GDBusConnection, void (*)(::GDBusConnection*)> dbus_; const std::string inhibitors_; int handle_ = -1; + // Module actions + void toggle(); + // Module Action Map + static inline std::map actionMap_{ + {"toggle", &waybar::modules::Inhibitor::toggle}}; }; } // namespace waybar::modules diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index be90eee4d..eda9bc0f2 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -4,24 +4,23 @@ #include #include -#include #include "AModule.hpp" #include "bar.hpp" #include "util/sleeper_thread.hpp" extern "C" { -#include #include } namespace waybar::modules { -class KeyboardState : public AModule { +class KeyboardState final : public AModule { public: KeyboardState(const std::string&, const waybar::Bar&, const Json::Value&); virtual ~KeyboardState(); auto update() -> void override; + operator Gtk::Widget&() override; private: auto tryAddDevice(const std::string&) -> void; diff --git a/include/modules/load.hpp b/include/modules/load.hpp index c4c06d264..275108bd1 100644 --- a/include/modules/load.hpp +++ b/include/modules/load.hpp @@ -1,14 +1,5 @@ #pragma once -#include - -#include -#include -#include -#include -#include -#include - #include "ALabel.hpp" #include "util/sleeper_thread.hpp" diff --git a/include/modules/memory.hpp b/include/modules/memory.hpp index 3b6342b34..628a8b3a0 100644 --- a/include/modules/memory.hpp +++ b/include/modules/memory.hpp @@ -1,8 +1,5 @@ #pragma once -#include - -#include #include #include "ALabel.hpp" @@ -10,7 +7,7 @@ namespace waybar::modules { -class Memory : public ALabel { +class Memory final : public ALabel { public: Memory(const std::string&, const Json::Value&); virtual ~Memory() = default; diff --git a/include/modules/mpd/mpd.hpp b/include/modules/mpd/mpd.hpp index 32d526e93..ac289e681 100644 --- a/include/modules/mpd/mpd.hpp +++ b/include/modules/mpd/mpd.hpp @@ -12,7 +12,7 @@ namespace waybar::modules { -class MPD : public ALabel { +class MPD final : public ALabel { friend class detail::Context; // State machine @@ -47,7 +47,7 @@ class MPD : public ALabel { std::string getOptionIcon(std::string optionName, bool activated) const; // GUI-side methods - bool handlePlayPause(GdkEventButton* const&); + void handlePlayPause(int n_press, double dx, double dy); void emit() { dp.emit(); } // MPD-side, Non-GUI methods. diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris.hpp similarity index 96% rename from include/modules/mpris/mpris.hpp rename to include/modules/mpris.hpp index ad4dac1e1..8e7c5d625 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris.hpp @@ -16,12 +16,12 @@ extern "C" { namespace waybar::modules::mpris { -class Mpris : public ALabel { +class Mpris final : public ALabel { public: Mpris(const std::string&, const Json::Value&); virtual ~Mpris(); auto update() -> void override; - bool handleToggle(GdkEventButton* const&) override; + void handleToggle(int n_press, double dx, double dy) override; private: static auto onPlayerNameAppeared(PlayerctlPlayerManager*, PlayerctlPlayerName*, gpointer) -> void; diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 4a84b02f9..52d97c169 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -1,15 +1,8 @@ #pragma once -#include -#include -#include -#include #include -#include #include -#include - #include "ALabel.hpp" #include "util/sleeper_thread.hpp" #ifdef WANT_RFKILL @@ -18,7 +11,7 @@ namespace waybar::modules { -class Network : public ALabel { +class Network final : public ALabel { public: Network(const std::string&, const Json::Value&); virtual ~Network(); diff --git a/include/modules/niri/workspaces.hpp b/include/modules/niri/workspaces.hpp index a6850ed10..5dd362362 100644 --- a/include/modules/niri/workspaces.hpp +++ b/include/modules/niri/workspaces.hpp @@ -14,6 +14,7 @@ class Workspaces : public AModule, public EventHandler { Workspaces(const std::string &, const Bar &, const Json::Value &); ~Workspaces() override; void update() override; + operator Gtk::Widget &() override; private: void onEvent(const Json::Value &ev) override; diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index a2bd38587..9829f3a88 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -24,7 +24,7 @@ class PowerProfilesDaemon : public ALabel { void getAllPropsCb(Glib::RefPtr &r); void setPropCb(Glib::RefPtr &r); void populateInitState(); - bool handleToggle(GdkEventButton *const &e) override; + void handleToggle(int n_press, double dx, double dy) override; private: // True if we're connected to the dbug interface. False if we're diff --git a/include/modules/privacy/privacy.hpp b/include/modules/privacy/privacy.hpp index d7656d312..4a3d5d7ef 100644 --- a/include/modules/privacy/privacy.hpp +++ b/include/modules/privacy/privacy.hpp @@ -1,20 +1,19 @@ #pragma once -#include +#include -#include "gtkmm/box.h" -#include "modules/privacy/privacy_item.hpp" +#include "AModule.hpp" #include "util/pipewire/pipewire_backend.hpp" -#include "util/pipewire/privacy_node_info.hpp" using waybar::util::PipewireBackend::PrivacyNodeInfo; namespace waybar::modules::privacy { -class Privacy : public AModule { +class Privacy final : public AModule { public: Privacy(const std::string &, const Json::Value &, const std::string &pos); auto update() -> void override; + operator Gtk::Widget &() override; void onPrivacyNodesChanged(); diff --git a/include/modules/privacy/privacy_item.hpp b/include/modules/privacy/privacy_item.hpp index 836bd994c..a1a9be8ae 100644 --- a/include/modules/privacy/privacy_item.hpp +++ b/include/modules/privacy/privacy_item.hpp @@ -2,9 +2,8 @@ #include -#include - #include "gtkmm/box.h" +#include "gtkmm/icontheme.h" #include "gtkmm/image.h" #include "gtkmm/revealer.h" #include "util/pipewire/privacy_node_info.hpp" @@ -14,7 +13,7 @@ using waybar::util::PipewireBackend::PrivacyNodeType; namespace waybar::modules::privacy { -class PrivacyItem : public Gtk::Revealer { +class PrivacyItem final : public Gtk::Revealer { public: PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_, std::list *nodes, const std::string &pos, const uint icon_size, @@ -41,6 +40,7 @@ class PrivacyItem : public Gtk::Revealer { Gtk::Box box_; Gtk::Image icon_; + Glib::RefPtr gtkTheme_; void update_tooltip(); }; diff --git a/include/modules/pulseaudio.hpp b/include/modules/pulseaudio.hpp index eead664f1..abc3724fe 100644 --- a/include/modules/pulseaudio.hpp +++ b/include/modules/pulseaudio.hpp @@ -1,27 +1,21 @@ #pragma once -#include - -#include -#include -#include - #include "ALabel.hpp" #include "util/audio_backend.hpp" namespace waybar::modules { -class Pulseaudio : public ALabel { +class Pulseaudio final : public ALabel { public: Pulseaudio(const std::string&, const Json::Value&); virtual ~Pulseaudio() = default; auto update() -> void override; private: - bool handleScroll(GdkEventScroll* e) override; + bool handleScroll(double dx, double dy) override; const std::vector getPulseIcon() const; - std::shared_ptr backend = nullptr; + std::shared_ptr backend{nullptr}; }; } // namespace waybar::modules diff --git a/include/modules/pulseaudio_slider.hpp b/include/modules/pulseaudio_slider.hpp index 3ef446847..7b3d994fa 100644 --- a/include/modules/pulseaudio_slider.hpp +++ b/include/modules/pulseaudio_slider.hpp @@ -11,7 +11,7 @@ enum class PulseaudioSliderTarget { Source, }; -class PulseaudioSlider : public ASlider { +class PulseaudioSlider final : public ASlider { public: PulseaudioSlider(const std::string&, const Json::Value&); virtual ~PulseaudioSlider() = default; @@ -20,8 +20,8 @@ class PulseaudioSlider : public ASlider { void onValueChanged() override; private: - std::shared_ptr backend = nullptr; - PulseaudioSliderTarget target = PulseaudioSliderTarget::Sink; + std::shared_ptr backend{nullptr}; + PulseaudioSliderTarget target{PulseaudioSliderTarget::Sink}; }; -} // namespace waybar::modules \ No newline at end of file +} // namespace waybar::modules diff --git a/include/modules/river/tags.hpp b/include/modules/river/tags.hpp index fb3eefaab..67f4791a6 100644 --- a/include/modules/river/tags.hpp +++ b/include/modules/river/tags.hpp @@ -15,14 +15,15 @@ class Tags : public waybar::AModule { public: Tags(const std::string &, const waybar::Bar &, const Json::Value &); virtual ~Tags(); + operator Gtk::Widget &() override; // Handlers for wayland events void handle_focused_tags(uint32_t tags); void handle_view_tags(struct wl_array *tags); void handle_urgent_tags(uint32_t tags); - void handle_primary_clicked(uint32_t tag); - bool handle_button_press(GdkEventButton *event_button, uint32_t tag); + void handleRlse(int n_press, double dx, double dy, uint32_t tag); + void handlePress(int n_press, double dx, double dy, uint32_t tag); struct zriver_status_manager_v1 *status_manager_; struct zriver_control_v1 *control_; diff --git a/include/modules/sndio.hpp b/include/modules/sndio.hpp index 3fe36fadf..0f394f778 100644 --- a/include/modules/sndio.hpp +++ b/include/modules/sndio.hpp @@ -2,24 +2,23 @@ #include -#include - #include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { -class Sndio : public ALabel { +class Sndio final : public ALabel { public: Sndio(const std::string &, const Json::Value &); virtual ~Sndio(); auto update() -> void override; auto set_desc(struct sioctl_desc *, unsigned int) -> void; auto put_val(unsigned int, unsigned int) -> void; - bool handleScroll(GdkEventScroll *) override; - bool handleToggle(GdkEventButton *const &) override; private: + bool handleScroll(double dx, double dy) override; + void handleToggle(int n_press, double dx, double dy) override; + auto connect_to_sndio() -> void; util::SleeperThread thread_; struct sioctl_hdl *hdl_; diff --git a/include/modules/sway/bar.hpp b/include/modules/sway/bar.hpp index fd48e5a35..68aaeff37 100644 --- a/include/modules/sway/bar.hpp +++ b/include/modules/sway/bar.hpp @@ -1,6 +1,4 @@ #pragma once -#include -#include #include "modules/sway/ipc/client.hpp" #include "util/SafeSignal.hpp" diff --git a/include/modules/sway/ipc/client.hpp b/include/modules/sway/ipc/client.hpp index a9a3e4e9d..5ca476a52 100644 --- a/include/modules/sway/ipc/client.hpp +++ b/include/modules/sway/ipc/client.hpp @@ -1,22 +1,14 @@ #pragma once -#include #include #include -#include - -#include -#include -#include -#include -#include #include "ipc.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules::sway { -class Ipc { +class Ipc final { public: Ipc(); ~Ipc(); @@ -27,15 +19,15 @@ class Ipc { std::string payload; }; - sigc::signal signal_event; - sigc::signal signal_cmd; + sigc::signal signal_event; + sigc::signal signal_cmd; void sendCmd(uint32_t type, const std::string &payload = ""); void subscribe(const std::string &payload); void handleEvent(); void setWorker(std::function &&func); - protected: + private: static inline const std::string ipc_magic_ = "i3-ipc"; static inline const size_t ipc_header_size_ = ipc_magic_.size() + 8; diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index ea58c4f09..030e72940 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -1,20 +1,14 @@ #pragma once -#include #include -#include -#include - #include "ALabel.hpp" -#include "bar.hpp" -#include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" namespace waybar::modules::sway { -class Language : public ALabel, public sigc::trackable { +class Language final : public ALabel, public sigc::trackable { public: Language(const std::string& id, const Json::Value& config); virtual ~Language() = default; diff --git a/include/modules/sway/mode.hpp b/include/modules/sway/mode.hpp index 44585355c..507537b6c 100644 --- a/include/modules/sway/mode.hpp +++ b/include/modules/sway/mode.hpp @@ -1,16 +1,12 @@ #pragma once -#include - #include "ALabel.hpp" -#include "bar.hpp" -#include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" namespace waybar::modules::sway { -class Mode : public ALabel, public sigc::trackable { +class Mode final : public ALabel, public sigc::trackable { public: Mode(const std::string&, const Json::Value&); virtual ~Mode() = default; diff --git a/include/modules/sway/scratchpad.hpp b/include/modules/sway/scratchpad.hpp index 551cc8c8e..f43290887 100644 --- a/include/modules/sway/scratchpad.hpp +++ b/include/modules/sway/scratchpad.hpp @@ -1,18 +1,13 @@ #pragma once -#include - #include -#include #include "ALabel.hpp" -#include "bar.hpp" -#include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" namespace waybar::modules::sway { -class Scratchpad : public ALabel { +class Scratchpad final : public ALabel { public: Scratchpad(const std::string&, const Json::Value&); virtual ~Scratchpad() = default; diff --git a/include/modules/sway/window.hpp b/include/modules/sway/window.hpp index 427c2e81a..6394146dd 100644 --- a/include/modules/sway/window.hpp +++ b/include/modules/sway/window.hpp @@ -1,18 +1,13 @@ #pragma once -#include - -#include - #include "AAppIconLabel.hpp" -#include "bar.hpp" #include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" namespace waybar::modules::sway { -class Window : public AAppIconLabel, public sigc::trackable { +class Window final : public AAppIconLabel, public sigc::trackable { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); virtual ~Window() = default; diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 97f4e9503..9740df7cf 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -1,14 +1,8 @@ #pragma once -#include #include #include -#include -#include - -#include "AModule.hpp" -#include "bar.hpp" #include "client.hpp" #include "modules/sway/ipc/client.hpp" #include "util/json.hpp" @@ -16,11 +10,12 @@ namespace waybar::modules::sway { -class Workspaces : public AModule, public sigc::trackable { +class Workspaces final : public AModule, public sigc::trackable { public: Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); ~Workspaces() override = default; auto update() -> void override; + operator Gtk::Widget&() override; private: static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\""; @@ -41,7 +36,7 @@ class Workspaces : public AModule, public sigc::trackable { std::string getCycleWorkspace(std::vector::iterator, bool prev) const; uint16_t getWorkspaceIndex(const std::string& name) const; static std::string trimWorkspaceName(std::string); - bool handleScroll(GdkEventScroll* /*unused*/) override; + bool handleScroll(double dx, double dy) override; const Bar& bar_; std::vector workspaces_; diff --git a/include/modules/systemd_failed_units.hpp b/include/modules/systemd_failed_units.hpp index 9c3fbcee1..26ad44487 100644 --- a/include/modules/systemd_failed_units.hpp +++ b/include/modules/systemd_failed_units.hpp @@ -8,7 +8,7 @@ namespace waybar::modules { -class SystemdFailedUnits : public ALabel { +class SystemdFailedUnits final : public ALabel { public: SystemdFailedUnits(const std::string &, const Json::Value &); virtual ~SystemdFailedUnits(); diff --git a/include/modules/temperature.hpp b/include/modules/temperature.hpp index 918281be5..3fb4185f3 100644 --- a/include/modules/temperature.hpp +++ b/include/modules/temperature.hpp @@ -1,15 +1,11 @@ #pragma once -#include - -#include - #include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { -class Temperature : public ALabel { +class Temperature final : public ALabel { public: Temperature(const std::string&, const Json::Value&); virtual ~Temperature() = default; diff --git a/include/modules/ui.hpp b/include/modules/ui.hpp new file mode 100644 index 000000000..a50a0d92a --- /dev/null +++ b/include/modules/ui.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "AModule.hpp" + +namespace waybar::modules { + +class UI final : public AModule { + public: + UI(const std::string&, const std::string&, const Json::Value&); + virtual ~UI() = default; + operator Gtk::Widget&() override; + + private: + Glib::RefPtr uiWg_; +}; + +} // namespace waybar::modules diff --git a/include/modules/upower.hpp b/include/modules/upower.hpp index 60a276dbf..7f87bdd12 100644 --- a/include/modules/upower.hpp +++ b/include/modules/upower.hpp @@ -37,8 +37,9 @@ class UPower final : public AIconLabel { guint64 time_empty{0u}; gchar *icon_name{(char *)'\0'}; bool upDeviceValid{false}; - UpDeviceState state; - UpDeviceKind kind; + UpDeviceState state{UP_DEVICE_STATE_UNKNOWN}; + UpDeviceLevel level{UP_DEVICE_LEVEL_UNKNOWN}; + UpDeviceKind kind{UP_DEVICE_KIND_UNKNOWN}; char *nativePath{(char *)'\0'}; char *model{(char *)'\0'}; }; @@ -50,6 +51,7 @@ class UPower final : public AIconLabel { Glib::ustring label_markup_; std::mutex mutex_; Glib::RefPtr gtkTheme_; + const char *lastWarningLevel_{nullptr}; bool sleeping_; // Technical functions diff --git a/include/modules/user.hpp b/include/modules/user.hpp index bcb03da47..ffc01c28f 100644 --- a/include/modules/user.hpp +++ b/include/modules/user.hpp @@ -1,20 +1,15 @@ #pragma once -#include -#include -#include - #include "AIconLabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { -class User : public AIconLabel { +class User final : public AIconLabel { public: User(const std::string&, const Json::Value&); virtual ~User() = default; auto update() -> void override; - - bool handleToggle(GdkEventButton* const& e) override; + void handleToggle(int n_press, double dx, double dy) override; private: util::SleeperThread thread_; diff --git a/include/modules/wireplumber.hpp b/include/modules/wireplumber.hpp index 6255b95fd..f88c7133e 100644 --- a/include/modules/wireplumber.hpp +++ b/include/modules/wireplumber.hpp @@ -10,7 +10,7 @@ namespace waybar::modules { -class Wireplumber : public ALabel { +class Wireplumber final : public ALabel { public: Wireplumber(const std::string&, const Json::Value&); virtual ~Wireplumber(); @@ -30,7 +30,7 @@ class Wireplumber : public ALabel { static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id); static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self); - bool handleScroll(GdkEventScroll* e) override; + bool handleScroll(double dx, double dy) override; WpCore* wp_core_; GPtrArray* apis_; diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 07110ddee..67606199c 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -1,31 +1,21 @@ #pragma once -#include -#include -#include +#include #include #include #include #include -#include -#include -#include -#include #include -#include -#include "AModule.hpp" #include "bar.hpp" -#include "client.hpp" -#include "giomm/desktopappinfo.h" -#include "util/json.hpp" #include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" namespace waybar::modules::wlr { struct widget_geometry { - int x, y, w, h; + double x, y; + int w, h; }; class Taskbar; @@ -45,13 +35,13 @@ class Task { INVALID = (1 << 4) }; // made public so TaskBar can reorder based on configuration. - Gtk::Button button; + Glib::RefPtr const button; struct widget_geometry minimize_hint; private: static uint32_t global_id; + Glib::RefPtr const controllClick_; - private: const waybar::Bar &bar_; const Json::Value &config_; Taskbar *tbar_; @@ -80,15 +70,9 @@ class Task { std::string app_id_; uint32_t state_ = 0; - int32_t drag_start_x; - int32_t drag_start_y; - int32_t drag_start_button = -1; - - private: std::string repr() const; std::string state_string(bool = false) const; void set_minimize_hint(); - void on_button_size_allocated(Gtk::Allocation &alloc); void set_app_info_from_app_id_list(const std::string &app_id_list); bool image_load_icon(Gtk::Image &image, const Glib::RefPtr &icon_theme, Glib::RefPtr app_info, int size); @@ -105,7 +89,6 @@ class Task { bool active() const { return state_ & ACTIVE; } bool fullscreen() const { return state_ & FULLSCREEN; } - public: /* Callbacks for the wlr protocol */ void handle_title(const char *); void handle_app_id(const char *); @@ -116,22 +99,14 @@ class Task { void handle_closed(); /* Callbacks for Gtk events */ - bool handle_clicked(GdkEventButton *); - bool handle_button_release(GdkEventButton *); - bool handle_motion_notify(GdkEventMotion *); - void handle_drag_data_get(const Glib::RefPtr &context, - Gtk::SelectionData &selection_data, guint info, guint time); - void handle_drag_data_received(const Glib::RefPtr &context, int x, int y, - Gtk::SelectionData selection_data, guint info, guint time); + void handleClick(int n_press, double dx, double dy); + bool handleDropData(const Glib::ValueBase &, double, double); - public: bool operator==(const Task &) const; bool operator!=(const Task &) const; - public: void update(); - public: /* Interaction with the tasks */ void maximize(bool); void minimize(bool); @@ -147,6 +122,7 @@ class Taskbar : public waybar::AModule { Taskbar(const std::string &, const waybar::Bar &, const Json::Value &); ~Taskbar(); void update(); + operator Gtk::Widget &() override; private: const waybar::Bar &bar_; @@ -169,10 +145,9 @@ class Taskbar : public waybar::AModule { void handle_toplevel_create(struct zwlr_foreign_toplevel_handle_v1 *); void handle_finished(); - public: - void add_button(Gtk::Button &); - void move_button(Gtk::Button &, int); - void remove_button(Gtk::Button &); + void add_button(Glib::RefPtr); + void move_button(Glib::RefPtr, int); + void remove_button(Glib::RefPtr); void remove_task(uint32_t); bool show_output(struct wl_output *) const; diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index f7cc759ec..4818e58a2 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -1,16 +1,8 @@ #pragma once -#include #include -#include #include -#include -#include -#include -#include - -#include "AModule.hpp" #include "bar.hpp" #include "ext-workspace-unstable-v1-client-protocol.h" @@ -41,7 +33,7 @@ class Workspace { auto handle_duplicate() -> void; auto handle_done() -> void; - auto handle_clicked(GdkEventButton *bt) -> bool; + void handleClick(int n_press, double dx, double dy); auto show() -> void; auto hide() -> void; auto get_button_ref() -> Gtk::Button & { return button_; } @@ -75,6 +67,7 @@ class Workspace { bool persistent_ = false; Gtk::Button button_; + Glib::RefPtr const controllClick_; Gtk::Box content_; Gtk::Label label_; }; @@ -133,6 +126,7 @@ class WorkspaceManager : public AModule { WorkspaceManager(const std::string &id, const waybar::Bar &bar, const Json::Value &config); ~WorkspaceManager() override; auto update() -> void override; + operator Gtk::Widget &() override; auto all_outputs() const -> bool { return all_outputs_; } auto active_only() const -> bool { return active_only_; } diff --git a/include/util/SafeSignal.hpp b/include/util/SafeSignal.hpp index 458bc57ec..53f2cc37e 100644 --- a/include/util/SafeSignal.hpp +++ b/include/util/SafeSignal.hpp @@ -1,15 +1,10 @@ #pragma once #include -#include -#include #include #include #include -#include -#include -#include namespace waybar { @@ -35,7 +30,7 @@ struct SafeSignal : sigc::signal...)> { signal_t::emit(std::forward(args)...); } else { { - std::unique_lock lock(mutex_); + std::unique_lock lock{mutex_}; queue_.emplace(std::forward(args)...); } dp_.emit(); @@ -52,12 +47,12 @@ struct SafeSignal : sigc::signal...)> { using slot_t = decltype(std::declval().make_slot()); using arg_tuple_t = std::tuple...>; // ensure that unwrapped methods are not accessible - using signal_t::emit_reverse; + using signal_t::emit; using signal_t::make_slot; void handle_event() { - for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) { - auto args = queue_.front(); + for (std::unique_lock lock{mutex_}; !queue_.empty(); lock.lock()) { + auto args{queue_.front()}; queue_.pop(); lock.unlock(); std::apply(cached_fn_, args); @@ -67,9 +62,9 @@ struct SafeSignal : sigc::signal...)> { Glib::Dispatcher dp_; std::mutex mutex_; std::queue queue_; - const std::thread::id main_tid_ = std::this_thread::get_id(); + const std::thread::id main_tid_{std::this_thread::get_id()}; // cache functor for signal emission to avoid recreating it on each event - const slot_t cached_fn_ = make_slot(); + const slot_t cached_fn_{make_slot()}; }; } // namespace waybar diff --git a/include/util/audio_backend.hpp b/include/util/audio_backend.hpp index 3737ae264..c73fe8d3a 100644 --- a/include/util/audio_backend.hpp +++ b/include/util/audio_backend.hpp @@ -1,14 +1,10 @@ #pragma once #include -#include #include #include -#include #include -#include -#include #include "util/backend_common.hpp" @@ -95,4 +91,4 @@ class AudioBackend { bool isBluetooth(); }; -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util diff --git a/include/util/backend_common.hpp b/include/util/backend_common.hpp index dda6ac57b..6f6f348bd 100644 --- a/include/util/backend_common.hpp +++ b/include/util/backend_common.hpp @@ -1,10 +1,8 @@ #pragma once -#include "AModule.hpp" - namespace waybar::util { const static auto NOOP = []() {}; enum class ChangeType : char { Increase, Decrease }; -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util diff --git a/include/util/backlight_backend.hpp b/include/util/backlight_backend.hpp index eb42d3ccf..5b39c334d 100644 --- a/include/util/backlight_backend.hpp +++ b/include/util/backlight_backend.hpp @@ -1,14 +1,6 @@ #pragma once #include -#include - -#include -#include -#include -#include -#include -#include #include "giomm/dbusproxy.h" #include "util/backend_common.hpp" @@ -42,9 +34,9 @@ class BacklightDevice { private: std::string name_; - int actual_ = 1; - int max_ = 1; - bool powered_ = true; + int actual_{1}; + int max_{1}; + bool powered_{true}; }; class BacklightBackend { @@ -81,7 +73,7 @@ class BacklightBackend { Glib::RefPtr login_proxy_; - static constexpr int EPOLL_MAX_EVENTS = 16; + static constexpr int EPOLL_MAX_EVENTS{16}; }; } // namespace waybar::util diff --git a/include/util/css_reload_helper.hpp b/include/util/css_reload_helper.hpp index 032b23826..b5d6ba5dc 100644 --- a/include/util/css_reload_helper.hpp +++ b/include/util/css_reload_helper.hpp @@ -1,14 +1,14 @@ #pragma once +#include +#include +#include + #include #include #include #include -#include "giomm/file.h" -#include "giomm/filemonitor.h" -#include "glibmm/refptr.h" - struct pollfd; namespace waybar { @@ -37,7 +37,7 @@ class CssReloadHelper { void handleFileChange(Glib::RefPtr const& file, Glib::RefPtr const& other_type, - Gio::FileMonitorEvent event_type); + Gio::FileMonitor::Event event_type); private: std::string m_cssFile; diff --git a/include/util/format.hpp b/include/util/format.hpp index c8ed837a5..2321fdbff 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -16,12 +16,12 @@ class pow_format { namespace fmt { template <> struct formatter { - char spec = 0; - int width = 0; + char spec{0}; + int width{0}; template constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); + auto it{ctx.begin()}, end{ctx.end()}; if (it != end && *it == ':') ++it; if (it && (*it == '>' || *it == '<' || *it == '=')) { spec = *it; @@ -46,21 +46,21 @@ struct formatter { template auto format(const pow_format& s, FormatContext& ctx) const -> decltype(ctx.out()) { - const char* units[] = {"", "k", "M", "G", "T", "P", nullptr}; + const char* units[]{"", "k", "M", "G", "T", "P", nullptr}; - auto base = s.binary_ ? 1024ull : 1000ll; - auto fraction = (double)s.val_; + auto base{s.binary_ ? 1024ull : 1000ll}; + auto fraction{(double)s.val_}; int pow; for (pow = 0; units[pow + 1] != nullptr && fraction / base >= 1; ++pow) { fraction /= base; } - auto number_width = 5 // coeff in {:.1f} format - + s.binary_; // potential 4th digit before the decimal point - auto max_width = number_width + 1 // prefix from units array - + s.binary_ // for the 'i' in GiB. - + s.unit_.length(); + auto number_width{5 // coeff in {:.1f} format + + s.binary_}; // potential 4th digit before the decimal point + auto max_width{number_width + 1 // prefix from units array + + s.binary_ // for the 'i' in GiB. + + s.unit_.length()}; const char* format; std::string string; diff --git a/include/util/gtk_icon.hpp b/include/util/gtk_icon.hpp deleted file mode 100644 index 44555f652..000000000 --- a/include/util/gtk_icon.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include - -#include -#include - -class DefaultGtkIconThemeWrapper { - private: - static std::mutex default_theme_mutex; - - public: - static bool has_icon(const std::string&); - static Glib::RefPtr load_icon(const char*, int, Gtk::IconLookupFlags); -}; diff --git a/include/util/pipewire/pipewire_backend.hpp b/include/util/pipewire/pipewire_backend.hpp index 90fb2bb22..8d80f4d5d 100644 --- a/include/util/pipewire/pipewire_backend.hpp +++ b/include/util/pipewire/pipewire_backend.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -9,7 +10,7 @@ namespace waybar::util::PipewireBackend { -class PipewireBackend { +class PipewireBackend final { private: pw_thread_loop* mainloop_; pw_context* context_; @@ -27,7 +28,7 @@ class PipewireBackend { struct PrivateConstructorTag {}; public: - sigc::signal privacy_nodes_changed_signal_event; + sigc::signal privacy_nodes_changed_signal_event; std::unordered_map privacy_nodes; std::mutex mutex_; diff --git a/include/util/pipewire/privacy_node_info.hpp b/include/util/pipewire/privacy_node_info.hpp index 7b8df0181..8715c009b 100644 --- a/include/util/pipewire/privacy_node_info.hpp +++ b/include/util/pipewire/privacy_node_info.hpp @@ -1,11 +1,8 @@ #pragma once +#include #include -#include - -#include "util/gtk_icon.hpp" - namespace waybar::util::PipewireBackend { enum PrivacyNodeType { @@ -15,31 +12,30 @@ enum PrivacyNodeType { PRIVACY_NODE_TYPE_AUDIO_OUTPUT }; -class PrivacyNodeInfo { +class PrivacyNodeInfo final { public: - PrivacyNodeType type = PRIVACY_NODE_TYPE_NONE; uint32_t id; - uint32_t client_id; + PrivacyNodeType type = PRIVACY_NODE_TYPE_NONE; enum pw_node_state state = PW_NODE_STATE_IDLE; + void *data; std::string media_class; - std::string media_name; - std::string node_name; - std::string application_name; - - std::string pipewire_access_portal_app_id; - std::string application_icon_name; - struct spa_hook object_listener; struct spa_hook proxy_listener; - void *data; - std::string getName(); - std::string getIconName(); - + std::string getIconName(const Glib::RefPtr theme); // Handlers for PipeWire events void handleProxyEventDestroy(); void handleNodeEventInfo(const struct pw_node_info *info); + + private: + uint32_t client_id; + std::string media_name; + std::string node_name; + std::string application_name; + + std::string pipewire_access_portal_app_id; + std::string application_icon_name; }; } // namespace waybar::util::PipewireBackend diff --git a/include/util/portal.hpp b/include/util/portal.hpp index 236191691..05458c7f2 100644 --- a/include/util/portal.hpp +++ b/include/util/portal.hpp @@ -19,7 +19,7 @@ class Portal : private DBus::Proxy { void refreshAppearance(); Appearance getAppearance(); - typedef sigc::signal type_signal_appearance_changed; + typedef sigc::signal type_signal_appearance_changed; type_signal_appearance_changed signal_appearance_changed() { return m_signal_appearance_changed; } private: diff --git a/include/util/regex_collection.hpp b/include/util/regex_collection.hpp index 30d26d4a8..3da487f52 100644 --- a/include/util/regex_collection.hpp +++ b/include/util/regex_collection.hpp @@ -5,7 +5,6 @@ #include #include #include -#include namespace waybar::util { diff --git a/include/util/rfkill.hpp b/include/util/rfkill.hpp index f620db534..8021eb4fd 100644 --- a/include/util/rfkill.hpp +++ b/include/util/rfkill.hpp @@ -2,12 +2,10 @@ #include #include -#include -#include namespace waybar::util { -class Rfkill : public sigc::trackable { +class Rfkill final : public sigc::trackable { public: Rfkill(enum rfkill_type rfkill_type); ~Rfkill(); @@ -17,8 +15,8 @@ class Rfkill : public sigc::trackable { private: enum rfkill_type rfkill_type_; - bool state_ = false; - int fd_ = -1; + bool state_{false}; + int fd_{-1}; bool on_event(Glib::IOCondition cond); }; diff --git a/meson.build b/meson.build index 726d492bb..c1239366b 100644 --- a/meson.build +++ b/meson.build @@ -1,10 +1,10 @@ project( 'waybar', 'cpp', 'c', - version: '0.11.0', + version: '4.1.0', license: 'MIT', - meson_version: '>= 0.59.0', + meson_version: '>= 1.3.0', default_options : [ - 'cpp_std=c++20', + 'cpp_std=c++23', 'buildtype=release', 'default_library=static' ], @@ -67,17 +67,16 @@ is_freebsd = host_machine.system() == 'freebsd' is_netbsd = host_machine.system() == 'netbsd' is_openbsd = host_machine.system() == 'openbsd' -thread_dep = dependency('threads') fmt = dependency('fmt', version : ['>=8.1.1'], fallback : ['fmt', 'fmt_dep']) spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled', 'std_format=disabled', 'tests=disabled']) +jsoncpp = dependency('jsoncpp', version : ['>=1.9.6'], fallback : ['jsoncpp', 'jsoncpp_dep']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') -gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0']) +gtkmm = dependency('gtkmm-4.0', version : ['>=4.16.0']) dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk')) -giounix = dependency('gio-unix-2.0') -jsoncpp = dependency('jsoncpp', version : ['>=1.9.2'], fallback : ['jsoncpp', 'jsoncpp_dep']) -sigcpp = dependency('sigc++-2.0') +giounix = dependency('gio-unix-2.0', version: ['>=2.76.4']) +sigcpp = dependency('sigc++-3.0', version: ['>=3.4.0']) libinotify = dependency('libinotify', required: false) libepoll = dependency('epoll-shim', required: false) libinput = dependency('libinput', required: get_option('libinput')) @@ -106,9 +105,11 @@ if libsndio.found() endif endif -gtk_layer_shell = dependency('gtk-layer-shell-0', version: ['>=0.9.0'], - default_options: ['introspection=false', 'vapi=false'], - fallback: ['gtk-layer-shell', 'gtk_layer_shell']) +gtk_layer_shell = dependency('gtk4-layer-shell-0', + version : ['>=1.0.3'], + default_options : ['introspection=false', 'vapi=false'], + fallback : ['gtk4-layer-shell', 'gtk_layer_shell']) + systemd = dependency('systemd', required: get_option('systemd')) cpp_lib_chrono = compiler.compute_int('__cpp_lib_chrono', prefix : '#include ') @@ -181,10 +182,9 @@ src_files = files( 'src/util/ustring_clen.cpp', 'src/util/sanitize_str.cpp', 'src/util/rewrite_string.cpp', - 'src/util/gtk_icon.cpp', 'src/util/regex_collection.cpp', - 'src/util/css_reload_helper.cpp' -) + 'src/util/css_reload_helper.cpp', + 'src/modules/ui.cpp') man_files = files( 'man/waybar-custom.5.scd', @@ -249,88 +249,67 @@ elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd endif endif -if true - add_project_arguments('-DHAVE_SWAY', language: 'cpp') - src_files += files( - 'src/modules/sway/ipc/client.cpp', - 'src/modules/sway/bar.cpp', - 'src/modules/sway/mode.cpp', - 'src/modules/sway/language.cpp', - 'src/modules/sway/window.cpp', - 'src/modules/sway/workspaces.cpp', - 'src/modules/sway/scratchpad.cpp' - ) - man_files += files( - 'man/waybar-sway-language.5.scd', - 'man/waybar-sway-mode.5.scd', - 'man/waybar-sway-scratchpad.5.scd', - 'man/waybar-sway-window.5.scd', - 'man/waybar-sway-workspaces.5.scd', - ) -endif - -if true - add_project_arguments('-DHAVE_WLR_TASKBAR', language: 'cpp') - src_files += files('src/modules/wlr/taskbar.cpp') - man_files += files('man/waybar-wlr-taskbar.5.scd') -endif - -if true - add_project_arguments('-DHAVE_RIVER', language: 'cpp') - src_files += files( - 'src/modules/river/layout.cpp', - 'src/modules/river/mode.cpp', - 'src/modules/river/tags.cpp', - 'src/modules/river/window.cpp', - ) - man_files += files( - 'man/waybar-river-layout.5.scd', - 'man/waybar-river-mode.5.scd', - 'man/waybar-river-tags.5.scd', - 'man/waybar-river-window.5.scd', - ) -endif - -if true - add_project_arguments('-DHAVE_DWL', language: 'cpp') - src_files += files('src/modules/dwl/tags.cpp') - src_files += files('src/modules/dwl/window.cpp') - man_files += files('man/waybar-dwl-tags.5.scd') - man_files += files('man/waybar-dwl-window.5.scd') -endif - -if true - add_project_arguments('-DHAVE_HYPRLAND', language: 'cpp') - src_files += files( - 'src/modules/hyprland/backend.cpp', - 'src/modules/hyprland/language.cpp', - 'src/modules/hyprland/submap.cpp', - 'src/modules/hyprland/window.cpp', - 'src/modules/hyprland/workspace.cpp', - 'src/modules/hyprland/workspaces.cpp', - 'src/modules/hyprland/windowcreationpayload.cpp', - ) - man_files += files( - 'man/waybar-hyprland-language.5.scd', - 'man/waybar-hyprland-submap.5.scd', - 'man/waybar-hyprland-window.5.scd', - 'man/waybar-hyprland-workspaces.5.scd', - ) -endif +add_project_arguments('-DHAVE_SWAY', language: 'cpp') +src_files += files('src/modules/sway/ipc/client.cpp', + 'src/modules/sway/bar.cpp', + 'src/modules/sway/mode.cpp', + 'src/modules/sway/language.cpp', + 'src/modules/sway/window.cpp', + 'src/modules/sway/workspaces.cpp', + 'src/modules/sway/scratchpad.cpp') + +man_files += files('man/waybar-sway-language.5.scd', + 'man/waybar-sway-mode.5.scd', + 'man/waybar-sway-scratchpad.5.scd', + 'man/waybar-sway-window.5.scd', + 'man/waybar-sway-workspaces.5.scd') + +add_project_arguments('-DHAVE_WLR_TASKBAR', language: 'cpp') +src_files += files('src/modules/wlr/taskbar.cpp') +man_files += files('man/waybar-wlr-taskbar.5.scd') + +add_project_arguments('-DHAVE_RIVER', language: 'cpp') +src_files += files('src/modules/river/layout.cpp', + 'src/modules/river/mode.cpp', + 'src/modules/river/tags.cpp', + 'src/modules/river/window.cpp') +man_files += files('man/waybar-river-layout.5.scd', + 'man/waybar-river-mode.5.scd', + 'man/waybar-river-tags.5.scd', + 'man/waybar-river-window.5.scd') + +add_project_arguments('-DHAVE_DWL', language: 'cpp') +src_files += files('src/modules/dwl/tags.cpp', + 'src/modules/dwl/window.cpp') +man_files += files('man/waybar-dwl-tags.5.scd', + 'man/waybar-dwl-window.5.scd') + +add_project_arguments('-DHAVE_HYPRLAND', language: 'cpp') +src_files += files('src/modules/hyprland/backend.cpp', + 'src/modules/hyprland/language.cpp', + 'src/modules/hyprland/submap.cpp', + 'src/modules/hyprland/window.cpp', + 'src/modules/hyprland/workspace.cpp', + 'src/modules/hyprland/workspaces.cpp', + 'src/modules/hyprland/windowcreationpayload.cpp') +man_files += files('man/waybar-hyprland-language.5.scd', + 'man/waybar-hyprland-submap.5.scd', + 'man/waybar-hyprland-window.5.scd', + 'man/waybar-hyprland-workspaces.5.scd') if get_option('niri') - add_project_arguments('-DHAVE_NIRI', language: 'cpp') - src_files += files( - 'src/modules/niri/backend.cpp', - 'src/modules/niri/language.cpp', - 'src/modules/niri/window.cpp', - 'src/modules/niri/workspaces.cpp', - ) - man_files += files( - 'man/waybar-niri-language.5.scd', - 'man/waybar-niri-window.5.scd', - 'man/waybar-niri-workspaces.5.scd', - ) +add_project_arguments('-DHAVE_NIRI', language: 'cpp') +src_files += files( + 'src/modules/niri/backend.cpp', + 'src/modules/niri/language.cpp', + 'src/modules/niri/window.cpp', + 'src/modules/niri/workspaces.cpp', +) +man_files += files( + 'man/waybar-niri-language.5.scd', + 'man/waybar-niri-window.5.scd', + 'man/waybar-niri-workspaces.5.scd', +) endif if libnl.found() and libnlgen.found() @@ -357,7 +336,6 @@ if (upower_glib.found() and not get_option('logind').disabled()) man_files += files('man/waybar-upower.5.scd') endif - if pipewire.found() add_project_arguments('-DHAVE_PIPEWIRE', language: 'cpp') src_files += files( @@ -371,7 +349,7 @@ endif if playerctl.found() add_project_arguments('-DHAVE_MPRIS', language: 'cpp') - src_files += files('src/modules/mpris/mpris.cpp') + src_files += files('src/modules/mpris.cpp') man_files += files('man/waybar-mpris.5.scd') endif @@ -400,30 +378,25 @@ if libwireplumber.found() man_files += files('man/waybar-wireplumber.5.scd') endif +if 1 == 0 if dbusmenu_gtk.found() add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp') src_files += files( 'src/modules/sni/tray.cpp', 'src/modules/sni/watcher.cpp', 'src/modules/sni/host.cpp', - 'src/modules/sni/item.cpp' - ) - man_files += files( - 'man/waybar-tray.5.scd', - ) + 'src/modules/sni/item.cpp') + man_files += files('man/waybar-tray.5.scd') +endif endif if libudev.found() and (is_linux or libepoll.found()) add_project_arguments('-DHAVE_LIBUDEV', language: 'cpp') - src_files += files( - 'src/modules/backlight.cpp', - 'src/modules/backlight_slider.cpp', - 'src/util/backlight_backend.cpp', - ) - man_files += files( - 'man/waybar-backlight.5.scd', - 'man/waybar-backlight-slider.5.scd', - ) + src_files += files('src/modules/backlight.cpp', + 'src/modules/backlight_slider.cpp', + 'src/util/backlight_backend.cpp') + man_files += files('man/waybar-backlight.5.scd', + 'man/waybar-backlight-slider.5.scd') endif if libevdev.found() and (is_linux or libepoll.found()) and libinput.found() and (is_linux or libinotify.found()) @@ -435,13 +408,9 @@ endif if libmpdclient.found() add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') - src_files += files( - 'src/modules/mpd/mpd.cpp', - 'src/modules/mpd/state.cpp', - ) - man_files += files( - 'man/waybar-mpd.5.scd', - ) + src_files += files('src/modules/mpd/mpd.cpp', + 'src/modules/mpd/state.cpp') + man_files += files('man/waybar-mpd.5.scd') endif if libsndio.found() @@ -452,9 +421,7 @@ endif if get_option('rfkill').enabled() and is_linux add_project_arguments('-DWANT_RFKILL', language: 'cpp') - src_files += files( - 'src/util/rfkill.cpp' - ) + src_files += files('src/util/rfkill.cpp') endif if have_chrono_timezones @@ -472,13 +439,9 @@ endif if get_option('experimental') add_project_arguments('-DHAVE_WLR_WORKSPACES', language: 'cpp') - src_files += files( - 'src/modules/wlr/workspace_manager.cpp', - 'src/modules/wlr/workspace_manager_binding.cpp', - ) - man_files += files( - 'man/waybar-wlr-workspaces.5.scd', - ) + src_files += files('src/modules/wlr/workspace_manager.cpp', + 'src/modules/wlr/workspace_manager_binding.cpp') + man_files += files('man/waybar-wlr-workspaces.5.scd') endif cava = dependency('cava', @@ -495,43 +458,36 @@ endif subdir('protocol') -app_resources = [] -subdir('resources/icons') - executable( 'waybar', - [src_files, app_resources], + [src_files], dependencies: [ - thread_dep, - client_protos, - wayland_client, - fmt, - spdlog, + gtk_layer_shell, + gtkmm, + giounix, sigcpp, + libevdev, + libudev, jsoncpp, + wayland_client, wayland_cursor, - gtkmm, - dbusmenu_gtk, - giounix, + client_protos, + spdlog, + xkbregistry, + cava, + libinotify, + libepoll, libinput, libnl, libnlgen, - upower_glib, - pipewire, playerctl, libpulse, + libmpdclient, libjack, libwireplumber, - libudev, - libinotify, - libepoll, - libmpdclient, - libevdev, - gtk_layer_shell, libsndio, - tz_dep, - xkbregistry, - cava + upower_glib, + pipewire ], include_directories: inc_dirs, install: true, @@ -543,6 +499,11 @@ install_data( install_dir: sysconfdir / 'xdg/waybar' ) +install_subdir( + 'resources/ui', + install_dir: sysconfdir / 'xdg/waybar' +) + scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() diff --git a/resources/custom_modules/cffi_example/main.c b/resources/custom_modules/cffi_example/main.c index ba2c8cf46..4f5f3994b 100644 --- a/resources/custom_modules/cffi_example/main.c +++ b/resources/custom_modules/cffi_example/main.c @@ -3,7 +3,6 @@ typedef struct { wbcffi_module* waybar_module; - GtkBox* container; GtkButton* button; } ExampleMod; @@ -30,24 +29,22 @@ void* wbcffi_init(const wbcffi_init_info* init_info, const wbcffi_config_entry* ExampleMod* inst = malloc(sizeof(ExampleMod)); inst->waybar_module = init_info->obj; - GtkContainer* root = init_info->get_root_widget(init_info->obj); - - // Add a container for displaying the next widgets - inst->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5)); - gtk_container_add(GTK_CONTAINER(root), GTK_WIDGET(inst->container)); + GtkWidget* root = init_info->get_root_widget(init_info->obj); + gtk_box_set_spacing(GTK_BOX(root), 5); + gtk_orientable_set_orientation(GTK_ORIENTABLE(root), GTK_ORIENTATION_HORIZONTAL); // Add a label GtkLabel* label = GTK_LABEL(gtk_label_new("[Example C FFI Module:")); - gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(label)); + gtk_box_append(GTK_BOX(root), GTK_WIDGET(label)); // Add a button inst->button = GTK_BUTTON(gtk_button_new_with_label("click me !")); g_signal_connect(inst->button, "clicked", G_CALLBACK(onclicked), NULL); - gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(inst->button)); + gtk_box_append(GTK_BOX(root), GTK_WIDGET(inst->button)); // Add a label label = GTK_LABEL(gtk_label_new("]")); - gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(label)); + gtk_box_append(GTK_BOX(root), GTK_WIDGET(label)); // Return instance object printf("cffi_example inst=%p: init success ! (%d total instances)\n", inst, ++instance_count); @@ -67,4 +64,4 @@ void wbcffi_refresh(void* instance, int signal) { void wbcffi_doaction(void* instance, const char* name) { printf("cffi_example inst=%p: doAction(%s)\n", instance, name); -} \ No newline at end of file +} diff --git a/resources/custom_modules/cffi_example/meson.build b/resources/custom_modules/cffi_example/meson.build index dcde10482..f4adad5e8 100644 --- a/resources/custom_modules/cffi_example/meson.build +++ b/resources/custom_modules/cffi_example/meson.build @@ -1,13 +1,13 @@ project( 'waybar_cffi_example', 'c', - version: '0.1.0', + version: '4.1.0', license: 'MIT', ) shared_library('wb_cffi_example', ['main.c'], dependencies: [ - dependency('gtk+-3.0', version : ['>=3.22.0']) + dependency('gtk4', version : ['>=4.17.0']) ], name_prefix: '' ) diff --git a/resources/custom_modules/cffi_example/waybar_cffi_module.h b/resources/custom_modules/cffi_example/waybar_cffi_module.h index a7886bea4..3f2836c94 100644 --- a/resources/custom_modules/cffi_example/waybar_cffi_module.h +++ b/resources/custom_modules/cffi_example/waybar_cffi_module.h @@ -23,7 +23,7 @@ typedef struct { /// Returns the waybar widget allocated for this module /// @param obj Waybar CFFI object pointer - GtkContainer* (*get_root_widget)(wbcffi_module* obj); + GtkWidget* (*get_root_widget)(wbcffi_module* obj); /// Queues a request for calling wbcffi_update() on the next GTK main event /// loop iteration diff --git a/resources/ui/ui-power.xml b/resources/ui/ui-power.xml new file mode 100644 index 000000000..6b45b780c --- /dev/null +++ b/resources/ui/ui-power.xml @@ -0,0 +1,43 @@ + + + + +
+ + Suspend + ui-power.doAction + loginctl suspend + + + Hibernate + ui-power.doAction + loginctl hibernate + + + Shutdown + ui-power.doAction + loginctl poweroff + +
+
+ + Reboot + ui-power.doAction + loginctl reboot + +
+
+ + + waybar-power-menu + Power menu + + true + false + true + false + true + GTK_ARROW_NONE + label + +
diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index 3f47eff18..32cc9c913 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -1,15 +1,11 @@ #include "AAppIconLabel.hpp" -#include #include #include #include #include #include -#include - -#include "util/gtk_icon.hpp" namespace waybar { @@ -17,6 +13,9 @@ AAppIconLabel::AAppIconLabel(const Json::Value& config, const std::string& name, const std::string& id, const std::string& format, uint16_t interval, bool ellipsize, bool enable_click, bool enable_scroll) : AIconLabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) { + // Get current theme + gtkTheme_ = Gtk::IconTheme::get_for_display(label_.get_display()); + // Icon size if (config["icon-size"].isUInt()) { app_icon_size_ = config["icon-size"].asUInt(); @@ -63,13 +62,13 @@ std::optional getDesktopFilePath(const std::string& app_identifier, return {}; } - const auto data_dirs = Glib::get_system_data_dirs(); + const auto data_dirs{Glib::get_system_data_dirs()}; for (const auto& data_dir : data_dirs) { - const auto data_app_dir = data_dir + "/applications/"; - auto desktop_file_suffix = app_identifier + ".desktop"; + const auto data_app_dir{data_dir + "/applications/"}; + auto desktop_file_suffix{app_identifier + ".desktop"}; // searching for file by suffix catches cases like terminal emulator "foot" where class is // "footclient" and desktop file is named "org.codeberg.dnkl.footclient.desktop" - auto desktop_file_path = getFileBySuffix(data_app_dir, desktop_file_suffix, true); + auto desktop_file_path{getFileBySuffix(data_app_dir, desktop_file_suffix, true)}; // "true" argument allows checking for lowercase - this catches cases where class name is // "LibreWolf" and desktop file is named "librewolf.desktop" if (desktop_file_path.has_value()) { @@ -87,32 +86,33 @@ std::optional getDesktopFilePath(const std::string& app_identifier, } std::optional getIconName(const std::string& app_identifier, - const std::string& alternative_app_identifier) { - const auto desktop_file_path = getDesktopFilePath(app_identifier, alternative_app_identifier); + const std::string& alternative_app_identifier, + const Glib::RefPtr gtkTheme) { + const auto desktop_file_path{getDesktopFilePath(app_identifier, alternative_app_identifier)}; if (!desktop_file_path.has_value()) { // Try some heuristics to find a matching icon - if (DefaultGtkIconThemeWrapper::has_icon(app_identifier)) { + if (gtkTheme->has_icon(app_identifier)) { return app_identifier; } - auto app_identifier_desktop = app_identifier + "-desktop"; - if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) { + const auto app_identifier_desktop{app_identifier + "-desktop"}; + if (gtkTheme->has_icon(app_identifier_desktop)) { return app_identifier_desktop; } - auto first_space = app_identifier.find_first_of(' '); + const auto first_space{app_identifier.find_first_of(' ')}; if (first_space != std::string::npos) { - auto first_word = toLowerCase(app_identifier.substr(0, first_space)); - if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { + const auto first_word{toLowerCase(app_identifier.substr(0, first_space))}; + if (gtkTheme->has_icon(first_word)) { return first_word; } } - const auto first_dash = app_identifier.find_first_of('-'); + const auto first_dash{app_identifier.find_first_of('-')}; if (first_dash != std::string::npos) { - auto first_word = toLowerCase(app_identifier.substr(0, first_dash)); - if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { + const auto first_word{toLowerCase(app_identifier.substr(0, first_dash))}; + if (gtkTheme->has_icon(first_word)) { return first_word; } } @@ -121,15 +121,15 @@ std::optional getIconName(const std::string& app_identifier, } try { - Glib::KeyFile desktop_file; - desktop_file.load_from_file(desktop_file_path.value()); - return desktop_file.get_string("Desktop Entry", "Icon"); + Glib::RefPtr desktop_file; + desktop_file->load_from_file(desktop_file_path.value()); + return desktop_file->get_string("Desktop Entry", "Icon"); } catch (Glib::FileError& error) { spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), - error.what().c_str()); + error.what()); } catch (Glib::KeyFileError& error) { spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), - error.what().c_str()); + error.what()); } return {}; } @@ -140,7 +140,7 @@ void AAppIconLabel::updateAppIconName(const std::string& app_identifier, return; } - const auto icon_name = getIconName(app_identifier, alternative_app_identifier); + const auto icon_name{getIconName(app_identifier, alternative_app_identifier, gtkTheme_)}; if (icon_name.has_value()) { app_icon_name_ = icon_name.value(); } else { @@ -155,16 +155,10 @@ void AAppIconLabel::updateAppIcon() { if (app_icon_name_.empty()) { image_.set_visible(false); } else if (app_icon_name_.front() == '/') { - auto pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_); - int scaled_icon_size = app_icon_size_ * image_.get_scale_factor(); - pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size); - - auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), - image_.get_window()); - image_.set(surface); + image_.set(app_icon_name_); image_.set_visible(true); } else { - image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); + image_.set_from_icon_name(app_icon_name_); image_.set_visible(true); } } diff --git a/src/AIconLabel.cpp b/src/AIconLabel.cpp index d7ee666e6..963805d03 100644 --- a/src/AIconLabel.cpp +++ b/src/AIconLabel.cpp @@ -1,14 +1,11 @@ #include "AIconLabel.hpp" -#include - namespace waybar { AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const std::string &id, const std::string &format, uint16_t interval, bool ellipsize, bool enable_click, bool enable_scroll) : ALabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) { - event_box_.remove(); label_.unset_name(); label_.get_style_context()->remove_class(MODULE_CLASS); box_.get_style_context()->add_class(MODULE_CLASS); @@ -17,16 +14,14 @@ AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const box_.get_style_context()->add_class(id); } - box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL); + box_.set_orientation(Gtk::Orientation::HORIZONTAL); box_.set_name(name); int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : 8; box_.set_spacing(spacing); - box_.add(image_); - box_.add(label_); - - event_box_.add(box_); + box_.append(image_); + box_.append(label_); } auto AIconLabel::update() -> void { @@ -38,4 +33,6 @@ bool AIconLabel::iconEnabled() const { return config_["icon"].isBool() ? config_["icon"].asBool() : false; } +AIconLabel::operator Gtk::Widget &() { return box_; }; + } // namespace waybar diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 467572f18..b174aaa36 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -1,39 +1,28 @@ #include "ALabel.hpp" -#include - -#include -#include -#include - -#include "config.hpp" - namespace waybar { ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id, const std::string& format, uint16_t interval, bool ellipsize, bool enable_click, bool enable_scroll) - : AModule(config, name, id, - config["format-alt"].isString() || config["menu"].isString() || enable_click, - enable_scroll), - format_(config_["format"].isString() ? config_["format"].asString() : format), - interval_(config_["interval"] == "once" + : AModule(config, name, id, config["format-alt"].isString() || enable_click, enable_scroll), + format_{config_["format"].isString() ? config_["format"].asString() : format}, + interval_{config_["interval"] == "once" ? std::chrono::seconds::max() : std::chrono::seconds( - config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)), - default_format_(format_) { + config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)}, + default_format_{format_} { label_.set_name(name); if (!id.empty()) { label_.get_style_context()->add_class(id); } label_.get_style_context()->add_class(MODULE_CLASS); - event_box_.add(label_); if (config_["max-length"].isUInt()) { label_.set_max_width_chars(config_["max-length"].asInt()); - label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); + label_.set_ellipsize(Pango::EllipsizeMode::END); label_.set_single_line_mode(true); } else if (ellipsize && label_.get_max_width_chars() == -1) { - label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); + label_.set_ellipsize(Pango::EllipsizeMode::END); label_.set_single_line_mode(true); } @@ -41,17 +30,18 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st label_.set_width_chars(config_["min-length"].asUInt()); } - uint rotate = 0; - + uint rotate{0}; + // gtk4 todo + /* if (config_["rotate"].isUInt()) { rotate = config["rotate"].asUInt(); if (not(rotate == 0 || rotate == 90 || rotate == 180 || rotate == 270)) spdlog::warn("'rotate' is only supported in 90 degree increments {} is not valid.", rotate); label_.set_angle(rotate); - } + }*/ if (config_["align"].isDouble()) { - auto align = config_["align"].asFloat(); + auto align{config_["align"].asFloat()}; if (rotate == 90 || rotate == 270) { label_.set_yalign(align); } else { @@ -59,71 +49,24 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st } } - // If a GTKMenu is requested in the config - if (config_["menu"].isString()) { - // Create the GTKMenu widget - try { - // Check that the file exists - std::string menuFile = config_["menu-file"].asString(); - - // there might be "~" or "$HOME" in original path, try to expand it. - auto result = Config::tryExpandPath(menuFile, ""); - if (!result.has_value()) { - throw std::runtime_error("Failed to expand file: " + menuFile); - } - - menuFile = result.value(); - // Read the menu descriptor file - std::ifstream file(menuFile); - if (!file.is_open()) { - throw std::runtime_error("Failed to open file: " + menuFile); - } - std::stringstream fileContent; - fileContent << file.rdbuf(); - GtkBuilder* builder = gtk_builder_new(); - - // Make the GtkBuilder and check for errors in his parsing - if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) { - throw std::runtime_error("Error found in the file " + menuFile); - } - - menu_ = gtk_builder_get_object(builder, "menu"); - if (menu_ == nullptr) { - throw std::runtime_error("Failed to get 'menu' object from GtkBuilder"); - } - submenus_ = std::map(); - menuActionsMap_ = std::map(); - - // Linking actions to the GTKMenu based on - for (Json::Value::const_iterator it = config_["menu-actions"].begin(); - it != config_["menu-actions"].end(); ++it) { - std::string key = it.key().asString(); - submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); - menuActionsMap_[key] = it->asString(); - g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), - (gpointer)menuActionsMap_[key].c_str()); - } - } catch (std::runtime_error& e) { - spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what()); - } - } - if (config_["justify"].isString()) { auto justify_str = config_["justify"].asString(); if (justify_str == "left") { - label_.set_justify(Gtk::Justification::JUSTIFY_LEFT); + label_.set_justify(Gtk::Justification::LEFT); } else if (justify_str == "right") { - label_.set_justify(Gtk::Justification::JUSTIFY_RIGHT); + label_.set_justify(Gtk::Justification::RIGHT); } else if (justify_str == "center") { - label_.set_justify(Gtk::Justification::JUSTIFY_CENTER); + label_.set_justify(Gtk::Justification::CENTER); } } + + AModule::bindEvents(*this); } auto ALabel::update() -> void { AModule::update(); } std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) { - auto format_icons = config_["format-icons"]; + auto format_icons{config_["format-icons"]}; if (format_icons.isObject()) { if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) { format_icons = format_icons[alt]; @@ -132,9 +75,9 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_ } } if (format_icons.isArray()) { - auto size = format_icons.size(); + auto size{format_icons.size()}; if (size != 0U) { - auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); + auto idx{std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1)}; format_icons = format_icons[idx]; } } @@ -146,9 +89,9 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_ std::string ALabel::getIcon(uint16_t percentage, const std::vector& alts, uint16_t max) { - auto format_icons = config_["format-icons"]; + auto format_icons{config_["format-icons"]}; if (format_icons.isObject()) { - std::string _alt = "default"; + std::string _alt{"default"}; for (const auto& alt : alts) { if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) { _alt = alt; @@ -158,9 +101,9 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector& format_icons = format_icons[_alt]; } if (format_icons.isArray()) { - auto size = format_icons.size(); + auto size{format_icons.size()}; if (size != 0U) { - auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); + auto idx{std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1)}; format_icons = format_icons[idx]; } } @@ -170,8 +113,9 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector& return ""; } -bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { - if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) { +void waybar::ALabel::handleToggle(int n_press, double dx, double dy) { + if (config_["format-alt-click"].isUInt() && + controllClick_->get_current_button() == config_["format-alt-click"].asUInt()) { alt_ = !alt_; if (alt_ && config_["format-alt"].isString()) { format_ = config_["format-alt"].asString(); @@ -179,11 +123,8 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { format_ = default_format_; } } - return AModule::handleToggle(e); -} -void ALabel::handleGtkMenuEvent(GtkMenuItem* /*menuitem*/, gpointer data) { - waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu"); + AModule::handleToggle(n_press, dx, dy); } std::string ALabel::getState(uint8_t value, bool lesser) { @@ -193,7 +134,7 @@ std::string ALabel::getState(uint8_t value, bool lesser) { // Get current state std::vector> states; if (config_["states"].isObject()) { - for (auto it = config_["states"].begin(); it != config_["states"].end(); ++it) { + for (auto it{config_["states"].begin()}; it != config_["states"].end(); ++it) { if (it->isUInt() && it.key().isString()) { states.emplace_back(it.key().asString(), it->asUInt()); } @@ -215,4 +156,6 @@ std::string ALabel::getState(uint8_t value, bool lesser) { return valid_state; } +ALabel::operator Gtk::Widget&() { return label_; }; + } // namespace waybar diff --git a/src/AModule.cpp b/src/AModule.cpp index c180b4801..d4a0c8958 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -1,13 +1,6 @@ #include "AModule.hpp" -#include -#include - #include - -#include "gdk/gdk.h" -#include "gdkmm/cursor.h" - namespace waybar { AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id, @@ -15,12 +8,14 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: : name_(name), config_(config), isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true}, - distance_scrolled_y_(0.0), - distance_scrolled_x_(0.0) { + enableClick_{enable_click}, + enableScroll_{enable_scroll}, + curDefault{Gdk::Cursor::create("default")}, + curPoint{Gdk::Cursor::create("pointer")} { // Configure module action Map const Json::Value actions{config_["actions"]}; - for (Json::Value::const_iterator it = actions.begin(); it != actions.end(); ++it) { + for (Json::Value::const_iterator it{actions.begin()}; it != actions.end(); ++it) { if (it.key().isString() && it->isString()) if (!eventActionMap_.contains(it.key().asString())) { eventActionMap_.insert({it.key().asString(), it->asString()}); @@ -32,49 +27,37 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); } - event_box_.signal_enter_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseEnter)); - event_box_.signal_leave_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave)); - // configure events' user commands // hasUserEvents is true if any element from eventMap_ is satisfying the condition in the lambda - bool hasUserEvents = + bool hasPressEvents{ std::find_if(eventMap_.cbegin(), eventMap_.cend(), [&config](const auto& eventEntry) { // True if there is any non-release type event - return eventEntry.first.second != GdkEventType::GDK_BUTTON_RELEASE && + return eventEntry.first.second != Gdk::Event::Type::BUTTON_RELEASE && config[eventEntry.second].isString(); - }) != eventMap_.cend(); - - if (enable_click || hasUserEvents) { - hasUserEvents_ = true; - event_box_.add_events(Gdk::BUTTON_PRESS_MASK); - event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); + }) != eventMap_.cend()}; + if (enable_click || hasPressEvents) { + hasPressEvents_ = true; } else { - hasUserEvents_ = false; + hasPressEvents_ = false; } - bool hasReleaseEvent = + hasReleaseEvents_ = std::find_if(eventMap_.cbegin(), eventMap_.cend(), [&config](const auto& eventEntry) { - // True if there is any non-release type event - return eventEntry.first.second == GdkEventType::GDK_BUTTON_RELEASE && + // True if there is any release type event + return eventEntry.first.second == Gdk::Event::Type::BUTTON_RELEASE && config[eventEntry.second].isString(); }) != eventMap_.cend(); - if (hasReleaseEvent) { - event_box_.add_events(Gdk::BUTTON_RELEASE_MASK); - event_box_.signal_button_release_event().connect(sigc::mem_fun(*this, &AModule::handleRelease)); - } - if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || - config_["on-scroll-left"].isString() || config_["on-scroll-right"].isString() || - enable_scroll) { - event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); - event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll)); - } + + makeControllClick(); + makeControllScroll(); + makeControllMotion(); // Respect user configuration of cursor if (config_.isMember("cursor")) { if (config_["cursor"].isBool() && config_["cursor"].asBool()) { - setCursor(Gdk::HAND2); - } else if (config_["cursor"].isInt()) { - setCursor(Gdk::CursorType(config_["cursor"].asInt())); + setCursor(curPoint); + } else if (config_["cursor"].isString()) { + setCursor(config_["cursor"].asString()); } else { spdlog::warn("unknown cursor option configured on module {}", name_); } @@ -105,74 +88,46 @@ auto AModule::doAction(const std::string& name) -> void { } } -void AModule::setCursor(Gdk::CursorType const& c) { - auto gdk_window = event_box_.get_window(); - if (gdk_window) { - auto cursor = Gdk::Cursor::create(c); - gdk_window->set_cursor(cursor); - } else { - // window may not be accessible yet, in this case, - // schedule another call for setting the cursor in 1 sec - Glib::signal_timeout().connect_seconds( - [this, c]() { - setCursor(c); - return false; - }, - 1); - } +void AModule::handleToggle(int n_press, double dx, double dy) { + handleClickEvent(controllClick_->get_current_button(), n_press, Gdk::Event::Type::BUTTON_PRESS); +} +void AModule::handleRelease(int n_press, double dx, double dy) { + handleClickEvent(controllClick_->get_current_button(), n_press, Gdk::Event::Type::BUTTON_RELEASE); } -bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { - if (auto* module = event_box_.get_child(); module != nullptr) { - module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); - } +void AModule::setCursor(const Glib::RefPtr& cur) { + ((Gtk::Widget&)*this).set_cursor(cur); +} + +void AModule::setCursor(const Glib::ustring& name) { ((Gtk::Widget&)*this).set_cursor(name); } + +void AModule::handleMouseEnter(double x, double y) { + controllMotion_->get_widget()->set_state_flags(Gtk::StateFlags::PRELIGHT); // Default behavior indicating event availability - if (hasUserEvents_ && !config_.isMember("cursor")) { - setCursor(Gdk::HAND2); + if (hasPressEvents_ && !config_.isMember("cursor")) { + setCursor(curPoint); } - - return false; } -bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { - if (auto* module = event_box_.get_child(); module != nullptr) { - module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); - } +void AModule::handleMouseLeave() { + controllMotion_->get_widget()->unset_state_flags(Gtk::StateFlags::PRELIGHT); // Default behavior indicating event availability - if (hasUserEvents_ && !config_.isMember("cursor")) { - setCursor(Gdk::ARROW); + if (hasPressEvents_ && !config_.isMember("cursor")) { + setCursor(curDefault); } - - return false; } -bool AModule::handleToggle(GdkEventButton* const& e) { return handleUserEvent(e); } - -bool AModule::handleRelease(GdkEventButton* const& e) { return handleUserEvent(e); } - -bool AModule::handleUserEvent(GdkEventButton* const& e) { +void AModule::handleClickEvent(uint n_button, int n_press, Gdk::Event::Type n_evtype) { std::string format{}; - const std::map, std::string>::const_iterator& rec{ - eventMap_.find(std::pair(e->button, e->type))}; - + const std::map, Gdk::Event::Type>, std::string>::const_iterator& + rec{eventMap_.find(std::pair(std::pair(n_button, n_press), n_evtype))}; if (rec != eventMap_.cend()) { - // First call module actions + // First call module action this->AModule::doAction(rec->second); - format = rec->second; } - - // Check that a menu has been configured - if (config_["menu"].isString()) { - // Check if the event is the one specified for the "menu" option - if (rec->second == config_["menu"].asString()) { - // Popup the menu - gtk_widget_show_all(GTK_WIDGET(menu_)); - gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast(e)); - } - } // Second call user scripts if (!format.empty()) { if (config_[format].isString()) @@ -180,40 +135,39 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) { else format.clear(); } - if (!format.empty()) { - pid_.push_back(util::command::forkExec(format)); - } + if (!format.empty()) pid_.push_back(util::command::forkExec(format)); + dp.emit(); - return true; } -AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { +const AModule::SCROLL_DIR AModule::getScrollDir(Glib::RefPtr e) { // only affects up/down - bool reverse = config_["reverse-scrolling"].asBool(); - bool reverse_mouse = config_["reverse-mouse-scrolling"].asBool(); + bool reverse{config_["reverse-scrolling"].asBool()}; + bool reverse_mouse{config_["reverse-mouse-scrolling"].asBool()}; // ignore reverse-scrolling if event comes from a mouse wheel - GdkDevice* device = gdk_event_get_source_device((GdkEvent*)e); - if (device != nullptr && gdk_device_get_source(device) == GDK_SOURCE_MOUSE) { - reverse = reverse_mouse; - } + const auto device{e->get_device()}; + if ((device) && device->get_source() == Gdk::InputSource::MOUSE) reverse = reverse_mouse; - switch (e->direction) { - case GDK_SCROLL_UP: + switch (e->get_direction()) { + case Gdk::ScrollDirection::UP: return reverse ? SCROLL_DIR::DOWN : SCROLL_DIR::UP; - case GDK_SCROLL_DOWN: + case Gdk::ScrollDirection::DOWN: return reverse ? SCROLL_DIR::UP : SCROLL_DIR::DOWN; - case GDK_SCROLL_LEFT: - return SCROLL_DIR::LEFT; - case GDK_SCROLL_RIGHT: - return SCROLL_DIR::RIGHT; - case GDK_SCROLL_SMOOTH: { + case Gdk::ScrollDirection::LEFT: + return reverse ? SCROLL_DIR::RIGHT : SCROLL_DIR::LEFT; + case Gdk::ScrollDirection::RIGHT: + return reverse ? SCROLL_DIR::LEFT : SCROLL_DIR::RIGHT; + case Gdk::ScrollDirection::SMOOTH: { SCROLL_DIR dir{SCROLL_DIR::NONE}; - distance_scrolled_y_ += e->delta_y; - distance_scrolled_x_ += e->delta_x; + double delta_x, delta_y; + e->get_deltas(delta_x, delta_y); - gdouble threshold = 0; + distance_scrolled_y_ += delta_y; + distance_scrolled_x_ += delta_x; + + double threshold{0.0}; if (config_["smooth-scrolling-threshold"].isNumeric()) { threshold = config_["smooth-scrolling-threshold"].asDouble(); } @@ -231,11 +185,11 @@ AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { switch (dir) { case SCROLL_DIR::UP: case SCROLL_DIR::DOWN: - distance_scrolled_y_ = 0; + distance_scrolled_y_ = 0.0; break; case SCROLL_DIR::LEFT: case SCROLL_DIR::RIGHT: - distance_scrolled_x_ = 0; + distance_scrolled_x_ = 0.0; break; case SCROLL_DIR::NONE: break; @@ -249,31 +203,113 @@ AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { } } -bool AModule::handleScroll(GdkEventScroll* e) { - auto dir = getScrollDir(e); - std::string eventName{}; - - if (dir == SCROLL_DIR::UP) - eventName = "on-scroll-up"; - else if (dir == SCROLL_DIR::DOWN) - eventName = "on-scroll-down"; - else if (dir == SCROLL_DIR::LEFT) - eventName = "on-scroll-left"; - else if (dir == SCROLL_DIR::RIGHT) - eventName = "on-scroll-right"; - - // First call module actions - this->AModule::doAction(eventName); - // Second call user scripts - if (config_[eventName].isString()) - pid_.push_back(util::command::forkExec(config_[eventName].asString())); +bool AModule::handleScroll(double dx, double dy) { + currEvent_ = controllScroll_->get_current_event(); + + if (currEvent_) { + std::string format{}; + const auto dir{getScrollDir(currEvent_)}; + + if (dir == SCROLL_DIR::UP) + format = "on-scroll-up"; + else if (dir == SCROLL_DIR::DOWN) + format = "on-scroll-down"; + else if (dir == SCROLL_DIR::LEFT) + format = "on-scroll-left"; + else if (dir == SCROLL_DIR::RIGHT) + format = "on-scroll-right"; + + // First call module action + this->AModule::doAction(format); + // Second call user scripts + if (config_[format].isString()) + pid_.push_back(util::command::forkExec(config_[format].asString())); + + dp.emit(); + } - dp.emit(); return true; } bool AModule::tooltipEnabled() const { return isTooltip; } -AModule::operator Gtk::Widget&() { return event_box_; } +AModule::operator Gtk::Widget&() { return this->operator Gtk::Widget&(); }; + +void AModule::bindEvents(Gtk::Widget& wg) { + wg.set_cursor(curDefault); + + if (!controllClick_) makeControllClick(); + if (!controllScroll_) makeControllScroll(); + if (!controllMotion_) makeControllMotion(); + + if (controllClick_) wg.add_controller(controllClick_); + if (controllScroll_) wg.add_controller(controllScroll_); + if (controllMotion_) wg.add_controller(controllMotion_); +} + +void AModule::unBindEvents() { + removeControllClick(); + removeControllScroll(); + removeControllMotion(); +} + +void AModule::makeControllClick() { + if (enableClick_ || hasPressEvents_ || hasReleaseEvents_) { + controllClick_ = Gtk::GestureClick::create(); + controllClick_->set_propagation_phase(Gtk::PropagationPhase::TARGET); + controllClick_->set_button(0u); + + if (enableClick_ || hasPressEvents_) + controllClick_->signal_pressed().connect(sigc::mem_fun(*this, &AModule::handleToggle), + isAfter); + if (hasReleaseEvents_) + controllClick_->signal_released().connect(sigc::mem_fun(*this, &AModule::handleRelease), + isAfter); + } +} + +void AModule::makeControllScroll() { + if (enableScroll_ || config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || + config_["on-scroll-left"].isString() || config_["on-scroll-right"].isString()) { + controllScroll_ = Gtk::EventControllerScroll::create(); + controllScroll_->set_propagation_phase(Gtk::PropagationPhase::TARGET); + controllScroll_->set_flags(Gtk::EventControllerScroll::Flags::BOTH_AXES); + controllScroll_->signal_scroll().connect(sigc::mem_fun(*this, &AModule::handleScroll), isAfter); + } +} + +void AModule::makeControllMotion() { + controllMotion_ = Gtk::EventControllerMotion::create(); + controllMotion_->signal_enter().connect(sigc::mem_fun(*this, &AModule::handleMouseEnter)); + controllMotion_->signal_leave().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave)); +} + +static void removeControll(Glib::RefPtr controll) { + if (controll) { + Gtk::Widget* widget{controll->get_widget()}; + if (widget) widget->remove_controller(controll); + } +} + +void AModule::removeControllClick() { + if (controllClick_) { + removeControll(controllClick_); + controllClick_ = nullptr; + } +} + +void AModule::removeControllScroll() { + if (controllScroll_) { + removeControll(controllScroll_); + controllScroll_ = nullptr; + } +} + +void AModule::removeControllMotion() { + if (controllMotion_) { + removeControll(controllMotion_); + controllMotion_ = nullptr; + } +} } // namespace waybar diff --git a/src/ASlider.cpp b/src/ASlider.cpp index b434be301..322f7ac91 100644 --- a/src/ASlider.cpp +++ b/src/ASlider.cpp @@ -1,20 +1,18 @@ #include "ASlider.hpp" #include "gtkmm/adjustment.h" -#include "gtkmm/enums.h" namespace waybar { ASlider::ASlider(const Json::Value& config, const std::string& name, const std::string& id) : AModule(config, name, id, false, false), vertical_(config_["orientation"].asString() == "vertical"), - scale_(vertical_ ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL) { + scale_(vertical_ ? Gtk::Orientation::VERTICAL : Gtk::Orientation::HORIZONTAL) { scale_.set_name(name); if (!id.empty()) { scale_.get_style_context()->add_class(id); } scale_.get_style_context()->add_class(MODULE_CLASS); - event_box_.add(scale_); scale_.signal_value_changed().connect(sigc::mem_fun(*this, &ASlider::onValueChanged)); if (config_["min"].isUInt()) { @@ -28,8 +26,12 @@ ASlider::ASlider(const Json::Value& config, const std::string& name, const std:: scale_.set_inverted(vertical_); scale_.set_draw_value(false); scale_.set_adjustment(Gtk::Adjustment::create(curr_, min_, max_ + 1, 1, 1, 1)); + + AModule::bindEvents(scale_); } void ASlider::onValueChanged() {} -} // namespace waybar \ No newline at end of file +ASlider::operator Gtk::Widget&() { return scale_; }; + +} // namespace waybar diff --git a/src/bar.cpp b/src/bar.cpp index 5068e90d0..49579f7d8 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -1,13 +1,10 @@ #include "bar.hpp" -#include +#include #include -#include - #include "client.hpp" #include "factory.hpp" -#include "group.hpp" #ifdef HAVE_SWAY #include "modules/sway/bar.hpp" @@ -90,26 +87,28 @@ void from_json(const Json::Value& j, bar_mode& m) { /* Deserializer for enum Gtk::PositionType */ void from_json(const Json::Value& j, Gtk::PositionType& pos) { if (j == "left") { - pos = Gtk::POS_LEFT; + pos = Gtk::PositionType::LEFT; } else if (j == "right") { - pos = Gtk::POS_RIGHT; + pos = Gtk::PositionType::RIGHT; } else if (j == "top") { - pos = Gtk::POS_TOP; + pos = Gtk::PositionType::TOP; } else if (j == "bottom") { - pos = Gtk::POS_BOTTOM; + pos = Gtk::PositionType::BOTTOM; } } Glib::ustring to_string(Gtk::PositionType pos) { switch (pos) { - case Gtk::POS_LEFT: + case Gtk::PositionType::LEFT: return "left"; - case Gtk::POS_RIGHT: + case Gtk::PositionType::RIGHT: return "right"; - case Gtk::POS_TOP: + case Gtk::PositionType::TOP: return "top"; - case Gtk::POS_BOTTOM: + case Gtk::PositionType::BOTTOM: return "bottom"; + default: + return ""; } throw std::runtime_error("Invalid Gtk::PositionType"); } @@ -130,34 +129,35 @@ void from_json(const Json::Value& j, std::map& m) { }; // namespace waybar waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) - : output(w_output), - config(w_config), - surface(nullptr), - window{Gtk::WindowType::WINDOW_TOPLEVEL}, - x_global(0), - y_global(0), + : output{w_output}, + config{w_config}, + surface{nullptr}, + window{Gtk::Window()}, + x_global{0}, + y_global{0}, margins_{.top = 0, .right = 0, .bottom = 0, .left = 0}, - left_(Gtk::ORIENTATION_HORIZONTAL, 0), - center_(Gtk::ORIENTATION_HORIZONTAL, 0), - right_(Gtk::ORIENTATION_HORIZONTAL, 0), - box_(Gtk::ORIENTATION_HORIZONTAL, 0) { + left_{Gtk::Orientation::HORIZONTAL, 0}, + center_{Gtk::Orientation::HORIZONTAL, 0}, + right_{Gtk::Orientation::HORIZONTAL, 0}, + box_{} { window.set_title("waybar"); window.set_name("waybar"); window.set_decorated(false); + window.set_child(box_); window.get_style_context()->add_class(output->name); window.get_style_context()->add_class(config["name"].asString()); from_json(config["position"], position); - orientation = (position == Gtk::POS_LEFT || position == Gtk::POS_RIGHT) - ? Gtk::ORIENTATION_VERTICAL - : Gtk::ORIENTATION_HORIZONTAL; + orientation = (position == Gtk::PositionType::LEFT || position == Gtk::PositionType::RIGHT) + ? Gtk::Orientation::VERTICAL + : Gtk::Orientation::HORIZONTAL; window.get_style_context()->add_class(to_string(position)); - left_ = Gtk::Box(orientation, 0); - center_ = Gtk::Box(orientation, 0); - right_ = Gtk::Box(orientation, 0); - box_ = Gtk::Box(orientation, 0); + left_.set_orientation(orientation); + center_.set_orientation(orientation); + right_.set_orientation(orientation); + box_.set_orientation(orientation); left_.get_style_context()->add_class("modules-left"); center_.get_style_context()->add_class("modules-center"); @@ -218,12 +218,11 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps}; } - window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure)); output->monitor->property_geometry().signal_changed().connect( sigc::mem_fun(*this, &Bar::onOutputGeometryChanged)); // this has to be executed before GtkWindow.realize - auto* gtk_window = window.gobj(); + auto* gtk_window{window.gobj()}; gtk_layer_init_for_window(gtk_window); gtk_layer_set_keyboard_mode(gtk_window, GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); gtk_layer_set_monitor(gtk_window, output->monitor->gobj()); @@ -258,7 +257,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) setVisible(false); } - window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap)); + window.signal_map().connect(sigc::mem_fun(*this, &Bar::onMap)); #if HAVE_SWAY if (auto ipc = config["ipc"]; ipc.isBool() && ipc.asBool()) { @@ -278,16 +277,12 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) #endif setupWidgets(); - window.show_all(); + window.show(); if (spdlog::should_log(spdlog::level::debug)) { - // Unfortunately, this function isn't in the C++ bindings, so we have to call the C version. - char* gtk_tree = gtk_style_context_to_string( - window.get_style_context()->gobj(), - (GtkStyleContextPrintFlags)(GTK_STYLE_CONTEXT_PRINT_RECURSE | - GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE)); - spdlog::debug("GTK widget tree:\n{}", gtk_tree); - g_free(gtk_tree); + auto gtk_tree{window.get_style_context()->to_string(Gtk::StyleContext::PrintFlags::RECURSE | + Gtk::StyleContext::PrintFlags::SHOW_STYLE)}; + spdlog::debug("GTK widget tree:\n{}", gtk_tree.c_str()); } } @@ -345,18 +340,13 @@ void waybar::Bar::setMode(const struct bar_mode& mode) { * gtk-layer-shell schedules a commit on the next frame event in GTK, but this could fail in * certain scenarios, such as fully occluded bar. */ - gtk_layer_try_force_commit(gtk_window); wl_display_flush(Client::inst()->wl_display); } void waybar::Bar::setPassThrough(bool passthrough) { - auto gdk_window = window.get_window(); - if (gdk_window) { - Cairo::RefPtr region; - if (passthrough) { - region = Cairo::Region::create(); - } - gdk_window->input_shape_combine_region(region, 0, 0); + if (passthrough && gdk_surface_) { + auto region{Cairo::Region::create()}; + gdk_surface_->set_input_region(region); } } @@ -364,18 +354,18 @@ void waybar::Bar::setPosition(Gtk::PositionType position) { std::array anchors; anchors.fill(TRUE); - auto orientation = (position == Gtk::POS_LEFT || position == Gtk::POS_RIGHT) - ? Gtk::ORIENTATION_VERTICAL - : Gtk::ORIENTATION_HORIZONTAL; + auto orientation = (position == Gtk::PositionType::LEFT || position == Gtk::PositionType::RIGHT) + ? Gtk::Orientation::VERTICAL + : Gtk::Orientation::HORIZONTAL; switch (position) { - case Gtk::POS_LEFT: + case Gtk::PositionType::LEFT: anchors[GTK_LAYER_SHELL_EDGE_RIGHT] = FALSE; break; - case Gtk::POS_RIGHT: + case Gtk::PositionType::RIGHT: anchors[GTK_LAYER_SHELL_EDGE_LEFT] = FALSE; break; - case Gtk::POS_BOTTOM: + case Gtk::PositionType::BOTTOM: anchors[GTK_LAYER_SHELL_EDGE_TOP] = FALSE; break; default: /* Gtk::POS_TOP */ @@ -387,10 +377,10 @@ void waybar::Bar::setPosition(Gtk::PositionType position) { // otherwise the bar will use all space uint32_t configured_width = config["width"].isUInt() ? config["width"].asUInt() : 0; uint32_t configured_height = config["height"].isUInt() ? config["height"].asUInt() : 0; - if (orientation == Gtk::ORIENTATION_VERTICAL && configured_height > 1) { + if (orientation == Gtk::Orientation::VERTICAL && configured_height > 1) { anchors[GTK_LAYER_SHELL_EDGE_TOP] = FALSE; anchors[GTK_LAYER_SHELL_EDGE_BOTTOM] = FALSE; - } else if (orientation == Gtk::ORIENTATION_HORIZONTAL && configured_width > 1) { + } else if (orientation == Gtk::Orientation::HORIZONTAL && configured_width > 1) { anchors[GTK_LAYER_SHELL_EDGE_LEFT] = FALSE; anchors[GTK_LAYER_SHELL_EDGE_RIGHT] = FALSE; } @@ -401,14 +391,14 @@ void waybar::Bar::setPosition(Gtk::PositionType position) { } } -void waybar::Bar::onMap(GdkEventAny* /*unused*/) { +void waybar::Bar::onMap() { /* * Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor). */ - auto* gdk_window = window.get_window()->gobj(); - surface = gdk_wayland_window_get_wl_surface(gdk_window); - configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window)); - + gdk_surface_ = window.get_surface(); + gdk_surface_->signal_layout().connect(sigc::mem_fun(*this, &Bar::onConfigure)); + surface = gdk_wayland_surface_get_wl_surface(gdk_surface_->gobj()); + configureGlobalOffset(gdk_surface_->get_width(), gdk_surface_->get_height()); setPassThrough(passthrough_); } @@ -493,8 +483,8 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, auto id_name = ref.substr(6, hash_pos - 6); auto class_name = hash_pos != std::string::npos ? ref.substr(hash_pos + 1) : ""; - auto vertical = (group != nullptr ? group->getBox().get_orientation() - : box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL; + auto vertical = (group ? group->getBox().get_orientation() : box_.get_orientation()) == + Gtk::Orientation::VERTICAL; auto* group_module = new waybar::Group(id_name, class_name, config[ref], vertical); getModules(factory, ref, group_module); @@ -533,15 +523,13 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, } auto waybar::Bar::setupWidgets() -> void { - window.add(box_); - box_.pack_start(left_, false, false); + box_.set_start_widget(left_); if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) { box_.set_center_widget(center_); } else { - box_.pack_start(center_, true, false); + box_.set_start_widget(center_); } - box_.pack_end(right_, false, false); - + box_.set_end_widget(right_); // Convert to button code for every module that is used. setupAltFormatKeyForModuleList("modules-left"); setupAltFormatKeyForModuleList("modules-right"); @@ -552,18 +540,17 @@ auto waybar::Bar::setupWidgets() -> void { getModules(factory, "modules-center"); getModules(factory, "modules-right"); for (auto const& module : modules_left_) { - left_.pack_start(*module, false, false); + left_.append(*module); } for (auto const& module : modules_center_) { - center_.pack_start(*module, false, false); + center_.append(*module); } - std::reverse(modules_right_.begin(), modules_right_.end()); for (auto const& module : modules_right_) { - right_.pack_end(*module, false, false); + right_.append(*module); } } -void waybar::Bar::onConfigure(GdkEventConfigure* ev) { +void waybar::Bar::onConfigure(int width, int height) { /* * GTK wants new size for the window. * Actual resizing and management of the exclusve zone is handled within the gtk-layer-shell @@ -572,20 +559,20 @@ void waybar::Bar::onConfigure(GdkEventConfigure* ev) { * Note: forced resizing to a window smaller than required by GTK would not work with * gtk-layer-shell. */ - if (orientation == Gtk::ORIENTATION_VERTICAL) { - if (width_ > 1 && ev->width > static_cast(width_)) { - spdlog::warn(MIN_WIDTH_MSG, width_, ev->width); + if (orientation == Gtk::Orientation::VERTICAL) { + if (width_ > 1 && width > static_cast(width_)) { + spdlog::warn(MIN_WIDTH_MSG, width_, width); } } else { - if (height_ > 1 && ev->height > static_cast(height_)) { - spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height); + if (height_ > 1 && height > static_cast(height_)) { + spdlog::warn(MIN_HEIGHT_MSG, height_, height); } } - width_ = ev->width; - height_ = ev->height; + width_ = width; + height_ = height; - configureGlobalOffset(ev->width, ev->height); - spdlog::info(BAR_SIZE_MSG, ev->width, ev->height, output->name); + configureGlobalOffset(width, height); + spdlog::info(BAR_SIZE_MSG, width, height, output->name); } void waybar::Bar::configureGlobalOffset(int width, int height) { @@ -593,28 +580,28 @@ void waybar::Bar::configureGlobalOffset(int width, int height) { int x; int y; switch (position) { - case Gtk::POS_BOTTOM: + case Gtk::PositionType::BOTTOM: if (width + margins_.left + margins_.right >= monitor_geometry.width) x = margins_.left; else x = (monitor_geometry.width - width) / 2; y = monitor_geometry.height - height - margins_.bottom; break; - case Gtk::POS_LEFT: + case Gtk::PositionType::LEFT: x = margins_.left; if (height + margins_.top + margins_.bottom >= monitor_geometry.height) y = margins_.top; else y = (monitor_geometry.height - height) / 2; break; - case Gtk::POS_RIGHT: + case Gtk::PositionType::RIGHT: x = monitor_geometry.width - width - margins_.right; if (height + margins_.top + margins_.bottom >= monitor_geometry.height) y = margins_.top; else y = (monitor_geometry.height - height) / 2; break; - default: /* Gtk::POS_TOP */ + default: /* Gtk::PositionType::TOP */ if (width + margins_.left + margins_.right >= monitor_geometry.width) x = margins_.left; else diff --git a/src/client.cpp b/src/client.cpp index 63a9276a6..7f6a732fc 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,12 +1,12 @@ #include "client.hpp" -#include +#include +#include +#include #include #include -#include -#include "gtkmm/icontheme.h" #include "idle-inhibit-unstable-v1-client-protocol.h" #include "util/clara.hpp" #include "util/format.hpp" @@ -178,15 +178,16 @@ const std::string waybar::Client::getStyle(const std::string &style, auto waybar::Client::setupCss(const std::string &css_file) -> void { css_provider_ = Gtk::CssProvider::create(); - style_context_ = Gtk::StyleContext::create(); // Load our css file, wherever that may be hiding - if (!css_provider_->load_from_path(css_file)) { - throw std::runtime_error("Can't open style file"); + try { + css_provider_->load_from_path(css_file); + } catch (const Glib::Error &e) { + spdlog::error("{}", e.what()); } // there's always only one screen - style_context_->add_provider_for_screen(Gdk::Screen::get_default(), css_provider_, - GTK_STYLE_PROVIDER_PRIORITY_USER); + Gtk::StyleContext::add_provider_for_display(Gdk::Display::get_default(), css_provider_, + GTK_STYLE_PROVIDER_PRIORITY_USER); } void waybar::Client::bindInterfaces() { @@ -206,13 +207,23 @@ void waybar::Client::bindInterfaces() { throw std::runtime_error("Failed to acquire required resources."); } // add existing outputs and subscribe to updates - for (auto i = 0; i < gdk_display->get_n_monitors(); ++i) { - auto monitor = gdk_display->get_monitor(i); - handleMonitorAdded(monitor); + // auto monitors{gdk_display->get_monitors()}; + for (guint i{0}; i < monitors_->get_n_items(); ++i) { + handleMonitorAdded(std::dynamic_pointer_cast(monitors_->get_object(i))); } - gdk_display->signal_monitor_added().connect(sigc::mem_fun(*this, &Client::handleMonitorAdded)); - gdk_display->signal_monitor_removed().connect( - sigc::mem_fun(*this, &Client::handleMonitorRemoved)); + + monitors_->signal_items_changed().connect( + [=, this](const guint &position, const guint &removed, const guint &added) { + for (auto i{removed}; i >= 0; --i) { + handleMonitorRemoved( + std::dynamic_pointer_cast(monitors_->get_object(position + i))); + } + + for (auto i{added}; i >= 0; --i) { + handleMonitorAdded( + std::dynamic_pointer_cast(monitors_->get_object(position + i))); + } + }); } int waybar::Client::main(int argc, char *argv[]) { @@ -245,13 +256,8 @@ int waybar::Client::main(int argc, char *argv[]) { if (!log_level.empty()) { spdlog::set_level(spdlog::level::from_str(log_level)); } - gtk_app = Gtk::Application::create(argc, argv, "fr.arouillard.waybar", - Gio::APPLICATION_HANDLES_COMMAND_LINE); - - // Initialize Waybars GTK resources with our custom icons - auto theme = Gtk::IconTheme::get_default(); - theme->add_resource_path("/fr/arouillard/waybar/icons"); - + gtk_app = Gtk::Application::create("fr.arouillard.waybar", + Gio::Application::Flags::HANDLES_COMMAND_LINE); gdk_display = Gdk::Display::get_default(); if (!gdk_display) { throw std::runtime_error("Can't find display"); @@ -259,7 +265,13 @@ int waybar::Client::main(int argc, char *argv[]) { if (!GDK_IS_WAYLAND_DISPLAY(gdk_display->gobj())) { throw std::runtime_error("Bar need to run under Wayland"); } + + // Initialize Waybars GTK resources with our custom icons + auto theme{Gtk::IconTheme::get_for_display(gdk_display)}; + theme->add_resource_path("/fr/arouillard/waybar/icons"); + wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj()); + monitors_ = gdk_display->get_monitors(); config.load(config_opt); if (!portal) { portal = std::make_unique(); @@ -289,6 +301,7 @@ int waybar::Client::main(int argc, char *argv[]) { gtk_app->run(); m_cssReloadHelper.reset(); // stop watching css file bars.clear(); + return 0; } diff --git a/src/factory.cpp b/src/factory.cpp index 6c2313e38..01ee926d6 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -59,7 +59,7 @@ #include "modules/sni/tray.hpp" #endif #ifdef HAVE_MPRIS -#include "modules/mpris/mpris.hpp" +#include "modules/mpris.hpp" #endif #ifdef HAVE_LIBNL #include "modules/network.hpp" @@ -113,9 +113,10 @@ #include "modules/custom.hpp" #include "modules/image.hpp" #include "modules/temperature.hpp" +#include "modules/ui.hpp" #include "modules/user.hpp" -waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {} +waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_{bar}, config_{config} {} waybar::AModule* waybar::Factory::makeModule(const std::string& name, const std::string& pos) const { @@ -125,7 +126,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : ""; #if defined(__FreeBSD__) || defined(__linux__) if (ref == "battery") { - return new waybar::modules::Battery(id, bar_, config_[name]); + return new waybar::modules::Battery(id, config_[name]); } #endif #ifdef HAVE_GAMEMODE @@ -258,6 +259,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "image") { return new waybar::modules::Image(id, config_[name]); } + // gtk4 todo #ifdef HAVE_DBUSMENU if (ref == "tray") { return new waybar::modules::SNI::Tray(id, bar_, config_[name]); @@ -309,7 +311,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, #endif #ifdef HAVE_LOGIND_INHIBITOR if (ref == "inhibitor") { - return new waybar::modules::Inhibitor(id, bar_, config_[name]); + return new waybar::modules::Inhibitor(id, config_[name]); } #endif #ifdef HAVE_LIBJACK @@ -341,6 +343,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref.compare(0, 5, "cffi/") == 0 && ref.size() > 5) { return new waybar::modules::CFFI(ref.substr(5), id, config_[name]); } + if (ref.compare(0, 3, "ui/") == 0 && ref.size() > 3) { + return new waybar::modules::UI(ref.substr(3), id, config_[name]); + } } catch (const std::exception& e) { auto err = fmt::format("Disabling module \"{}\", {}", name, e.what()); throw std::runtime_error(err); diff --git a/src/group.cpp b/src/group.cpp index 50841efd6..609efd2d8 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -1,12 +1,5 @@ #include "group.hpp" -#include - -#include - -#include "gtkmm/enums.h" -#include "gtkmm/widget.h" - namespace waybar { Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) { @@ -18,20 +11,20 @@ Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) { */ if (is_vertical) { - return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_UP; + return Gtk::RevealerTransitionType::SLIDE_UP; } - return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_LEFT; + return Gtk::RevealerTransitionType::SLIDE_LEFT; } Group::Group(const std::string& name, const std::string& id, const Json::Value& config, bool vertical) : AModule(config, name, id, true, true), - box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, - revealer_box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} { - box.set_name(name_); + box_{vertical ? Gtk::Orientation::VERTICAL : Gtk::Orientation::HORIZONTAL, 0}, + revealer_box_{vertical ? Gtk::Orientation::VERTICAL : Gtk::Orientation::HORIZONTAL, 0} { + box_.set_name(name_); if (!id.empty()) { - box.get_style_context()->add_class(id); + box_.get_style_context()->add_class(id); } // default orientation: orthogonal to parent @@ -40,102 +33,95 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& if (orientation == "inherit") { // keep orientation passed } else if (orientation == "orthogonal") { - box.set_orientation(vertical ? Gtk::ORIENTATION_HORIZONTAL : Gtk::ORIENTATION_VERTICAL); + box_.set_orientation(vertical ? Gtk::Orientation::HORIZONTAL : Gtk::Orientation::VERTICAL); } else if (orientation == "vertical") { - box.set_orientation(Gtk::ORIENTATION_VERTICAL); + box_.set_orientation(Gtk::Orientation::VERTICAL); } else if (orientation == "horizontal") { - box.set_orientation(Gtk::ORIENTATION_HORIZONTAL); + box_.set_orientation(Gtk::Orientation::HORIZONTAL); } else { throw std::runtime_error("Invalid orientation value: " + orientation); } if (config_["drawer"].isObject()) { - is_drawer = true; + is_drawer_ = true; const auto& drawer_config = config_["drawer"]; const int transition_duration = (drawer_config["transition-duration"].isInt() ? drawer_config["transition-duration"].asInt() : 500); - add_class_to_drawer_children = + add_class_to_drawer_children_ = (drawer_config["children-class"].isString() ? drawer_config["children-class"].asString() : "drawer-child"); const bool left_to_right = (drawer_config["transition-left-to-right"].isBool() ? drawer_config["transition-left-to-right"].asBool() : true); - click_to_reveal = drawer_config["click-to-reveal"].asBool(); + click_to_reveal_ = drawer_config["click-to-reveal"].asBool(); auto transition_type = getPreferredTransitionType(vertical); - revealer.set_transition_type(transition_type); - revealer.set_transition_duration(transition_duration); - revealer.set_reveal_child(false); - - revealer.get_style_context()->add_class("drawer"); + revealer_.set_transition_type(transition_type); + revealer_.set_transition_duration(transition_duration); + revealer_.set_reveal_child(false); - revealer.add(revealer_box); + revealer_.get_style_context()->add_class("drawer"); - if (left_to_right) { - box.pack_end(revealer); - } else { - box.pack_start(revealer); - } + revealer_.set_child(revealer_box_); + if (left_to_right) + box_.append(revealer_); + else + box_.prepend(revealer_); } - event_box_.add(box); + AModule::bindEvents(box_); } void Group::show_group() { - box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); - revealer.set_reveal_child(true); + box_.set_state_flags(Gtk::StateFlags::PRELIGHT); + revealer_.set_reveal_child(true); } void Group::hide_group() { - box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); - revealer.set_reveal_child(false); + box_.unset_state_flags(Gtk::StateFlags::PRELIGHT); + revealer_.set_reveal_child(false); } -bool Group::handleMouseEnter(GdkEventCrossing* const& e) { - if (!click_to_reveal) { +void Group::handleMouseEnter(double x, double y) { + if (!click_to_reveal_) { show_group(); } - return false; } -bool Group::handleMouseLeave(GdkEventCrossing* const& e) { - if (!click_to_reveal && e->detail != GDK_NOTIFY_INFERIOR) { +void Group::handleMouseLeave() { + if (!click_to_reveal_ && AModule::controllScroll_->get_current_event()->get_crossing_detail() != + Gdk::NotifyType::INFERIOR) { hide_group(); } - return false; } -bool Group::handleToggle(GdkEventButton* const& e) { - if (!click_to_reveal || e->button != 1) { - return false; - } - if ((box.get_state_flags() & Gtk::StateFlags::STATE_FLAG_PRELIGHT) != 0U) { - hide_group(); - } else { - show_group(); +void Group::handleToggle(int n_press, double dx, double dy) { + if (click_to_reveal_ && AModule::controllClick_->get_current_button() == 1 /* left click */) { + if (box_.get_state_flags() == Gtk::StateFlags::PRELIGHT) { + hide_group(); + } else { + show_group(); + } } - return true; } auto Group::update() -> void { // noop } -Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; } +Gtk::Box& Group::getBox() { return is_drawer_ ? (is_first_widget_ ? box_ : revealer_box_) : box_; } void Group::addWidget(Gtk::Widget& widget) { - getBox().pack_start(widget, false, false); + getBox().prepend(widget); - if (is_drawer && !is_first_widget) { - widget.get_style_context()->add_class(add_class_to_drawer_children); + if (is_drawer_ && !is_first_widget_) { + widget.get_style_context()->add_class(add_class_to_drawer_children_); } - is_first_widget = false; + is_first_widget_ = false; } -Group::operator Gtk::Widget&() { return event_box_; } - } // namespace waybar diff --git a/src/main.cpp b/src/main.cpp index 442c530cb..c51d66c2a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -109,15 +109,15 @@ int main(int argc, char* argv[]) { std::signal(SIGUSR1, SIG_IGN); std::signal(SIGUSR2, SIG_IGN); - std::signal(SIGINT, SIG_IGN); + std::signal(SIGINT, SIG_IGN); delete client; return ret; + } catch (const Glib::Error& e) { + spdlog::error("{}", static_cast(e.what())); + return 1; } catch (const std::exception& e) { spdlog::error("{}", e.what()); return 1; - } catch (const Glib::Exception& e) { - spdlog::error("{}", static_cast(e.what())); - return 1; } } diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index ff58951cd..9c477ef3a 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -1,27 +1,12 @@ #include "modules/backlight.hpp" #include -#include -#include -#include -#include - -#include -#include -#include - -#include "util/backend_common.hpp" -#include "util/backlight_backend.hpp" waybar::modules::Backlight::Backlight(const std::string &id, const Json::Value &config) - : ALabel(config, "backlight", id, "{percent}%", 2), + : ALabel(config, "backlight", id, "{percent}%", 2, false, false, true), preferred_device_(config["device"].isString() ? config["device"].asString() : ""), backend(interval_, [this] { dp.emit(); }) { dp.emit(); - - // Set up scroll handler - event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); - event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Backlight::handleScroll)); } auto waybar::modules::Backlight::update() -> void { @@ -35,7 +20,7 @@ auto waybar::modules::Backlight::update() -> void { } if (best->get_powered()) { - event_box_.show(); + label_.show(); const uint8_t percent = best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max()); std::string desc = fmt::format(fmt::runtime(format_), fmt::arg("percent", percent), @@ -56,7 +41,7 @@ auto waybar::modules::Backlight::update() -> void { } } } else { - event_box_.hide(); + label_.hide(); } } else { if (previous_best_device == nullptr) { @@ -69,10 +54,10 @@ auto waybar::modules::Backlight::update() -> void { ALabel::update(); } -bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { +bool waybar::modules::Backlight::handleScroll(double dx, double dy) { // Check if the user has set a custom command for scrolling if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { - return AModule::handleScroll(e); + return AModule::handleScroll(dx, dy); } // Fail fast if the proxy could not be initialized @@ -81,7 +66,7 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { } // Check scroll direction - auto dir = AModule::getScrollDir(e); + auto dir = AModule::getScrollDir(controllScroll_->get_current_event()); // No worries, it will always be set because of the switch below. This is purely to suppress a // warning diff --git a/src/modules/backlight_slider.cpp b/src/modules/backlight_slider.cpp index 6269dddbf..e44791e88 100644 --- a/src/modules/backlight_slider.cpp +++ b/src/modules/backlight_slider.cpp @@ -20,4 +20,4 @@ void BacklightSlider::onValueChanged() { backend.set_scaled_brightness(preferred_device_, brightness); } -} // namespace waybar::modules \ No newline at end of file +} // namespace waybar::modules diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index d87cc6129..0886c8dd6 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -1,14 +1,15 @@ #include "modules/battery.hpp" -#include +#include + +#include + #if defined(__FreeBSD__) #include #endif -#include -#include -waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const Json::Value& config) - : ALabel(config, "battery", id, "{capacity}%", 60), bar_(bar) { +waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config) + : ALabel(config, "battery", id, "{capacity}%", 60) { #if defined(__linux__) battery_watch_fd_ = inotify_init1(IN_CLOEXEC); if (battery_watch_fd_ == -1) { @@ -659,7 +660,7 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai auto waybar::modules::Battery::update() -> void { #if defined(__linux__) if (batteries_.empty()) { - event_box_.hide(); + label_.hide(); return; } #endif @@ -712,9 +713,9 @@ auto waybar::modules::Battery::update() -> void { format = config_["format-" + state].asString(); } if (format.empty()) { - event_box_.hide(); + label_.hide(); } else { - event_box_.show(); + label_.show(); auto icons = std::vector{status + "-" + state, status, state}; label_.set_markup(fmt::format( fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), @@ -726,10 +727,11 @@ auto waybar::modules::Battery::update() -> void { } void waybar::modules::Battery::setBarClass(std::string& state) { - auto classes = bar_.window.get_style_context()->list_classes(); - const std::string prefix = "battery-"; + // Drop style + auto classes = label_.get_css_classes(); + const Glib::ustring prefix{"battery-"}; - auto old_class_it = std::find_if(classes.begin(), classes.end(), [&prefix](auto classname) { + auto old_class_it = std::find_if(classes.cbegin(), classes.cend(), [&prefix](auto classname) { return classname.rfind(prefix, 0) == 0; }); @@ -738,7 +740,7 @@ void waybar::modules::Battery::setBarClass(std::string& state) { // If the bar doesn't have any `battery-` class if (old_class_it == classes.end()) { if (!state.empty()) { - bar_.window.get_style_context()->add_class(new_class); + label_.get_style_context()->add_class(new_class); } return; } @@ -748,14 +750,14 @@ void waybar::modules::Battery::setBarClass(std::string& state) { // If the bar has a `battery-` class, // but `state` is empty if (state.empty()) { - bar_.window.get_style_context()->remove_class(old_class); + label_.get_style_context()->remove_class(old_class); return; } // If the bar has a `battery-` class, // and `state` is NOT empty if (old_class != new_class) { - bar_.window.get_style_context()->remove_class(old_class); - bar_.window.get_style_context()->add_class(new_class); + label_.get_style_context()->remove_class(old_class); + label_.get_style_context()->add_class(new_class); } } diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index c8f1f9966..ec16d590a 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -1,11 +1,7 @@ #include "modules/bluetooth.hpp" -#include #include -#include -#include - #include "util/scope_guard.hpp" namespace { @@ -216,9 +212,9 @@ auto waybar::modules::Bluetooth::update() -> void { state_ = state; if (format_.empty()) { - event_box_.hide(); + label_.hide(); } else { - event_box_.show(); + label_.show(); label_.set_markup(fmt::format( fmt::runtime(format_), fmt::arg("status", state_), fmt::arg("num_connections", connected_devices_.size()), diff --git a/src/modules/cffi.cpp b/src/modules/cffi.cpp index e560659b8..ab566717c 100644 --- a/src/modules/cffi.cpp +++ b/src/modules/cffi.cpp @@ -1,16 +1,14 @@ #include "modules/cffi.hpp" #include -#include -#include -#include -#include +#include namespace waybar::modules { CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& config) - : AModule(config, name, id, true, true) { + : AModule(config, name, id, true, true), box_{new Gtk::Box()} { + box_->set_name(name_); const auto dynlib_path = config_["module_path"].asString(); if (dynlib_path.empty()) { throw std::runtime_error{"Missing or empty 'module_path' in module config"}; @@ -76,7 +74,7 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co .waybar_version = VERSION, .get_root_widget = [](ffi::wbcffi_module* obj) { - return dynamic_cast(&((CFFI*)obj)->event_box_)->gobj(); + return dynamic_cast(&((CFFI*)obj)->operator Gtk::Widget&())->gobj(); }, .queue_update = [](ffi::wbcffi_module* obj) { ((CFFI*)obj)->dp.emit(); }, }; @@ -104,6 +102,11 @@ auto CFFI::update() -> void { AModule::update(); } +CFFI::operator Gtk::Widget&() { + assert(box_); + return *box_; +} + auto CFFI::refresh(int signal) -> void { assert(cffi_instance_ != nullptr); hooks_.refresh(cffi_instance_, signal); diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 7f5a4d558..8ff338227 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -3,10 +3,7 @@ #include #include -#include -#include #include -#include #include "util/ustring_clen.hpp" @@ -118,19 +115,17 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } } else cldMonCols_ = 1; + if (config_[kCldPlaceholder]["on-scroll"].isInt()) { cldShift_ = config_[kCldPlaceholder]["on-scroll"].asInt(); - event_box_.add_events(Gdk::LEAVE_NOTIFY_MASK); - event_box_.signal_leave_notify_event().connect([this](GdkEventCrossing*) { - cldCurrShift_ = months{0}; - return false; - }); + AModule::controllMotion_->set_propagation_phase(Gtk::PropagationPhase::TARGET); + AModule::controllMotion_->signal_leave().connect([this]() { cldCurrShift_ = months{0}; }); } } if (tooltipEnabled()) { label_.set_has_tooltip(true); - label_.signal_query_tooltip().connect(sigc::mem_fun(*this, &Clock::query_tlp_cb)); + label_.signal_query_tooltip().connect(sigc::mem_fun(*this, &Clock::query_tlp_cb), false); } thread_ = [this] { diff --git a/src/modules/cpu.cpp b/src/modules/cpu.cpp index 0703eaf7c..93bad0c49 100644 --- a/src/modules/cpu.cpp +++ b/src/modules/cpu.cpp @@ -37,9 +37,9 @@ auto waybar::modules::Cpu::update() -> void { } if (format.empty()) { - event_box_.hide(); + label_.hide(); } else { - event_box_.show(); + label_.show(); auto icons = std::vector{state}; fmt::dynamic_format_arg_store store; store.push_back(fmt::arg("load", load1)); diff --git a/src/modules/cpu_frequency/common.cpp b/src/modules/cpu_frequency/common.cpp index e47364ba7..66c47bd22 100644 --- a/src/modules/cpu_frequency/common.cpp +++ b/src/modules/cpu_frequency/common.cpp @@ -1,3 +1,7 @@ +#include + +#include + #include "modules/cpu_frequency.hpp" // In the 80000 version of fmt library authors decided to optimize imports @@ -33,9 +37,9 @@ auto waybar::modules::CpuFrequency::update() -> void { } if (format.empty()) { - event_box_.hide(); + label_.hide(); } else { - event_box_.show(); + label_.show(); auto icons = std::vector{state}; fmt::dynamic_format_arg_store store; store.push_back(fmt::arg("icon", getIcon(avg_frequency, icons))); diff --git a/src/modules/cpu_frequency/linux.cpp b/src/modules/cpu_frequency/linux.cpp index 1f368789b..6e18d3ec6 100644 --- a/src/modules/cpu_frequency/linux.cpp +++ b/src/modules/cpu_frequency/linux.cpp @@ -1,4 +1,5 @@ #include +#include #include "modules/cpu_frequency.hpp" diff --git a/src/modules/cpu_usage/common.cpp b/src/modules/cpu_usage/common.cpp index e39479678..793f5b3bc 100644 --- a/src/modules/cpu_usage/common.cpp +++ b/src/modules/cpu_usage/common.cpp @@ -31,9 +31,9 @@ auto waybar::modules::CpuUsage::update() -> void { } if (format.empty()) { - event_box_.hide(); + label_.hide(); } else { - event_box_.show(); + label_.show(); auto icons = std::vector{state}; fmt::dynamic_format_arg_store store; store.push_back(fmt::arg("usage", total_usage)); diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index e023aaf6e..2ac2f1481 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -135,23 +135,22 @@ void waybar::modules::Custom::handleEvent() { } } -bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) { - auto ret = ALabel::handleScroll(e); +bool waybar::modules::Custom::handleScroll(double dx, double dy) { + auto ret = ALabel::handleScroll(dx, dy); handleEvent(); return ret; } -bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) { - auto ret = ALabel::handleToggle(e); +void waybar::modules::Custom::handleToggle(int n_press, double dx, double dy) { + ALabel::handleToggle(n_press, dx, dy); handleEvent(); - return ret; } auto waybar::modules::Custom::update() -> void { // Hide label if output is empty if ((config_["exec"].isString() || config_["exec-if"].isString()) && (output_.out.empty() || output_.exit_code != 0)) { - event_box_.hide(); + label_.hide(); } else { if (config_["return-type"].asString() == "json") { parseOutputJson(); @@ -164,7 +163,7 @@ auto waybar::modules::Custom::update() -> void { fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { - event_box_.hide(); + label_.hide(); } else { label_.set_markup(str); if (tooltipEnabled()) { @@ -175,19 +174,19 @@ auto waybar::modules::Custom::update() -> void { fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); label_.set_tooltip_markup(tooltip); } else if (text_ == tooltip_) { - if (label_.get_tooltip_markup() != str) { + if (label_.get_tooltip_markup().c_str() != str) { label_.set_tooltip_markup(str); } } else { - if (label_.get_tooltip_markup() != tooltip_) { + if (label_.get_tooltip_markup().c_str() != tooltip_) { label_.set_tooltip_markup(tooltip_); } } } auto style = label_.get_style_context(); - auto classes = style->list_classes(); + auto classes{label_.get_css_classes()}; for (auto const& c : classes) { - if (c == id_) continue; + if (c.c_str() == id_) continue; style->remove_class(c); } for (auto const& c : class_) { @@ -196,7 +195,7 @@ auto waybar::modules::Custom::update() -> void { style->add_class("flat"); style->add_class("text-button"); style->add_class(MODULE_CLASS); - event_box_.show(); + label_.show(); } } catch (const fmt::format_error& e) { if (std::strcmp(e.what(), "cannot switch from manual to automatic argument indexing") != 0) diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index ef257b721..360ec6ebe 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -1,5 +1,10 @@ #include "modules/disk.hpp" +#include +#include + +#include "util/format.hpp" + using namespace waybar::util; waybar::modules::Disk::Disk(const std::string& id, const Json::Value& config) @@ -42,7 +47,7 @@ auto waybar::modules::Disk::update() -> void { */ if (err != 0) { - event_box_.hide(); + label_.hide(); return; } @@ -65,9 +70,9 @@ auto waybar::modules::Disk::update() -> void { } if (format.empty()) { - event_box_.hide(); + label_.hide(); } else { - event_box_.show(); + label_.show(); label_.set_markup(fmt::format( fmt::runtime(format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used), @@ -112,4 +117,4 @@ float waybar::modules::Disk::calc_specific_divisor(std::string divisor) { } else { // default to Bytes if it is anything that we don't recongnise return 1.0; } -} \ No newline at end of file +} diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index f8b250c8c..a0d816a63 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -1,14 +1,8 @@ #include "modules/dwl/tags.hpp" -#include -#include #include -#include - -#include #include "client.hpp" -#include "dwl-ipc-unstable-v2-client-protocol.h" #define TAG_INACTIVE 0 #define TAG_ACTIVE 1 @@ -19,7 +13,7 @@ namespace waybar::modules::dwl { /* dwl stuff */ wl_array tags, layouts; -static uint num_tags = 0; +static uint num_tags{0}; static void toggle_visibility(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { // Intentionally empty @@ -95,8 +89,8 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con bar_(bar), box_{bar.orientation, 0}, output_status_{nullptr} { - struct wl_display *display = Client::inst()->wl_display; - struct wl_registry *registry = wl_display_get_registry(display); + struct wl_display *display{Client::inst()->wl_display}; + struct wl_registry *registry{wl_display_get_registry(display)}; wl_registry_add_listener(registry, ®istry_listener_impl, this); wl_display_roundtrip(display); @@ -115,39 +109,42 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con box_.get_style_context()->add_class(id); } box_.get_style_context()->add_class(MODULE_CLASS); - event_box_.add(box_); // Default to 9 tags, cap at 32 - const uint32_t num_tags = - config["num-tags"].isUInt() ? std::min(32, config_["num-tags"].asUInt()) : 9; + const uint32_t num_tags{ + config["num-tags"].isUInt() ? std::min(32, config_["num-tags"].asUInt()) : 9}; std::vector tag_labels(num_tags); - for (uint32_t tag = 0; tag < num_tags; ++tag) { + for (uint32_t tag{0}; tag < num_tags; ++tag) { tag_labels[tag] = std::to_string(tag + 1); } - const Json::Value custom_labels = config["tag-labels"]; + const Json::Value custom_labels{config["tag-labels"]}; if (custom_labels.isArray() && !custom_labels.empty()) { - for (uint32_t tag = 0; tag < std::min(num_tags, custom_labels.size()); ++tag) { + for (uint32_t tag{0}; tag < std::min(num_tags, custom_labels.size()); ++tag) { tag_labels[tag] = custom_labels[tag].asString(); } } - uint32_t i = 1; + uint32_t i{1}; for (const auto &tag_label : tag_labels) { - Gtk::Button &button = buttons_.emplace_back(tag_label); - button.set_relief(Gtk::RELIEF_NONE); - box_.pack_start(button, false, false, 0); + Gtk::Button &button{buttons_.emplace_back(tag_label)}; + auto controlClick{clickControls_.emplace_back(Gtk::GestureClick::create())}; + controlClick->set_propagation_phase(Gtk::PropagationPhase::CAPTURE); + controlClick->set_button(1u); + button.add_controller(controlClick); + button.set_has_frame(false); + box_.prepend(button); if (!config_["disable-click"].asBool()) { - button.signal_clicked().connect( + controlClick->signal_pressed().connect( + sigc::bind(sigc::mem_fun(*this, &Tags::handle_button_press), i, controlClick)); + controlClick->signal_released().connect( sigc::bind(sigc::mem_fun(*this, &Tags::handle_primary_clicked), i)); - button.signal_button_press_event().connect( - sigc::bind(sigc::mem_fun(*this, &Tags::handle_button_press), i)); } button.show(); i <<= 1; } - struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + struct wl_output *output{gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj())}; output_status_ = zdwl_ipc_manager_v2_get_output(status_manager_, output); zdwl_ipc_output_v2_add_listener(output_status_, &output_status_listener_impl, this); @@ -164,23 +161,22 @@ Tags::~Tags() { } } -void Tags::handle_primary_clicked(uint32_t tag) { +void Tags::handle_primary_clicked(int n_press, double dx, double dy, uint32_t tag) { if (!output_status_) return; - zdwl_ipc_output_v2_set_tags(output_status_, tag, 1); } -bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) { - if (event_button->type == GDK_BUTTON_PRESS && event_button->button == 3) { - if (!output_status_) return true; +void Tags::handle_button_press(int n_press, double dx, double dy, uint32_t tag, + Glib::RefPtr controlClick) { + if (controlClick->get_current_button() == 3) { + if (!output_status_) return; zdwl_ipc_output_v2_set_tags(output_status_, num_tags ^ tag, 0); } - return true; } void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) { // First clear all occupied state - auto &button = buttons_[tag]; + auto &button{buttons_[tag]}; if (clients) { button.get_style_context()->add_class("occupied"); } else { @@ -200,4 +196,6 @@ void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint } } +Tags::operator Gtk::Widget &() { return box_; }; + } /* namespace waybar::modules::dwl */ diff --git a/src/modules/dwl/window.cpp b/src/modules/dwl/window.cpp index 870d87e4e..bd90e23b9 100644 --- a/src/modules/dwl/window.cpp +++ b/src/modules/dwl/window.cpp @@ -72,8 +72,8 @@ static void handle_global_remove(void *data, struct wl_registry *registry, uint3 static const wl_registry_listener registry_listener_impl = {.global = handle_global, .global_remove = handle_global_remove}; -Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) - : AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar) { +Window::Window(const std::string &id, const waybar::Bar &bar, const Json::Value &config) + : AAppIconLabel(config, "window", id, "{}", 0, true), bar_{bar} { struct wl_display *display = Client::inst()->wl_display; struct wl_registry *registry = wl_display_get_registry(display); diff --git a/src/modules/gamemode.cpp b/src/modules/gamemode.cpp index 811f13ca0..f803685fb 100644 --- a/src/modules/gamemode.cpp +++ b/src/modules/gamemode.cpp @@ -1,32 +1,22 @@ #include "modules/gamemode.hpp" -#include +#include #include -#include -#include -#include - #include "AModule.hpp" -#include "giomm/dbusconnection.h" -#include "giomm/dbusinterface.h" -#include "giomm/dbusproxy.h" -#include "giomm/dbuswatchname.h" -#include "glibmm/error.h" -#include "glibmm/ustring.h" -#include "glibmm/variant.h" -#include "glibmm/varianttype.h" -#include "gtkmm/label.h" -#include "gtkmm/tooltip.h" -#include "util/gtk_icon.hpp" namespace waybar::modules { Gamemode::Gamemode(const std::string& id, const Json::Value& config) - : AModule(config, "gamemode", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0), icon_(), label_() { - box_.pack_start(icon_); - box_.pack_start(label_); + : ALabel(config, "gamemode", id, "{}"), + box_(Gtk::Orientation::HORIZONTAL, 0), + icon_(), + label_() { + box_.prepend(icon_); + box_.prepend(label_); box_.set_name(name_); - event_box_.add(box_); + + // Get current theme + gtkTheme_ = Gtk::IconTheme::get_for_display(label_.get_display()); // Tooltip if (config_["tooltip"].isBool()) { @@ -82,13 +72,12 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config) } gamemodeWatcher_id = Gio::DBus::watch_name( - Gio::DBus::BUS_TYPE_SESSION, dbus_name, sigc::mem_fun(*this, &Gamemode::appear), - sigc::mem_fun(*this, &Gamemode::disappear), - Gio::DBus::BusNameWatcherFlags::BUS_NAME_WATCHER_FLAGS_AUTO_START); + Gio::DBus::BusType::SESSION, dbus_name, sigc::mem_fun(*this, &Gamemode::appear), + sigc::mem_fun(*this, &Gamemode::disappear), Gio::DBus::BusNameWatcherFlags::AUTO_START); // Connect to gamemode - gamemode_proxy = Gio::DBus::Proxy::create_for_bus_sync(Gio::DBus::BusType::BUS_TYPE_SESSION, - dbus_name, dbus_obj_path, dbus_interface); + gamemode_proxy = Gio::DBus::Proxy::create_for_bus_sync(Gio::DBus::BusType::SESSION, dbus_name, + dbus_obj_path, dbus_interface); if (!gamemode_proxy) { throw std::runtime_error("Unable to connect to gamemode DBus!..."); } else { @@ -96,7 +85,7 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config) } // Connect to Login1 PrepareForSleep signal - system_connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::BUS_TYPE_SYSTEM); + system_connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SYSTEM); if (!system_connection) { throw std::runtime_error("Unable to connect to the SYSTEM Bus!..."); } else { @@ -104,8 +93,6 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config) sigc::mem_fun(*this, &Gamemode::prepareForSleep_cb), "org.freedesktop.login1", "org.freedesktop.login1.Manager", "PrepareForSleep", "/org/freedesktop/login1"); } - - event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &Gamemode::handleToggle)); } Gamemode::~Gamemode() { @@ -137,7 +124,7 @@ void Gamemode::getData() { } } } catch (Glib::Error& e) { - spdlog::error("Gamemode Error {}", e.what().c_str()); + spdlog::error("Gamemode Error {}", e.what()); } } gameCount = 0; @@ -172,7 +159,7 @@ void Gamemode::prepareForSleep_cb(const Glib::RefPtr& con void Gamemode::appear(const Glib::RefPtr& connection, const Glib::ustring& name, const Glib::ustring& name_owner) { gamemodeRunning = true; - event_box_.set_visible(true); + label_.set_visible(true); getData(); dp.emit(); } @@ -180,24 +167,23 @@ void Gamemode::appear(const Glib::RefPtr& connection, void Gamemode::disappear(const Glib::RefPtr& connection, const Glib::ustring& name) { gamemodeRunning = false; - event_box_.set_visible(false); + label_.set_visible(false); } -bool Gamemode::handleToggle(GdkEventButton* const& event) { +void Gamemode::handleToggle(int n_press, double dx, double dy) { showAltText = !showAltText; dp.emit(); - return true; } auto Gamemode::update() -> void { // Don't update widget if the Gamemode service isn't running if (!gamemodeRunning || (gameCount <= 0 && hideNotRunning)) { - event_box_.set_visible(false); + label_.set_visible(false); return; } // Show the module - if (!event_box_.get_visible()) event_box_.set_visible(true); + if (!label_.get_visible()) label_.set_visible(true); // CSS status class const std::string status = gamemodeRunning && gameCount > 0 ? "running" : ""; @@ -224,10 +210,8 @@ auto Gamemode::update() -> void { label_.set_markup(str); if (useIcon) { - if (!DefaultGtkIconThemeWrapper::has_icon(iconName)) { - iconName = DEFAULT_ICON_NAME; - } - icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID); + if (!gtkTheme_->has_icon(iconName)) iconName = DEFAULT_ICON_NAME; + icon_.set_from_icon_name(iconName); } // Call parent update diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 39341a14b..ce40010fe 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -1,17 +1,6 @@ #include "modules/hyprland/backend.hpp" -#include -#include #include -#include -#include -#include -#include -#include - -#include -#include -#include namespace waybar::modules::hyprland { diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 96677d127..0178afb90 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -53,13 +53,13 @@ auto Submap::update() -> void { std::lock_guard lg(mutex_); if (submap_.empty()) { - event_box_.hide(); + label_.hide(); } else { label_.set_markup(fmt::format(fmt::runtime(format_), submap_)); if (tooltipEnabled()) { label_.set_tooltip_text(submap_); } - event_box_.show(); + label_.show(); } // Call parent update ALabel::update(); diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index b5ed8f021..8b31cf1cc 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -1,14 +1,7 @@ #include "modules/hyprland/window.hpp" -#include -#include -#include #include -#include -#include - -#include "modules/hyprland/backend.hpp" #include "util/rewrite_string.hpp" #include "util/sanitize_str.hpp" @@ -75,14 +68,14 @@ auto Window::update() -> void { setClass("fullscreen", fullscreen_); if (!lastSoloClass_.empty() && soloClass_ != lastSoloClass_) { - if (bar_.window.get_style_context()->has_class(lastSoloClass_)) { - bar_.window.get_style_context()->remove_class(lastSoloClass_); + if (box_.get_style_context()->has_class(lastSoloClass_)) { + box_.get_style_context()->remove_class(lastSoloClass_); spdlog::trace("Removing solo class: {}", lastSoloClass_); } } if (!soloClass_.empty() && soloClass_ != lastSoloClass_) { - bar_.window.get_style_context()->add_class(soloClass_); + box_.get_style_context()->add_class(soloClass_); spdlog::trace("Adding solo class: {}", soloClass_); } lastSoloClass_ = soloClass_; @@ -222,11 +215,11 @@ void Window::onEvent(const std::string& ev) { void Window::setClass(const std::string& classname, bool enable) { if (enable) { - if (!bar_.window.get_style_context()->has_class(classname)) { - bar_.window.get_style_context()->add_class(classname); + if (!box_.get_style_context()->has_class(classname)) { + box_.get_style_context()->add_class(classname); } } else { - bar_.window.get_style_context()->remove_class(classname); + box_.get_style_context()->remove_class(classname); } } diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index e575d1c4d..133f1dc49 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -1,10 +1,5 @@ -#include #include -#include -#include -#include - #include "modules/hyprland/workspaces.hpp" namespace waybar::modules::hyprland { @@ -18,7 +13,8 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma m_windows(workspace_data["windows"].asInt()), m_isActive(true), m_isPersistentRule(workspace_data["persistent-rule"].asBool()), - m_isPersistentConfig(workspace_data["persistent-config"].asBool()) { + m_isPersistentConfig(workspace_data["persistent-config"].asBool()), + controllClick_{Gtk::GestureClick::create()} { if (m_name.starts_with("name:")) { m_name = m_name.substr(5); } else if (m_name.starts_with("special")) { @@ -26,13 +22,12 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma m_isSpecial = true; } - m_button.add_events(Gdk::BUTTON_PRESS_MASK); - m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked), - false); + controllClick_->signal_pressed().connect(sigc::mem_fun(*this, &Workspace::handleToggle), true); + m_button.add_controller(controllClick_); - m_button.set_relief(Gtk::RELIEF_NONE); - m_content.set_center_widget(m_label); - m_button.add(m_content); + m_button.set_has_frame(false); + m_content.append(m_label); + m_button.set_child(m_content); initializeWindowMap(clients_data); } @@ -53,32 +48,28 @@ std::optional Workspace::closeWindow(WindowAddress const &addr) { return std::nullopt; } -bool Workspace::handleClicked(GdkEventButton *bt) const { - if (bt->type == GDK_BUTTON_PRESS) { - try { - if (id() > 0) { // normal - if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); - } else { - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); - } - } else if (!isSpecial()) { // named (this includes persistent) - if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); - } else { - gIPC->getSocket1Reply("dispatch workspace name:" + name()); - } - } else if (id() != -99) { // named special - gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); - } else { // special - gIPC->getSocket1Reply("dispatch togglespecialworkspace"); +void Workspace::handleToggle(int n_press, double dx, double dy) { + try { + if (id() > 0) { // normal + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); + } else { + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } + } else if (!isSpecial()) { // named (this includes persistent) + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); + } else { + gIPC->getSocket1Reply("dispatch workspace name:" + name()); } - return true; - } catch (const std::exception &e) { - spdlog::error("Failed to dispatch workspace: {}", e.what()); + } else if (id() != -99) { // named special + gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); + } else { // special + gIPC->getSocket1Reply("dispatch togglespecialworkspace"); } + } catch (const std::exception &e) { + spdlog::error("Failed to dispatch workspace: {}", e.what()); } - return false; } void Workspace::initializeWindowMap(const Json::Value &clients_data) { diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 13364f3f2..df71e621d 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -22,7 +22,6 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value m_box.get_style_context()->add_class(id); } m_box.get_style_context()->add_class(MODULE_CLASS); - event_box_.add(m_box); if (!gIPC) { gIPC = std::make_unique(); @@ -100,9 +99,9 @@ void Workspaces::createWorkspace(Json::Value const &workspace_data, // create new workspace m_workspaces.emplace_back(std::make_unique(workspace_data, *this, clients_data)); Gtk::Button &newWorkspaceButton = m_workspaces.back()->button(); - m_box.pack_start(newWorkspaceButton, false, false); + m_box.append(newWorkspaceButton); sortWorkspaces(); - newWorkspaceButton.show_all(); + newWorkspaceButton.show(); } void Workspaces::createWorkspacesToCreate() { @@ -783,7 +782,10 @@ void Workspaces::sortWorkspaces() { }); for (size_t i = 0; i < m_workspaces.size(); ++i) { - m_box.reorder_child(m_workspaces[i]->button(), i); + if (i == 0) + m_box.reorder_child_at_start(m_workspaces[i]->button()); + else + m_box.reorder_child_after(m_workspaces[i]->button(), m_workspaces[i - 1]->button()); } } diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index a5fc9ac73..caa88dc69 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -1,13 +1,15 @@ #include "modules/idle_inhibitor.hpp" +#include + #include "idle-inhibit-unstable-v1-client-protocol.h" -#include "util/command.hpp" -std::list waybar::modules::IdleInhibitor::modules; -bool waybar::modules::IdleInhibitor::status = false; +namespace waybar::modules { + +std::list IdleInhibitor::modules; +bool IdleInhibitor::status = false; -waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar, - const Json::Value& config) +IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "idle_inhibitor", id, "{status}", 0, false, true), bar_(bar), idle_inhibitor_(nullptr), @@ -16,29 +18,25 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& throw std::runtime_error("idle-inhibit not available"); } - if (waybar::modules::IdleInhibitor::modules.empty() && config_["start-activated"].isBool() && + if (IdleInhibitor::modules.empty() && config_["start-activated"].isBool() && config_["start-activated"].asBool() != status) { toggleStatus(); } - event_box_.add_events(Gdk::BUTTON_PRESS_MASK); - event_box_.signal_button_press_event().connect( - sigc::mem_fun(*this, &IdleInhibitor::handleToggle)); - // Add this to the modules list - waybar::modules::IdleInhibitor::modules.push_back(this); + IdleInhibitor::modules.push_back(this); dp.emit(); } -waybar::modules::IdleInhibitor::~IdleInhibitor() { +IdleInhibitor::~IdleInhibitor() { if (idle_inhibitor_ != nullptr) { zwp_idle_inhibitor_v1_destroy(idle_inhibitor_); idle_inhibitor_ = nullptr; } // Remove this from the modules list - waybar::modules::IdleInhibitor::modules.remove(this); + IdleInhibitor::modules.remove(this); if (pid_ != -1) { kill(-pid_, 9); @@ -46,7 +44,7 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() { } } -auto waybar::modules::IdleInhibitor::update() -> void { +auto IdleInhibitor::update() -> void { // Check status if (status) { label_.get_style_context()->remove_class("deactivated"); @@ -77,7 +75,7 @@ auto waybar::modules::IdleInhibitor::update() -> void { ALabel::update(); } -void waybar::modules::IdleInhibitor::toggleStatus() { +void IdleInhibitor::toggleStatus() { status = !status; if (timeout_.connected()) { @@ -96,7 +94,7 @@ void waybar::modules::IdleInhibitor::toggleStatus() { */ spdlog::info("deactivating idle_inhibitor by timeout"); status = false; - for (auto const& module : waybar::modules::IdleInhibitor::modules) { + for (auto const& module : IdleInhibitor::modules) { module->update(); } /* disconnect */ @@ -106,18 +104,19 @@ void waybar::modules::IdleInhibitor::toggleStatus() { } } -bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) { - if (e->button == 1) { +void IdleInhibitor::handleToggle(int n_press, double dx, double dy) { + if (AModule::controllClick_->get_current_button() == 1) { toggleStatus(); // Make all other idle inhibitor modules update - for (auto const& module : waybar::modules::IdleInhibitor::modules) { + for (auto const& module : IdleInhibitor::modules) { if (module != this) { module->update(); } } } - ALabel::handleToggle(e); - return true; + ALabel::handleToggle(n_press, dx, dy); } + +} /* namespace waybar::modules */ diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 71e93b946..6a67c2250 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -1,14 +1,16 @@ #include "modules/image.hpp" -waybar::modules::Image::Image(const std::string& id, const Json::Value& config) - : AModule(config, "image", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0) { - box_.pack_start(image_); +namespace waybar::modules { + +Image::Image(const std::string& id, const Json::Value& config) + : AModule(config, "image", id), box_(Gtk::Orientation::HORIZONTAL, 0) { + box_.append(image_); box_.set_name("image"); if (!id.empty()) { box_.get_style_context()->add_class(id); } box_.get_style_context()->add_class(MODULE_CLASS); - event_box_.add(box_); + AModule::bindEvents(box_); dp.emit(); @@ -27,15 +29,15 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config) delayWorker(); } -void waybar::modules::Image::delayWorker() { +void Image::delayWorker() { thread_ = [this] { dp.emit(); - auto interval = std::chrono::seconds(interval_); + auto interval{std::chrono::seconds(interval_)}; thread_.sleep_for(interval); }; } -void waybar::modules::Image::refresh(int sig) { +void Image::refresh(int sig) { if (sig == SIGRTMIN + config_["signal"].asInt()) { thread_.wake_up(); } @@ -51,15 +53,8 @@ auto waybar::modules::Image::update() -> void { path_ = ""; } - if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) { - Glib::RefPtr pixbuf; - - int scaled_icon_size = size_ * image_.get_scale_factor(); - pixbuf = Gdk::Pixbuf::create_from_file(path_, scaled_icon_size, scaled_icon_size); - - auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), - image_.get_window()); - image_.set(surface); + if (Glib::file_test(path_, Glib::FileTest::EXISTS)) { + image_.set(path_); image_.show(); if (tooltipEnabled() && !tooltip_.empty()) { @@ -78,10 +73,10 @@ auto waybar::modules::Image::update() -> void { AModule::update(); } -void waybar::modules::Image::parseOutputRaw() { +void Image::parseOutputRaw() { std::istringstream output(output_.out); std::string line; - int i = 0; + int i{0}; while (getline(output, line)) { if (i == 0) { path_ = line; @@ -93,3 +88,7 @@ void waybar::modules::Image::parseOutputRaw() { i++; } } + +Image::operator Gtk::Widget&() { return box_; }; + +} // namespace waybar::modules diff --git a/src/modules/inhibitor.cpp b/src/modules/inhibitor.cpp index fe2a4be41..1ea0e38df 100644 --- a/src/modules/inhibitor.cpp +++ b/src/modules/inhibitor.cpp @@ -1,7 +1,5 @@ #include "modules/inhibitor.hpp" -#include -#include #include namespace { @@ -97,12 +95,10 @@ auto getInhibitors(const Json::Value& config) -> std::string { namespace waybar::modules { -Inhibitor::Inhibitor(const std::string& id, const Bar& bar, const Json::Value& config) +Inhibitor::Inhibitor(const std::string& id, const Json::Value& config) : ALabel(config, "inhibitor", id, "{status}", true), dbus_(::dbus()), inhibitors_(::getInhibitors(config)) { - event_box_.add_events(Gdk::BUTTON_PRESS_MASK); - event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &Inhibitor::handleToggle)); dp.emit(); } @@ -129,20 +125,21 @@ auto Inhibitor::update() -> void { return ALabel::update(); } -auto Inhibitor::handleToggle(GdkEventButton* const& e) -> bool { - if (e->button == 1) { - if (activated()) { - ::close(handle_); - handle_ = -1; - } else { - handle_ = ::getLocks(dbus_, inhibitors_); - if (handle_ == -1) { - spdlog::error("cannot get inhibitor locks"); - } - } - } +auto Inhibitor::doAction(const std::string& name) -> void { + if (actionMap_[name]) { + (this->*actionMap_[name])(); + } else + spdlog::info("Inhibitor. Unsupported action \"{0}\"", name); +} - return ALabel::handleToggle(e); +void Inhibitor::toggle() { + if (activated()) { + ::close(handle_); + handle_ = -1; + } else { + handle_ = ::getLocks(dbus_, inhibitors_); + if (handle_ == -1) spdlog::error("cannot get inhibitor locks"); + } } } // namespace waybar::modules diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 18ce0a7c4..0b987d1dd 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -1,20 +1,12 @@ #include "modules/keyboard_state.hpp" -#include #include -#include - -#include extern "C" { #include -#include -#include +#include #include #include -#include -#include -#include } class errno_error : public std::runtime_error { @@ -32,12 +24,12 @@ class errno_error : public std::runtime_error { #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 32) // strerrorname_np gets the error code's name; it's nice to have, but it's a recent GNU // extension - const auto errno_name = strerrorname_np(err); + const auto errno_name{strerrorname_np(err)}; error_msg += errno_name; error_msg += " "; #endif - const auto errno_str = strerror(err); + const auto errno_str{strerror(err)}; error_msg += errno_str; return error_msg; @@ -45,7 +37,7 @@ class errno_error : public std::runtime_error { }; auto openFile(const std::string& path, int flags) -> int { - int fd = open(path.c_str(), flags); + int fd{open(path.c_str(), flags)}; if (fd < 0) { if (errno == EACCES) { throw errno_error(errno, "Can't open " + path + " (are you in the input group?)"); @@ -57,7 +49,7 @@ auto openFile(const std::string& path, int flags) -> int { } auto closeFile(int fd) -> void { - int res = close(fd); + int res{close(fd)}; if (res < 0) { throw errno_error(errno, "Can't close file"); } @@ -65,7 +57,7 @@ auto closeFile(int fd) -> void { auto openDevice(int fd) -> libevdev* { libevdev* dev; - int err = libevdev_new_from_fd(fd, &dev); + int err{libevdev_new_from_fd(fd, &dev)}; if (err < 0) { throw errno_error(-err, "Can't create libevdev device"); } @@ -78,38 +70,40 @@ auto supportsLockStates(const libevdev* dev) -> bool { libevdev_has_event_code(dev, EV_LED, LED_SCROLLL); } -waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, - const Json::Value& config) +namespace 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()), - box_(bar.orientation, 0), - numlock_label_(""), - capslock_label_(""), - numlock_format_(config_["format"].isString() ? config_["format"].asString() + box_{bar.orientation, 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() + : "{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() + : "{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() + : "{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() + : "locked"}, + icon_unlocked_{config_["format-icons"]["unlocked"].isString() ? config_["format-icons"]["unlocked"].asString() - : "unlocked"), - devices_path_("/dev/input/"), - libinput_(nullptr), - libinput_devices_({}) { - static struct libinput_interface interface = { - [](const char* path, int flags, void* user_data) { return open(path, flags); }, - [](int fd, void* user_data) { close(fd); }}; + : "unlocked"}, + devices_path_{"/dev/input/"}, + libinput_{nullptr}, + libinput_devices_{{}} { + static struct libinput_interface interface { + [](const char* path, int flags, void* user_data) { return open(path, flags); }, + [](int fd, void* user_data) { close(fd); } + }; if (config_["interval"].isUInt()) { spdlog::warn("keyboard-state: interval is deprecated"); } @@ -119,31 +113,32 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& box_.set_name("keyboard-state"); if (config_["numlock"].asBool()) { numlock_label_.get_style_context()->add_class("numlock"); - box_.pack_end(numlock_label_, false, false, 0); + box_.append(numlock_label_); } if (config_["capslock"].asBool()) { capslock_label_.get_style_context()->add_class("capslock"); - box_.pack_end(capslock_label_, false, false, 0); + box_.append(capslock_label_); } if (config_["scrolllock"].asBool()) { scrolllock_label_.get_style_context()->add_class("scrolllock"); - box_.pack_end(scrolllock_label_, false, false, 0); + box_.append(scrolllock_label_); } if (!id.empty()) { box_.get_style_context()->add_class(id); } box_.get_style_context()->add_class(MODULE_CLASS); - event_box_.add(box_); if (config_["device-path"].isString()) { - std::string dev_path = config_["device-path"].asString(); + std::string dev_path{config_["device-path"].asString()}; tryAddDevice(dev_path); if (libinput_devices_.empty()) { spdlog::error("keyboard-state: Cannot find device {}", dev_path); } } - auto keys = config_["binding-keys"]; + AModule::bindEvents(box_); + + auto keys{config_["binding-keys"]}; if (keys.isArray()) { for (const auto& key : keys) { if (key.isInt()) { @@ -158,14 +153,14 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& binding_keys.insert(KEY_SCROLLLOCK); } - DIR* dev_dir = opendir(devices_path_.c_str()); + DIR* dev_dir{opendir(devices_path_.c_str())}; if (dev_dir == nullptr) { throw errno_error(errno, "Failed to open " + devices_path_); } dirent* ep; while ((ep = readdir(dev_dir))) { if (ep->d_type == DT_DIR) continue; - std::string dev_path = devices_path_ + ep->d_name; + std::string dev_path{devices_path_ + ep->d_name}; tryAddDevice(dev_path); } @@ -176,17 +171,19 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& libinput_thread_ = [this] { dp.emit(); while (1) { - struct pollfd fd = {libinput_get_fd(libinput_), POLLIN, 0}; + struct pollfd fd { + libinput_get_fd(libinput_), POLLIN, 0 + }; poll(&fd, 1, -1); libinput_dispatch(libinput_); struct libinput_event* event; while ((event = libinput_get_event(libinput_))) { - auto type = libinput_event_get_type(event); + auto type{libinput_event_get_type(event)}; if (type == LIBINPUT_EVENT_KEYBOARD_KEY) { - auto keyboard_event = libinput_event_get_keyboard_event(event); - auto state = libinput_event_keyboard_get_key_state(keyboard_event); + auto keyboard_event{libinput_event_get_keyboard_event(event)}; + auto state{libinput_event_keyboard_get_key_state(keyboard_event)}; if (state == LIBINPUT_KEY_STATE_RELEASED) { - uint32_t key = libinput_event_keyboard_get_key(keyboard_event); + uint32_t key{libinput_event_keyboard_get_key(keyboard_event)}; if (binding_keys.contains(key)) { dp.emit(); } @@ -206,22 +203,22 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& } inotify_add_watch(fd, devices_path_.c_str(), IN_CREATE | IN_DELETE); while (1) { - int BUF_LEN = 1024 * (sizeof(struct inotify_event) + 16); + int BUF_LEN{1024 * (sizeof(struct inotify_event) + 16)}; char buf[BUF_LEN]; - int length = read(fd, buf, 1024); + int length{read(fd, buf, 1024)}; if (length < 0) { spdlog::error("Failed to read inotify: {}", strerror(errno)); return; } - for (int i = 0; i < length;) { - struct inotify_event* event = (struct inotify_event*)&buf[i]; - std::string dev_path = devices_path_ + event->name; + for (int i{0}; i < length;) { + struct inotify_event* event{(struct inotify_event*)&buf[i]}; + std::string dev_path{devices_path_ + event->name}; if (event->mask & IN_CREATE) { // Wait for device setup - int timeout = 10; + int timeout{10}; while (timeout--) { try { - int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); + int fd{openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY)}; closeFile(fd); break; } catch (const errno_error& e) { @@ -232,7 +229,7 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& } tryAddDevice(dev_path); } else if (event->mask & IN_DELETE) { - auto it = libinput_devices_.find(dev_path); + auto it{libinput_devices_.find(dev_path)}; if (it != libinput_devices_.end()) { spdlog::info("Keyboard {} has been removed.", dev_path); libinput_devices_.erase(it); @@ -244,15 +241,15 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& }; } -waybar::modules::KeyboardState::~KeyboardState() { +KeyboardState::~KeyboardState() { for (const auto& [_, dev_ptr] : libinput_devices_) { libinput_path_remove_device(dev_ptr); } } -auto waybar::modules::KeyboardState::update() -> void { +auto KeyboardState::update() -> void { sleep(0); // Wait for keyboard status change - int numl = 0, capsl = 0, scrolll = 0; + int numl{0}, capsl{0}, scrolll{0}; try { std::string dev_path; @@ -262,8 +259,8 @@ auto waybar::modules::KeyboardState::update() -> void { } else { dev_path = libinput_devices_.begin()->first; } - int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); - auto dev = openDevice(fd); + int fd{openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY)}; + auto dev{openDevice(fd)}; numl = libevdev_get_event_value(dev, EV_LED, LED_NUML); capsl = libevdev_get_event_value(dev, EV_LED, LED_CAPSL); scrolll = libevdev_get_event_value(dev, EV_LED, LED_SCROLLL); @@ -281,16 +278,16 @@ auto waybar::modules::KeyboardState::update() -> void { Gtk::Label& label; const std::string& format; const char* name; - } label_states[] = { + } 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(fmt::runtime(label_state.format), - fmt::arg("icon", label_state.state ? icon_locked_ : icon_unlocked_), - fmt::arg("name", label_state.name)); + std::string text{ + fmt::format(fmt::runtime(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"); @@ -302,14 +299,14 @@ auto waybar::modules::KeyboardState::update() -> void { AModule::update(); } -auto waybar::modules ::KeyboardState::tryAddDevice(const std::string& dev_path) -> void { +auto KeyboardState::tryAddDevice(const std::string& dev_path) -> void { try { - int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); - auto dev = openDevice(fd); + int fd{openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY)}; + auto dev{openDevice(fd)}; if (supportsLockStates(dev)) { spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path); if (libinput_devices_.find(dev_path) == libinput_devices_.end()) { - auto device = libinput_path_add_device(libinput_, dev_path.c_str()); + auto device{libinput_path_add_device(libinput_, dev_path.c_str())}; libinput_device_ref(device); libinput_devices_[dev_path] = device; } @@ -323,3 +320,7 @@ auto waybar::modules ::KeyboardState::tryAddDevice(const std::string& dev_path) } } } + +KeyboardState::operator Gtk::Widget&() { return box_; }; + +} // namespace waybar::modules diff --git a/src/modules/load.cpp b/src/modules/load.cpp index 69a37b4eb..dd25cc268 100644 --- a/src/modules/load.cpp +++ b/src/modules/load.cpp @@ -1,5 +1,7 @@ #include "modules/load.hpp" +#include + // In the 80000 version of fmt library authors decided to optimize imports // and moved declarations required for fmt::dynamic_format_arg_store in new // header fmt/args.h @@ -31,9 +33,9 @@ auto waybar::modules::Load::update() -> void { } if (format.empty()) { - event_box_.hide(); + label_.hide(); } else { - event_box_.show(); + label_.show(); auto icons = std::vector{state}; fmt::dynamic_format_arg_store store; store.push_back(fmt::arg("load1", load1)); diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index 544d78145..52d2f193f 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -1,3 +1,5 @@ +#include + #include "modules/memory.hpp" waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config) @@ -51,9 +53,9 @@ auto waybar::modules::Memory::update() -> void { } if (format.empty()) { - event_box_.hide(); + label_.hide(); } else { - event_box_.show(); + label_.show(); auto icons = std::vector{state}; label_.set_markup(fmt::format( fmt::runtime(format), used_ram_percentage, @@ -80,7 +82,7 @@ auto waybar::modules::Memory::update() -> void { } } } else { - event_box_.hide(); + label_.hide(); } // Call parent update ALabel::update(); diff --git a/src/modules/memory/linux.cpp b/src/modules/memory/linux.cpp index 50f50d6f1..7e7cc0203 100644 --- a/src/modules/memory/linux.cpp +++ b/src/modules/memory/linux.cpp @@ -1,3 +1,5 @@ +#include + #include "modules/memory.hpp" static unsigned zfsArcSize() { diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 192e6c1ad..b12474d6f 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -40,8 +40,7 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config) server_ = config["server"].asCString(); } - event_box_.add_events(Gdk::BUTTON_PRESS_MASK); - event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &MPD::handlePlayPause)); + controllClick_->signal_pressed().connect(sigc::mem_fun(*this, &MPD::handlePlayPause), false); } auto waybar::modules::MPD::update() -> void { @@ -354,19 +353,17 @@ void waybar::modules::MPD::fetchState() { checkErrors(conn); } -bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) { - if (e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS || connection_ == nullptr) { - return false; - } +void waybar::modules::MPD::handlePlayPause(int n_press, double dx, double dy) { + if (n_press != 1 || connection_ == nullptr) return; + + auto button{controllClick_->get_current_button()}; - if (e->button == 1) { + if (button == 1u) { if (state_ == MPD_STATE_PLAY) context_.pause(); else context_.play(); - } else if (e->button == 3) { + } else if (button == 3u) { context_.stop(); } - - return true; } diff --git a/src/modules/mpd/state.cpp b/src/modules/mpd/state.cpp index 3d7c8561e..7bc2b0e00 100644 --- a/src/modules/mpd/state.cpp +++ b/src/modules/mpd/state.cpp @@ -66,10 +66,11 @@ void Idle::entry() noexcept { spdlog::error("mpd: Idle: failed to register for IDLE events"); } else { spdlog::debug("mpd: Idle: watching FD"); - sigc::slot idle_slot = sigc::mem_fun(*this, &Idle::on_io); + sigc::slot idle_slot = sigc::mem_fun(*this, &Idle::on_io); idle_connection_ = Glib::signal_io().connect(idle_slot, mpd_connection_get_fd(conn), - Glib::IO_IN | Glib::IO_PRI | Glib::IO_ERR | Glib::IO_HUP); + Glib::IOCondition::IO_IN | Glib::IOCondition::IO_PRI | + Glib::IOCondition::IO_ERR | Glib::IOCondition::IO_HUP); } } @@ -118,7 +119,7 @@ bool Idle::on_io(Glib::IOCondition const&) { } void Playing::entry() noexcept { - sigc::slot timer_slot = sigc::mem_fun(*this, &Playing::on_timer); + sigc::slot timer_slot = sigc::mem_fun(*this, &Playing::on_timer); timer_connection_ = Glib::signal_timeout().connect_seconds(timer_slot, 1); spdlog::debug("mpd: Playing: enabled 1 second periodic timer."); } @@ -186,7 +187,7 @@ void Playing::pause() { void Playing::update() noexcept { ctx_->do_update(); } void Paused::entry() noexcept { - sigc::slot timer_slot = sigc::mem_fun(*this, &Paused::on_timer); + sigc::slot timer_slot = sigc::mem_fun(*this, &Paused::on_timer); timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200); spdlog::debug("mpd: Paused: enabled 200 ms periodic timer."); } @@ -257,7 +258,7 @@ void Paused::stop() { void Paused::update() noexcept { ctx_->do_update(); } void Stopped::entry() noexcept { - sigc::slot timer_slot = sigc::mem_fun(*this, &Stopped::on_timer); + sigc::slot timer_slot = sigc::mem_fun(*this, &Stopped::on_timer); timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200); spdlog::debug("mpd: Stopped: enabled 200 ms periodic timer."); } @@ -337,7 +338,7 @@ bool Disconnected::arm_timer(int interval) noexcept { // register timer last_interval_ = interval; - sigc::slot timer_slot = sigc::mem_fun(*this, &Disconnected::on_timer); + sigc::slot timer_slot = sigc::mem_fun(*this, &Disconnected::on_timer); timer_connection_ = Glib::signal_timeout().connect_seconds(timer_slot, interval); spdlog::debug("mpd: Disconnected: enabled {}s interval timer.", interval); return false; diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris.cpp similarity index 98% rename from src/modules/mpris/mpris.cpp rename to src/modules/mpris.cpp index ed383b0ce..8495dc166 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris.cpp @@ -1,4 +1,4 @@ -#include "modules/mpris/mpris.hpp" +#include "modules/mpris.hpp" #include @@ -13,6 +13,7 @@ extern "C" { } #include +#include #include namespace waybar::modules::mpris { @@ -459,7 +460,7 @@ auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void { spdlog::debug("mpris: player-stop callback"); // hide widget - mpris->event_box_.set_visible(false); + mpris->label_.set_visible(false); // update widget mpris->dp.emit(); } @@ -583,7 +584,7 @@ auto Mpris::getPlayerInfo() -> std::optional { return std::nullopt; } -bool Mpris::handleToggle(GdkEventButton* const& e) { +void Mpris::handleToggle(int n_press, double dx, double dy) { GError* error = nullptr; waybar::util::ScopeGuard error_deleter([error]() { if (error) { @@ -592,36 +593,33 @@ bool Mpris::handleToggle(GdkEventButton* const& e) { }); auto info = getPlayerInfo(); - if (!info) return false; + if (!info) return; - if (e->type == GdkEventType::GDK_BUTTON_PRESS) { - switch (e->button) { + if (n_press == 1) { + switch (controllClick_->get_current_button()) { case 1: // left-click if (config_["on-click"].isString()) { - return ALabel::handleToggle(e); + return ALabel::handleToggle(n_press, dx, dy); } playerctl_player_play_pause(player, &error); break; case 2: // middle-click if (config_["on-click-middle"].isString()) { - return ALabel::handleToggle(e); + return ALabel::handleToggle(n_press, dx, dy); } playerctl_player_previous(player, &error); break; case 3: // right-click if (config_["on-click-right"].isString()) { - return ALabel::handleToggle(e); + return ALabel::handleToggle(n_press, dx, dy); } playerctl_player_next(player, &error); break; } } - if (error) { + if (error) spdlog::error("mpris[{}]: error running builtin on-click action: {}", (*info).name, error->message); - return false; - } - return true; } auto Mpris::update() -> void { @@ -631,7 +629,7 @@ auto Mpris::update() -> void { auto opt = getPlayerInfo(); if (!opt) { - event_box_.set_visible(false); + label_.set_visible(false); ALabel::update(); return; } @@ -729,7 +727,7 @@ auto Mpris::update() -> void { } } - event_box_.set_visible(true); + label_.set_visible(true); // call parent update ALabel::update(); } diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 0bbea6315..70bb2690d 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -1,29 +1,27 @@ #include "modules/network.hpp" +#include +#include #include +#include +#include #include #include -#include #include -#include -#include #include "util/format.hpp" -#ifdef WANT_RFKILL -#include "util/rfkill.hpp" -#endif namespace { using namespace waybar::util; -constexpr const char *DEFAULT_FORMAT = "{ifname}"; +constexpr const char *DEFAULT_FORMAT{"{ifname}"}; } // namespace -constexpr const char *NETDEV_FILE = - "/proc/net/dev"; // std::ifstream does not take std::string_view as param +constexpr const char *NETDEV_FILE{ + "/proc/net/dev"}; // std::ifstream does not take std::string_view as param std::optional> waybar::modules::Network::readBandwidthUsage() { - std::ifstream netdev(NETDEV_FILE); + std::ifstream netdev{NETDEV_FILE}; if (!netdev) { spdlog::warn("Failed to open netdev file {}", NETDEV_FILE); return {}; @@ -34,10 +32,10 @@ waybar::modules::Network::readBandwidthUsage() { std::getline(netdev, line); std::getline(netdev, line); - unsigned long long receivedBytes = 0ull; - unsigned long long transmittedBytes = 0ull; + unsigned long long receivedBytes{0ull}; + unsigned long long transmittedBytes{0ull}; while (std::getline(netdev, line)) { - std::istringstream iss(line); + std::istringstream iss{line}; std::string ifacename; iss >> ifacename; // ifacename contains "eth0:" @@ -52,12 +50,12 @@ waybar::modules::Network::readBandwidthUsage() { // // We only care about the bytes count, so we'll just ignore the 7 other // columns. - unsigned long long r = 0ull; - unsigned long long t = 0ull; + unsigned long long r{0ull}; + unsigned long long t{0ull}; // Read received bytes iss >> r; // Skip all the other columns in the received group - for (int colsToSkip = 7; colsToSkip > 0; colsToSkip--) { + for (int colsToSkip{7}; colsToSkip > 0; colsToSkip--) { // skip whitespace between columns while (iss.peek() == ' ') { iss.ignore(); @@ -79,22 +77,22 @@ waybar::modules::Network::readBandwidthUsage() { waybar::modules::Network::Network(const std::string &id, const Json::Value &config) : ALabel(config, "network", id, DEFAULT_FORMAT, 60), - ifid_(-1), - family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), - efd_(-1), - ev_fd_(-1), - want_route_dump_(false), - want_link_dump_(false), - want_addr_dump_(false), - dump_in_progress_(false), - is_p2p_(false), - cidr_(0), - signal_strength_dbm_(0), - signal_strength_(0), + ifid_{-1}, + family_{config["family"] == "ipv6" ? AF_INET6 : AF_INET}, + efd_{-1}, + ev_fd_{-1}, + want_route_dump_{false}, + want_link_dump_{false}, + want_addr_dump_{false}, + dump_in_progress_{false}, + is_p2p_{false}, + cidr_{0}, + signal_strength_dbm_{0}, + signal_strength_{0}, #ifdef WANT_RFKILL rfkill_{RFKILL_TYPE_WLAN}, #endif - frequency_(0.0) { + frequency_{0.0} { // Start with some "text" in the module's label_. update() will then // update it. Since the text should be different, update() will be able @@ -102,7 +100,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf // the module start with no text, but the event_box_ is shown. label_.set_markup(""); - auto bandwidth = readBandwidthUsage(); + auto bandwidth{readBandwidthUsage()}; if (bandwidth.has_value()) { bandwidth_down_total_ = (*bandwidth).first; bandwidth_up_total_ = (*bandwidth).second; @@ -161,7 +159,7 @@ void waybar::modules::Network::createEventSocket() { nl_socket_disable_seq_check(ev_sock_); nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this); nl_socket_modify_cb(ev_sock_, NL_CB_FINISH, NL_CB_CUSTOM, handleEventsDone, this); - auto groups = RTMGRP_LINK | (family_ == AF_INET ? RTMGRP_IPV4_IFADDR : RTMGRP_IPV6_IFADDR); + auto groups{RTMGRP_LINK | (family_ == AF_INET ? RTMGRP_IPV4_IFADDR : RTMGRP_IPV6_IFADDR)}; nl_join_groups(ev_sock_, groups); // Deprecated if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) { throw std::runtime_error("Can't connect network socket"); @@ -198,7 +196,7 @@ void waybar::modules::Network::createEventSocket() { } } { - auto fd = nl_socket_get_fd(ev_sock_); + auto fd{nl_socket_get_fd(ev_sock_)}; struct epoll_event event; memset(&event, 0, sizeof(event)); event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; @@ -227,7 +225,7 @@ void waybar::modules::Network::worker() { // update via here not working thread_timer_ = [this] { { - std::lock_guard lock(mutex_); + std::lock_guard lock{mutex_}; if (ifid_ > 0) { getInfo(); dp.emit(); @@ -249,11 +247,11 @@ void waybar::modules::Network::worker() { thread_ = [this] { std::array events{}; - int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1); + int ec{epoll_wait(efd_, events.data(), EPOLL_MAX, -1)}; if (ec > 0) { - for (auto i = 0; i < ec; i++) { + for (auto i{0}; i < ec; i++) { if (events[i].data.fd == nl_socket_get_fd(ev_sock_)) { - int rc = 0; + int rc{0}; // Read as many message as possible, until the socket blocks while (true) { errno = 0; @@ -291,15 +289,15 @@ const std::string waybar::modules::Network::getNetworkState() const { } auto waybar::modules::Network::update() -> void { - std::lock_guard lock(mutex_); + std::lock_guard lock{mutex_}; std::string tooltip_format; - auto bandwidth = readBandwidthUsage(); - auto bandwidth_down = 0ull; - auto bandwidth_up = 0ull; + auto bandwidth{readBandwidthUsage()}; + auto bandwidth_down{0ull}; + auto bandwidth_up{0ull}; if (bandwidth.has_value()) { - auto down_octets = (*bandwidth).first; - auto up_octets = (*bandwidth).second; + auto down_octets{(*bandwidth).first}; + auto up_octets{(*bandwidth).second}; bandwidth_down = down_octets - bandwidth_down_total_; bandwidth_down_total_ = down_octets; @@ -309,7 +307,7 @@ auto waybar::modules::Network::update() -> void { } if (!alt_) { - auto state = getNetworkState(); + auto state{getNetworkState()}; if (!state_.empty() && label_.get_style_context()->has_class(state_)) { label_.get_style_context()->remove_class(state_); } @@ -331,7 +329,7 @@ auto waybar::modules::Network::update() -> void { } getState(signal_strength_); - auto text = fmt::format( + const Glib::ustring text{fmt::format( fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), @@ -349,13 +347,13 @@ auto waybar::modules::Network::update() -> void { fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")), fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")), fmt::arg("bandwidthTotalBytes", - pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s"))); + pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s")))}; if (text.compare(label_.get_label()) != 0) { label_.set_markup(text); if (text.empty()) { - event_box_.hide(); + label_.hide(); } else { - event_box_.show(); + label_.show(); } } if (tooltipEnabled()) { @@ -363,7 +361,7 @@ auto waybar::modules::Network::update() -> void { tooltip_format = config_["tooltip-format"].asString(); } if (!tooltip_format.empty()) { - auto tooltip_text = fmt::format( + const Glib::ustring tooltip_text{fmt::format( fmt::runtime(tooltip_format), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), @@ -382,7 +380,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")), fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")), fmt::arg("bandwidthTotalBytes", - pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s"))); + pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s")))}; if (label_.get_tooltip_text() != tooltip_text) { label_.set_tooltip_markup(tooltip_text); } @@ -420,20 +418,20 @@ void waybar::modules::Network::clearIface() { } int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { - auto net = static_cast(data); - std::lock_guard lock(net->mutex_); - auto nh = nlmsg_hdr(msg); - bool is_del_event = false; + auto net{static_cast(data)}; + std::lock_guard lock{net->mutex_}; + auto nh{nlmsg_hdr(msg)}; + bool is_del_event{false}; switch (nh->nlmsg_type) { case RTM_DELLINK: is_del_event = true; case RTM_NEWLINK: { - struct ifinfomsg *ifi = static_cast(NLMSG_DATA(nh)); - ssize_t attrlen = IFLA_PAYLOAD(nh); - struct rtattr *ifla = IFLA_RTA(ifi); - const char *ifname = NULL; - size_t ifname_len = 0; + struct ifinfomsg *ifi{static_cast(NLMSG_DATA(nh))}; + ssize_t attrlen{IFLA_PAYLOAD(nh)}; + struct rtattr *ifla{IFLA_RTA(ifi)}; + const char *ifname{NULL}; + size_t ifname_len{0}; std::optional carrier; if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) { @@ -471,7 +469,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { if (!is_del_event && ifi->ifi_index == net->ifid_) { // Update interface information if (net->ifname_.empty() && ifname != NULL) { - std::string new_ifname(ifname, ifname_len); + std::string new_ifname{ifname, ifname_len}; net->ifname_ = new_ifname; } if (carrier.has_value()) { @@ -493,7 +491,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } } else if (!is_del_event && net->ifid_ == -1) { // Checking if it's an interface we care about. - std::string new_ifname(ifname, ifname_len); + std::string new_ifname{ifname, ifname_len}; if (net->checkInterface(new_ifname)) { spdlog::debug("network: selecting new interface {}/{}", new_ifname, ifi->ifi_index); @@ -523,9 +521,9 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { case RTM_DELADDR: is_del_event = true; case RTM_NEWADDR: { - struct ifaddrmsg *ifa = static_cast(NLMSG_DATA(nh)); - ssize_t attrlen = IFA_PAYLOAD(nh); - struct rtattr *ifa_rta = IFA_RTA(ifa); + struct ifaddrmsg *ifa{static_cast(NLMSG_DATA(nh))}; + ssize_t attrlen{IFA_PAYLOAD(nh)}; + struct rtattr *ifa_rta{IFA_RTA(ifa)}; if ((int)ifa->ifa_index != net->ifid_) { return NL_OK; @@ -558,8 +556,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } case AF_INET6: { struct in6_addr netmask; - for (int i = 0; i < 16; i++) { - int v = (i + 1) * 8 - ifa->ifa_prefixlen; + for (int i{0}; i < 16; i++) { + int v{(i + 1) * 8 - ifa->ifa_prefixlen}; if (v < 0) v = 0; if (v > 8) v = 8; netmask.s6_addr[i] = ~0 << v; @@ -590,13 +588,13 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { // Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698 // to find the interface used to reach the outside world - struct rtmsg *rtm = static_cast(NLMSG_DATA(nh)); - ssize_t attrlen = RTM_PAYLOAD(nh); - struct rtattr *attr = RTM_RTA(rtm); - bool has_gateway = false; - bool has_destination = false; - int temp_idx = -1; - uint32_t priority = 0; + struct rtmsg *rtm{static_cast(NLMSG_DATA(nh))}; + ssize_t attrlen{RTM_PAYLOAD(nh)}; + struct rtattr *attr{RTM_RTA(rtm)}; + bool has_gateway{false}; + bool has_destination{false}; + int temp_idx{-1}; + uint32_t priority{0}; /* Find the message(s) concerting the main routing table, each message * corresponds to a single routing table entry. @@ -625,13 +623,13 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { /* The destination address. * Should be either missing, or maybe all 0s. Accept both. */ - const uint32_t nr_zeroes = (net->family_ == AF_INET) ? 4 : 16; - unsigned char c = 0; - size_t dstlen = RTA_PAYLOAD(attr); + const uint32_t nr_zeroes{(net->family_ == AF_INET) ? 4 : 16}; + unsigned char c{0}; + size_t dstlen{RTA_PAYLOAD(attr)}; if (dstlen != nr_zeroes) { break; } - for (uint32_t i = 0; i < dstlen; i += 1) { + for (uint32_t i{0}; i < dstlen; i += 1) { c |= *((unsigned char *)RTA_DATA(attr) + i); } has_destination = (c == 0); @@ -668,9 +666,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { temp_idx, priority); /* Ask ifname associated with temp_idx as well as carrier status */ - struct ifinfomsg ifinfo_hdr = { - .ifi_family = AF_UNSPEC, - .ifi_index = temp_idx, + 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, @@ -712,8 +709,8 @@ void waybar::modules::Network::askForStateDump(void) { * messages. handleEventsDone() is called when a dump is done. */ if (dump_in_progress_) return; - struct rtgenmsg rt_hdr = { - .rtgen_family = AF_UNSPEC, + struct rtgenmsg rt_hdr { + .rtgen_family = AF_UNSPEC, }; if (want_route_dump_) { @@ -736,15 +733,15 @@ void waybar::modules::Network::askForStateDump(void) { } int waybar::modules::Network::handleEventsDone(struct nl_msg *msg, void *data) { - auto net = static_cast(data); + auto net{static_cast(data)}; net->dump_in_progress_ = false; net->askForStateDump(); return NL_OK; } int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) { - auto net = static_cast(data); - auto gnlh = static_cast(nlmsg_data(nlmsg_hdr(msg))); + auto net{static_cast(data)}; + auto gnlh{static_cast(nlmsg_data(nlmsg_hdr(msg)))}; struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct nlattr *bss[NL80211_BSS_MAX + 1]; struct nla_policy bss_policy[NL80211_BSS_MAX + 1]{}; @@ -780,16 +777,16 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) { void waybar::modules::Network::parseEssid(struct nlattr **bss) { if (bss[NL80211_BSS_INFORMATION_ELEMENTS] != nullptr) { - auto ies = static_cast(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS])); - auto ies_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); - const auto hdr_len = 2; + auto ies{static_cast(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]))}; + auto ies_len{nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS])}; + const auto hdr_len{2}; while (ies_len > hdr_len && ies[0] != 0) { ies_len -= ies[1] + hdr_len; ies += ies[1] + hdr_len; } if (ies_len > hdr_len && ies_len > ies[1] + hdr_len) { - auto essid_begin = ies + hdr_len; - auto essid_end = essid_begin + ies[1]; + auto essid_begin{ies + hdr_len}; + auto essid_end{essid_begin + ies[1]}; std::string essid_raw; std::copy(essid_begin, essid_end, std::back_inserter(essid_raw)); essid_ = Glib::Markup::escape_text(essid_raw); @@ -806,12 +803,11 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) { // If a signal is too strong, it can overwhelm receiving circuity that is designed // to pick up and process a certain signal level. The following percentage is scaled to // punish signals that are too strong (>= -45dBm) or too weak (<= -45 dBm). - const int hardwareOptimum = -45; - const int hardwareMin = -90; - const int strength = - 100 - - ((abs(signal_strength_dbm_ - hardwareOptimum) / double{hardwareOptimum - hardwareMin}) * - 100); + const int hardwareOptimum{-45}; + const int hardwareMin{-90}; + const int strength{100 - ((abs(signal_strength_dbm_ - hardwareOptimum) / + double{hardwareOptimum - hardwareMin}) * + 100)}; signal_strength_ = std::clamp(strength, 0, 100); if (signal_strength_dbm_ >= -50) { @@ -855,7 +851,7 @@ bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) { if (bss[NL80211_BSS_STATUS] == nullptr) { return false; } - auto status = nla_get_u32(bss[NL80211_BSS_STATUS]); + auto status{nla_get_u32(bss[NL80211_BSS_STATUS])}; switch (status) { case NL80211_BSS_STATUS_ASSOCIATED: case NL80211_BSS_STATUS_IBSS_JOINED: @@ -867,7 +863,7 @@ bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) { } auto waybar::modules::Network::getInfo() -> void { - struct nl_msg *nl_msg = nlmsg_alloc(); + struct nl_msg *nl_msg{nlmsg_alloc()}; if (nl_msg == nullptr) { return; } @@ -883,11 +879,11 @@ auto waybar::modules::Network::getInfo() -> void { // https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495 bool waybar::modules::Network::wildcardMatch(const std::string &pattern, const std::string &text) const { - auto P = int(pattern.size()); - auto T = int(text.size()); + auto P{int(pattern.size())}; + auto T{int(text.size())}; - auto p = 0, fallback_p = -1; - auto t = 0, fallback_t = -1; + auto p{0}, fallback_p{-1}; + auto t{0}, fallback_t{-1}; while (t < T) { // Wildcard match: diff --git a/src/modules/niri/window.cpp b/src/modules/niri/window.cpp index 6e6fd36ff..1e3228fd6 100644 --- a/src/modules/niri/window.cpp +++ b/src/modules/niri/window.cpp @@ -98,11 +98,11 @@ void Window::update() { void Window::setClass(const std::string &className, bool enable) { auto styleContext = bar_.window.get_style_context(); if (enable) { - if (!styleContext->has_class(className)) { - styleContext->add_class(className); + if (!label_.get_style_context()->has_class(className)) { + label_.get_style_context()->add_class(className); } } else { - styleContext->remove_class(className); + label_.get_style_context()->remove_class(className); } } diff --git a/src/modules/niri/workspaces.cpp b/src/modules/niri/workspaces.cpp index d2fcad5d9..7a02c5266 100644 --- a/src/modules/niri/workspaces.cpp +++ b/src/modules/niri/workspaces.cpp @@ -13,7 +13,6 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value box_.get_style_context()->add_class(id); } box_.get_style_context()->add_class(MODULE_CLASS); - event_box_.add(box_); if (!gIPC) gIPC = std::make_unique(); @@ -121,7 +120,8 @@ void Workspaces::doUpdate() { if (alloutputs) pos = it - my_workspaces.cbegin(); auto &button = buttons_[ws["id"].asUInt64()]; - box_.reorder_child(button, pos); + const auto sibling{box_.get_children().at(pos - 1)}; + if (sibling) box_.reorder_child_after(button, *sibling); } } @@ -130,6 +130,8 @@ void Workspaces::update() { AModule::update(); } +Workspaces::operator Gtk::Widget &() { return box_; }; + Gtk::Button &Workspaces::addButton(const Json::Value &ws) { std::string name; if (ws["name"]) { @@ -140,11 +142,12 @@ Gtk::Button &Workspaces::addButton(const Json::Value &ws) { auto pair = buttons_.emplace(ws["id"].asUInt64(), name); auto &&button = pair.first->second; - box_.pack_start(button, false, false, 0); - button.set_relief(Gtk::RELIEF_NONE); + box_.append(button); + button.set_has_frame(false); if (!config_["disable-click"].asBool()) { + AModule::bindEvents(button); const auto id = ws["id"].asUInt64(); - button.signal_pressed().connect([=] { + controllClick_->signal_pressed().connect([=](int n_press, double dx, double dy) { try { // {"Action":{"FocusWorkspace":{"reference":{"Id":1}}}} Json::Value request(Json::objectValue); diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index 3ae3ae830..4ed8e7b8c 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -39,8 +39,7 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // adresses for compatibility sake. // // Revisit this in 2026, systems should be updated by then. - - Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", + Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", "net.hadess.PowerProfiles", sigc::mem_fun(*this, &PowerProfilesDaemon::busConnectedCb)); // Schedule update to set the initial visibility @@ -166,17 +165,19 @@ auto PowerProfilesDaemon::update() -> void { } label_.get_style_context()->add_class(profile.name); currentStyle_ = profile.name; - event_box_.set_visible(true); + label_.set_visible(true); } else { - event_box_.set_visible(false); + label_.set_visible(false); } ALabel::update(); } -bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { - if (e->type == GdkEventType::GDK_BUTTON_PRESS && connected_) { - if (e->button == 1) /* left click */ { +void PowerProfilesDaemon::handleToggle(int n_press, double dx, double dy) { + if (Gdk::Event::Type::BUTTON_PRESS == + (AModule::controllClick_->get_current_event())->get_event_type() && + connected_) { + if (AModule::controllClick_->get_current_button() == 1 /* left click */) { activeProfile_++; if (activeProfile_ == availableProfiles_.end()) { activeProfile_ = availableProfiles_.begin(); @@ -196,7 +197,6 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { powerProfilesProxy_->call("org.freedesktop.DBus.Properties.Set", sigc::mem_fun(*this, &PowerProfilesDaemon::setPropCb), callArgs); } - return true; } void PowerProfilesDaemon::setPropCb(Glib::RefPtr& r) { diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 97996c336..9be09b0af 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -1,11 +1,5 @@ #include "modules/privacy/privacy.hpp" -#include -#include - -#include - -#include "AModule.hpp" #include "modules/privacy/privacy_item.hpp" namespace waybar::modules::privacy { @@ -21,11 +15,9 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st nodes_audio_in(), nodes_audio_out(), visibility_conn(), - box_(Gtk::ORIENTATION_HORIZONTAL, 0) { + box_(Gtk::Orientation::HORIZONTAL, 0) { box_.set_name(name_); - event_box_.add(box_); - // Icon Spacing if (config_["icon-spacing"].isUInt()) { iconSpacing = config_["icon-spacing"].asUInt(); @@ -69,10 +61,12 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st auto& [nodePtr, nodeType] = iter->second; auto* item = Gtk::make_managed(module, nodeType, nodePtr, pos, iconSize, transition_duration); - box_.add(*item); + box_.append(*item); } } + AModule::bindEvents(*this); + backend = util::PipewireBackend::PipewireBackend::getInstance(); backend->privacy_nodes_changed_signal_event.connect( sigc::mem_fun(*this, &Privacy::onPrivacyNodesChanged)); @@ -124,9 +118,10 @@ auto Privacy::update() -> void { bool useAudioOut = false; mutex_.lock(); - for (Gtk::Widget* widget : box_.get_children()) { - auto* module = dynamic_cast(widget); - if (module == nullptr) continue; + auto children{box_.observe_children()}; // Inefficient + for (guint i{0u}; i < children->get_n_items(); ++i) { + auto module = dynamic_pointer_cast(children->get_object(i)); + if (!module) continue; switch (module->privacy_type) { case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: setScreenshare = true; @@ -153,12 +148,12 @@ auto Privacy::update() -> void { bool isVisible = (setScreenshare && useScreenshare) || (setAudioIn && useAudioIn) || (setAudioOut && useAudioOut); - if (isVisible != event_box_.get_visible()) { + if (isVisible != box_.get_visible()) { // Disconnect any previous connection so that it doesn't get activated in // the future, hiding the module when it should be visible visibility_conn.disconnect(); if (isVisible) { - event_box_.set_visible(true); + box_.set_visible(true); } else { // Hides the widget when all of the privacy_item revealers animations // have finished animating @@ -171,7 +166,7 @@ auto Privacy::update() -> void { visible |= setAudioIn && !nodes_audio_in.empty(); visible |= setAudioOut && !nodes_audio_out.empty(); mutex_.unlock(); - event_box_.set_visible(visible); + box_.set_visible(visible); return false; }, *this), @@ -183,4 +178,6 @@ auto Privacy::update() -> void { AModule::update(); } +Privacy::operator Gtk::Widget&() { return box_; }; + } // namespace waybar::modules::privacy diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index a38b95a4a..570d9817e 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -1,12 +1,8 @@ #include "modules/privacy/privacy_item.hpp" -#include - -#include "glibmm/main.h" -#include "gtkmm/label.h" -#include "gtkmm/revealer.h" -#include "gtkmm/tooltip.h" -#include "util/pipewire/privacy_node_info.hpp" +#include +#include +#include namespace waybar::modules::privacy { @@ -17,8 +13,8 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac privacy_type(privacy_type_), nodes(nodes_), signal_conn(), - tooltip_window(Gtk::ORIENTATION_VERTICAL, 0), - box_(Gtk::ORIENTATION_HORIZONTAL, 0), + tooltip_window(Gtk::Orientation::VERTICAL, 0), + box_(Gtk::Orientation::HORIZONTAL, 0), icon_() { switch (privacy_type) { case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT: @@ -40,24 +36,27 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac // Set the reveal transition to not look weird when sliding in if (pos == "modules-left") { - set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_SLIDE_RIGHT); + set_transition_type(Gtk::RevealerTransitionType::SLIDE_RIGHT); } else if (pos == "modules-center") { - set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_CROSSFADE); + set_transition_type(Gtk::RevealerTransitionType::CROSSFADE); } else if (pos == "modules-right") { - set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_SLIDE_LEFT); + set_transition_type(Gtk::RevealerTransitionType::SLIDE_LEFT); } set_transition_duration(transition_duration); box_.set_name("privacy-item"); - box_.add(icon_); + box_.append(icon_); icon_.set_pixel_size(icon_size); - add(box_); + set_child(box_); + + // Get current theme + gtkTheme_ = Gtk::IconTheme::get_for_display(box_.get_display()); // Icon Name if (config_["icon-name"].isString()) { iconName = config_["icon-name"].asString(); } - icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID); + icon_.set_from_icon_name(iconName); // Tooltip Icon Size if (config_["tooltip-icon-size"].isUInt()) { @@ -71,12 +70,14 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac if (tooltip) { // Sets the window to use when showing the tooltip update_tooltip(); - this->signal_query_tooltip().connect(sigc::track_obj( - [this](int x, int y, bool keyboard_tooltip, const Glib::RefPtr &tooltip) { - tooltip->set_custom(tooltip_window); - return true; - }, - *this)); + this->signal_query_tooltip().connect( + sigc::track_obj( + [this](int x, int y, bool keyboard_tooltip, const Glib::RefPtr &tooltip) { + tooltip->set_custom(tooltip_window); + return true; + }, + *this), + false); } // Don't show by default @@ -86,27 +87,26 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac void PrivacyItem::update_tooltip() { // Removes all old nodes - for (auto *child : tooltip_window.get_children()) { - delete child; - } + for (auto child{tooltip_window.get_last_child()}; child; child = tooltip_window.get_last_child()) + tooltip_window.remove(*child); for (auto *node : *nodes) { - Gtk::Box *box = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4); + Gtk::Box *box = new Gtk::Box(Gtk::Orientation::HORIZONTAL, 4); // Set device icon Gtk::Image *node_icon = new Gtk::Image(); node_icon->set_pixel_size(tooltipIconSize); - node_icon->set_from_icon_name(node->getIconName(), Gtk::ICON_SIZE_INVALID); - box->add(*node_icon); + node_icon->set_from_icon_name(node->getIconName(gtkTheme_)); + box->append(*node_icon); // Set model auto *nodeName = new Gtk::Label(node->getName()); - box->add(*nodeName); + box->append(*nodeName); - tooltip_window.add(*box); + tooltip_window.append(*box); } - tooltip_window.show_all(); + tooltip_window.show(); } void PrivacyItem::set_in_use(bool in_use) { diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 255ca571f..cce4e763e 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -1,26 +1,27 @@ #include "modules/pulseaudio.hpp" -waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config) - : ALabel(config, "pulseaudio", id, "{volume}%") { - event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); - event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleScroll)); +#include +namespace waybar::modules { + +Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config) + : ALabel(config, "pulseaudio", id, "{volume}%", 60, false, false, true) { backend = util::AudioBackend::getInstance([this] { this->dp.emit(); }); backend->setIgnoredSinks(config_["ignored-sinks"]); } -bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { +bool Pulseaudio::handleScroll(double dx, double dy) { // change the pulse volume only when no user provided // events are configured if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { - return AModule::handleScroll(e); + return AModule::handleScroll(dx, dy); } - auto dir = AModule::getScrollDir(e); + auto dir{AModule::getScrollDir(controllScroll_->get_current_event())}; if (dir == SCROLL_DIR::NONE) { return true; } - int max_volume = 100; - double step = 1; + int max_volume{100}; + double step{1}; // isDouble returns true for integers as well, just in case if (config_["scroll-step"].isDouble()) { step = config_["scroll-step"].asDouble(); @@ -29,15 +30,15 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { max_volume = config_["max-volume"].asInt(); } - auto change_type = (dir == SCROLL_DIR::UP || dir == SCROLL_DIR::RIGHT) - ? util::ChangeType::Increase - : util::ChangeType::Decrease; + auto change_type{(dir == SCROLL_DIR::UP || dir == SCROLL_DIR::RIGHT) + ? util::ChangeType::Increase + : util::ChangeType::Decrease}; backend->changeVolume(change_type, step, max_volume); return true; } -static const std::array ports = { +static const std::array ports{ "headphone", "speaker", "hdmi", "headset", "hands-free", "portable", "car", "hifi", "phone", }; @@ -66,12 +67,12 @@ const std::vector waybar::modules::Pulseaudio::getPulseIcon() const return res; } -auto waybar::modules::Pulseaudio::update() -> void { - auto format = format_; +auto Pulseaudio::update() -> void { + auto format{format_}; std::string tooltip_format; - auto sink_volume = backend->getSinkVolume(); + auto sink_volume{backend->getSinkVolume()}; if (!alt_) { - std::string format_name = "format"; + std::string format_name{"format"}; if (backend->isBluetooth()) { format_name = format_name + "-bluetooth"; label_.get_style_context()->add_class("bluetooth"); @@ -90,7 +91,7 @@ auto waybar::modules::Pulseaudio::update() -> void { label_.get_style_context()->remove_class("muted"); label_.get_style_context()->remove_class("sink-muted"); } - auto state = getState(sink_volume, true); + auto state{getState(sink_volume, true)}; if (!state.empty() && config_[format_name + "-" + state].isString()) { format = config_[format_name + "-" + state].asString(); } else if (config_[format_name].isString()) { @@ -98,7 +99,7 @@ auto waybar::modules::Pulseaudio::update() -> void { } } // TODO: find a better way to split source/sink - std::string format_source = "{volume}%"; + std::string format_source{"{volume}%"}; if (backend->getSourceMuted()) { label_.get_style_context()->add_class("source-muted"); if (config_["format-source-muted"].isString()) { @@ -111,15 +112,16 @@ auto waybar::modules::Pulseaudio::update() -> void { } } - auto source_volume = backend->getSourceVolume(); - auto sink_desc = backend->getSinkDesc(); - auto source_desc = backend->getSourceDesc(); + auto source_volume{backend->getSourceVolume()}; + auto sink_desc{backend->getSinkDesc()}; + auto source_desc{backend->getSourceDesc()}; format_source = fmt::format(fmt::runtime(format_source), fmt::arg("volume", source_volume)); - auto text = fmt::format( - fmt::runtime(format), fmt::arg("desc", sink_desc), fmt::arg("volume", sink_volume), - fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume), - fmt::arg("source_desc", source_desc), fmt::arg("icon", getIcon(sink_volume, getPulseIcon()))); + auto text{fmt::format(fmt::runtime(format), fmt::arg("desc", sink_desc), + fmt::arg("volume", sink_volume), fmt::arg("format_source", format_source), + fmt::arg("source_volume", source_volume), + fmt::arg("source_desc", source_desc), + fmt::arg("icon", getIcon(sink_volume, getPulseIcon())))}; if (text.empty()) { label_.hide(); } else { @@ -145,3 +147,5 @@ auto waybar::modules::Pulseaudio::update() -> void { // Call parent update ALabel::update(); } + +} // namespace waybar::modules diff --git a/src/modules/pulseaudio_slider.cpp b/src/modules/pulseaudio_slider.cpp index bf85584ed..64fe33d74 100644 --- a/src/modules/pulseaudio_slider.cpp +++ b/src/modules/pulseaudio_slider.cpp @@ -8,7 +8,7 @@ PulseaudioSlider::PulseaudioSlider(const std::string& id, const Json::Value& con backend->setIgnoredSinks(config_["ignored-sinks"]); if (config_["target"].isString()) { - std::string target = config_["target"].asString(); + std::string target{config_["target"].asString()}; if (target == "sink") { this->target = PulseaudioSliderTarget::Sink; } else if (target == "source") { @@ -38,7 +38,7 @@ void PulseaudioSlider::update() { } void PulseaudioSlider::onValueChanged() { - bool is_mute = false; + bool is_mute{false}; switch (target) { case PulseaudioSliderTarget::Sink: @@ -54,7 +54,7 @@ void PulseaudioSlider::onValueChanged() { break; } - uint16_t volume = scale_.get_value(); + uint16_t volume{scale_.get_value()}; if (is_mute) { // Avoid setting sink/source to volume 0 if the user muted if via another mean. @@ -79,4 +79,4 @@ void PulseaudioSlider::onValueChanged() { backend->changeVolume(volume, min_, max_); } -} // namespace waybar::modules \ No newline at end of file +} // namespace waybar::modules diff --git a/src/modules/river/layout.cpp b/src/modules/river/layout.cpp index e938400f8..c5b471ba5 100644 --- a/src/modules/river/layout.cpp +++ b/src/modules/river/layout.cpp @@ -1,5 +1,6 @@ #include "modules/river/layout.hpp" +#include #include #include diff --git a/src/modules/river/mode.cpp b/src/modules/river/mode.cpp index 1f788e09d..0f2049e85 100644 --- a/src/modules/river/mode.cpp +++ b/src/modules/river/mode.cpp @@ -1,5 +1,6 @@ #include "modules/river/mode.hpp" +#include #include #include diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index 9e7cd5aa4..6757c6a90 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -112,7 +112,6 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con box_.get_style_context()->add_class(id); } box_.get_style_context()->add_class(MODULE_CLASS); - event_box_.add(box_); // Default to 9 tags, cap at 32 const int num_tags = @@ -130,22 +129,25 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con } auto &button = buttons_[tag]; - button.set_relief(Gtk::RELIEF_NONE); - box_.pack_start(button, false, false, 0); + button.set_has_frame(false); + box_.append(button); if (!config_["disable-click"].asBool()) { + AModule::bindEvents(button); + if (set_tags.isArray() && !set_tags.empty()) - button.signal_clicked().connect(sigc::bind( - sigc::mem_fun(*this, &Tags::handle_primary_clicked), set_tags[tag].asUInt())); + controllClick_->signal_released().connect( + sigc::bind(sigc::mem_fun(*this, &Tags::handleRlse), set_tags[tag].asUInt()), true); else - button.signal_clicked().connect( - sigc::bind(sigc::mem_fun(*this, &Tags::handle_primary_clicked), (1 << tag))); + controllClick_->signal_released().connect( + sigc::bind(sigc::mem_fun(*this, &Tags::handleRlse), (1 << tag)), true); + if (toggle_tags.isArray() && !toggle_tags.empty()) - button.signal_button_press_event().connect(sigc::bind( - sigc::mem_fun(*this, &Tags::handle_button_press), toggle_tags[tag].asUInt())); + controllClick_->signal_pressed().connect( + sigc::bind(sigc::mem_fun(*this, &Tags::handlePress), toggle_tags[tag].asUInt()), true); else - button.signal_button_press_event().connect( - sigc::bind(sigc::mem_fun(*this, &Tags::handle_button_press), (1 << tag))); + controllClick_->signal_pressed().connect( + sigc::bind(sigc::mem_fun(*this, &Tags::handlePress), (1 << tag)), true); } button.show(); } @@ -167,7 +169,9 @@ Tags::~Tags() { } } -void Tags::handle_primary_clicked(uint32_t tag) { +Tags::operator Gtk::Widget &() { return box_; }; + +void Tags::handleRlse(int n_press, double dx, double dy, uint32_t tag) { // Send river command to select tag on left mouse click zriver_command_callback_v1 *callback; zriver_control_v1_add_argument(control_, "set-focused-tags"); @@ -176,8 +180,8 @@ void Tags::handle_primary_clicked(uint32_t tag) { zriver_command_callback_v1_add_listener(callback, &command_callback_listener_impl, nullptr); } -bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) { - if (event_button->type == GDK_BUTTON_PRESS && event_button->button == 3) { +void Tags::handlePress(int n_press, double dx, double dy, uint32_t tag) { + if (controllClick_->get_current_button() == 3) { // Send river command to toggle tag on right mouse click zriver_command_callback_v1 *callback; zriver_control_v1_add_argument(control_, "toggle-focused-tags"); @@ -185,7 +189,6 @@ bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) { callback = zriver_control_v1_run_command(control_, seat_); zriver_command_callback_v1_add_listener(callback, &command_callback_listener_impl, nullptr); } - return true; } void Tags::handle_focused_tags(uint32_t tags) { diff --git a/src/modules/river/window.cpp b/src/modules/river/window.cpp index dc7a6459b..a63799817 100644 --- a/src/modules/river/window.cpp +++ b/src/modules/river/window.cpp @@ -1,5 +1,6 @@ #include "modules/river/window.hpp" +#include #include #include diff --git a/src/modules/sndio.cpp b/src/modules/sndio.cpp index 72e7207ce..81d0b509d 100644 --- a/src/modules/sndio.cpp +++ b/src/modules/sndio.cpp @@ -1,6 +1,5 @@ #include "modules/sndio.hpp" -#include #include #include @@ -10,7 +9,7 @@ namespace waybar::modules { void ondesc(void *arg, struct sioctl_desc *d, int curval) { - auto self = static_cast(arg); + auto self{static_cast(arg)}; if (d == NULL) { // d is NULL when the list is done return; @@ -19,7 +18,7 @@ void ondesc(void *arg, struct sioctl_desc *d, int curval) { } void onval(void *arg, unsigned int addr, unsigned int val) { - auto self = static_cast(arg); + auto self{static_cast(arg)}; self->put_val(addr, val); } @@ -41,26 +40,22 @@ auto Sndio::connect_to_sndio() -> void { } Sndio::Sndio(const std::string &id, const Json::Value &config) - : ALabel(config, "sndio", id, "{volume}%", 1, false, true), - hdl_(nullptr), - pfds_(0), - addr_(0), - volume_(0), - old_volume_(0), - maxval_(0), - muted_(false) { + : ALabel(config, "sndio", id, "{volume}%", 1, false, true, true), + hdl_{nullptr}, + pfds_{0}, + addr_{0}, + volume_{0}, + old_volume_{0}, + maxval_{0}, + muted_{false} { connect_to_sndio(); - event_box_.show(); - - event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK | Gdk::BUTTON_PRESS_MASK); - event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Sndio::handleScroll)); - event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &Sndio::handleToggle)); + label_.show(); thread_ = [this] { dp.emit(); - int nfds = sioctl_pollfd(hdl_, pfds_.data(), POLLIN); + int nfds{sioctl_pollfd(hdl_, pfds_.data(), POLLIN)}; if (nfds == 0) { throw std::runtime_error("sioctl_pollfd() failed."); } @@ -70,7 +65,7 @@ Sndio::Sndio(const std::string &id, const Json::Value &config) } } - int revents = sioctl_revents(hdl_, pfds_.data()); + int revents{sioctl_revents(hdl_, pfds_.data())}; if (revents & POLLHUP) { spdlog::warn("sndio disconnected!"); sioctl_close(hdl_); @@ -101,8 +96,8 @@ Sndio::Sndio(const std::string &id, const Json::Value &config) Sndio::~Sndio() { sioctl_close(hdl_); } auto Sndio::update() -> void { - auto format = format_; - unsigned int vol = 100. * static_cast(volume_) / static_cast(maxval_); + auto format{format_}; + unsigned int vol{100. * static_cast(volume_) / static_cast(maxval_)}; if (volume_ == 0) { label_.get_style_context()->add_class("muted"); @@ -110,8 +105,8 @@ auto Sndio::update() -> void { label_.get_style_context()->remove_class("muted"); } - auto text = - fmt::format(fmt::runtime(format), fmt::arg("volume", vol), fmt::arg("raw_value", volume_)); + auto text{ + fmt::format(fmt::runtime(format), fmt::arg("volume", vol), fmt::arg("raw_value", volume_))}; if (text.empty()) { label_.hide(); } else { @@ -140,27 +135,27 @@ auto Sndio::put_val(unsigned int addr, unsigned int val) -> void { } } -bool Sndio::handleScroll(GdkEventScroll *e) { +bool Sndio::handleScroll(double dx, double dy) { // change the volume only when no user provided // events are configured if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { - return AModule::handleScroll(e); + return AModule::handleScroll(dx, dy); } // only try to talk to sndio if connected if (hdl_ == nullptr) return true; - auto dir = AModule::getScrollDir(e); + auto dir{AModule::getScrollDir(controllScroll_->get_current_event())}; if (dir == SCROLL_DIR::NONE) { return true; } - int step = 5; + int step{5}; if (config_["scroll-step"].isInt()) { step = config_["scroll-step"].asInt(); } - int new_volume = volume_; + int new_volume{volume_}; if (muted_) { new_volume = old_volume_; } @@ -180,14 +175,14 @@ bool Sndio::handleScroll(GdkEventScroll *e) { return true; } -bool Sndio::handleToggle(GdkEventButton *const &e) { +void Sndio::handleToggle(int n_press, double dx, double dy) { // toggle mute only when no user provided events are configured if (config_["on-click"].isString()) { - return AModule::handleToggle(e); + AModule::handleToggle(n_press, dx, dy); } // only try to talk to sndio if connected - if (hdl_ == nullptr) return true; + if (hdl_ == nullptr) return; muted_ = !muted_; if (muted_) { @@ -197,8 +192,6 @@ bool Sndio::handleToggle(GdkEventButton *const &e) { } else { sioctl_setval(hdl_, addr_, old_volume_); } - - return true; } } /* namespace waybar::modules */ diff --git a/src/modules/sway/bar.cpp b/src/modules/sway/bar.cpp index f28b05025..f3d63f842 100644 --- a/src/modules/sway/bar.cpp +++ b/src/modules/sway/bar.cpp @@ -2,9 +2,6 @@ #include -#include -#include - #include "bar.hpp" #include "modules/sway/ipc/ipc.hpp" @@ -12,8 +9,8 @@ namespace waybar::modules::sway { BarIpcClient::BarIpcClient(waybar::Bar& bar) : bar_{bar} { { - sigc::connection handle = - ipc_.signal_cmd.connect(sigc::mem_fun(*this, &BarIpcClient::onInitialConfig)); + sigc::connection handle{ + ipc_.signal_cmd.connect(sigc::mem_fun(*this, &BarIpcClient::onInitialConfig))}; ipc_.sendCmd(IPC_GET_BAR_CONFIG, bar_.bar_id); handle.disconnect(); @@ -23,8 +20,8 @@ BarIpcClient::BarIpcClient(waybar::Bar& bar) : bar_{bar} { subscribe_events.append("bar_state_update"); subscribe_events.append("barconfig_update"); - bool has_mode = isModuleEnabled("sway/mode"); - bool has_workspaces = isModuleEnabled("sway/workspaces"); + bool has_mode{isModuleEnabled("sway/mode")}; + bool has_workspaces{isModuleEnabled("sway/workspaces")}; if (has_mode) { subscribe_events.append("mode"); @@ -62,7 +59,7 @@ BarIpcClient::BarIpcClient(waybar::Bar& bar) : bar_{bar} { bool BarIpcClient::isModuleEnabled(std::string name) { for (const auto& section : {"modules-left", "modules-center", "modules-right"}) { - if (const auto& modules = bar_.config.get(section, {}); modules.isArray()) { + if (const auto& modules{bar_.config.get(section, {})}; modules.isArray()) { for (const auto& module : modules) { if (module.asString().rfind(name, 0) == 0) { return true; @@ -75,38 +72,38 @@ bool BarIpcClient::isModuleEnabled(std::string name) { struct swaybar_config parseConfig(const Json::Value& payload) { swaybar_config conf; - if (auto id = payload["id"]; id.isString()) { + if (auto id{payload["id"]}; id.isString()) { conf.id = id.asString(); } - if (auto mode = payload["mode"]; mode.isString()) { + if (auto mode{payload["mode"]}; mode.isString()) { conf.mode = mode.asString(); } - if (auto hs = payload["hidden_state"]; hs.isString()) { + if (auto hs{payload["hidden_state"]}; hs.isString()) { conf.hidden_state = hs.asString(); } return conf; } void BarIpcClient::onInitialConfig(const struct Ipc::ipc_response& res) { - auto payload = parser_.parse(res.payload); - if (auto success = payload.get("success", true); !success.asBool()) { - auto err = payload.get("error", "Unknown error"); + auto payload{parser_.parse(res.payload)}; + if (auto success{payload.get("success", true)}; !success.asBool()) { + auto err{payload.get("error", "Unknown error")}; throw std::runtime_error(err.asString()); } - auto config = parseConfig(payload); + auto config{parseConfig(payload)}; onConfigUpdate(config); } void BarIpcClient::onIpcEvent(const struct Ipc::ipc_response& res) { try { - auto payload = parser_.parse(res.payload); + auto payload{parser_.parse(res.payload)}; switch (res.type) { case IPC_EVENT_WORKSPACE: if (payload.isMember("change")) { // only check and send signal if the workspace update reason was because of a urgent // change if (payload["change"] == "urgent") { - auto urgent = payload["current"]["urgent"]; + auto urgent{payload["current"]["urgent"]}; if (urgent.asBool()) { // Event for a new urgency, update the visibly signal_urgency_(true); @@ -139,7 +136,7 @@ void BarIpcClient::onIpcEvent(const struct Ipc::ipc_response& res) { signal_visible_(payload["visible_by_modifier"].asBool()); } else { // configuration update - auto config = parseConfig(payload); + auto config{parseConfig(payload)}; signal_config_(std::move(config)); } break; @@ -152,7 +149,7 @@ void BarIpcClient::onIpcEvent(const struct Ipc::ipc_response& res) { void BarIpcClient::onCmd(const struct Ipc::ipc_response& res) { if (res.type == IPC_GET_WORKSPACES) { try { - auto payload = parser_.parse(res.payload); + auto payload{parser_.parse(res.payload)}; for (auto& ws : payload) { if (ws["urgent"].asBool()) { spdlog::debug("Found workspace {} with urgency set. Stopping search.", ws["name"]); @@ -209,7 +206,7 @@ void BarIpcClient::onUrgencyUpdate(bool visible_by_urgency) { } void BarIpcClient::update() { - bool visible = visible_by_modifier_ || visible_by_mode_ || visible_by_urgency_; + bool visible{visible_by_modifier_ || visible_by_mode_ || visible_by_urgency_}; if (bar_config_.mode == "invisible") { visible = false; } else if (bar_config_.mode != "hide" || bar_config_.hidden_state != "hide") { diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index 4139a53b5..7bfdd5bee 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -3,12 +3,10 @@ #include #include -#include - namespace waybar::modules::sway { Ipc::Ipc() { - const std::string& socketPath = getSocketPath(); + const std::string& socketPath{getSocketPath()}; fd_ = open(socketPath); fd_event_ = open(socketPath); } @@ -36,7 +34,7 @@ Ipc::~Ipc() { void Ipc::setWorker(std::function&& func) { thread_ = func; } const std::string Ipc::getSocketPath() const { - const char* env = getenv("SWAYSOCK"); + const char* env{getenv("SWAYSOCK")}; if (env != nullptr) { return std::string(env); } @@ -64,7 +62,7 @@ const std::string Ipc::getSocketPath() const { } int Ipc::open(const std::string& socketPath) const { - int32_t fd = socket(AF_UNIX, SOCK_STREAM, 0); + int32_t fd{socket(AF_UNIX, SOCK_STREAM, 0)}; if (fd == -1) { throw std::runtime_error("Unable to open Unix socket"); } @@ -74,7 +72,7 @@ int Ipc::open(const std::string& socketPath) const { addr.sun_family = AF_UNIX; strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); addr.sun_path[sizeof(addr.sun_path) - 1] = 0; - int l = sizeof(struct sockaddr_un); + int l{sizeof(struct sockaddr_un)}; if (::connect(fd, reinterpret_cast(&addr), l) == -1) { throw std::runtime_error("Unable to connect to Sway"); } @@ -84,11 +82,11 @@ int Ipc::open(const std::string& socketPath) const { struct Ipc::ipc_response Ipc::recv(int fd) { std::string header; header.resize(ipc_header_size_); - auto data32 = reinterpret_cast(header.data() + ipc_magic_.size()); - size_t total = 0; + auto data32{reinterpret_cast(header.data() + ipc_magic_.size())}; + size_t total{0}; while (total < ipc_header_size_) { - auto res = ::recv(fd, header.data() + total, ipc_header_size_ - total, 0); + auto res{::recv(fd, header.data() + total, ipc_header_size_ - total, 0)}; if (fd_event_ == -1 || fd_ == -1) { // IPC is closed so just return an empty response return {0, 0, ""}; @@ -98,7 +96,7 @@ struct Ipc::ipc_response Ipc::recv(int fd) { } total += res; } - auto magic = std::string(header.data(), header.data() + ipc_magic_.size()); + auto magic{std::string(header.data(), header.data() + ipc_magic_.size())}; if (magic != ipc_magic_) { throw std::runtime_error("Invalid IPC magic"); } @@ -107,7 +105,7 @@ struct Ipc::ipc_response Ipc::recv(int fd) { std::string payload; payload.resize(data32[0]); while (total < data32[0]) { - auto res = ::recv(fd, payload.data() + total, data32[0] - total, 0); + auto res{::recv(fd, payload.data() + total, data32[0] - total, 0)}; if (res < 0) { if (errno == EINTR || errno == EAGAIN) { continue; @@ -122,7 +120,7 @@ struct Ipc::ipc_response Ipc::recv(int fd) { struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& payload) { std::string header; header.resize(ipc_header_size_); - auto data32 = reinterpret_cast(header.data() + ipc_magic_.size()); + auto data32{reinterpret_cast(header.data() + ipc_magic_.size())}; memcpy(header.data(), ipc_magic_.c_str(), ipc_magic_.size()); data32[0] = payload.size(); data32[1] = type; @@ -137,20 +135,20 @@ struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& pay } void Ipc::sendCmd(uint32_t type, const std::string& payload) { - std::lock_guard lock(mutex_); - const auto res = Ipc::send(fd_, type, payload); + std::lock_guard lock{mutex_}; + const auto res{Ipc::send(fd_, type, payload)}; signal_cmd.emit(res); } void Ipc::subscribe(const std::string& payload) { - auto res = Ipc::send(fd_event_, IPC_SUBSCRIBE, payload); + auto res{Ipc::send(fd_event_, IPC_SUBSCRIBE, payload)}; if (res.payload != "{\"success\": true}") { throw std::runtime_error("Unable to subscribe ipc event"); } } void Ipc::handleEvent() { - const auto res = Ipc::recv(fd_event_); + const auto res{Ipc::recv(fd_event_)}; signal_event.emit(res); } diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index a005df17c..72cfbbaf9 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -1,21 +1,13 @@ #include "modules/sway/language.hpp" -#include -#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"; +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) { @@ -51,14 +43,14 @@ void Language::onCmd(const struct Ipc::ipc_response& res) { } try { - std::lock_guard lock(mutex_); - auto payload = parser_.parse(res.payload); + 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(); + 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; @@ -83,8 +75,8 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { } try { - std::lock_guard lock(mutex_); - auto payload = parser_.parse(res.payload)["input"]; + std::lock_guard lock{mutex_}; + auto payload{parser_.parse(res.payload)["input"]}; if (payload["type"].asString() == "keyboard") { set_current_layout(payload[XKB_ACTIVE_LAYOUT_NAME_KEY].asString()); } @@ -95,30 +87,30 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { } auto Language::update() -> void { - std::lock_guard lock(mutex_); + std::lock_guard lock{mutex_}; if (hide_single_ && layouts_map_.size() <= 1) { - event_box_.hide(); + label_.hide(); return; } - auto display_layout = trim(fmt::format( + auto display_layout{trim(fmt::format( fmt::runtime(format_), fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), - fmt::arg("variant", layout_.variant), fmt::arg("flag", layout_.country_flag()))); + fmt::arg("variant", layout_.variant), fmt::arg("flag", layout_.country_flag())))}; label_.set_markup(display_layout); if (tooltipEnabled()) { if (tooltip_format_ != "") { - auto tooltip_display_layout = trim( + auto tooltip_display_layout{trim( fmt::format(fmt::runtime(tooltip_format_), fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), fmt::arg("variant", layout_.variant), - fmt::arg("flag", layout_.country_flag()))); + fmt::arg("flag", layout_.country_flag())))}; label_.set_tooltip_markup(tooltip_display_layout); } else { label_.set_tooltip_markup(display_layout); } } - event_box_.show(); + label_.show(); // Call parent update ALabel::update(); @@ -133,7 +125,7 @@ auto Language::set_current_layout(std::string current_layout) -> void { auto Language::init_layouts_map(const std::vector& used_layouts) -> void { std::map> found_by_short_names; XKBContext xkb_context; - auto layout = xkb_context.next_layout(); + 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()) { @@ -141,7 +133,7 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> } if (!is_variant_displayed) { - auto short_name = layout->short_name; + 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 { @@ -158,10 +150,10 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> std::map short_name_to_number_map; for (const auto& used_layout_name : used_layouts) { - auto found = layouts_map_.find(used_layout_name); + auto found{layouts_map_.find(used_layout_name)}; if (found == layouts_map_.end()) continue; - auto used_layout = &found->second; - auto layouts_with_same_name_list = found_by_short_names[used_layout->short_name]; + auto used_layout{&found->second}; + auto layouts_with_same_name_list{found_by_short_names[used_layout->short_name]}; if (layouts_with_same_name_list.size() < 2) { continue; } @@ -171,7 +163,7 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> } if (displayed_short_flag != static_cast(0)) { - int& number = short_name_to_number_map[used_layout->short_name]; + int& number{short_name_to_number_map[used_layout->short_name]}; used_layout->short_name = used_layout->short_name + std::to_string(number); used_layout->short_description = used_layout->short_description + std::to_string(number); ++number; @@ -195,17 +187,17 @@ auto Language::XKBContext::next_layout() -> Layout* { 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_); - auto short_description_ = rxkb_layout_get_brief(xkb_layout_); + 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_)}; + auto short_description_{rxkb_layout_get_brief(xkb_layout_)}; std::string short_description; if (short_description_ != nullptr) { short_description = std::string(short_description_); base_layouts_by_name_.emplace(name, xkb_layout_); } else { - auto base_layout = base_layouts_by_name_[name]; + auto base_layout{base_layouts_by_name_[name]}; short_description = base_layout == nullptr ? "" : std::string(rxkb_layout_get_brief(base_layout)); } @@ -221,7 +213,7 @@ Language::XKBContext::~XKBContext() { std::string Language::Layout::country_flag() const { if (short_name.size() != 2) return ""; - unsigned char result[] = "\xf0\x9f\x87\x00\xf0\x9f\x87\x00"; + unsigned char result[]{"\xf0\x9f\x87\x00\xf0\x9f\x87\x00"}; result[3] = short_name[0] + 0x45; result[7] = short_name[1] + 0x45; // Check if both emojis are in A-Z symbol bounds diff --git a/src/modules/sway/mode.cpp b/src/modules/sway/mode.cpp index b81735e54..1da4ddcea 100644 --- a/src/modules/sway/mode.cpp +++ b/src/modules/sway/mode.cpp @@ -1,5 +1,6 @@ #include "modules/sway/mode.hpp" +#include #include namespace waybar::modules::sway { @@ -21,8 +22,8 @@ Mode::Mode(const std::string& id, const Json::Value& config) void Mode::onEvent(const struct Ipc::ipc_response& res) { try { - std::lock_guard lock(mutex_); - auto payload = parser_.parse(res.payload); + std::lock_guard lock{mutex_}; + auto payload{parser_.parse(res.payload)}; if (payload["change"] != "default") { if (payload["pango_markup"].asBool()) { mode_ = payload["change"].asString(); @@ -40,13 +41,13 @@ void Mode::onEvent(const struct Ipc::ipc_response& res) { auto Mode::update() -> void { if (mode_.empty()) { - event_box_.hide(); + label_.hide(); } else { label_.set_markup(fmt::format(fmt::runtime(format_), mode_)); if (tooltipEnabled()) { label_.set_tooltip_text(mode_); } - event_box_.show(); + label_.show(); } // Call parent update ALabel::update(); diff --git a/src/modules/sway/scratchpad.cpp b/src/modules/sway/scratchpad.cpp index 17dc27071..85bfa6e62 100644 --- a/src/modules/sway/scratchpad.cpp +++ b/src/modules/sway/scratchpad.cpp @@ -2,18 +2,16 @@ #include -#include - namespace waybar::modules::sway { Scratchpad::Scratchpad(const std::string& id, const Json::Value& config) : ALabel(config, "scratchpad", id, config["format"].isString() ? config["format"].asString() : "{icon} {count}"), - tooltip_format_(config_["tooltip-format"].isString() ? config_["tooltip-format"].asString() - : "{app}: {title}"), - show_empty_(config_["show-empty"].isBool() ? config_["show-empty"].asBool() : false), - tooltip_enabled_(config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true), - tooltip_text_(""), - count_(0) { + tooltip_format_{config_["tooltip-format"].isString() ? config_["tooltip-format"].asString() + : "{app}: {title}"}, + show_empty_{config_["show-empty"].isBool() ? config_["show-empty"].asBool() : false}, + tooltip_enabled_{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true}, + tooltip_text_{""}, + count_{0} { ipc_.subscribe(R"(["window"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Scratchpad::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Scratchpad::onCmd)); @@ -30,7 +28,7 @@ Scratchpad::Scratchpad(const std::string& id, const Json::Value& config) } auto Scratchpad::update() -> void { if (count_ || show_empty_) { - event_box_.show(); + label_.show(); label_.set_markup( fmt::format(fmt::runtime(format_), fmt::arg("icon", getIcon(count_, "", config_["format-icons"].size())), @@ -39,7 +37,7 @@ auto Scratchpad::update() -> void { label_.set_tooltip_markup(tooltip_text_); } } else { - event_box_.hide(); + label_.hide(); } if (count_) { label_.get_style_context()->remove_class("empty"); @@ -59,8 +57,8 @@ auto Scratchpad::getTree() -> void { auto Scratchpad::onCmd(const struct Ipc::ipc_response& res) -> void { try { - std::lock_guard lock(mutex_); - auto tree = parser_.parse(res.payload); + std::lock_guard lock{mutex_}; + auto tree{parser_.parse(res.payload)}; count_ = tree["nodes"][0]["nodes"][0]["floating_nodes"].size(); if (tooltip_enabled_) { tooltip_text_.clear(); @@ -80,4 +78,4 @@ auto Scratchpad::onCmd(const struct Ipc::ipc_response& res) -> void { } auto Scratchpad::onEvent(const struct Ipc::ipc_response& res) -> void { getTree(); } -} // namespace waybar::modules::sway \ No newline at end of file +} // namespace waybar::modules::sway diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 25e430a76..e36cc624e 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -1,23 +1,14 @@ #include "modules/sway/window.hpp" -#include -#include -#include -#include -#include +#include #include -#include -#include -#include - -#include "util/gtk_icon.hpp" #include "util/rewrite_string.hpp" namespace waybar::modules::sway { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) { + : AAppIconLabel(config, "window", id, "{}", 0, true), bar_{bar}, windowId_{-1} { ipc_.subscribe(R"(["window","workspace"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd)); @@ -38,9 +29,9 @@ void Window::onEvent(const struct Ipc::ipc_response& res) { getTree(); } void Window::onCmd(const struct Ipc::ipc_response& res) { try { - std::lock_guard lock(mutex_); - auto payload = parser_.parse(res.payload); - auto output = payload["output"].isString() ? payload["output"].asString() : ""; + std::lock_guard lock{mutex_}; + auto payload{parser_.parse(res.payload)}; + auto output{payload["output"].isString() ? payload["output"].asString() : ""}; std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) = getFocusedNode(payload["nodes"], output); updateAppIconName(app_id_, app_class_); @@ -55,7 +46,7 @@ auto Window::update() -> void { spdlog::trace("workspace layout {}, tiled count {}, floating count {}", layout_, app_nb_, floating_count_); - int mode = 0; + int mode{0}; if (app_nb_ == 0) { if (floating_count_ == 0) { mode += 1; @@ -75,9 +66,9 @@ auto Window::update() -> void { } if (!old_app_id_.empty() && ((mode & 2) == 0 || old_app_id_ != app_id_) && - bar_.window.get_style_context()->has_class(old_app_id_)) { + label_.get_style_context()->has_class(old_app_id_)) { spdlog::trace("Removing app_id class: {}", old_app_id_); - bar_.window.get_style_context()->remove_class(old_app_id_); + label_.get_style_context()->remove_class(old_app_id_); old_app_id_ = ""; } @@ -88,9 +79,9 @@ auto Window::update() -> void { setClass("stacked", ((mode & 16) > 0)); setClass("tiled", ((mode & 32) > 0)); - if ((mode & 2) > 0 && !app_id_.empty() && !bar_.window.get_style_context()->has_class(app_id_)) { + if ((mode & 2) > 0 && !app_id_.empty() && !label_.get_style_context()->has_class(app_id_)) { spdlog::trace("Adding app_id class: {}", app_id_); - bar_.window.get_style_context()->add_class(app_id_); + label_.get_style_context()->add_class(app_id_); old_app_id_ = app_id_; } @@ -110,17 +101,17 @@ auto Window::update() -> void { void Window::setClass(std::string classname, bool enable) { if (enable) { - if (!bar_.window.get_style_context()->has_class(classname)) { - bar_.window.get_style_context()->add_class(classname); + if (!label_.get_style_context()->has_class(classname)) { + label_.get_style_context()->add_class(classname); } } else { - bar_.window.get_style_context()->remove_class(classname); + label_.get_style_context()->remove_class(classname); } } std::pair leafNodesInWorkspace(const Json::Value& node) { - auto const& nodes = node["nodes"]; - auto const& floating_nodes = node["floating_nodes"]; + auto const& nodes{node["nodes"]}; + auto const& floating_nodes{node["floating_nodes"]}; if (nodes.empty() && floating_nodes.empty()) { if (node["type"].asString() == "workspace") return {0, 0}; @@ -130,8 +121,8 @@ std::pair leafNodesInWorkspace(const Json::Value& node) { return {1, 0}; } } - int sum = 0; - int floating_sum = 0; + int sum{0}; + int floating_sum{0}; for (auto const& node : nodes) { std::pair all_leaf_nodes = leafNodesInWorkspace(node); sum += all_leaf_nodes.first; @@ -147,7 +138,7 @@ std::pair leafNodesInWorkspace(const Json::Value& node) { std::optional> getSingleChildNode( const Json::Value& node) { - auto const& nodes = node["nodes"]; + auto const& nodes{node["nodes"]}; if (nodes.empty()) { if (node["type"].asString() == "workspace") return {}; @@ -157,11 +148,11 @@ std::optional> getSingleChildNode( return {std::cref(node)}; } } - auto it = std::cbegin(nodes); + auto it{std::cbegin(nodes)}; if (it == std::cend(nodes)) { return {}; } - auto const& child = *it; + auto const& child{*it}; ++it; if (it != std::cend(nodes)) { return {}; @@ -170,12 +161,12 @@ std::optional> getSingleChildNode( } std::tuple getWindowInfo(const Json::Value& node) { - const auto app_id = node["app_id"].isString() ? node["app_id"].asString() - : node["window_properties"]["instance"].asString(); - const auto app_class = node["window_properties"]["class"].isString() - ? node["window_properties"]["class"].asString() - : ""; - const auto shell = node["shell"].isString() ? node["shell"].asString() : ""; + const auto app_id{node["app_id"].isString() ? node["app_id"].asString() + : node["window_properties"]["instance"].asString()}; + const auto app_class{node["window_properties"]["class"].isString() + ? node["window_properties"]["class"].asString() + : ""}; + const auto shell{node["shell"].isString() ? node["shell"].asString() : ""}; return {app_id, app_class, shell}; } @@ -196,7 +187,7 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu continue; } if (node["focused"].asBool()) { - std::pair all_leaf_nodes = leafNodesInWorkspace(node); + std::pair all_leaf_nodes{leafNodesInWorkspace(node)}; return {all_leaf_nodes.first, all_leaf_nodes.second, node["id"].asInt(), @@ -215,12 +206,12 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu // found node spdlog::trace("actual output {}, output found {}, node (focused) found {}", bar_.output->name, output, node["name"].asString()); - const auto [app_id, app_class, shell] = getWindowInfo(node); - int nb = node.size(); - int floating_count = 0; - std::string workspace_layout = ""; + const auto [app_id, app_class, shell]{getWindowInfo(node)}; + int nb{node.size()}; + int floating_count{0}; + std::string workspace_layout{""}; if (!parentWorkspace.isNull()) { - std::pair all_leaf_nodes = leafNodesInWorkspace(parentWorkspace); + std::pair all_leaf_nodes{leafNodesInWorkspace(parentWorkspace)}; nb = all_leaf_nodes.first; floating_count = all_leaf_nodes.second; workspace_layout = parentWorkspace["layout"].asString(); @@ -236,10 +227,10 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu } // iterate - auto [nb, f, id, name, app_id, app_class, shell, workspace_layout] = - gfnWithWorkspace(node["nodes"], output, config_, bar_, parentWorkspace, node); - auto [nb2, f2, id2, name2, app_id2, app_class2, shell2, workspace_layout2] = - gfnWithWorkspace(node["floating_nodes"], output, config_, bar_, parentWorkspace, node); + auto [nb, f, id, name, app_id, app_class, shell, workspace_layout]{ + gfnWithWorkspace(node["nodes"], output, config_, bar_, parentWorkspace, node)}; + auto [nb2, f2, id2, name2, app_id2, app_class2, shell2, workspace_layout2]{ + gfnWithWorkspace(node["floating_nodes"], output, config_, bar_, parentWorkspace, node)}; // if ((id > 0 || ((id2 < 0 || name2.empty()) && id > -1)) && !name.empty()) { if ((id > 0) || (id2 < 0 && id > -1)) { @@ -252,14 +243,14 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu // this only comes into effect when no focused children are present if (config_["all-outputs"].asBool() && config_["offscreen-css"].asBool() && immediateParent["type"].asString() == "workspace") { - std::pair all_leaf_nodes = leafNodesInWorkspace(immediateParent); + std::pair all_leaf_nodes{leafNodesInWorkspace(immediateParent)}; // using an empty string as default ensures that no window depending styles are set due to the // checks above for !name.empty() - std::string app_id = ""; - std::string app_class = ""; - std::string workspace_layout = ""; + std::string app_id{""}; + std::string app_class{""}; + std::string workspace_layout{""}; if (all_leaf_nodes.first == 1) { - const auto single_child = getSingleChildNode(immediateParent); + const auto single_child{getSingleChildNode(immediateParent)}; if (single_child.has_value()) { std::tie(app_id, app_class, workspace_layout) = getWindowInfo(single_child.value()); } @@ -281,7 +272,7 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu std::tuple Window::getFocusedNode(const Json::Value& nodes, std::string& output) { - Json::Value placeholder = Json::Value::null; + Json::Value placeholder{Json::Value::null}; return gfnWithWorkspace(nodes, output, config_, bar_, placeholder, placeholder); } diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 8f273300e..33d425f98 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -2,10 +2,6 @@ #include -#include -#include -#include - namespace waybar::modules::sway { // Helper function to assign a number to a workspace, just like sway. In fact @@ -42,8 +38,9 @@ int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) { } Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) - : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), - bar_(bar), + : AModule(config, "workspaces", id, false, + !config["disable-scroll"].asBool() || config["enable-bar-scroll"].asBool()), + bar_{bar}, box_(bar.orientation, 0) { if (config["format-icons"]["high-priority-named"].isArray()) { for (const auto &it : config["format-icons"]["high-priority-named"]) { @@ -55,7 +52,12 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value box_.get_style_context()->add_class(id); } box_.get_style_context()->add_class(MODULE_CLASS); - event_box_.add(box_); + + if (!config["disable-scroll"].asBool() || config["enable-bar-scroll"].asBool()) { + controllScroll_->signal_scroll().connect(sigc::mem_fun(*this, &Workspaces::handleScroll), true); + controllScroll_->set_propagation_phase(Gtk::PropagationPhase::BUBBLE); + } + if (config_["format-window-separator"].isString()) { m_formatWindowSeperator = config_["format-window-separator"].asString(); } else { @@ -75,11 +77,6 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd)); ipc_.sendCmd(IPC_GET_TREE); - if (config["enable-bar-scroll"].asBool()) { - auto &window = const_cast(bar_).window; - window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); - window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll)); - } // Launch worker ipc_.setWorker([this] { try { @@ -88,6 +85,8 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value spdlog::error("Workspaces: {}", e.what()); } }); + + AModule::bindEvents(*this); } void Workspaces::onEvent(const struct Ipc::ipc_response &res) { @@ -102,11 +101,11 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { if (res.type == IPC_GET_TREE) { try { { - std::lock_guard lock(mutex_); - auto payload = parser_.parse(res.payload); + std::lock_guard lock{mutex_}; + const auto payload{parser_.parse(res.payload)}; workspaces_.clear(); std::vector outputs; - bool alloutputs = config_["all-outputs"].asBool(); + const bool alloutputs{config_["all-outputs"].asBool()}; std::copy_if(payload["nodes"].begin(), payload["nodes"].end(), std::back_inserter(outputs), [&](const auto &output) { if (alloutputs && output["name"].asString() != "__i3") { @@ -127,15 +126,15 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { // adding persistent workspaces (as per the config file) if (config_["persistent-workspaces"].isObject()) { - const Json::Value &p_workspaces = config_["persistent-workspaces"]; - const std::vector p_workspaces_names = p_workspaces.getMemberNames(); + const Json::Value &p_workspaces{config_["persistent-workspaces"]}; + const std::vector p_workspaces_names{p_workspaces.getMemberNames()}; for (const std::string &p_w_name : p_workspaces_names) { - const Json::Value &p_w = p_workspaces[p_w_name]; - auto it = std::find_if(workspaces_.begin(), workspaces_.end(), - [&p_w_name](const Json::Value &node) { - return node["name"].asString() == p_w_name; - }); + const Json::Value &p_w{p_workspaces[p_w_name]}; + auto it{std::find_if(workspaces_.begin(), workspaces_.end(), + [&p_w_name](const Json::Value &node) { + return node["name"].asString() == p_w_name; + })}; if (it != workspaces_.end()) { continue; // already displayed by some bar @@ -185,12 +184,12 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { // Note: if the 'alphabetical_sort' option is true, the user is in // agreement that the "workspace prev/next" commands may not follow // the order displayed in Waybar. - int max_num = -1; + int max_num{-1}; for (auto &workspace : workspaces_) { max_num = std::max(workspace["num"].asInt(), max_num); } for (auto &workspace : workspaces_) { - auto workspace_num = workspace["num"].asInt(); + auto workspace_num{workspace["num"].asInt()}; if (workspace_num > -1) { workspace["sort"] = workspace_num; } else { @@ -199,10 +198,10 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { } std::sort(workspaces_.begin(), workspaces_.end(), [this](const Json::Value &lhs, const Json::Value &rhs) { - auto lname = lhs["name"].asString(); - auto rname = rhs["name"].asString(); - int l = lhs["sort"].asInt(); - int r = rhs["sort"].asInt(); + auto lname{lhs["name"].asString()}; + auto rname{rhs["name"].asString()}; + int l{lhs["sort"].asInt()}; + int r{rhs["sort"].asInt()}; if (l == r || config_["alphabetical_sort"].asBool()) { // In case both integers are the same, lexicographical @@ -225,10 +224,10 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { } bool Workspaces::filterButtons() { - bool needReorder = false; - for (auto it = buttons_.begin(); it != buttons_.end();) { - auto ws = std::find_if(workspaces_.begin(), workspaces_.end(), - [it](const auto &node) { return node["name"].asString() == it->first; }); + bool needReorder{false}; + for (auto it{buttons_.begin()}; it != buttons_.end();) { + auto ws{std::find_if(workspaces_.begin(), workspaces_.end(), + [it](const auto &node) { return node["name"].asString() == it->first; })}; if (ws == workspaces_.end() || (!config_["all-outputs"].asBool() && (*ws)["output"].asString() != bar_.output->name)) { it = buttons_.erase(it); @@ -282,18 +281,25 @@ void Workspaces::updateWindows(const Json::Value &node, std::string &windows) { } auto Workspaces::update() -> void { - std::lock_guard lock(mutex_); - bool needReorder = filterButtons(); - for (auto it = workspaces_.begin(); it != workspaces_.end(); ++it) { - auto bit = buttons_.find((*it)["name"].asString()); + std::lock_guard lock{mutex_}; + bool needReorder{filterButtons()}; + for (auto it{workspaces_.begin()}; it != workspaces_.end(); ++it) { + auto bit{buttons_.find((*it)["name"].asString())}; if (bit == buttons_.end()) { needReorder = true; } - auto &button = bit == buttons_.end() ? addButton(*it) : bit->second; + auto &button{bit == buttons_.end() ? addButton(*it) : bit->second}; if (needReorder) { - box_.reorder_child(button, it - workspaces_.begin()); + std::unordered_map::const_iterator prevBit; + + if (it != workspaces_.begin()) prevBit = buttons_.find((*(it - 1))["name"].asString()); + + if (prevBit != buttons_.end()) + box_.reorder_child_after(button, prevBit->second); + else + box_.reorder_child_at_start(button); } - bool noNodes = (*it)["nodes"].empty() && (*it)["floating_nodes"].empty(); + bool noNodes{(*it)["nodes"].empty() && (*it)["floating_nodes"].empty()}; if (hasFlag((*it), "focused")) { button.get_style_context()->add_class("focused"); } else { @@ -328,13 +334,13 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("current_output"); } - std::string output = (*it)["name"].asString(); - std::string windows = ""; + std::string output{(*it)["name"].asString()}; + std::string windows{""}; if (config_["window-format"].isString()) { updateWindows((*it), windows); } if (config_["format"].isString()) { - auto format = config_["format"].asString(); + auto format{config_["format"].asString()}; output = fmt::format( fmt::runtime(format), fmt::arg("icon", getIcon(output, *it)), fmt::arg("value", output), fmt::arg("name", trimWorkspaceName(output)), fmt::arg("index", (*it)["num"].asString()), @@ -343,7 +349,7 @@ auto Workspaces::update() -> void { fmt::arg("output", (*it)["output"].asString())); } if (!config_["disable-markup"].asBool()) { - static_cast(button.get_children()[0])->set_markup(output); + static_cast(button.get_child())->set_markup(output); } else { button.set_label(output); } @@ -354,13 +360,15 @@ auto Workspaces::update() -> void { } Gtk::Button &Workspaces::addButton(const Json::Value &node) { - auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString()); - auto &&button = pair.first->second; - box_.pack_start(button, false, false, 0); + auto pair{buttons_.emplace(node["name"].asString(), node["name"].asString())}; + auto &&button{pair.first->second}; + box_.append(button); button.set_name("sway-workspace-" + node["name"].asString()); - button.set_relief(Gtk::RELIEF_NONE); + button.set_has_frame(false); if (!config_["disable-click"].asBool()) { - button.signal_pressed().connect([this, node] { + auto controlClick{Gtk::GestureClick::create()}; + button.add_controller(controlClick); + controlClick->signal_pressed().connect([this, node](int n_press, double dx, double dy) { try { if (node["target_output"].isString()) { ipc_.sendCmd(IPC_COMMAND, @@ -378,16 +386,18 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { spdlog::error("Workspaces: {}", e.what()); } }); + controlClick->set_propagation_phase(Gtk::PropagationPhase::CAPTURE); + controlClick->set_button(1u); } return button; } std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) { - std::vector keys = {"high-priority-named", "urgent", "focused", name, "default"}; + std::vector keys{"high-priority-named", "urgent", "focused", name, "default"}; for (auto const &key : keys) { if (key == "high-priority-named") { - auto it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(), - [&](const std::string &member) { return member == name; }); + auto it{std::find_if(high_priority_named_.begin(), high_priority_named_.end(), + [&](const std::string &member) { return member == name; })}; if (it != high_priority_named_.end()) { return config_["format-icons"][name].asString(); } @@ -416,29 +426,30 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node return name; } -bool Workspaces::handleScroll(GdkEventScroll *e) { - if (gdk_event_get_pointer_emulated((GdkEvent *)e) != 0) { +bool Workspaces::handleScroll(double dx, double dy) { + auto currEvent{controllScroll_->get_current_event()}; + if (currEvent->get_pointer_emulated()) { /** * Ignore emulated scroll events on window */ return false; } - auto dir = AModule::getScrollDir(e); + auto dir{AModule::getScrollDir(currEvent)}; if (dir == SCROLL_DIR::NONE) { return true; } std::string name; { - bool alloutputs = config_["all-outputs"].asBool(); - std::lock_guard lock(mutex_); - auto it = + std::lock_guard lock{mutex_}; + const bool alloutputs{config_["all-outputs"].asBool()}; + auto it{ std::find_if(workspaces_.begin(), workspaces_.end(), [alloutputs](const auto &workspace) { if (alloutputs) { return hasFlag(workspace, "focused"); } - bool noNodes = workspace["nodes"].empty() && workspace["floating_nodes"].empty(); + const bool noNodes{workspace["nodes"].empty() && workspace["floating_nodes"].empty()}; return hasFlag(workspace, "visible") || (workspace["output"].isString() && noNodes); - }); + })}; if (it == workspaces_.end()) { return true; } @@ -486,7 +497,7 @@ std::string Workspaces::getCycleWorkspace(std::vector::iterator it, } std::string Workspaces::trimWorkspaceName(std::string name) { - std::size_t found = name.find(':'); + std::size_t found{name.find(':')}; if (found != std::string::npos) { return name.substr(found + 1); } @@ -512,4 +523,6 @@ void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) { } } +Workspaces::operator Gtk::Widget &() { return box_; }; + } // namespace waybar::modules::sway diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp index 56e624cf2..7b33ff2db 100644 --- a/src/modules/systemd_failed_units.cpp +++ b/src/modules/systemd_failed_units.cpp @@ -1,7 +1,5 @@ #include "modules/systemd_failed_units.hpp" -#include -#include #include #include @@ -29,8 +27,8 @@ SystemdFailedUnits::SystemdFailedUnits(const std::string& id, const Json::Value& /* Default to enable both "system" and "user". */ if (!config["system"].isBool() || config["system"].asBool()) { system_proxy = Gio::DBus::Proxy::create_for_bus_sync( - Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", "org.freedesktop.DBus.Properties"); + Gio::DBus::BusType::SYSTEM, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", + "org.freedesktop.DBus.Properties"); if (!system_proxy) { throw std::runtime_error("Unable to connect to systemwide systemd DBus!"); } @@ -38,8 +36,8 @@ SystemdFailedUnits::SystemdFailedUnits(const std::string& id, const Json::Value& } if (!config["user"].isBool() || config["user"].asBool()) { user_proxy = Gio::DBus::Proxy::create_for_bus_sync( - Gio::DBus::BusType::BUS_TYPE_SESSION, "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", "org.freedesktop.DBus.Properties"); + Gio::DBus::BusType::SESSION, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", + "org.freedesktop.DBus.Properties"); if (!user_proxy) { throw std::runtime_error("Unable to connect to user systemd DBus!"); } @@ -85,7 +83,7 @@ void SystemdFailedUnits::updateData() { } } } catch (Glib::Error& e) { - spdlog::error("Failed to get {} failed units: {}", kind, e.what().c_str()); + spdlog::error("Failed to get {} failed units: {}", kind, e.what()); } return 0; }; @@ -104,11 +102,11 @@ auto SystemdFailedUnits::update() -> void { // Hide if needed. if (nr_failed == 0 && hide_on_ok) { - event_box_.set_visible(false); + label_.set_visible(false); return; } - if (!event_box_.get_visible()) { - event_box_.set_visible(true); + if (!label_.get_visible()) { + label_.set_visible(true); } // Set state class. diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 356536f94..69bc1e7d8 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -1,6 +1,9 @@ #include "modules/temperature.hpp" +#include + #include +#include #include #if defined(__FreeBSD__) @@ -77,17 +80,17 @@ auto waybar::modules::Temperature::update() -> void { } else if (warning) { format = config_["format-warning"].isString() ? config_["format-warning"].asString() : format; label_.get_style_context()->add_class("warning"); - } else { + } else { label_.get_style_context()->remove_class("critical"); label_.get_style_context()->remove_class("warning"); } if (format.empty()) { - event_box_.hide(); + label_.hide(); return; } - event_box_.show(); + label_.show(); auto max_temp = config_["critical-threshold"].isInt() ? config_["critical-threshold"].asInt() : 0; label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("temperatureC", temperature_c), @@ -148,4 +151,4 @@ bool waybar::modules::Temperature::isWarning(uint16_t temperature_c) { bool waybar::modules::Temperature::isCritical(uint16_t temperature_c) { return config_["critical-threshold"].isInt() && temperature_c >= config_["critical-threshold"].asInt(); -} \ No newline at end of file +} diff --git a/src/modules/ui.cpp b/src/modules/ui.cpp new file mode 100644 index 000000000..c3c49a5b7 --- /dev/null +++ b/src/modules/ui.cpp @@ -0,0 +1,40 @@ +#include "modules/ui.hpp" + +#include +#include + +#include + +waybar::modules::UI::UI(const std::string& name, const std::string& id, const Json::Value& config) + : AModule(config, "ui-" + name, id, false, false) { + if (config_["file-path"].isString()) { + Glib::RefPtr builder{ + Gtk::Builder::create_from_file(config_["file-path"].asString())}; + uiWg_ = builder->get_object(name_); + + if (uiWg_) { + uiWg_->set_name(name_); + if (!id.empty()) { + uiWg_->get_style_context()->add_class(id); + } + uiWg_->get_style_context()->add_class(MODULE_CLASS); + + Glib::RefPtr actionGroup{Gio::SimpleActionGroup::create()}; + Glib::RefPtr action{actionGroup->add_action_with_parameter( + "doAction", Glib::VARIANT_TYPE_STRING, [this](const Glib::VariantBase& param) { + assert(param.is_of_type(Glib::VARIANT_TYPE_STRING)); + waybar::util::command::res res = + waybar::util::command::exec(param.get_dynamic(), "TLP"); + })}; + + uiWg_->insert_action_group(name_, actionGroup); + AModule::bindEvents(*uiWg_.get()); + } else { + spdlog::error("UI: object id \"{}\" is not found at \"{}\"", name_, + config_["file-path"].asString()); + exit(EXIT_FAILURE); + } + } +} + +waybar::modules::UI::operator Gtk::Widget&() { return *uiWg_.get(); }; diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 5ee6d64c5..3d127ca2f 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -12,11 +12,11 @@ UPower::UPower(const std::string &id, const Json::Value &config) box_.set_spacing(0); box_.set_has_tooltip(AModule::tooltipEnabled()); // Tooltip box - contentBox_.set_orientation((box_.get_orientation() == Gtk::ORIENTATION_HORIZONTAL) - ? Gtk::ORIENTATION_VERTICAL - : Gtk::ORIENTATION_HORIZONTAL); + contentBox_.set_orientation((box_.get_orientation() == Gtk::Orientation::HORIZONTAL) + ? Gtk::Orientation::VERTICAL + : Gtk::Orientation::HORIZONTAL); // Get current theme - gtkTheme_ = Gtk::IconTheme::get_default(); + gtkTheme_ = Gtk::IconTheme::get_for_display(box_.get_display()); // Icon Size if (config_["icon-size"].isInt()) { @@ -41,10 +41,7 @@ UPower::UPower(const std::string &id, const Json::Value &config) // Tooltip Padding if (config_["tooltip-padding"].isInt()) { tooltip_padding_ = config_["tooltip-padding"].asInt(); - contentBox_.set_margin_top(tooltip_padding_); - contentBox_.set_margin_bottom(tooltip_padding_); - contentBox_.set_margin_left(tooltip_padding_); - contentBox_.set_margin_right(tooltip_padding_); + contentBox_.set_margin(tooltip_padding_); } // Tooltip Format @@ -52,12 +49,10 @@ UPower::UPower(const std::string &id, const Json::Value &config) // Start watching DBUS watcherID_ = Gio::DBus::watch_name( - Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.UPower", - sigc::mem_fun(*this, &UPower::onAppear), sigc::mem_fun(*this, &UPower::onVanished), - Gio::DBus::BusNameWatcherFlags::BUS_NAME_WATCHER_FLAGS_AUTO_START); + Gio::DBus::BusType::SYSTEM, "org.freedesktop.UPower", sigc::mem_fun(*this, &UPower::onAppear), + sigc::mem_fun(*this, &UPower::onVanished), Gio::DBus::BusNameWatcherFlags::AUTO_START); // Get DBus async connect - Gio::DBus::Connection::get(Gio::DBus::BusType::BUS_TYPE_SYSTEM, - sigc::mem_fun(*this, &UPower::getConn_cb)); + Gio::DBus::Connection::get(Gio::DBus::BusType::SYSTEM, sigc::mem_fun(*this, &UPower::getConn_cb)); // Make UPower client GError **gErr = NULL; @@ -91,6 +86,17 @@ UPower::~UPower() { removeDevices(); } +static const char *getDeviceWarningLevel(UpDeviceLevel level) { + switch (level) { + case UP_DEVICE_LEVEL_CRITICAL: + return "critical"; + case UP_DEVICE_LEVEL_LOW: + return "low"; + default: + return nullptr; + } +} + static const std::string getDeviceStatus(UpDeviceState &state) { switch (state) { case UP_DEVICE_STATE_CHARGING: @@ -211,6 +217,15 @@ auto UPower::update() -> void { if (!box_.get_style_context()->has_class(status)) box_.get_style_context()->add_class(status); lastStatus_ = status; + const char *warning_level = getDeviceWarningLevel(upDevice_.level); + if (lastWarningLevel_ && box_.get_style_context()->has_class(lastWarningLevel_)) { + box_.get_style_context()->remove_class(lastWarningLevel_); + } + if (warning_level && !box_.get_style_context()->has_class(warning_level)) { + box_.get_style_context()->add_class(warning_level); + } + lastWarningLevel_ = warning_level; + if (devices_.size() == 0 && !upDeviceValid && hideIfEmpty_) { box_.hide(); // Call parent update @@ -222,7 +237,7 @@ auto UPower::update() -> void { // Set icon if (upDevice_.icon_name == NULL || !gtkTheme_->has_icon(upDevice_.icon_name)) upDevice_.icon_name = (char *)NO_BATTERY.c_str(); - image_.set_from_icon_name(upDevice_.icon_name, Gtk::ICON_SIZE_INVALID); + image_.set_from_icon_name(upDevice_.icon_name); box_.show(); @@ -239,7 +254,7 @@ void UPower::getConn_cb(Glib::RefPtr &result) { "PrepareForSleep", "/org/freedesktop/login1"); } catch (const Glib::Error &e) { - spdlog::error("Upower. DBus connection error. {}", e.what().c_str()); + spdlog::error("Upower. DBus connection error. {}", e.what()); } } @@ -403,14 +418,14 @@ void UPower::getUpDeviceInfo(upDevice_output &upDevice_) { "percentage", &upDevice_.percentage, "icon-name", &upDevice_.icon_name, "time-to-empty", &upDevice_.time_empty, "time-to-full", &upDevice_.time_full, "temperature", &upDevice_.temperature, "native-path", &upDevice_.nativePath, - "model", &upDevice_.model, NULL); + "model", &upDevice_.model, "warning-level", &upDevice_.level, NULL); spdlog::debug( "UPower. getUpDeviceInfo. kind: \"{0}\". state: \"{1}\". percentage: \"{2}\". \ icon_name: \"{3}\". time-to-empty: \"{4}\". time-to-full: \"{5}\". temperature: \"{6}\". \ -native_path: \"{7}\". model: \"{8}\"", +native_path: \"{7}\". model: \"{8}\". level: \"{9}\"", fmt::format_int(upDevice_.kind).str(), fmt::format_int(upDevice_.state).str(), upDevice_.percentage, upDevice_.icon_name, upDevice_.time_empty, upDevice_.time_full, - upDevice_.temperature, upDevice_.nativePath, upDevice_.model); + upDevice_.temperature, upDevice_.nativePath, upDevice_.model, upDevice_.level); } } @@ -447,8 +462,8 @@ bool UPower::queryTooltipCb(int x, int y, bool keyboard_tooltip, std::lock_guard guard{mutex_}; // Clear content box - contentBox_.forall([this](Gtk::Widget &wg) { contentBox_.remove(wg); }); - + for (auto child{contentBox_.get_last_child()}; child; child = contentBox_.get_last_child()) + contentBox_.remove(*child); // Fill content box with the content for (auto pairDev : devices_) { // Get device info @@ -458,38 +473,37 @@ bool UPower::queryTooltipCb(int x, int y, bool keyboard_tooltip, pairDev.second.kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER) { // Make box record Gtk::Box *boxRec{new Gtk::Box{box_.get_orientation(), tooltip_spacing_}}; - contentBox_.add(*boxRec); + contentBox_.append(*boxRec); Gtk::Box *boxDev{new Gtk::Box{box_.get_orientation()}}; Gtk::Box *boxUsr{new Gtk::Box{box_.get_orientation()}}; - boxRec->add(*boxDev); - boxRec->add(*boxUsr); + boxRec->append(*boxDev); + boxRec->append(*boxUsr); // Construct device box // Set icon from kind std::string iconNameDev{getDeviceIcon(pairDev.second.kind)}; if (!gtkTheme_->has_icon(iconNameDev)) iconNameDev = (char *)NO_BATTERY.c_str(); Gtk::Image *iconDev{new Gtk::Image{}}; - iconDev->set_from_icon_name(iconNameDev, Gtk::ICON_SIZE_INVALID); + iconDev->set_from_icon_name(iconNameDev); iconDev->set_pixel_size(iconSize_); - boxDev->add(*iconDev); + boxDev->append(*iconDev); // Set label from model Gtk::Label *labelDev{new Gtk::Label{pairDev.second.model}}; - boxDev->add(*labelDev); + boxDev->append(*labelDev); // Construct user box // Set icon from icon state if (pairDev.second.icon_name == NULL || !gtkTheme_->has_icon(pairDev.second.icon_name)) pairDev.second.icon_name = (char *)NO_BATTERY.c_str(); Gtk::Image *iconTooltip{new Gtk::Image{}}; - iconTooltip->set_from_icon_name(pairDev.second.icon_name, Gtk::ICON_SIZE_INVALID); + iconTooltip->set_from_icon_name(pairDev.second.icon_name); iconTooltip->set_pixel_size(iconSize_); - boxUsr->add(*iconTooltip); + boxUsr->append(*iconTooltip); // Set markup text Gtk::Label *labelTooltip{new Gtk::Label{}}; labelTooltip->set_markup(getText(pairDev.second, tooltipFormat_)); - boxUsr->add(*labelTooltip); + boxUsr->append(*labelTooltip); } } tooltip->set_custom(contentBox_); - contentBox_.show_all(); return true; } diff --git a/src/modules/user.cpp b/src/modules/user.cpp index 418fc5858..0acdcda55 100644 --- a/src/modules/user.cpp +++ b/src/modules/user.cpp @@ -1,18 +1,8 @@ #include "modules/user.hpp" #include +#include #include -#include - -#include -#include - -#include "gdkmm/cursor.h" -#include "gdkmm/event.h" -#include "gdkmm/types.h" -#include "glibmm/fileutils.h" -#include "sigc++/functors/mem_fun.h" -#include "sigc++/functors/ptr_fun.h" #if HAVE_CPU_LINUX #include @@ -34,9 +24,10 @@ User::User(const std::string& id, const Json::Value& config) this->init_update_worker(); } -bool User::handleToggle(GdkEventButton* const& e) { +void User::handleToggle(int n_press, double dx, double dy) { if (AIconLabel::config_["open-on-click"].isBool() && - AIconLabel::config_["open-on-click"].asBool() && e->button == LEFT_MOUSE_BUTTON_CODE) { + AIconLabel::config_["open-on-click"].asBool() && + controllClick_->get_current_button() == LEFT_MOUSE_BUTTON_CODE) { std::string openPath = this->get_user_home_dir(); if (AIconLabel::config_["open-path"].isString()) { std::string customPath = AIconLabel::config_["open-path"].asString(); @@ -47,7 +38,6 @@ bool User::handleToggle(GdkEventButton* const& e) { Gio::AppInfo::launch_default_for_uri("file:///" + openPath); } - return true; } long User::uptime_as_seconds() { @@ -108,7 +98,7 @@ void User::init_default_user_avatar(int width, int height) { } void User::init_user_avatar(const std::string& path, int width, int height) { - if (Glib::file_test(path, Glib::FILE_TEST_EXISTS)) { + if (Glib::file_test(path, Glib::FileTest::EXISTS)) { Glib::RefPtr pixbuf_ = Gdk::Pixbuf::create_from_file(path, width, height); AIconLabel::image_.set(pixbuf_); } else { diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index eddc3e6be..9113cded6 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -292,9 +292,6 @@ void waybar::modules::Wireplumber::onMixerApiLoaded(WpObject* p, GAsyncResult* r self->activatePlugins(); self->dp.emit(); - - self->event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); - self->event_box_.signal_scroll_event().connect(sigc::mem_fun(*self, &Wireplumber::handleScroll)); } void waybar::modules::Wireplumber::asyncLoadRequiredApiModules() { @@ -340,11 +337,11 @@ auto waybar::modules::Wireplumber::update() -> void { ALabel::update(); } -bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) { +bool waybar::modules::Wireplumber::handleScroll(double dx, double dy) { if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { - return AModule::handleScroll(e); + return AModule::handleScroll(dx, dy); } - auto dir = AModule::getScrollDir(e); + auto dir = AModule::getScrollDir(controllScroll_->get_current_event()); if (dir == SCROLL_DIR::NONE) { return true; } diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 30e4ee488..115cee459 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -1,26 +1,13 @@ #include "modules/wlr/taskbar.hpp" -#include -#include #include -#include -#include +#include +#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include - -#include "gdkmm/general.h" -#include "glibmm/error.h" -#include "glibmm/fileutils.h" -#include "glibmm/refptr.h" -#include "util/format.hpp" -#include "util/gtk_icon.hpp" +#include "client.hpp" #include "util/rewrite_string.hpp" #include "util/string.hpp" @@ -55,15 +42,6 @@ static std::vector search_prefix() { return prefixes; } -static Glib::RefPtr load_icon_from_file(std::string icon_path, int size) { - try { - auto pb = Gdk::Pixbuf::create_from_file(icon_path, size, size); - return pb; - } catch (...) { - return {}; - } -} - static Glib::RefPtr get_app_info_by_name(const std::string &app_id) { static std::vector prefixes = search_prefix(); @@ -165,49 +143,25 @@ static std::string get_icon_name_from_icon_theme(const Glib::RefPtr &icon_theme, Glib::RefPtr app_info, int size) { - std::string ret_icon_name = "unknown"; - if (app_info) { - std::string icon_name = - get_icon_name_from_icon_theme(icon_theme, app_info->get_startup_wm_class()); - if (!icon_name.empty()) { - ret_icon_name = icon_name; - } else { - if (app_info->get_icon()) { - ret_icon_name = app_info->get_icon()->to_string(); - } + Glib::ustring iconName{(app_info) ? app_info->get_icon()->to_string() : "unknown"}; + if (app_info && iconName.empty()) + iconName = get_icon_name_from_icon_theme(icon_theme, app_info->get_startup_wm_class()); + if (iconName.empty()) iconName = "unknown"; + + auto themeIcon{ + icon_theme->lookup_icon((icon_theme->has_icon(iconName)) ? iconName : "image-missing", size, + (int)image.get_scale_factor(), Gtk::TextDirection::NONE, + Gtk::IconLookupFlags::FORCE_REGULAR)}; + if (!(icon_theme->has_icon(iconName))) { + if (Glib::file_test(iconName, Glib::FileTest::EXISTS)) { + themeIcon = Gtk::IconPaintable::create(Gio::File::create_for_path(iconName), size, + image.get_scale_factor()); } } - Glib::RefPtr pixbuf; - auto scaled_icon_size = size * image.get_scale_factor(); - - try { - pixbuf = icon_theme->load_icon(ret_icon_name, scaled_icon_size, Gtk::ICON_LOOKUP_FORCE_SIZE); - spdlog::debug("{} Loaded icon '{}'", repr(), ret_icon_name); - } catch (...) { - if (Glib::file_test(ret_icon_name, Glib::FILE_TEST_EXISTS)) { - pixbuf = load_icon_from_file(ret_icon_name, scaled_icon_size); - spdlog::debug("{} Loaded icon from file '{}'", repr(), ret_icon_name); - } else { - try { - pixbuf = DefaultGtkIconThemeWrapper::load_icon( - "image-missing", scaled_icon_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); - spdlog::debug("{} Loaded icon from resource", repr()); - } catch (...) { - pixbuf = {}; - spdlog::debug("{} Unable to load icon.", repr()); - } - } - } + if (themeIcon) { + image.set(themeIcon); - if (pixbuf) { - if (pixbuf->get_width() != scaled_icon_size) { - int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height(); - pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); - } - auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image.get_scale_factor(), - image.get_window()); - image.set(surface); return true; } @@ -266,12 +220,11 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_imp .parent = tl_handle_parent, }; -static const std::vector target_entries = { - Gtk::TargetEntry("WAYBAR_TOPLEVEL", Gtk::TARGET_SAME_APP, 0)}; - Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, struct zwlr_foreign_toplevel_handle_v1 *tl_handle, struct wl_seat *seat) - : bar_{bar}, + : button{new Gtk::Button()}, + controllClick_{Gtk::GestureClick::create()}, + bar_{bar}, config_{config}, tbar_{tbar}, handle_{tl_handle}, @@ -280,14 +233,14 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, content_{bar.orientation, 0} { zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this); - button.set_relief(Gtk::RELIEF_NONE); + button->set_has_frame(false); - content_.add(text_before_); - content_.add(icon_); - content_.add(text_after_); + content_.append(text_before_); + content_.append(icon_); + content_.append(text_after_); content_.show(); - button.add(content_); + button->set_child(content_); format_before_.clear(); format_after_.clear(); @@ -331,20 +284,24 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, /* Handle click events if configured */ if (config_["on-click"].isString() || config_["on-click-middle"].isString() || config_["on-click-right"].isString()) { - } - - button.add_events(Gdk::BUTTON_PRESS_MASK); - button.signal_button_release_event().connect(sigc::mem_fun(*this, &Task::handle_clicked), false); - - button.signal_motion_notify_event().connect(sigc::mem_fun(*this, &Task::handle_motion_notify), - false); - - button.drag_source_set(target_entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE); - button.drag_dest_set(target_entries, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_MOVE); - - button.signal_drag_data_get().connect(sigc::mem_fun(*this, &Task::handle_drag_data_get), false); - button.signal_drag_data_received().connect(sigc::mem_fun(*this, &Task::handle_drag_data_received), - false); + controllClick_->set_propagation_phase(Gtk::PropagationPhase::TARGET); + controllClick_->signal_released().connect(sigc::mem_fun(*this, &Task::handleClick), false); + button->add_controller(controllClick_); + } + + // Setup DND source + Glib::Value> buttonValue; + auto source = Gtk::DragSource::create(); + source->set_actions(Gdk::DragAction::COPY); + buttonValue.init(buttonValue.value_type()); + buttonValue.set(button); + source->set_content(Gdk::ContentProvider::create(buttonValue)); + button->add_controller(source); + // Setup DND target + const GType target_type = Glib::Value>::value_type(); + auto target = Gtk::DropTarget::create(target_type, Gdk::DragAction::COPY); + target->signal_drop().connect(sigc::mem_fun(*this, &Task::handleDropData), false); + button->add_controller(target); } Task::~Task() { @@ -452,13 +409,6 @@ void Task::handle_app_id(const char *app_id) { spdlog::debug("Couldn't find icon for {}", app_id_); } -void Task::on_button_size_allocated(Gtk::Allocation &alloc) { - gtk_widget_translate_coordinates(GTK_WIDGET(button.gobj()), GTK_WIDGET(bar_.window.gobj()), 0, 0, - &minimize_hint.x, &minimize_hint.y); - minimize_hint.w = button.get_width(); - minimize_hint.h = button.get_height(); -} - void Task::handle_output_enter(struct wl_output *output) { if (ignored_) { spdlog::debug("{} is ignored", repr()); @@ -469,10 +419,13 @@ void Task::handle_output_enter(struct wl_output *output) { if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) { /* The task entered the output of the current bar make the button visible */ - button.signal_size_allocate().connect_notify( - sigc::mem_fun(this, &Task::on_button_size_allocated)); + button->property_width_request().signal_changed().connect( + [&]() { minimize_hint.w = button->get_width(); }); + button->property_height_request().signal_changed().connect( + [&]() { minimize_hint.h = button->get_height(); }); + tbar_->add_button(button); - button.show(); + button->show(); button_visible_ = true; spdlog::debug("{} now visible on {}", repr(), bar_.output->name); } @@ -484,7 +437,7 @@ void Task::handle_output_leave(struct wl_output *output) { if (button_visible_ && !tbar_->all_outputs() && tbar_->show_output(output)) { /* The task left the output of the current bar, make the button invisible */ tbar_->remove_button(button); - button.hide(); + button->hide(); button_visible_ = false; spdlog::debug("{} now invisible on {}", repr(), bar_.output->name); } @@ -506,27 +459,27 @@ void Task::handle_done() { spdlog::debug("{} changed", repr()); if (state_ & MAXIMIZED) { - button.get_style_context()->add_class("maximized"); + button->get_style_context()->add_class("maximized"); } else if (!(state_ & MAXIMIZED)) { - button.get_style_context()->remove_class("maximized"); + button->get_style_context()->remove_class("maximized"); } if (state_ & MINIMIZED) { - button.get_style_context()->add_class("minimized"); + button->get_style_context()->add_class("minimized"); } else if (!(state_ & MINIMIZED)) { - button.get_style_context()->remove_class("minimized"); + button->get_style_context()->remove_class("minimized"); } if (state_ & ACTIVE) { - button.get_style_context()->add_class("active"); + button->get_style_context()->add_class("active"); } else if (!(state_ & ACTIVE)) { - button.get_style_context()->remove_class("active"); + button->get_style_context()->remove_class("active"); } if (state_ & FULLSCREEN) { - button.get_style_context()->add_class("fullscreen"); + button->get_style_context()->add_class("fullscreen"); } else if (!(state_ & FULLSCREEN)) { - button.get_style_context()->remove_class("fullscreen"); + button->get_style_context()->remove_class("fullscreen"); } if (config_["active-first"].isBool() && config_["active-first"].asBool() && active()) @@ -546,25 +499,19 @@ void Task::handle_closed() { tbar_->remove_task(id_); } -bool Task::handle_clicked(GdkEventButton *bt) { - /* filter out additional events for double/triple clicks */ - if (bt->type == GDK_BUTTON_PRESS) { - /* save where the button press occurred in case it becomes a drag */ - drag_start_button = bt->button; - drag_start_x = bt->x; - drag_start_y = bt->y; - } +void Task::handleClick(int n_press, double dx, double dy) { + auto currButton{controllClick_->get_current_button()}; std::string action; - if (config_["on-click"].isString() && bt->button == 1) + if (config_["on-click"].isString() && currButton == 1) action = config_["on-click"].asString(); - else if (config_["on-click-middle"].isString() && bt->button == 2) + else if (config_["on-click-middle"].isString() && currButton == 2) action = config_["on-click-middle"].asString(); - else if (config_["on-click-right"].isString() && bt->button == 3) + else if (config_["on-click-right"].isString() && currButton == 3) action = config_["on-click-right"].asString(); if (action.empty()) - return true; + return; else if (action == "activate") activate(); else if (action == "minimize") { @@ -586,52 +533,34 @@ bool Task::handle_clicked(GdkEventButton *bt) { close(); else spdlog::warn("Unknown action {}", action); - - drag_start_button = -1; - return true; } -bool Task::handle_motion_notify(GdkEventMotion *mn) { - if (drag_start_button == -1) return false; +bool Task::handleDropData(const Glib::ValueBase &value, double, double) { + if (G_VALUE_HOLDS(value.gobj(), Glib::Value>::value_type())) { + // Expand incoming value + Glib::Value> dropValue; + dropValue.init(value.gobj()); + const Glib::RefPtr draggedButton = dropValue.get(); + + if (draggedButton) { + if (draggedButton != this->button) { + auto draggedParent{draggedButton->get_parent()}; + auto droppedParent{this->button->get_parent()}; + if (draggedParent == droppedParent) { + auto draggedBox{(Gtk::Box *)draggedParent}; + // Need to move dragged button before the current + draggedBox->reorder_child_after(*draggedButton, *this->button); + draggedBox->reorder_child_after(*this->button, *draggedButton); + } + } + } - if (button.drag_check_threshold(drag_start_x, drag_start_y, mn->x, mn->y)) { - /* start drag in addition to other assigned action */ - auto target_list = Gtk::TargetList::create(target_entries); - auto refptr = Glib::RefPtr(target_list); - auto drag_context = - button.drag_begin(refptr, Gdk::DragAction::ACTION_MOVE, drag_start_button, (GdkEvent *)mn); + return true; + } else { + spdlog::warn("WLR taskbar. Received unexpected data type {} in button", + G_VALUE_TYPE_NAME(value.gobj())); + return false; } - - return false; -} - -void Task::handle_drag_data_get(const Glib::RefPtr &context, - Gtk::SelectionData &selection_data, guint info, guint time) { - spdlog::debug("drag_data_get"); - void *button_addr = (void *)&this->button; - - selection_data.set("WAYBAR_TOPLEVEL", 32, (const guchar *)&button_addr, sizeof(gpointer)); -} - -void Task::handle_drag_data_received(const Glib::RefPtr &context, int x, int y, - Gtk::SelectionData selection_data, guint info, guint time) { - spdlog::debug("drag_data_received"); - gpointer handle = *(gpointer *)selection_data.get_data(); - auto dragged_button = (Gtk::Button *)handle; - - if (dragged_button == &this->button) return; - - auto parent_of_dragged = dragged_button->get_parent(); - auto parent_of_dest = this->button.get_parent(); - - if (parent_of_dragged != parent_of_dest) return; - - auto box = (Gtk::Box *)parent_of_dragged; - - auto position_prop = box->child_property_position(this->button); - auto position = position_prop.get_value(); - - box->reorder_child(*dragged_button, position); } bool Task::operator==(const Task &o) const { return o.id_ == id_; } @@ -683,9 +612,9 @@ void Task::update() { fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true))); if (markup) - button.set_tooltip_markup(txt); + button->set_tooltip_markup(txt); else - button.set_tooltip_text(txt); + button->set_tooltip_text(txt); } } @@ -749,7 +678,6 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu } box_.get_style_context()->add_class(MODULE_CLASS); box_.get_style_context()->add_class("empty"); - event_box_.add(box_); struct wl_display *display = Client::inst()->wl_display; struct wl_registry *registry = wl_display_get_registry(display); @@ -772,7 +700,7 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu auto it_name = c.asString(); auto it = Gtk::IconTheme::create(); - it->set_custom_theme(it_name); + it->set_theme_name(it_name); spdlog::debug("Use custom icon theme: {}", it_name); icon_themes_.push_back(it); @@ -781,7 +709,7 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu auto it_name = config_["icon-theme"].asString(); auto it = Gtk::IconTheme::create(); - it->set_custom_theme(it_name); + it->set_theme_name(it_name); spdlog::debug("Use custom icon theme: {}", it_name); icon_themes_.push_back(it); @@ -803,7 +731,7 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu } } - icon_themes_.push_back(Gtk::IconTheme::get_default()); + icon_themes_.push_back(Gtk::IconTheme::get_for_display(box_.get_display())); for (auto &t : tasks_) { t->handle_app_id(t->app_id().c_str()); @@ -848,6 +776,8 @@ void Taskbar::update() { AModule::update(); } +Taskbar::operator Gtk::Widget &() { return box_; } + static void tm_handle_toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager, struct zwlr_foreign_toplevel_handle_v1 *tl_handle) { return static_cast(data)->handle_toplevel_create(tl_handle); @@ -905,15 +835,23 @@ void Taskbar::handle_finished() { manager_ = nullptr; } -void Taskbar::add_button(Gtk::Button &bt) { - box_.pack_start(bt, false, false); +void Taskbar::add_button(Glib::RefPtr bt) { + box_.append(*bt); box_.get_style_context()->remove_class("empty"); } -void Taskbar::move_button(Gtk::Button &bt, int pos) { box_.reorder_child(bt, pos); } +void Taskbar::move_button(Glib::RefPtr bt, int pos) { + try { + const auto sibling{(box_.get_children()).at(pos - 1)}; + + if (sibling) box_.reorder_child_after(*bt, *sibling); + } catch (const std::out_of_range &ex) { + spdlog::error("WLR taskbar. {}", ex.what()); + } +} -void Taskbar::remove_button(Gtk::Button &bt) { - box_.remove(bt); +void Taskbar::remove_button(Glib::RefPtr bt) { + box_.remove(*bt); if (box_.get_children().empty()) { box_.get_style_context()->add_class("empty"); } diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 3c630d814..21be8f86e 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -1,16 +1,8 @@ #include "modules/wlr/workspace_manager.hpp" -#include -#include #include -#include -#include -#include -#include - #include "client.hpp" -#include "gtkmm/widget.h" #include "modules/wlr/workspace_manager_binding.hpp" namespace waybar::modules::wlr { @@ -53,7 +45,6 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar box_.get_style_context()->add_class(id); } box_.get_style_context()->add_class(MODULE_CLASS); - event_box_.add(box_); add_registry_listener(this); if (!workspace_manager_) { @@ -114,7 +105,11 @@ auto WorkspaceManager::sort_workspaces() -> void { std::sort(all_workspaces.begin(), all_workspaces.end(), workspace_comparator()); for (size_t i = 0; i < all_workspaces.size(); ++i) { - box_.reorder_child(all_workspaces[i].get()->get_button_ref(), i); + if (i == 0) + box_.reorder_child_at_start(all_workspaces[i].get()->get_button_ref()); + else + box_.reorder_child_after(all_workspaces[i].get()->get_button_ref(), + all_workspaces[i - 1].get()->get_button_ref()); } } @@ -161,6 +156,8 @@ auto WorkspaceManager::update() -> void { AModule::update(); } +WorkspaceManager::operator Gtk::Widget &() { return box_; } + WorkspaceManager::~WorkspaceManager() { if (!workspace_manager_) { return; @@ -253,9 +250,7 @@ auto WorkspaceGroup::creation_delayed() const -> bool { return workspace_manager_.creation_delayed(); } -auto WorkspaceGroup::add_button(Gtk::Button &button) -> void { - box_.pack_start(button, false, false); -} +auto WorkspaceGroup::add_button(Gtk::Button &button) -> void { box_.append(button); } WorkspaceGroup::~WorkspaceGroup() { if (!workspace_group_handle_) { @@ -366,7 +361,11 @@ auto WorkspaceGroup::commit() -> void { workspace_manager_.commit(); } auto WorkspaceGroup::sort_workspaces() -> void { std::sort(workspaces_.begin(), workspaces_.end(), workspace_manager_.workspace_comparator()); for (size_t i = 0; i < workspaces_.size(); ++i) { - box_.reorder_child(workspaces_[i]->get_button_ref(), i); + if (i == 0) + box_.reorder_child_at_start(workspaces_[i]->get_button_ref()); + else + box_.reorder_child_after(workspaces_[i]->get_button_ref(), + workspaces_[i - 1]->get_button_ref()); } } @@ -379,7 +378,8 @@ Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup & workspace_group_(workspace_group), workspace_handle_(workspace), id_(id), - name_(name) { + name_(name), + controllClick_{Gtk::GestureClick::create()} { if (workspace) { add_workspace_listener(workspace, this); } else { @@ -401,21 +401,21 @@ Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup & /* Handle click events if configured */ if (config_["on-click"].isString() || config_["on-click-middle"].isString() || config_["on-click-right"].isString()) { - button_.add_events(Gdk::BUTTON_PRESS_MASK); - button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked), - false); + controllClick_->set_propagation_phase(Gtk::PropagationPhase::TARGET); + controllClick_->signal_released().connect(sigc::mem_fun(*this, &Workspace::handleClick), false); + button_.add_controller(controllClick_); } - button_.set_relief(Gtk::RELIEF_NONE); - content_.set_center_widget(label_); - button_.add(content_); + button_.set_has_frame(false); + content_.append(label_); + button_.set_child(content_); if (!workspace_group.is_visible()) { return; } workspace_group.add_button(button_); - button_.show_all(); + button_.show(); } Workspace::~Workspace() { @@ -484,7 +484,7 @@ auto Workspace::handle_done() -> void { } if (workspace_group_.active_only() && (is_active() || is_urgent())) { - button_.show_all(); + button_.show(); } else if (workspace_group_.active_only() && !(is_active() || is_urgent())) { button_.hide(); } @@ -518,18 +518,20 @@ auto Workspace::get_icon() -> std::string { return name_; } -auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { +void Workspace::handleClick(int n_press, double dx, double dy) { + auto currButton{controllClick_->get_current_button()}; + std::string action; - if (config_["on-click"].isString() && bt->button == 1) { + if (config_["on-click"].isString() && currButton == 1) { action = config_["on-click"].asString(); - } else if (config_["on-click-middle"].isString() && bt->button == 2) { + } else if (config_["on-click-middle"].isString() && currButton == 2) { action = config_["on-click-middle"].asString(); - } else if (config_["on-click-right"].isString() && bt->button == 3) { + } else if (config_["on-click-right"].isString() && currButton == 3) { action = config_["on-click-right"].asString(); } if (action.empty()) - return true; + return; else if (action == "activate") { zext_workspace_handle_v1_activate(workspace_handle_); } else if (action == "close") { @@ -539,11 +541,9 @@ auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { } workspace_group_.commit(); - - return true; } -auto Workspace::show() -> void { button_.show_all(); } +auto Workspace::show() -> void { button_.show(); } auto Workspace::hide() -> void { button_.hide(); } auto Workspace::handle_name(const std::string &name) -> void { diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 73aac148b..5ddff8624 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -5,24 +5,20 @@ #include #include #include -#include #include -#include #include -#include -#include namespace waybar::util { AudioBackend::AudioBackend(std::function on_updated_cb, private_constructor_tag tag) - : mainloop_(nullptr), - mainloop_api_(nullptr), - context_(nullptr), - volume_(0), - muted_(false), - source_volume_(0), - source_muted_(false), + : mainloop_{nullptr}, + mainloop_api_{nullptr}, + context_{nullptr}, + volume_{0}, + muted_{false}, + source_volume_{0}, + source_muted_{false}, on_updated_cb_(std::move(on_updated_cb)) { mainloop_ = pa_threaded_mainloop_new(); if (mainloop_ == nullptr) { @@ -68,7 +64,7 @@ void AudioBackend::connectContext() { } void AudioBackend::contextStateCb(pa_context *c, void *data) { - auto *backend = static_cast(data); + auto *backend{static_cast(data)}; switch (pa_context_get_state(c)) { case PA_CONTEXT_TERMINATED: backend->mainloop_api_->quit(backend->mainloop_api_, 0); @@ -107,8 +103,8 @@ void AudioBackend::contextStateCb(pa_context *c, void *data) { */ void AudioBackend::subscribeCb(pa_context *context, pa_subscription_event_type_t type, uint32_t idx, void *data) { - unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; - unsigned operation = type & PA_SUBSCRIPTION_EVENT_TYPE_MASK; + unsigned facility{type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK}; + unsigned operation{type & PA_SUBSCRIPTION_EVENT_TYPE_MASK}; if (operation != PA_SUBSCRIPTION_EVENT_CHANGE) { return; } @@ -129,7 +125,7 @@ void AudioBackend::subscribeCb(pa_context *context, pa_subscription_event_type_t * Called in response to a volume change request */ void AudioBackend::volumeModifyCb(pa_context *c, int success, void *data) { - auto *backend = static_cast(data); + auto *backend{static_cast(data)}; if (success != 0) { pa_context_get_sink_info_by_index(backend->context_, backend->sink_idx_, sinkInfoCb, data); } @@ -146,7 +142,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i auto idle = i->state == PA_SINK_IDLE; spdlog::trace("Sink name {} Running:[{}] Idle:[{}]", i->name, running, idle); - auto *backend = static_cast(data); + auto *backend{static_cast(data)}; if (!backend->ignored_sinks_.empty()) { for (const auto &ignored_sink : backend->ignored_sinks_) { @@ -181,15 +177,15 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i if (backend->current_sink_name_ == i->name) { backend->pa_volume_ = i->volume; - float volume = - static_cast(pa_cvolume_avg(&(backend->pa_volume_))) / float{PA_VOLUME_NORM}; + float volume{static_cast(pa_cvolume_avg(&(backend->pa_volume_))) / + float{PA_VOLUME_NORM}}; backend->sink_idx_ = i->index; backend->volume_ = std::round(volume * 100.0F); backend->muted_ = i->mute != 0; backend->desc_ = i->description; backend->monitor_ = i->monitor_source_name; backend->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; - if (const auto *ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) { + if (const auto ff{pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)}) { backend->form_factor_ = ff; } else { backend->form_factor_ = ""; @@ -203,9 +199,9 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i */ void AudioBackend::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i, int /*eol*/, void *data) { - auto *backend = static_cast(data); + auto *backend{static_cast(data)}; if (i != nullptr && backend->default_source_name_ == i->name) { - auto source_volume = static_cast(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM}; + auto source_volume{static_cast(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM}}; backend->source_volume_ = std::round(source_volume * 100.0F); backend->source_idx_ = i->index; backend->source_muted_ = i->mute != 0; @@ -220,7 +216,7 @@ void AudioBackend::sourceInfoCb(pa_context * /*context*/, const pa_source_info * * used to find the default PulseAudio sink. */ void AudioBackend::serverInfoCb(pa_context *context, const pa_server_info *i, void *data) { - auto *backend = static_cast(data); + auto *backend{static_cast(data)}; backend->current_sink_name_ = i->default_sink_name; backend->default_sink_name = i->default_sink_name; backend->default_source_name_ = i->default_source_name; @@ -230,7 +226,7 @@ void AudioBackend::serverInfoCb(pa_context *context, const pa_server_info *i, vo } void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t max_volume) { - double volume_tick = static_cast(PA_VOLUME_NORM) / 100; + double volume_tick{static_cast(PA_VOLUME_NORM) / 100}; pa_cvolume pa_volume = pa_volume_; volume = std::clamp(volume, min_volume, max_volume); @@ -240,7 +236,7 @@ void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t m } void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t max_volume) { - double volume_tick = static_cast(PA_VOLUME_NORM) / 100; + double volume_tick{static_cast(PA_VOLUME_NORM) / 100}; pa_volume_t change = volume_tick; pa_cvolume pa_volume = pa_volume_; diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index bb102cd93..fb602a949 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -1,12 +1,9 @@ #include "util/backlight_backend.hpp" #include -#include #include #include -#include -#include namespace { class FileDescriptor { @@ -152,8 +149,8 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, // Connect to the login interface login_proxy_ = Gio::DBus::Proxy::create_for_bus_sync( - Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.login1", - "/org/freedesktop/login1/session/self", "org.freedesktop.login1.Session"); + Gio::DBus::BusType::SYSTEM, "org.freedesktop.login1", "/org/freedesktop/login1/session/self", + "org.freedesktop.login1.Session"); udev_thread_ = [this] { std::unique_ptr udev{udev_new()}; @@ -255,7 +252,7 @@ void BacklightBackend::set_brightness(const std::string &preferred_device, Chang if (best != nullptr) { const auto max = best->get_max(); - const auto abs_step = static_cast(round(step * max / 100.0F)); + const auto abs_step = static_cast(std::round(step * max / 100.0F)); const int new_brightness = change_type == ChangeType::Increase ? best->get_actual() + abs_step : best->get_actual() - abs_step; diff --git a/src/util/css_reload_helper.cpp b/src/util/css_reload_helper.cpp index 274bdeedd..a0fd8dd72 100644 --- a/src/util/css_reload_helper.cpp +++ b/src/util/css_reload_helper.cpp @@ -84,10 +84,10 @@ void waybar::CssReloadHelper::monitorChanges() { void waybar::CssReloadHelper::handleFileChange(Glib::RefPtr const& file, Glib::RefPtr const& other_type, - Gio::FileMonitorEvent event_type) { + Gio::FileMonitor::Event event_type) { // Multiple events are fired on file changed (attributes, write, changes done hint, etc.), only // fire for one - if (event_type == Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { + if (event_type == Gio::FileMonitor::Event::CHANGES_DONE_HINT) { spdlog::debug("Reloading style, file changed: {}", file->get_path()); m_callback(); } diff --git a/src/util/gtk_icon.cpp b/src/util/gtk_icon.cpp deleted file mode 100644 index 5dd741f9a..000000000 --- a/src/util/gtk_icon.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "util/gtk_icon.hpp" - -/* We need a global mutex for accessing the object returned by Gtk::IconTheme::get_default() - * because it always returns the same object across different threads, and concurrent - * access can cause data corruption and lead to invalid memory access and crashes. - * Even concurrent calls that seem read only such as has_icon can cause issues because - * the GTK lib may update the internal icon cache on this calls. - */ - -std::mutex DefaultGtkIconThemeWrapper::default_theme_mutex; - -bool DefaultGtkIconThemeWrapper::has_icon(const std::string& value) { - const std::lock_guard lock(default_theme_mutex); - - return Gtk::IconTheme::get_default()->has_icon(value); -} - -Glib::RefPtr DefaultGtkIconThemeWrapper::load_icon(const char* name, int tmp_size, - Gtk::IconLookupFlags flags) { - const std::lock_guard lock(default_theme_mutex); - - auto default_theme = Gtk::IconTheme::get_default(); - default_theme->rescan_if_needed(); - return default_theme->load_icon(name, tmp_size, flags); -} diff --git a/src/util/pipewire/pipewire_backend.cpp b/src/util/pipewire/pipewire_backend.cpp index 5bb7c19a1..e817a9a4e 100644 --- a/src/util/pipewire/pipewire_backend.cpp +++ b/src/util/pipewire/pipewire_backend.cpp @@ -1,7 +1,5 @@ #include "util/pipewire/pipewire_backend.hpp" -#include "util/pipewire/privacy_node_info.hpp" - namespace waybar::util::PipewireBackend { static void getNodeInfo(void *data_, const struct pw_node_info *info) { diff --git a/src/util/pipewire/privacy_node_info.cpp b/src/util/pipewire/privacy_node_info.cpp index 739dc528f..c4bce92b2 100644 --- a/src/util/pipewire/privacy_node_info.cpp +++ b/src/util/pipewire/privacy_node_info.cpp @@ -15,12 +15,12 @@ std::string PrivacyNodeInfo::getName() { return name; } -std::string PrivacyNodeInfo::getIconName() { +std::string PrivacyNodeInfo::getIconName(const Glib::RefPtr theme) { const std::vector names{&application_icon_name, &pipewire_access_portal_app_id, &application_name, &node_name}; std::string name = "application-x-executable-symbolic"; for (const auto &item : names) { - if (item != nullptr && !item->empty() && DefaultGtkIconThemeWrapper::has_icon(*item)) { + if (item != nullptr && !item->empty() && theme->has_icon(*item)) { return *item; } } diff --git a/src/util/portal.cpp b/src/util/portal.cpp index 5874871b9..ed4bb06c8 100644 --- a/src/util/portal.cpp +++ b/src/util/portal.cpp @@ -36,7 +36,7 @@ auto fmt::formatter::format(waybar::Appearance c, format_con } waybar::Portal::Portal() - : DBus::Proxy(DBus::Connection::get_sync(DBus::BusType::BUS_TYPE_SESSION), PORTAL_BUS_NAME, + : DBus::Proxy(DBus::Connection::get_sync(DBus::BusType::SESSION), PORTAL_BUS_NAME, PORTAL_OBJ_PATH, PORTAL_INTERFACE), currentMode(Appearance::UNKNOWN) { refreshAppearance(); diff --git a/src/util/regex_collection.cpp b/src/util/regex_collection.cpp index 929e67cd6..4cb37447a 100644 --- a/src/util/regex_collection.cpp +++ b/src/util/regex_collection.cpp @@ -3,8 +3,6 @@ #include #include -#include - namespace waybar::util { int default_priority_function(std::string& key) { return 0; } diff --git a/src/util/rfkill.cpp b/src/util/rfkill.cpp index 61be7c5f6..f6b1c13b5 100644 --- a/src/util/rfkill.cpp +++ b/src/util/rfkill.cpp @@ -20,11 +20,7 @@ #include #include -#include #include -#include - -#include waybar::util::Rfkill::Rfkill(const enum rfkill_type rfkill_type) : rfkill_type_(rfkill_type) { fd_ = open("/dev/rfkill", O_RDONLY); @@ -32,15 +28,16 @@ waybar::util::Rfkill::Rfkill(const enum rfkill_type rfkill_type) : rfkill_type_( spdlog::error("Can't open RFKILL control device"); return; } - int rc = fcntl(fd_, F_SETFL, O_NONBLOCK); + int rc{fcntl(fd_, F_SETFL, O_NONBLOCK)}; if (rc < 0) { spdlog::error("Can't set RFKILL control device to non-blocking: {}", errno); close(fd_); fd_ = -1; return; } - Glib::signal_io().connect(sigc::mem_fun(*this, &Rfkill::on_event), fd_, - Glib::IO_IN | Glib::IO_ERR | Glib::IO_HUP); + Glib::signal_io().connect( + sigc::mem_fun(*this, &Rfkill::on_event), fd_, + Glib::IOCondition::IO_IN | Glib::IOCondition::IO_ERR | Glib::IOCondition::IO_HUP); } waybar::util::Rfkill::~Rfkill() { @@ -50,7 +47,7 @@ waybar::util::Rfkill::~Rfkill() { } bool waybar::util::Rfkill::on_event(Glib::IOCondition cond) { - if (cond & Glib::IO_IN) { + if ((cond & Glib::IOCondition::IO_IN) == Glib::IOCondition::IO_IN) { struct rfkill_event event; ssize_t len; diff --git a/subprojects/gtk-layer-shell.wrap b/subprojects/gtk-layer-shell.wrap deleted file mode 100644 index fc0ddf743..000000000 --- a/subprojects/gtk-layer-shell.wrap +++ /dev/null @@ -1,5 +0,0 @@ -[wrap-file] -directory = gtk-layer-shell-0.9.0 -source_filename = gtk-layer-shell-0.9.0.tar.gz -source_hash = 3809e5565d9ed02e44bb73787ff218523e8760fef65830afe60ea7322e22da1c -source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.9.0/gtk-layer-shell-0.9.0.tar.gz diff --git a/subprojects/gtk4-layer-shell.wrap b/subprojects/gtk4-layer-shell.wrap new file mode 100644 index 000000000..70c5a2f3a --- /dev/null +++ b/subprojects/gtk4-layer-shell.wrap @@ -0,0 +1,8 @@ +[wrap-file] +directory = gtk4-layer-shell-1.0.3 +source_filename = gtk4-layer-shell-1.0.3.tar.gz +source_hash = 4d669c30b3dbc68ad69ade9752e6ebbe7be132db21a5a4734d42bc09c5481c34 +source_url = https://github.com/wmww/gtk4-layer-shell/archive/v1.0.3/gtk4-layer-shell-1.0.3.tar.gz + +[provide] +gtk-layer-shell = gtk_layer_shell