From bad6cfae6f638b166d55ad543050c69286038840 Mon Sep 17 00:00:00 2001 From: Bruce Blore Date: Sat, 22 Apr 2023 23:43:04 -0700 Subject: [PATCH 001/364] Added option to calculate battery percentage as total_energy * 100 / total_energy_full --- src/modules/battery.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index abd1240c..83e7398c 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -533,6 +533,13 @@ const std::tuple waybar::modules::Battery::g } } + // Handle weighted-average + if ((config_["weighted-average"].isBool() ? config_["weighted-average"].asBool() : false) && + total_energy_exists && total_energy_full_exists) { + if (total_energy_full > 0.0f) + calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full); + } + // Handle design-capacity if ((config_["design-capacity"].isBool() ? config_["design-capacity"].asBool() : false) && total_energy_exists && total_energy_full_design_exists) { From b91adc9f2920d1988c06c8aaa3322be03bebbb6e Mon Sep 17 00:00:00 2001 From: Jacob Birkett Date: Mon, 31 Jul 2023 09:37:33 -0700 Subject: [PATCH 002/364] flake: fix infrec in package overlay --- flake.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 97f4ed57..a063b824 100644 --- a/flake.nix +++ b/flake.nix @@ -30,14 +30,14 @@ ]); in { - overlays.default = _: prev: { - waybar = prev.callPackage ./nix/default.nix { + overlays.default = final: prev: { + waybar = final.callPackage ./nix/default.nix { version = prev.waybar.version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); }; }; packages = genSystems (system: - (self.overlays.default null pkgsFor.${system}) + (self.overlays.default pkgsFor.${system} pkgsFor.${system}) // { default = self.packages.${system}.waybar; }); From ce2ede70f2010147d8fa02c7525cd3669c4223c7 Mon Sep 17 00:00:00 2001 From: Marshall Moats Date: Tue, 1 Aug 2023 15:55:37 -0500 Subject: [PATCH 003/364] Fix logical errors, formatting error --- src/modules/hyprland/workspaces.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 787d812d..6433338d 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -100,7 +100,7 @@ void Workspaces::onEvent(const std::string &ev) { for (Json::Value workspace_json : workspaces_json) { if (workspace_json["name"].asString() == payload && (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (show_special() || workspace_json["name"].asString().starts_with("special"))) { + (show_special() || !workspace_json["name"].asString().starts_with("special"))) { workspaces_to_create_.push_back(workspace_json); break; } @@ -266,7 +266,7 @@ void Workspaces::init() { const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (Json::Value workspace_json : workspaces_json) { if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (workspace_json["name"].asString().starts_with("special") || show_special())) + (!workspace_json["name"].asString().starts_with("special") || show_special())) create_workspace(workspace_json); } @@ -325,7 +325,8 @@ void Workspace::update(const std::string &format, const std::string &icon) { add_or_remove_class(style_context, is_empty(), "persistent"); label_.set_markup( - fmt::format(fmt::runtime(format), fmt::arg("name", name()), fmt::arg("icon", icon))); + fmt::format(fmt::runtime(format), fmt::arg("id", id()), + fmt::arg("name", name()), fmt::arg("icon", icon))); } void Workspaces::sort_workspaces() { From 2bcd0eb09fe23a6984a97c15d2bb598f03423a1f Mon Sep 17 00:00:00 2001 From: Marshall Moats <46292427+marshallmoats@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:02:50 -0500 Subject: [PATCH 004/364] Change default workspace 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 6433338d..14cf8528 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -16,7 +16,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { Json::Value config_format = config["format"]; - format_ = config_format.isString() ? config_format.asString() : "{id}"; + format_ = config_format.isString() ? config_format.asString() : "{name}"; with_icon_ = format_.find("{icon}") != std::string::npos; if (with_icon_ && icons_map_.empty()) { @@ -325,8 +325,7 @@ void Workspace::update(const std::string &format, const std::string &icon) { add_or_remove_class(style_context, is_empty(), "persistent"); label_.set_markup( - fmt::format(fmt::runtime(format), fmt::arg("id", id()), - fmt::arg("name", name()), fmt::arg("icon", icon))); + fmt::format(fmt::runtime(format), fmt::arg("name", name()), fmt::arg("icon", icon))); } void Workspaces::sort_workspaces() { From e40860c3e9f1c4484faa611a33662b21bebe8725 Mon Sep 17 00:00:00 2001 From: Marshall Moats Date: Tue, 8 Aug 2023 17:17:58 -0500 Subject: [PATCH 005/364] keep id --- src/modules/hyprland/workspaces.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 14cf8528..3b4bd5f8 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -325,7 +325,8 @@ void Workspace::update(const std::string &format, const std::string &icon) { add_or_remove_class(style_context, is_empty(), "persistent"); label_.set_markup( - fmt::format(fmt::runtime(format), fmt::arg("name", name()), fmt::arg("icon", icon))); + fmt::format(fmt::runtime(format), fmt::arg("id", id()), + fmt::arg("name", name()), fmt::arg("icon", icon))); } void Workspaces::sort_workspaces() { From 2ba11e840139e6dbc2567068be33cd807388c5fd Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 10 Aug 2023 20:00:01 +0200 Subject: [PATCH 006/364] Hyprland/workspaces: use named icons instead of searching by id --- 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 3b4bd5f8..bd2586e9 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -383,7 +383,7 @@ std::string &Workspace::select_icon(std::map &icons_ma } } - auto named_icon_it = icons_map.find(std::to_string(id())); + auto named_icon_it = icons_map.find(name()); if (named_icon_it != icons_map.end()) { return named_icon_it->second; } From 6f7e7c51997c083ae1d1437a2e4042e8579ad562 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 10 Aug 2023 20:37:42 +0200 Subject: [PATCH 007/364] formatting --- 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 bd2586e9..16894842 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -324,9 +324,8 @@ void Workspace::update(const std::string &format, const std::string &icon) { add_or_remove_class(style_context, is_special(), "special"); add_or_remove_class(style_context, is_empty(), "persistent"); - label_.set_markup( - fmt::format(fmt::runtime(format), fmt::arg("id", id()), - fmt::arg("name", name()), fmt::arg("icon", icon))); + label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), + fmt::arg("name", name()), fmt::arg("icon", icon))); } void Workspaces::sort_workspaces() { From 46f5034030ac17e7aea9afd2250320ed32278afe Mon Sep 17 00:00:00 2001 From: KosmX Date: Sun, 13 Aug 2023 18:20:43 +0200 Subject: [PATCH 008/364] Add button release events to eventMap --- include/AModule.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/AModule.hpp b/include/AModule.hpp index 9b16076b..006546ee 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -44,18 +44,23 @@ class AModule : public IModule { 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"}}; }; From 3db5673e7029fed54d804efecc85ab122f1dd29f Mon Sep 17 00:00:00 2001 From: KosmX Date: Sun, 13 Aug 2023 18:35:35 +0200 Subject: [PATCH 009/364] register key hander --- src/AModule.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 2626cd89..5a8fcd0d 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -27,20 +27,18 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: } // configure events' user commands - if (enable_click) { + + bool hasEvent = std::find_if(eventMap_.cbegin(), eventMap_.cend(), + [&config](const auto& eventEntry) { + return config[eventEntry.second].isString(); + }) != eventMap_.cend(); + + if (enable_click || hasEvent) { event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); - } else { - std::map, std::string>::const_iterator it{eventMap_.cbegin()}; - while (it != eventMap_.cend()) { - if (config_[it->second].isString()) { - event_box_.add_events(Gdk::BUTTON_PRESS_MASK); - event_box_.signal_button_press_event().connect( - sigc::mem_fun(*this, &AModule::handleToggle)); - break; - } - ++it; - } + // register key release + event_box_.add_events(Gdk::BUTTON_RELEASE_MASK); + event_box_.signal_button_release_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); } if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || enable_scroll) { event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); From 8cf676176d33f4a6528ad7365d031ad6b16e9e6c Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Mon, 14 Aug 2023 11:17:49 +0200 Subject: [PATCH 010/364] Updated hyprland-workspaces manpage --- man/waybar-hyprland-workspaces.5.scd | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index c3deb52e..4f46fd7e 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -45,11 +45,13 @@ Additional to workspace name matching, the following *format-icons* can be set. - *default*: Will be shown, when no string match is found. - *active*: Will be shown, when workspace is active +- *special*: Will be shown on non-active special workspaces +- *persistent*: Will be shown on non-active persistent workspaces # EXAMPLES ``` -"wlr/workspaces": { +"hyprland/workspaces": { "format": "{name}: {icon}", "format-icons": { "1": "", @@ -60,8 +62,10 @@ Additional to workspace name matching, the following *format-icons* can be set. "active": "", "default": "" }, - "all-outputs": false, - "show-special": false + "persistent_workspaces": { + "*": 5, // 5 workspaces by default on every monitor + "HDMI-A-1": 3 // but only three on HDMI-A-1 + } } ``` @@ -70,3 +74,5 @@ Additional to workspace name matching, the following *format-icons* can be set. - *#workspaces* - *#workspaces button* - *#workspaces button.active* +- *#workspaces button.persistent* +- *#workspaces button.special* From 29cebaa0a7de5e6fb4a2edcc2d5597b0c6f8030c Mon Sep 17 00:00:00 2001 From: Frank Ebel Date: Mon, 14 Aug 2023 12:42:33 +0200 Subject: [PATCH 011/364] docs: fix Arch repository name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 718ceb44..ac9718b5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Waybar [![Licence](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Paypal Donate](https://img.shields.io/badge/Donate-Paypal-2244dd.svg)](https://paypal.me/ARouillard)
![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png) > Highly customizable Wayland bar for Sway and Wlroots based compositors.
-> Available in Arch [community](https://www.archlinux.org/packages/extra/x86_64/waybar/) or +> Available in Arch [extra](https://www.archlinux.org/packages/extra/x86_64/waybar/) or [AUR](https://aur.archlinux.org/packages/waybar-git/), [Gentoo](https://packages.gentoo.org/packages/gui-apps/waybar), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar).
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)* From db1d859881efeaa75680caaebbd0f34d2f2160ee Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 14 Aug 2023 14:20:08 +0200 Subject: [PATCH 012/364] fix: lint --- include/modules/clock.hpp | 6 ++---- src/AModule.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 0fcd0af2..3aa5c9bc 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -59,10 +59,8 @@ class Clock final : public ALabel { std::string cldMonCached_{}; date::day cldBaseDay_{0}; /*Calendar functions*/ - auto get_calendar(const date::year_month_day& today, - const date::year_month_day& ymd, - const date::time_zone* tz) - -> const std::string; + auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd, + const date::time_zone* tz) -> const std::string; /*Clock actions*/ void cldModeSwitch(); void cldShift_up(); diff --git a/src/AModule.cpp b/src/AModule.cpp index 5a8fcd0d..b405fede 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -28,11 +28,11 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: // configure events' user commands - bool hasEvent = std::find_if(eventMap_.cbegin(), eventMap_.cend(), - [&config](const auto& eventEntry) { - return config[eventEntry.second].isString(); - }) != eventMap_.cend(); - + bool hasEvent = + std::find_if(eventMap_.cbegin(), eventMap_.cend(), [&config](const auto& eventEntry) { + return config[eventEntry.second].isString(); + }) != eventMap_.cend(); + if (enable_click || hasEvent) { event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); From e90c66a10204bc6053ee295cf7024fb24f9f2266 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 14 Aug 2023 15:33:52 +0200 Subject: [PATCH 013/364] chore: 0.9.21 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 18b056ad..cf3230e3 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.20', + version: '0.9.21', license: 'MIT', meson_version: '>= 0.50.0', default_options : [ From 57544fe6944da7cccd2adbd8137a5e1ef5e5a8dd Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 15 Aug 2023 00:45:09 +0900 Subject: [PATCH 014/364] fix: typo in taskbar.cpp ocurred -> occurred --- 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 9e09d7a9..f4b137c0 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -517,7 +517,7 @@ void Task::handle_closed() { 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 ocurred in case it becomes a drag */ + /* 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; From c8237437d27fa255f4d8a380dec6db872d139e7d Mon Sep 17 00:00:00 2001 From: Jan Palus Date: Tue, 15 Aug 2023 20:57:07 +0200 Subject: [PATCH 015/364] Explicitly cast ustring to string when passing to fmt don't rely on implicit conversion which is no longer present in fmt 10.1.0 Fixes #2403 --- include/util/format.hpp | 2 +- src/modules/sni/item.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/util/format.hpp b/include/util/format.hpp index 00b6a31c..069d8897 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -93,7 +93,7 @@ template <> struct formatter : formatter { template auto format(const Glib::ustring& value, FormatContext& ctx) { - return formatter::format(value, ctx); + return formatter::format(static_cast(value), ctx); } }; } // namespace fmt diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 9d3fc4bd..871621fb 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -22,7 +22,7 @@ struct fmt::formatter : formatter { template auto format(const Glib::VariantBase& value, FormatContext& ctx) { if (is_printable(value)) { - return formatter::format(value.print(), ctx); + return formatter::format(static_cast(value.print()), ctx); } else { return formatter::format(value.get_type_string(), ctx); } From b5ea14c896f3659b333cc474c8a491672cbc6465 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Aug 2023 13:31:24 +0200 Subject: [PATCH 016/364] revert: release event --- include/AModule.hpp | 5 ----- src/AModule.cpp | 22 ++++++++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 006546ee..9b16076b 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -44,23 +44,18 @@ class AModule : public IModule { 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"}}; }; diff --git a/src/AModule.cpp b/src/AModule.cpp index b405fede..2626cd89 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -27,18 +27,20 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: } // configure events' user commands - - bool hasEvent = - std::find_if(eventMap_.cbegin(), eventMap_.cend(), [&config](const auto& eventEntry) { - return config[eventEntry.second].isString(); - }) != eventMap_.cend(); - - if (enable_click || hasEvent) { + if (enable_click) { event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); - // register key release - event_box_.add_events(Gdk::BUTTON_RELEASE_MASK); - event_box_.signal_button_release_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); + } else { + std::map, std::string>::const_iterator it{eventMap_.cbegin()}; + while (it != eventMap_.cend()) { + if (config_[it->second].isString()) { + event_box_.add_events(Gdk::BUTTON_PRESS_MASK); + event_box_.signal_button_press_event().connect( + sigc::mem_fun(*this, &AModule::handleToggle)); + break; + } + ++it; + } } if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || enable_scroll) { event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); From e30fba0b8f875c7f35e3173be2b9f6f3ffe3641e Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Aug 2023 13:34:05 +0200 Subject: [PATCH 017/364] chore: 0.9.22 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index cf3230e3..e71807ec 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.21', + version: '0.9.22', license: 'MIT', meson_version: '>= 0.50.0', default_options : [ From abd7a0cf25ba081d35de004c8a43d48265a48505 Mon Sep 17 00:00:00 2001 From: Jan Palus Date: Wed, 16 Aug 2023 14:32:48 +0200 Subject: [PATCH 018/364] Fix simpleclock module compilation with recent fmt both `format_` and `tooltip_format` are dynamically provided formats so wrap them in `fmt::runtime()` --- src/modules/simpleclock.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/simpleclock.cpp b/src/modules/simpleclock.cpp index 27c7ac77..b6a96ecc 100644 --- a/src/modules/simpleclock.cpp +++ b/src/modules/simpleclock.cpp @@ -18,13 +18,13 @@ auto waybar::modules::Clock::update() -> void { tzset(); // Update timezone information auto now = std::chrono::system_clock::now(); auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); - auto text = fmt::format(format_, localtime); + auto text = fmt::format(fmt::runtime(format_), localtime); label_.set_markup(text); if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { auto tooltip_format = config_["tooltip-format"].asString(); - auto tooltip_text = fmt::format(tooltip_format, localtime); + auto tooltip_text = fmt::format(fmt::runtime(tooltip_format), localtime); label_.set_tooltip_text(tooltip_text); } else { label_.set_tooltip_text(text); From 22817089db84f759b1d817765573c04213379603 Mon Sep 17 00:00:00 2001 From: PucklaJ Date: Wed, 16 Aug 2023 15:34:06 +0200 Subject: [PATCH 019/364] Add no-controller format to bluetooth module --- include/modules/bluetooth.hpp | 5 ++- man/waybar-bluetooth.5.scd | 8 ++++ src/modules/bluetooth.cpp | 75 ++++++++++++++++++++--------------- 3 files changed, 54 insertions(+), 34 deletions(-) diff --git a/include/modules/bluetooth.hpp b/include/modules/bluetooth.hpp index 18481e31..89658dcf 100644 --- a/include/modules/bluetooth.hpp +++ b/include/modules/bluetooth.hpp @@ -59,7 +59,8 @@ class Bluetooth : public ALabel { auto getDeviceProperties(GDBusObject*, DeviceInfo&) -> bool; auto getControllerProperties(GDBusObject*, ControllerInfo&) -> bool; - auto findCurController(ControllerInfo&) -> bool; + // Returns std::nullopt if no controller could be found + auto findCurController() -> std::optional; auto findConnectedDevices(const std::string&, std::vector&) -> void; #ifdef WANT_RFKILL @@ -68,7 +69,7 @@ class Bluetooth : public ALabel { const std::unique_ptr manager_; std::string state_; - ControllerInfo cur_controller_; + std::optional cur_controller_; std::vector connected_devices_; DeviceInfo cur_focussed_device_; std::string device_enumerate_; diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index cca7c35f..4dff9bf1 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -42,6 +42,10 @@ Addressed by *bluetooth* typeof: string ++ This format is used when the displayed controller is connected to at least 1 device. +*format-no-controller*: ++ + typeof: string ++ + This format is used when no bluetooth controller could be found + *format-icons*: ++ typeof: array/object ++ Based on the current battery percentage (see section *EXPERIMENTAL BATTERY PERCENTAGE FEATURE*), the corresponding icon gets selected. ++ @@ -113,6 +117,10 @@ Addressed by *bluetooth* typeof: string ++ This format is used when the displayed controller is connected to at least 1 device. +*tooltip-format-no-controller*: ++ + typeof: string ++ + This format is used when no bluetooth controller could be found + *tooltip-format-enumerate-connected*: ++ 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. diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index c3a25473..51c7cc39 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -92,9 +92,9 @@ 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 selcected stays unchanged + // NOTE: assumption made that the controller that is selected stays unchanged // for duration of the module - if (!findCurController(cur_controller_)) { + if (cur_controller_ = findCurController(); !cur_controller_) { if (config_["controller-alias"].isString()) { spdlog::error("findCurController() failed: no bluetooth controller found with alias '{}'", config_["controller-alias"].asString()); @@ -102,15 +102,20 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& spdlog::error("findCurController() failed: no bluetooth controller found"); } event_box_.hide(); + update(); return; } - findConnectedDevices(cur_controller_.path, connected_devices_); + if (cur_controller_) { + // These calls 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(), "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))); #endif @@ -144,12 +149,16 @@ auto waybar::modules::Bluetooth::update() -> void { std::string state; std::string tooltip_format; - if (!cur_controller_.powered) - state = "off"; - else if (!connected_devices_.empty()) - state = "connected"; - else - state = "on"; + if (cur_controller_) { + if (!cur_controller_->powered) + state = "off"; + else if (!connected_devices_.empty()) + state = "connected"; + else + state = "on"; + } else { + state = "no-controller"; + } #ifdef WANT_RFKILL if (rfkill_.getState()) state = "disabled"; #endif @@ -196,9 +205,9 @@ auto waybar::modules::Bluetooth::update() -> void { label_.get_style_context()->remove_class(style_class); } }; - update_style_context("discoverable", cur_controller_.discoverable); - update_style_context("discovering", cur_controller_.discovering); - update_style_context("pairable", cur_controller_.pairable); + update_style_context("discoverable", cur_controller_ ? cur_controller_->discoverable : false); + update_style_context("discovering", cur_controller_ ? cur_controller_->discovering : false); + update_style_context("pairable", cur_controller_ ? cur_controller_->pairable : false); if (!state_.empty()) { update_style_context(state_, false); } @@ -208,9 +217,9 @@ auto waybar::modules::Bluetooth::update() -> void { label_.set_markup(fmt::format( fmt::runtime(format_), fmt::arg("status", state_), fmt::arg("num_connections", connected_devices_.size()), - fmt::arg("controller_address", cur_controller_.address), - fmt::arg("controller_address_type", cur_controller_.address_type), - fmt::arg("controller_alias", cur_controller_.alias), + fmt::arg("controller_address", cur_controller_ ? cur_controller_->address : "null"), + fmt::arg("controller_address_type", cur_controller_ ? cur_controller_->address_type : "null"), + fmt::arg("controller_alias", cur_controller_ ? cur_controller_->alias : "null"), fmt::arg("device_address", cur_focussed_device_.address), fmt::arg("device_address_type", cur_focussed_device_.address_type), fmt::arg("device_alias", cur_focussed_device_.alias), fmt::arg("icon", icon_label), @@ -250,9 +259,10 @@ auto waybar::modules::Bluetooth::update() -> void { label_.set_tooltip_text(fmt::format( fmt::runtime(tooltip_format), fmt::arg("status", state_), fmt::arg("num_connections", connected_devices_.size()), - fmt::arg("controller_address", cur_controller_.address), - fmt::arg("controller_address_type", cur_controller_.address_type), - fmt::arg("controller_alias", cur_controller_.alias), + fmt::arg("controller_address", cur_controller_ ? cur_controller_->address : "null"), + fmt::arg("controller_address_type", + cur_controller_ ? cur_controller_->address_type : "null"), + fmt::arg("controller_alias", cur_controller_ ? cur_controller_->alias : "null"), fmt::arg("device_address", cur_focussed_device_.address), fmt::arg("device_address_type", cur_focussed_device_.address_type), fmt::arg("device_alias", cur_focussed_device_.alias), fmt::arg("icon", icon_tooltip), @@ -292,8 +302,8 @@ auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged( Bluetooth* bt = static_cast(user_data); if (interface_name == "org.bluez.Adapter1") { - if (object_path == bt->cur_controller_.path) { - bt->getControllerProperties(G_DBUS_OBJECT(object_proxy), bt->cur_controller_); + if (object_path == bt->cur_controller_->path) { + bt->getControllerProperties(G_DBUS_OBJECT(object_proxy), *bt->cur_controller_); bt->dp.emit(); } } else if (interface_name == "org.bluez.Device1" || interface_name == "org.bluez.Battery1") { @@ -378,22 +388,23 @@ auto waybar::modules::Bluetooth::getControllerProperties(GDBusObject* object, return false; } -auto waybar::modules::Bluetooth::findCurController(ControllerInfo& controller_info) -> bool { - bool found_controller = false; +auto waybar::modules::Bluetooth::findCurController() -> std::optional { + std::optional controller_info; 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); - if (getControllerProperties(object, controller_info) && + ControllerInfo info; + if (getControllerProperties(object, info) && (!config_["controller-alias"].isString() || - config_["controller-alias"].asString() == controller_info.alias)) { - found_controller = true; + config_["controller-alias"].asString() == info.alias)) { + controller_info = std::move(info); break; } } g_list_free_full(objects, g_object_unref); - return found_controller; + return controller_info; } auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path, @@ -404,7 +415,7 @@ auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_con GDBusObject* object = G_DBUS_OBJECT(l->data); DeviceInfo device; if (getDeviceProperties(object, device) && device.connected && - device.paired_controller == cur_controller_.path) { + device.paired_controller == cur_controller_->path) { connected_devices.push_back(device); } } From 70bc318a014b4c90d1768bc3c313d481cd213ea9 Mon Sep 17 00:00:00 2001 From: PucklaJ Date: Wed, 16 Aug 2023 16:30:59 +0200 Subject: [PATCH 020/364] Fix bluetooth module still being visible if format is empty --- src/modules/bluetooth.cpp | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 51c7cc39..9e207507 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -101,11 +101,8 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& } else { spdlog::error("findCurController() failed: no bluetooth controller found"); } - event_box_.hide(); update(); - return; - } - if (cur_controller_) { + } else { // These calls only make sense if a controller could be found findConnectedDevices(cur_controller_->path, connected_devices_); g_signal_connect(manager_.get(), "interface-proxy-properties-changed", @@ -114,11 +111,11 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& 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(); } @@ -196,8 +193,6 @@ auto waybar::modules::Bluetooth::update() -> void { tooltip_format = config_["tooltip-format"].asString(); } - format_.empty() ? event_box_.hide() : event_box_.show(); - auto update_style_context = [this](const std::string& style_class, bool in_next_state) { if (in_next_state && !label_.get_style_context()->has_class(style_class)) { label_.get_style_context()->add_class(style_class); @@ -214,16 +209,23 @@ auto waybar::modules::Bluetooth::update() -> void { update_style_context(state, true); state_ = state; - label_.set_markup(fmt::format( - fmt::runtime(format_), fmt::arg("status", state_), - fmt::arg("num_connections", connected_devices_.size()), - fmt::arg("controller_address", cur_controller_ ? cur_controller_->address : "null"), - fmt::arg("controller_address_type", cur_controller_ ? cur_controller_->address_type : "null"), - fmt::arg("controller_alias", cur_controller_ ? cur_controller_->alias : "null"), - fmt::arg("device_address", cur_focussed_device_.address), - fmt::arg("device_address_type", cur_focussed_device_.address_type), - fmt::arg("device_alias", cur_focussed_device_.alias), fmt::arg("icon", icon_label), - fmt::arg("device_battery_percentage", cur_focussed_device_.battery_percentage.value_or(0)))); + if (format_.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + label_.set_markup(fmt::format( + fmt::runtime(format_), fmt::arg("status", state_), + fmt::arg("num_connections", connected_devices_.size()), + fmt::arg("controller_address", cur_controller_ ? cur_controller_->address : "null"), + fmt::arg("controller_address_type", + cur_controller_ ? cur_controller_->address_type : "null"), + fmt::arg("controller_alias", cur_controller_ ? cur_controller_->alias : "null"), + fmt::arg("device_address", cur_focussed_device_.address), + fmt::arg("device_address_type", cur_focussed_device_.address_type), + fmt::arg("device_alias", cur_focussed_device_.alias), fmt::arg("icon", icon_label), + fmt::arg("device_battery_percentage", + cur_focussed_device_.battery_percentage.value_or(0)))); + } if (tooltipEnabled()) { bool tooltip_enumerate_connections_ = config_["tooltip-format-enumerate-connected"].isString(); From 718db716384076f2d941004f42c82cbc05588277 Mon Sep 17 00:00:00 2001 From: KosmX Date: Wed, 16 Aug 2023 17:11:44 +0200 Subject: [PATCH 021/364] Refactor enable click condition This shouldn't change behaviour. --- src/AModule.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 2626cd89..28feb4e1 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -27,20 +27,16 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: } // configure events' user commands - if (enable_click) { + // hasUserEvent is true if any element from eventMap_ is satisfying the condition in the lambda function + bool hasUserEvent = + 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) { event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); - } else { - std::map, std::string>::const_iterator it{eventMap_.cbegin()}; - while (it != eventMap_.cend()) { - if (config_[it->second].isString()) { - event_box_.add_events(Gdk::BUTTON_PRESS_MASK); - event_box_.signal_button_press_event().connect( - sigc::mem_fun(*this, &AModule::handleToggle)); - break; - } - ++it; - } } if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || enable_scroll) { event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); From 1d8331d0c585ad9d855b77dcfe2e249cab92359c Mon Sep 17 00:00:00 2001 From: KosmX Date: Wed, 16 Aug 2023 17:12:32 +0200 Subject: [PATCH 022/364] Add release events to event map --- include/AModule.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/AModule.hpp b/include/AModule.hpp index 9b16076b..006546ee 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -44,18 +44,23 @@ class AModule : public IModule { 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"}}; }; From 2ff347f9a8786f8f0f8c1f0d8e93249d3b288e26 Mon Sep 17 00:00:00 2001 From: KosmX Date: Wed, 16 Aug 2023 17:14:49 +0200 Subject: [PATCH 023/364] Add handleRelease method to release events This commit shouldn't change the handleToggle behaviour, it shouldn't break anything. --- include/AModule.hpp | 3 +++ src/AModule.cpp | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/include/AModule.hpp b/include/AModule.hpp index 006546ee..479755b7 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -36,8 +36,11 @@ class AModule : public IModule { virtual bool handleToggle(GdkEventButton *const &ev); virtual bool handleScroll(GdkEventScroll *); + virtual bool handleRelease(GdkEventButton *const &ev); private: + bool handleUserEvent(GdkEventButton *const &ev); + std::vector pid_; gdouble distance_scrolled_y_; gdouble distance_scrolled_x_; diff --git a/src/AModule.cpp b/src/AModule.cpp index 28feb4e1..59078b35 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -38,6 +38,16 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); } + + bool hasReleaseEvent = + 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 (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() || 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)); @@ -69,6 +79,14 @@ auto AModule::doAction(const std::string& name) -> void { } bool AModule::handleToggle(GdkEventButton* const& e) { + return handleUserEvent(e); +} + +bool AModule::handleRelease(GdkEventButton* const& e) { + return handleUserEvent(e); +} + +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))}; From 392e863e6daa70908cdb55889786ebae364bf315 Mon Sep 17 00:00:00 2001 From: KosmX Date: Wed, 16 Aug 2023 17:33:36 +0200 Subject: [PATCH 024/364] Apply formatting --- src/AModule.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 59078b35..398fa518 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -27,11 +27,12 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: } // configure events' user commands - // hasUserEvent is true if any element from eventMap_ is satisfying the condition in the lambda function + // hasUserEvent is true if any element from eventMap_ is satisfying the condition in the lambda bool hasUserEvent = 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(); + // 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) { @@ -41,8 +42,9 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: bool hasReleaseEvent = 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(); + // True if there is any non-release type event + return eventEntry.first.second == GdkEventType::GDK_BUTTON_RELEASE && + config[eventEntry.second].isString(); }) != eventMap_.cend(); if (hasReleaseEvent) { event_box_.add_events(Gdk::BUTTON_RELEASE_MASK); @@ -78,13 +80,9 @@ auto AModule::doAction(const std::string& name) -> void { } } -bool AModule::handleToggle(GdkEventButton* const& e) { - return handleUserEvent(e); -} +bool AModule::handleToggle(GdkEventButton* const& e) { return handleUserEvent(e); } -bool AModule::handleRelease(GdkEventButton* const& e) { - return handleUserEvent(e); -} +bool AModule::handleRelease(GdkEventButton* const& e) { return handleUserEvent(e); } bool AModule::handleUserEvent(GdkEventButton* const& e) { std::string format{}; From 19fe929d1f89027007d6b3922e772d465fbceeea Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 18 Aug 2023 09:48:03 +0200 Subject: [PATCH 025/364] chore: update subprojects --- subprojects/catch2.wrap | 12 ++++++------ subprojects/fmt.wrap | 17 +++++++++-------- subprojects/spdlog.wrap | 17 ++++++++--------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap index 4a6f836c..691d39c8 100644 --- a/subprojects/catch2.wrap +++ b/subprojects/catch2.wrap @@ -1,10 +1,10 @@ [wrap-file] -directory = Catch2-3.3.2 -source_url = https://github.com/catchorg/Catch2/archive/v3.3.2.tar.gz -source_filename = Catch2-3.3.2.tar.gz -source_hash = 8361907f4d9bff3ae7c1edb027f813659f793053c99b67837a0c0375f065bae2 -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.3.2-1/Catch2-3.3.2.tar.gz -wrapdb_version = 3.3.2-1 +directory = Catch2-3.4.0 +source_url = https://github.com/catchorg/Catch2/archive/v3.4.0.tar.gz +source_filename = Catch2-3.4.0.tar.gz +source_hash = 122928b814b75717316c71af69bd2b43387643ba076a6ec16e7882bfb2dfacbb +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 [provide] catch2 = catch2_dep diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap index 63869be1..9efe101e 100644 --- a/subprojects/fmt.wrap +++ b/subprojects/fmt.wrap @@ -1,12 +1,13 @@ [wrap-file] -directory = fmt-8.1.1 -source_url = https://github.com/fmtlib/fmt/archive/8.1.1.tar.gz -source_filename = fmt-8.1.1.tar.gz -source_hash = 3d794d3cf67633b34b2771eb9f073bde87e846e0d395d254df7b211ef1ec7346 -patch_filename = fmt_8.1.1-1_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/fmt_8.1.1-1/get_patch -patch_hash = 6035a67c7a8c90bed74c293c7265c769f47a69816125f7566bccb8e2543cee5e +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 [provide] fmt = fmt_dep - diff --git a/subprojects/spdlog.wrap b/subprojects/spdlog.wrap index 03a6d4c3..69ef566f 100644 --- a/subprojects/spdlog.wrap +++ b/subprojects/spdlog.wrap @@ -1,13 +1,12 @@ [wrap-file] -directory = spdlog-1.10.0 -source_url = https://github.com/gabime/spdlog/archive/v1.10.0.tar.gz -source_filename = v1.10.0.tar.gz -source_hash = 697f91700237dbae2326b90469be32b876b2b44888302afbc7aceb68bcfe8224 -patch_filename = spdlog_1.10.0-3_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.10.0-3/get_patch -patch_hash = 5bb07b4af1e971817d4b886efbe077aaf6c36d72d3d7e461bbcf6631f3725704 -wrapdb_version = 1.10.0-3 +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 [provide] spdlog = spdlog_dep - From 54a66688465b98e9a4775b5bbcb53258a93bb2d6 Mon Sep 17 00:00:00 2001 From: asas1asas200 Date: Sun, 20 Aug 2023 08:33:34 +0800 Subject: [PATCH 026/364] feat(keyboard-state): add binding-keys options --- include/modules/keyboard_state.hpp | 2 ++ man/waybar-keyboard-state.5.scd | 5 +++++ src/modules/keyboard_state.cpp | 25 +++++++++++++++++-------- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index deb577e2..be90eee4 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "AModule.hpp" @@ -40,6 +41,7 @@ class KeyboardState : public AModule { struct libinput* libinput_; std::unordered_map libinput_devices_; + std::set binding_keys; util::SleeperThread libinput_thread_, hotplug_thread_; }; diff --git a/man/waybar-keyboard-state.5.scd b/man/waybar-keyboard-state.5.scd index f07d6854..23804443 100644 --- a/man/waybar-keyboard-state.5.scd +++ b/man/waybar-keyboard-state.5.scd @@ -48,6 +48,11 @@ You must be a member of the input group to use this module. default: chooses first valid input device ++ Which libevdev input device to show the state of. Libevdev devices can be found in /dev/input. The device should support number lock, caps lock, and scroll lock events. +*binding-keys*: ++ + typeof: array ++ + default: [58, 69, 70] ++ + Customize the key to trigger this module, the key number can be find in /usr/include/linux/input-event-codes.h or running sudo libinput debug-events --show-keycodes. + # FORMAT REPLACEMENTS *{name}*: Caps, Num, or Scroll. diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 4c081d6a..5e5d4acd 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -142,6 +142,21 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& } } + auto keys = config_["binding-keys"]; + if (keys.isArray()) { + for (const auto& key : keys) { + if (key.isInt()) { + binding_keys.insert(key.asInt()); + } else { + spdlog::warn("Cannot read key binding {} as int.", key.asString()); + } + } + } else { + binding_keys.insert(KEY_CAPSLOCK); + binding_keys.insert(KEY_NUMLOCK); + binding_keys.insert(KEY_SCROLLLOCK); + } + DIR* dev_dir = opendir(devices_path_.c_str()); if (dev_dir == nullptr) { throw errno_error(errno, "Failed to open " + devices_path_); @@ -171,14 +186,8 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& 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); - switch (key) { - case KEY_CAPSLOCK: - case KEY_NUMLOCK: - case KEY_SCROLLLOCK: - dp.emit(); - break; - default: - break; + if (binding_keys.contains(key)) { + dp.emit(); } } } From 3081b0c576f54af9253c58b45d4392907aafe5a0 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 23 Aug 2023 12:14:35 -0500 Subject: [PATCH 027/364] flake lock update --- flake.lock | 76 +++++++++++++++++++++++++++++++++--------------------- flake.nix | 10 +++---- 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/flake.lock b/flake.lock index b10c9bf7..3b29a610 100644 --- a/flake.lock +++ b/flake.lock @@ -2,15 +2,15 @@ "nodes": { "devshell": { "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "systems": "systems" }, "locked": { - "lastModified": 1676293499, - "narHash": "sha256-uIOTlTxvrXxpKeTvwBI1JGDGtCxMXE3BI0LFwoQMhiQ=", + "lastModified": 1692523566, + "narHash": "sha256-VDJDihK6jNebVw9y3qKCVD6+6QaC/x8kxZzL4MaIPPY=", "owner": "numtide", "repo": "devshell", - "rev": "71e3022e3ab20bbf1342640547ef5bc14fb43bf4", + "rev": "d208c58e2f7afef838add5f18a9936b12a71d695", "type": "github" }, "original": { @@ -36,27 +36,15 @@ } }, "flake-utils": { - "locked": { - "lastModified": 1642700792, - "narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "846b2ae0fc4cc943637d3d1def4454213e203cba", - "type": "github" + "inputs": { + "systems": "systems_2" }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { "locked": { - "lastModified": 1676283394, - "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", "owner": "numtide", "repo": "flake-utils", - "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", "type": "github" }, "original": { @@ -67,11 +55,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1643381941, - "narHash": "sha256-pHTwvnN4tTsEKkWlXQ8JMY423epos8wUOhthpwJjtpc=", + "lastModified": 1677383253, + "narHash": "sha256-UfpzWfSxkfXHnb4boXZNaKsAcUrZT9Hw+tao1oZxd08=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5efc8ca954272c4376ac929f4c5ffefcc20551d5", + "rev": "9952d6bc395f5841262b006fbace8dd7e143b634", "type": "github" }, "original": { @@ -83,11 +71,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1676300157, - "narHash": "sha256-1HjRzfp6LOLfcj/HJHdVKWAkX9QRAouoh6AjzJiIerU=", + "lastModified": 1692638711, + "narHash": "sha256-J0LgSFgJVGCC1+j5R2QndadWI1oumusg6hCtYAzLID4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "545c7a31e5dedea4a6d372712a18e00ce097d462", + "rev": "91a22f76cd1716f9d0149e8a5c68424bb691de15", "type": "github" }, "original": { @@ -101,9 +89,39 @@ "inputs": { "devshell": "devshell", "flake-compat": "flake-compat", - "flake-utils": "flake-utils_2", + "flake-utils": "flake-utils", "nixpkgs": "nixpkgs_2" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index a063b824..24fcae9f 100644 --- a/flake.nix +++ b/flake.nix @@ -47,17 +47,12 @@ let pkgs = import nixpkgs { inherit system; - overlays = [ devshell.overlay ]; + overlays = [ devshell.overlays.default ]; }; in pkgs.devshell.mkShell { imports = [ "${pkgs.devshell.extraModulesDir}/language/c.nix" ]; - commands = [ - { - package = pkgs.devshell.cli; - help = "Per project developer environments"; - } - ]; + devshell.packages = with pkgs; [ clang-tools gdb @@ -79,6 +74,7 @@ at-spi2-atk atkmm cairo cairomm catch2 fmt_8 fontconfig gdk-pixbuf glibmm gtk3 harfbuzz pango pangomm wayland-protocols ]); + env = with pkgs; [ { name = "CPLUS_INCLUDE_PATH"; prefix = "$DEVSHELL_DIR/include"; } { name = "PKG_CONFIG_PATH"; prefix = "$DEVSHELL_DIR/lib/pkgconfig"; } From ee4fbc58f7ae52439c1a3af09fefed69a3a0d700 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 23 Aug 2023 12:18:35 -0500 Subject: [PATCH 028/364] hyprland add urgent ipc support --- include/modules/hyprland/workspaces.hpp | 4 ++++ man/waybar-hyprland-workspaces.5.scd | 1 + src/modules/hyprland/workspaces.cpp | 26 +++++++++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 353edb7a..96443629 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -22,10 +22,12 @@ class Workspace { bool is_special() const { return is_special_; }; bool is_persistent() const { return is_persistent_; }; bool is_empty() const { return windows_ == 0; }; + bool is_urgent() const { return is_urgent_; }; auto handle_clicked(GdkEventButton* bt) -> bool; void set_active(bool value = true) { active_ = value; }; void set_persistent(bool value = true) { is_persistent_ = value; }; + void set_urgent(bool value = true) { is_urgent_ = value; }; void set_windows(uint value) { windows_ = value; }; void update(const std::string& format, const std::string& icon); @@ -38,6 +40,7 @@ class Workspace { bool active_ = false; bool is_special_ = false; bool is_persistent_ = false; + bool is_urgent_ = false; Gtk::Button button_; Gtk::Box content_; @@ -62,6 +65,7 @@ class Workspaces : public AModule, public EventHandler { void sort_workspaces(); void create_workspace(Json::Value& value); void remove_workspace(std::string name); + void set_urgent_workspace(std::string windowaddress); bool all_outputs_ = false; bool show_special_ = false; diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 4f46fd7e..6f138488 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -76,3 +76,4 @@ Additional to workspace name matching, the following *format-icons* can be set. - *#workspaces button.active* - *#workspaces button.persistent* - *#workspaces button.special* +- *#workspaces button.urgent* diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 16894842..9f46b823 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -58,6 +58,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value gIPC->registerForIPC("openwindow", this); gIPC->registerForIPC("closewindow", this); gIPC->registerForIPC("movewindow", this); + gIPC->registerForIPC("urgent", this); } auto Workspaces::update() -> void { @@ -75,6 +76,9 @@ auto Workspaces::update() -> void { for (auto &workspace : workspaces_) { workspace->set_active(workspace->name() == active_workspace_name_); + if (workspace->name() == active_workspace_name_ && workspace.get()->is_urgent()) { + workspace->set_urgent(false); + } std::string &workspace_icon = icons_map_[""]; if (with_icon_) { workspace_icon = workspace->select_icon(icons_map_); @@ -126,6 +130,8 @@ void Workspaces::onEvent(const std::string &ev) { } } else if (eventName == "openwindow" || eventName == "closewindow" || eventName == "movewindow") { update_window_count(); + } else if (eventName == "urgent") { + set_urgent_workspace(payload); } dp.emit(); @@ -323,6 +329,7 @@ void Workspace::update(const std::string &format, const std::string &icon) { add_or_remove_class(style_context, active(), "active"); add_or_remove_class(style_context, is_special(), "special"); add_or_remove_class(style_context, is_empty(), "persistent"); + add_or_remove_class(style_context, is_urgent(), "urgent"); label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), fmt::arg("name", name()), fmt::arg("icon", icon))); @@ -419,4 +426,23 @@ auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { return false; } +void Workspaces::set_urgent_workspace(std::string windowaddress) { + const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); + int workspace_id; + + for (Json::Value client_json : clients_json) { + if (client_json["address"].asString().ends_with(windowaddress)) { + workspace_id = client_json["workspace"]["id"].asInt(); + break; + } + } + + auto workspace = + std::find_if(workspaces_.begin(), workspaces_.end(), + [&](std::unique_ptr &x) { return x->id() == workspace_id; }); + if (workspace->get() != nullptr) { + workspace->get()->set_urgent(); + } +} + } // namespace waybar::modules::hyprland From 06a9f988780e5dbcf7786787d032b062f44df1c1 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 24 Aug 2023 21:19:20 +0200 Subject: [PATCH 029/364] hyprland/workspaces: Add "empty" icon and class --- src/modules/hyprland/workspaces.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 9f46b823..04e4327d 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -328,7 +328,8 @@ void Workspace::update(const std::string &format, const std::string &icon) { auto style_context = button_.get_style_context(); add_or_remove_class(style_context, active(), "active"); add_or_remove_class(style_context, is_special(), "special"); - add_or_remove_class(style_context, is_empty(), "persistent"); + add_or_remove_class(style_context, is_empty(), "empty"); + add_or_remove_class(style_context, is_persistent(), "persistent"); add_or_remove_class(style_context, is_urgent(), "urgent"); label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), @@ -394,6 +395,13 @@ std::string &Workspace::select_icon(std::map &icons_ma return named_icon_it->second; } + if (is_empty()) { + auto empty_icon_it = icons_map.find("empty"); + if (empty_icon_it != icons_map.end()) { + return empty_icon_it->second; + } + } + if (is_persistent()) { auto persistent_icon_it = icons_map.find("persistent"); if (persistent_icon_it != icons_map.end()) { From e163dd82168490b038e462e6c4623229bf09924f Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 24 Aug 2023 22:51:06 +0200 Subject: [PATCH 030/364] hyprland/workspaces: update manpage --- man/waybar-hyprland-workspaces.5.scd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 6f138488..1bbbd385 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -46,7 +46,8 @@ Additional to workspace name matching, the following *format-icons* can be set. - *default*: Will be shown, when no string match is found. - *active*: Will be shown, when workspace is active - *special*: Will be shown on non-active special workspaces -- *persistent*: Will be shown on non-active persistent workspaces +- *empty*: Will be shown on empty persistent workspaces +- *persistent*: Will be shown on non-empty persistent workspaces # EXAMPLES @@ -74,6 +75,7 @@ Additional to workspace name matching, the following *format-icons* can be set. - *#workspaces* - *#workspaces button* - *#workspaces button.active* +- *#workspaces button.empty* - *#workspaces button.persistent* - *#workspaces button.special* - *#workspaces button.urgent* From 936937ec7803eaa4f11e0b94822401b34ad53a4c Mon Sep 17 00:00:00 2001 From: Cherser-s <29800876+Cherser-s@users.noreply.github.com> Date: Mon, 21 Aug 2023 22:03:20 +0300 Subject: [PATCH 031/364] store margins and global window offset in the bar object --- include/bar.hpp | 8 ++++++++ src/bar.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 7c5525f6..ee4a1d5e 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -91,6 +91,9 @@ class Bar { bool vertical = false; Gtk::Window window; + int x_global; + int y_global; + #ifdef HAVE_SWAY std::string bar_id; #endif @@ -102,11 +105,16 @@ class Bar { void setupAltFormatKeyForModule(const std::string &module_name); void setupAltFormatKeyForModuleList(const char *module_list_name); void setMode(const bar_mode &); + void onConfigure(GdkEventConfigure *ev); + void configureGlobalOffset(int width, int height); + void onOutputGeometryChanged(); /* Copy initial set of modes to allow customization */ bar_mode_map configured_modes = PRESET_MODES; std::string last_mode_{MODE_DEFAULT}; + struct bar_margins margins_; + std::unique_ptr surface_impl_; Gtk::Box left_; Gtk::Box center_; diff --git a/src/bar.cpp b/src/bar.cpp index 60104f0d..30cf7fad 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -481,6 +481,9 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) : output(w_output), config(w_config), window{Gtk::WindowType::WINDOW_TOPLEVEL}, + 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), @@ -516,8 +519,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) uint32_t height = config["height"].isUInt() ? config["height"].asUInt() : 0; uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0; - struct bar_margins margins_; - if (config["margin-top"].isInt() || config["margin-right"].isInt() || config["margin-bottom"].isInt() || config["margin-left"].isInt()) { margins_ = { @@ -563,6 +564,10 @@ 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)); + #ifdef HAVE_GTK_LAYER_SHELL bool use_gls = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true; if (use_gls) { @@ -674,6 +679,7 @@ 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)); } void waybar::Bar::setVisible(bool value) { @@ -815,3 +821,47 @@ auto waybar::Bar::setupWidgets() -> void { right_.pack_end(*module, false, false); } } + +void waybar::Bar::onConfigure(GdkEventConfigure* ev) { + configureGlobalOffset(ev->width, ev->height); +} + +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) + 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) + 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; + } + + x_global = x + monitor_geometry.x; + y_global = y + monitor_geometry.y; +} + +void waybar::Bar::onOutputGeometryChanged() { + configureGlobalOffset(window.get_width(), window.get_height()); +} From 65dfabc430fdc531b70da505b1481cbc706f5305 Mon Sep 17 00:00:00 2001 From: Cherser-s <29800876+Cherser-s@users.noreply.github.com> Date: Mon, 21 Aug 2023 22:03:43 +0300 Subject: [PATCH 032/364] sni: fix passing relative coordinates to dbus methods Doesn't correctly handle the case with both margin and width/height being set at the same time. --- include/modules/sni/item.hpp | 2 ++ src/modules/sni/item.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index 423ec7c5..1043157c 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -84,6 +84,8 @@ class Item : public sigc::trackable { // visibility of items with Status == Passive bool show_passive_ = false; + const Bar& bar_; + Glib::RefPtr proxy_; Glib::RefPtr cancellable_; std::set update_pending_; diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 871621fb..dfaca665 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -39,7 +39,8 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf object_path(op), icon_size(16), effective_icon_size(0), - icon_theme(Gtk::IconTheme::create()) { + icon_theme(Gtk::IconTheme::create()), + bar_(bar) { if (config["icon-size"].isUInt()) { icon_size = config["icon-size"].asUInt(); } @@ -410,7 +411,8 @@ void Item::makeMenu() { bool Item::handleClick(GdkEventButton* const& ev) { auto parameters = Glib::VariantContainerBase::create_tuple( - {Glib::Variant::create(ev->x), Glib::Variant::create(ev->y)}); + {Glib::Variant::create(ev->x_root + bar_.x_global), + Glib::Variant::create(ev->y_root + bar_.y_global)}); if ((ev->button == 1 && item_is_menu) || ev->button == 3) { makeMenu(); if (gtk_menu != nullptr) { From 52309615c1fe12dc48e384a4728caa9ac1f7b219 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 28 Aug 2023 23:34:28 -0500 Subject: [PATCH 033/364] hyprland new persistent_workspace configuration style --- man/waybar-hyprland-workspaces.5.scd | 19 +++++++++++++++ src/modules/hyprland/workspaces.cpp | 35 ++++++++++++++++++---------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 6f138488..87be8186 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -69,6 +69,25 @@ Additional to workspace name matching, the following *format-icons* can be set. } ``` +``` +"hyprland/workspaces": { + "format": "{name}: {icon}", + "format-icons": { + "1": "", + "2": "", + "3": "", + "4": "", + "5": "", + "active": "", + "default": "" + }, + "persistent_workspaces": { + "*": [ 2,3,4,5 ], // 2-5 on every monitor + "HDMI-A-1": [ 1 ] // but only workspace 1 on HDMI-A-1 + } +} +``` + # Style - *#workspaces* diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 9f46b823..872f5a07 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -201,14 +201,17 @@ void Workspaces::fill_persistent_workspaces() { const std::vector keys = persistent_workspaces.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 can_create = + (key == "*" && std::find(keys.begin(), keys.end(), bar_.output->name) == keys.end()) || + key == bar_.output->name; const Json::Value &value = persistent_workspaces[key]; + if (value.isInt()) { // value is a number => create that many workspaces for this monitor - // only add if either: - // 1. key is "*" and this monitor is not already defined in the config - // 2. key is the current monitor name - if ((key == "*" && std::find(keys.begin(), keys.end(), bar_.output->name) == keys.end()) || - key == bar_.output->name) { + if (can_create) { int amount = value.asInt(); spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, bar_.output->name); @@ -217,14 +220,22 @@ void Workspaces::fill_persistent_workspaces() { std::to_string(monitor_id_ * amount + i + 1)); } } - } else if (value.isArray() && !value.empty()) { - // value is an array => key is a workspace name - // values are monitor names this workspace should be shown on - for (const Json::Value &monitor : value) { - if (monitor.isString() && monitor.asString() == bar_.output->name) { - persistent_workspaces_to_create_.emplace_back(key); - break; + // value is an array => create defined workspaces for this monitor + if (can_create) { + for (const Json::Value &workspace : value) { + if (workspace.isInt()) { + spdlog::debug("Creating workspace {} on monitor {}", workspace, bar_.output->name); + persistent_workspaces_to_create_.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() == bar_.output->name) { + persistent_workspaces_to_create_.emplace_back(key); + break; + } } } } From a18b41911d853bfb8a91c1c752a5bb098e0de1da Mon Sep 17 00:00:00 2001 From: PucklaJ Date: Wed, 30 Aug 2023 13:35:19 +0200 Subject: [PATCH 034/364] hyprland/workspaces: Show workspace on all outputs if an empty array is given This behaviour is consistent with sway/workspaces and wlr/workspaces --- src/modules/hyprland/workspaces.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 9f46b823..170cb67c 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -227,6 +227,9 @@ void Workspaces::fill_persistent_workspaces() { break; } } + } else { + // this workspace should be displayed on all monitors + persistent_workspaces_to_create_.emplace_back(key); } } } From ce076927f3f5d131031cc1d76de9de4958dbafb8 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 2 Sep 2023 15:22:26 -0500 Subject: [PATCH 035/364] chore: cpplint fixes hyprland headers --- include/modules/hyprland/backend.hpp | 2 ++ include/modules/hyprland/language.hpp | 4 ++++ include/modules/hyprland/submap.hpp | 4 ++++ include/modules/hyprland/window.hpp | 4 ++++ include/modules/hyprland/workspaces.hpp | 7 ++++++- 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index e23b1582..7d97b553 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -1,10 +1,12 @@ #pragma once + #include #include #include #include #include #include +#include #include "util/json.hpp" diff --git a/include/modules/hyprland/language.hpp b/include/modules/hyprland/language.hpp index 30789d06..eb220609 100644 --- a/include/modules/hyprland/language.hpp +++ b/include/modules/hyprland/language.hpp @@ -1,5 +1,9 @@ +#pragma once + #include +#include + #include "ALabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" diff --git a/include/modules/hyprland/submap.hpp b/include/modules/hyprland/submap.hpp index e2a84981..4ff232ff 100644 --- a/include/modules/hyprland/submap.hpp +++ b/include/modules/hyprland/submap.hpp @@ -1,5 +1,9 @@ +#pragma once + #include +#include + #include "ALabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index fd68b049..c9f0be03 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -1,5 +1,9 @@ +#pragma once + #include +#include + #include "AAppIconLabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 96443629..b994c9e4 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -1,7 +1,12 @@ +#pragma once + #include #include +#include #include +#include +#include #include "AModule.hpp" #include "bar.hpp" @@ -11,7 +16,7 @@ namespace waybar::modules::hyprland { class Workspace { public: - Workspace(const Json::Value& workspace_data); + explicit Workspace(const Json::Value& workspace_data); std::string& select_icon(std::map& icons_map); Gtk::Button& button() { return button_; }; From 8fc41877135b6480785f9a4a3ed9f8ff934902a6 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 2 Sep 2023 23:21:44 -0500 Subject: [PATCH 036/364] refactor: replace strcpy with snprintf --- src/modules/hyprland/backend.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 6e586966..def38f81 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -166,9 +166,13 @@ std::string IPC::getSocket1Reply(const std::string& rq) { std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock"; - strcpy(serverAddress.sun_path, socketPath.c_str()); + // 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 ""; + } - if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) { + if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) { spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); return ""; } From 4cb8efbecc3596a1d32ae9186a5414903368b34f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 2 Sep 2023 23:34:11 -0500 Subject: [PATCH 037/364] chore: cpplint fixes hyprland classes --- src/modules/hyprland/backend.cpp | 6 ++++-- src/modules/hyprland/language.cpp | 4 +--- src/modules/hyprland/submap.cpp | 2 +- src/modules/hyprland/window.cpp | 3 +-- src/modules/hyprland/workspaces.cpp | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index def38f81..5a48d369 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -167,12 +167,14 @@ std::string IPC::getSocket1Reply(const std::string& rq) { std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.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()) < 0) { + 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 ""; } - if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) { + if (connect(SERVERSOCKET, reinterpret_cast(&serverAddress), sizeof(serverAddress)) < + 0) { spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); return ""; } diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 423e2b5c..5339ee9e 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -4,8 +4,7 @@ #include #include -#include - +#include "util/sanitize_str.hpp" #include "util/string.hpp" namespace waybar::modules::hyprland { @@ -97,7 +96,6 @@ void Language::initLanguage() { spdlog::debug("hyprland language initLanguage found {}", layout_.full_name); dp.emit(); - } catch (std::exception& e) { spdlog::error("hyprland language initLanguage failed with {}", e.what()); } diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 22acbf31..d1d9a116 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -2,7 +2,7 @@ #include -#include +#include "util/sanitize_str.hpp" namespace waybar::modules::hyprland { diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 60de074c..77723bc0 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -6,12 +6,11 @@ #include #include -#include -#include #include #include "modules/hyprland/backend.hpp" #include "util/rewrite_string.hpp" +#include "util/sanitize_str.hpp" namespace waybar::modules::hyprland { diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 75fb19e3..3f27df8a 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -327,7 +327,7 @@ Workspace::Workspace(const Json::Value &workspace_data) button_.set_relief(Gtk::RELIEF_NONE); content_.set_center_widget(label_); button_.add(content_); -}; +} void add_or_remove_class(const Glib::RefPtr &context, bool condition, const std::string &class_name) { From d40ccd0da8cc19654297cbefb3ffad6c7a7a2bd6 Mon Sep 17 00:00:00 2001 From: xercesblue Date: Mon, 4 Sep 2023 16:50:57 -0700 Subject: [PATCH 038/364] modules/hyprland: Fix segfault when attempting to set_urgent on a missing workspace --- 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 3f27df8a..346e7754 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -450,7 +450,7 @@ auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { void Workspaces::set_urgent_workspace(std::string windowaddress) { const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); - int workspace_id; + int workspace_id = -1; for (Json::Value client_json : clients_json) { if (client_json["address"].asString().ends_with(windowaddress)) { @@ -462,7 +462,7 @@ void Workspaces::set_urgent_workspace(std::string windowaddress) { auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr &x) { return x->id() == workspace_id; }); - if (workspace->get() != nullptr) { + if (workspace != workspaces_.end()) { workspace->get()->set_urgent(); } } From 9c49f46b0142267d493a15b38144cb287429a794 Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Tue, 5 Sep 2023 17:13:25 +0200 Subject: [PATCH 039/364] hyprland/workspaces: react on renameworkspace event --- include/modules/hyprland/workspaces.hpp | 1 + src/modules/hyprland/workspaces.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index b994c9e4..5fd0e457 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -34,6 +34,7 @@ class Workspace { void set_persistent(bool value = true) { is_persistent_ = value; }; void set_urgent(bool value = true) { is_urgent_ = value; }; void set_windows(uint value) { windows_ = value; }; + void set_name(std::string value) { name_ = value; }; void update(const std::string& format, const std::string& icon); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 346e7754..ebb47419 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -55,6 +55,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value 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); @@ -132,6 +133,16 @@ void Workspaces::onEvent(const std::string &ev) { update_window_count(); } else if (eventName == "urgent") { set_urgent_workspace(payload); + } else if (eventName == "renameworkspace") { + std::string workspace_id_str = payload.substr(0, payload.find(',')); + int workspace_id = workspace_id_str == "special" ? -99 : std::stoi(workspace_id_str); + std::string new_name = payload.substr(payload.find(',') + 1); + for (auto &workspace : workspaces_) { + if (workspace->id() == workspace_id) { + workspace->set_name(new_name); + break; + } + } } dp.emit(); From 04b39ea64e04c2a096bcf92a591a8ea9af0975cf Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 10 Aug 2023 19:40:14 +0200 Subject: [PATCH 040/364] hyprland/workspaces: implement 'active_only' option and visible class --- include/modules/hyprland/workspaces.hpp | 11 +++- man/waybar-hyprland-workspaces.5.scd | 13 ++++- src/modules/hyprland/workspaces.cpp | 73 ++++++++++++++++++++----- 3 files changed, 80 insertions(+), 17 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 5fd0e457..270c1e36 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -14,9 +14,11 @@ namespace waybar::modules::hyprland { +class Workspaces; + class Workspace { public: - explicit Workspace(const Json::Value& workspace_data); + explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager); std::string& select_icon(std::map& icons_map); Gtk::Button& button() { return button_; }; @@ -26,6 +28,7 @@ class Workspace { bool active() const { return active_; }; bool is_special() const { return is_special_; }; bool is_persistent() const { return is_persistent_; }; + bool is_visible() const { return is_visible_; }; bool is_empty() const { return windows_ == 0; }; bool is_urgent() const { return is_urgent_; }; @@ -33,12 +36,15 @@ class Workspace { void set_active(bool value = true) { active_ = value; }; void set_persistent(bool value = true) { is_persistent_ = value; }; void set_urgent(bool value = true) { is_urgent_ = value; }; + void set_visible(bool value = true) { is_visible_ = value; }; void set_windows(uint value) { windows_ = value; }; void set_name(std::string value) { name_ = value; }; void update(const std::string& format, const std::string& icon); private: + Workspaces& workspace_manager_; + int id_; std::string name_; std::string output_; @@ -47,6 +53,7 @@ class Workspace { bool is_special_ = false; bool is_persistent_ = false; bool is_urgent_ = false; + bool is_visible_ = false; Gtk::Button button_; Gtk::Box content_; @@ -62,6 +69,7 @@ class Workspaces : public AModule, public EventHandler { auto all_outputs() const -> bool { return all_outputs_; } auto show_special() const -> bool { return show_special_; } + auto active_only() const -> bool { return active_only_; } auto get_bar_output() const -> std::string { return bar_.output->name; } @@ -75,6 +83,7 @@ class Workspaces : public AModule, public EventHandler { bool all_outputs_ = false; bool show_special_ = false; + bool active_only_ = false; void fill_persistent_workspaces(); void create_persistent_workspaces(); diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 7e462358..13764752 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -24,13 +24,18 @@ Addressed by *hyprland/workspaces* *show-special*: ++ typeof: bool ++ default: false ++ - If set to true special workspaces will be shown. + If set to true, special workspaces will be shown. *all-outputs*: ++ typeof: bool ++ default: false ++ If set to false workspaces group will be shown only in assigned output. Otherwise all workspace groups are shown. +*active-only*: ++ + typeof: bool ++ + default: false ++ + If set to true, only the active workspace will be shown. + # FORMAT REPLACEMENTS *{id}*: id of workspace assigned by compositor @@ -43,10 +48,11 @@ Addressed by *hyprland/workspaces* Additional to workspace name matching, the following *format-icons* can be set. -- *default*: Will be shown, when no string match is found. +- *default*: Will be shown, when no string match is found and none of the below conditions have defined icons. - *active*: Will be shown, when workspace is active - *special*: Will be shown on non-active special workspaces -- *empty*: Will be shown on empty persistent workspaces +- *empty*: Will be shown on non-active, non-special empty persistent workspaces +- *visible*: Will be shown on workspaces that are visible but not active. For example: this is useful if you want your visible workspaces on other monitors to have the same look as active. - *persistent*: Will be shown on non-empty persistent workspaces # EXAMPLES @@ -95,6 +101,7 @@ Additional to workspace name matching, the following *format-icons* can be set. - *#workspaces button* - *#workspaces button.active* - *#workspaces button.empty* +- *#workspaces button.visible* - *#workspaces button.persistent* - *#workspaces button.special* - *#workspaces button.urgent* diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index ebb47419..64c7a9ed 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -38,6 +38,11 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value show_special_ = config_show_special.asBool(); } + auto config_active_only = config_["active-only"]; + if (config_active_only.isBool()) { + active_only_ = config_active_only.asBool(); + } + box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -75,11 +80,29 @@ auto Workspaces::update() -> void { workspaces_to_create_.clear(); + // get all active workspaces + auto monitors = gIPC->getSocket1JsonReply("monitors"); + std::vector visible_workspaces; + for (Json::Value &monitor : monitors) { + auto ws = monitor["activeWorkspace"]; + if (ws.isObject() && (ws["name"].isString())) { + visible_workspaces.push_back(ws["name"].asString()); + } + } + for (auto &workspace : workspaces_) { + // active workspace->set_active(workspace->name() == active_workspace_name_); - if (workspace->name() == active_workspace_name_ && workspace.get()->is_urgent()) { + // disable urgency if workspace is active + if (workspace->name() == active_workspace_name_ && workspace->is_urgent()) { workspace->set_urgent(false); } + + // visible + workspace->set_visible(std::find(visible_workspaces.begin(), visible_workspaces.end(), + workspace->name()) != visible_workspaces.end()); + + // set workspace icon std::string &workspace_icon = icons_map_[""]; if (with_icon_) { workspace_icon = workspace->select_icon(icons_map_); @@ -103,9 +126,10 @@ void Workspaces::onEvent(const std::string &ev) { } else if (eventName == "createworkspace") { const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (Json::Value workspace_json : workspaces_json) { - if (workspace_json["name"].asString() == payload && + std::string name = workspace_json["name"].asString(); + if (name == payload && (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (show_special() || !workspace_json["name"].asString().starts_with("special"))) { + (show_special() || !name.starts_with("special"))) { workspaces_to_create_.push_back(workspace_json); break; } @@ -120,8 +144,8 @@ void Workspaces::onEvent(const std::string &ev) { if (bar_.output->name == new_output) { // TODO: implement this better const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (Json::Value workspace_json : workspaces_json) { - if (workspace_json["name"].asString() == workspace && - bar_.output->name == workspace_json["monitor"].asString()) { + std::string name = workspace_json["name"].asString(); + if (name == workspace && bar_.output->name == workspace_json["monitor"].asString()) { workspaces_to_create_.push_back(workspace_json); break; } @@ -154,15 +178,15 @@ void Workspaces::update_window_count() { auto workspace_json = std::find_if( workspaces_json.begin(), workspaces_json.end(), [&](Json::Value const &x) { return x["name"].asString() == workspace->name(); }); + uint32_t count = 0; if (workspace_json != workspaces_json.end()) { try { - workspace->set_windows((*workspace_json)["windows"].asUInt()); + count = (*workspace_json)["windows"].asUInt(); } catch (const std::exception &e) { spdlog::error("Failed to update window count: {}", e.what()); } - } else { - workspace->set_windows(0); } + workspace->set_windows(count); } } @@ -181,7 +205,7 @@ void Workspaces::create_workspace(Json::Value &value) { } // create new workspace - workspaces_.emplace_back(std::make_unique(value)); + workspaces_.emplace_back(std::make_unique(value, *this)); Gtk::Button &new_workspace_button = workspaces_.back()->button(); box_.pack_start(new_workspace_button, false, false); sort_workspaces(); @@ -207,7 +231,7 @@ void Workspaces::remove_workspace(std::string name) { } void Workspaces::fill_persistent_workspaces() { - if (config_["persistent_workspaces"].isObject() && !all_outputs()) { + if (config_["persistent_workspaces"].isObject()) { const Json::Value persistent_workspaces = config_["persistent_workspaces"]; const std::vector keys = persistent_workspaces.getMemberNames(); @@ -297,8 +321,9 @@ void Workspaces::init() { const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (Json::Value workspace_json : workspaces_json) { if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (!workspace_json["name"].asString().starts_with("special") || show_special())) + (!workspace_json["name"].asString().starts_with("special") || show_special())) { create_workspace(workspace_json); + } } update_window_count(); @@ -314,8 +339,9 @@ Workspaces::~Workspaces() { std::lock_guard lg(mutex_); } -Workspace::Workspace(const Json::Value &workspace_data) - : id_(workspace_data["id"].asInt()), +Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager) + : workspace_manager_(workspace_manager), + id_(workspace_data["id"].asInt()), name_(workspace_data["name"].asString()), output_(workspace_data["monitor"].asString()), // TODO:allow using monitor desc windows_(workspace_data["windows"].asInt()), @@ -350,12 +376,26 @@ void add_or_remove_class(const Glib::RefPtr &context, bool co } void Workspace::update(const std::string &format, const std::string &icon) { + // clang-format off + if (this->workspace_manager_.active_only() && \ + !this->active() && \ + !this->is_persistent() && \ + !this->is_visible() && \ + !this->is_special()) { + // clang-format on + // if active_only is true, hide if not active, persistent, visible or special + button_.hide(); + return; + } + button_.show(); + auto style_context = button_.get_style_context(); add_or_remove_class(style_context, active(), "active"); add_or_remove_class(style_context, is_special(), "special"); add_or_remove_class(style_context, is_empty(), "empty"); add_or_remove_class(style_context, is_persistent(), "persistent"); add_or_remove_class(style_context, is_urgent(), "urgent"); + add_or_remove_class(style_context, is_visible(), "visible"); label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), fmt::arg("name", name()), fmt::arg("icon", icon))); @@ -420,6 +460,13 @@ std::string &Workspace::select_icon(std::map &icons_ma return named_icon_it->second; } + if (is_visible()) { + auto visible_icon_it = icons_map.find("visible"); + if (visible_icon_it != icons_map.end()) { + return visible_icon_it->second; + } + } + if (is_empty()) { auto empty_icon_it = icons_map.find("empty"); if (empty_icon_it != icons_map.end()) { From 44ac6b804488e1a4356a1426c23b8d2808fef512 Mon Sep 17 00:00:00 2001 From: khaneliman Date: Tue, 5 Sep 2023 13:10:34 -0500 Subject: [PATCH 041/364] refactor!: hyprland persistent workspaces config option name standardization --- man/waybar-hyprland-workspaces.5.scd | 4 ++-- src/modules/hyprland/workspaces.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 13764752..e975179f 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -69,7 +69,7 @@ Additional to workspace name matching, the following *format-icons* can be set. "active": "", "default": "" }, - "persistent_workspaces": { + "persistent-workspaces": { "*": 5, // 5 workspaces by default on every monitor "HDMI-A-1": 3 // but only three on HDMI-A-1 } @@ -88,7 +88,7 @@ Additional to workspace name matching, the following *format-icons* can be set. "active": "", "default": "" }, - "persistent_workspaces": { + "persistent-workspaces": { "*": [ 2,3,4,5 ], // 2-5 on every monitor "HDMI-A-1": [ 1 ] // but only workspace 1 on HDMI-A-1 } diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 64c7a9ed..d4b9cf2d 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -231,8 +231,8 @@ void Workspaces::remove_workspace(std::string name) { } void Workspaces::fill_persistent_workspaces() { - if (config_["persistent_workspaces"].isObject()) { - const Json::Value persistent_workspaces = config_["persistent_workspaces"]; + if (config_["persistent-workspaces"].isObject()) { + const Json::Value persistent_workspaces = config_["persistent-workspaces"]; const std::vector keys = persistent_workspaces.getMemberNames(); for (const std::string &key : keys) { From 4a6c1269fb33078ce7273ffce005c1faa1ed592f Mon Sep 17 00:00:00 2001 From: khaneliman Date: Tue, 5 Sep 2023 13:12:39 -0500 Subject: [PATCH 042/364] refactor!: sway persistent workspaces config name rename --- man/waybar-sway-workspaces.5.scd | 4 ++-- src/modules/sway/workspaces.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 82d858de..2441a936 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -60,7 +60,7 @@ Addressed by *sway/workspaces* default: false ++ If set to true. Only focused workspaces will be shown. -*persistent_workspaces*: ++ +*persistent-workspaces*: ++ typeof: json (see below) ++ default: empty ++ Lists workspaces that should always be shown, even when non existent @@ -112,7 +112,7 @@ an empty list denoting all outputs. ``` "sway/workspaces": { - "persistent_workspaces": { + "persistent-workspaces": { "3": [], // Always show a workspace with name '3', on all outputs if it does not exists "4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exists "5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exists diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index a5e5fa75..e2ddbbd3 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -80,8 +80,8 @@ 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"]; + 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 b405dc436c49d075d41da938cec7f7e190e0348a Mon Sep 17 00:00:00 2001 From: khaneliman Date: Tue, 5 Sep 2023 13:13:29 -0500 Subject: [PATCH 043/364] refactor!: wlr persistent workspaces config rename --- src/modules/wlr/workspace_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 8933d691..7f97d294 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -209,8 +209,8 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value } auto WorkspaceGroup::fill_persistent_workspaces() -> void { - if (config_["persistent_workspaces"].isObject() && !workspace_manager_.all_outputs()) { - const Json::Value &p_workspaces = config_["persistent_workspaces"]; + if (config_["persistent-workspaces"].isObject() && !workspace_manager_.all_outputs()) { + 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 c9e1899594f9c36dc7fc7bc201238438d59ca6ff Mon Sep 17 00:00:00 2001 From: khaneliman Date: Tue, 5 Sep 2023 13:45:09 -0500 Subject: [PATCH 044/364] refactor: deprecate instead of remove persistent_workspaces --- src/modules/hyprland/workspaces.cpp | 11 +++++++++-- src/modules/sway/workspaces.cpp | 13 +++++++++++-- src/modules/wlr/workspace_manager.cpp | 13 +++++++++++-- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index d4b9cf2d..fc94b39b 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -231,8 +231,15 @@ void Workspaces::remove_workspace(std::string name) { } void Workspaces::fill_persistent_workspaces() { - if (config_["persistent-workspaces"].isObject()) { - const Json::Value persistent_workspaces = config_["persistent-workspaces"]; + 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()) { + const Json::Value persistent_workspaces = config_["persistent-workspaces"].isObject() + ? config_["persistent-workspaces"] + : config_["persistent_workspaces"]; const std::vector keys = persistent_workspaces.getMemberNames(); for (const std::string &key : keys) { diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index e2ddbbd3..6674731f 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -79,9 +79,18 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { : true; }); + 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()) { - const Json::Value &p_workspaces = config_["persistent-workspaces"]; + if (config_["persistent-workspaces"].isObject() || + config_["persistent_workspaces"].isObject()) { + const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject() + ? config_["persistent-workspaces"] + : config_["persistent_workspaces"]; const std::vector p_workspaces_names = p_workspaces.getMemberNames(); for (const std::string &p_w_name : p_workspaces_names) { diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 7f97d294..ce14b3b5 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -209,8 +209,17 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value } auto WorkspaceGroup::fill_persistent_workspaces() -> void { - if (config_["persistent-workspaces"].isObject() && !workspace_manager_.all_outputs()) { - const Json::Value &p_workspaces = config_["persistent-workspaces"]; + 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()) && + !workspace_manager_.all_outputs()) { + const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject() + ? config_["persistent-workspaces"] + : config_["persistent_workspaces"]; const std::vector p_workspaces_names = p_workspaces.getMemberNames(); for (const std::string &p_w_name : p_workspaces_names) { From 09873f0ed9b45b2be72b7083c13cdd1604b4e274 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Wed, 6 Sep 2023 15:19:56 +0000 Subject: [PATCH 045/364] search for dark or light mode stylesheet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit summary: ------- This commit adds xdg-desktop-portal support to waybar. If a portal supporting `org.freedesktop.portal.Settings` exists, then it will be queried for the current colorscheme. This colorscheme will then be used to prefer a `style-light.css` or `style-dark.css` over the basic `style.css`. technical details: ----------------- Appearance is provided by several libraries, such as libhandy (mobile) and libadwaita. However, waybar links to neither of these libraries. As the amount of code required to communicate with xdg-desktop portal as a client is rather minimal, I believe doing so is better than linking to an additional library. The Gio library for communicating with dbus is rather messy, Instead of the `Portal` class containing a `Gio::Dbus::Proxy`, it extends it which simplifies signal handling. `Portal` then exposes its own signal, which can be listened to by waybar to update CSS. For a reference implementation, please see another one of my projects: https://github.com/4e554c4c/darkman.nvim/blob/main/portal.go test plan: --------- If no desktop portal which provides `Settings` exists, then waybar continues with the log line ``` [2023-09-06 14:14:37.754] [info] Unable to receive desktop appearance: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such interface “org.freedesktop.portal.Settings” on object at path /org/freedesktop/portal/desktop ``` Furthermore, if `style-light.css` or `style-dark.css` do not exist, then `style.css` will still be searched for. Waybar has been tested with both light and dark startup. E.g. if the appearance is dark on startup the log lines ``` [2023-09-06 14:27:45.379] [info] Discovered appearance 'dark' [2023-09-06 14:27:45.379] [debug] Try expanding: $XDG_CONFIG_HOME/waybar/style-dark.css [2023-09-06 14:27:45.379] [debug] Found config file: $XDG_CONFIG_HOME/waybar/style-dark.css [2023-09-06 14:27:45.379] [info] Using CSS file /home/pounce/.config/waybar/style-dark.css ``` will be observed. If the color then changes to light during the operation of waybar, it will change css files: ``` [2023-09-06 14:28:17.173] [info] Received new appearance 'dark' [2023-09-06 14:28:17.173] [debug] Try expanding: $XDG_CONFIG_HOME/waybar/style-light.css [2023-09-06 14:28:17.173] [debug] Found config file: $XDG_CONFIG_HOME/waybar/style-light.css [2023-09-06 14:28:17.173] [info] Using CSS file /home/pounce/.config/waybar/style-light.css ``` Finally, tested resetting waybar and toggling style (works, and style is only changed once). fixes: Alexays/Waybar#1973 --- include/client.hpp | 5 +- include/util/portal.hpp | 38 ++++++++++++++ meson.build | 1 + src/client.cpp | 35 +++++++++++-- src/util/portal.cpp | 106 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 include/util/portal.hpp create mode 100644 src/util/portal.cpp diff --git a/include/client.hpp b/include/client.hpp index aaba3b6b..8faa7198 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -7,6 +7,7 @@ #include "bar.hpp" #include "config.hpp" +#include "util/portal.hpp" struct zwlr_layer_shell_v1; struct zwp_idle_inhibitor_v1; @@ -33,7 +34,7 @@ class Client { private: Client() = default; - const std::string getStyle(const std::string &style); + const std::string getStyle(const std::string &style, std::optional appearance); void bindInterfaces(); void handleOutput(struct waybar_output &output); auto setupCss(const std::string &css_file) -> void; @@ -46,12 +47,14 @@ class Client { static void handleOutputDone(void *, struct zxdg_output_v1 *); static void handleOutputName(void *, struct zxdg_output_v1 *, const char *); static void handleOutputDescription(void *, struct zxdg_output_v1 *, const char *); + void handleAppearanceChanged(waybar::Appearance appearance); void handleMonitorAdded(Glib::RefPtr monitor); 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/util/portal.hpp b/include/util/portal.hpp new file mode 100644 index 00000000..23619169 --- /dev/null +++ b/include/util/portal.hpp @@ -0,0 +1,38 @@ +#include + +#include + +#include "fmt/format.h" + +namespace waybar { + +using namespace Gio; + +enum class Appearance { + UNKNOWN = 0, + DARK = 1, + LIGHT = 2, +}; +class Portal : private DBus::Proxy { + public: + Portal(); + void refreshAppearance(); + Appearance getAppearance(); + + typedef sigc::signal type_signal_appearance_changed; + type_signal_appearance_changed signal_appearance_changed() { return m_signal_appearance_changed; } + + private: + type_signal_appearance_changed m_signal_appearance_changed; + Appearance currentMode; + void on_signal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, + const Glib::VariantContainerBase& parameters); +}; + +} // namespace waybar + +template <> +struct fmt::formatter : formatter { + // parse is inherited from formatter. + auto format(waybar::Appearance c, format_context& ctx) const; +}; diff --git a/meson.build b/meson.build index e71807ec..296cbf56 100644 --- a/meson.build +++ b/meson.build @@ -171,6 +171,7 @@ src_files = files( 'src/client.cpp', 'src/config.cpp', 'src/group.cpp', + 'src/util/portal.cpp', 'src/util/prepare_for_sleep.cpp', 'src/util/ustring_clen.cpp', 'src/util/sanitize_str.cpp', diff --git a/src/client.cpp b/src/client.cpp index a815e2fe..10073df0 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -151,8 +151,26 @@ void waybar::Client::handleDeferredMonitorRemoval(Glib::RefPtr mon outputs_.remove_if([&monitor](const auto &output) { return output.monitor == monitor; }); } -const std::string waybar::Client::getStyle(const std::string &style) { - auto css_file = style.empty() ? Config::findConfigPath({"style.css"}) : style; +const std::string waybar::Client::getStyle(const std::string &style, + std::optional appearance = std::nullopt) { + std::optional css_file; + if (!style.empty()) { + css_file = style; + } else { + std::vector search_files; + switch (appearance.value_or(portal->getAppearance())) { + case waybar::Appearance::LIGHT: + search_files.push_back("style-light.css"); + break; + case waybar::Appearance::DARK: + search_files.push_back("style-dark.css"); + break; + case waybar::Appearance::UNKNOWN: + break; + } + search_files.push_back("style.css"); + css_file = Config::findConfigPath(search_files); + } if (!css_file) { throw std::runtime_error("Missing required resource files"); } @@ -235,8 +253,15 @@ int waybar::Client::main(int argc, char *argv[]) { } wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj()); config.load(config_opt); + if (!portal) { + portal = std::make_unique(); + } auto css_file = getStyle(style_opt); setupCss(css_file); + portal->signal_appearance_changed().connect([&](waybar::Appearance appearance) { + auto css_file = getStyle(style_opt, appearance); + setupCss(css_file); + }); bindInterfaces(); gtk_app->hold(); gtk_app->run(); @@ -244,4 +269,8 @@ int waybar::Client::main(int argc, char *argv[]) { return 0; } -void waybar::Client::reset() { gtk_app->quit(); } +void waybar::Client::reset() { + gtk_app->quit(); + // delete signal handler for css changes + portal->signal_appearance_changed().clear(); +} diff --git a/src/util/portal.cpp b/src/util/portal.cpp new file mode 100644 index 00000000..50c646c5 --- /dev/null +++ b/src/util/portal.cpp @@ -0,0 +1,106 @@ +#include "util/portal.hpp" + +#include +#include +#include + +#include +#include + +#include "fmt/format.h" + +namespace waybar { +static constexpr const char* PORTAL_BUS_NAME = "org.freedesktop.portal.Desktop"; +static constexpr const char* PORTAL_OBJ_PATH = "/org/freedesktop/portal/desktop"; +static constexpr const char* PORTAL_INTERFACE = "org.freedesktop.portal.Settings"; +static constexpr const char* PORTAL_NAMESPACE = "org.freedesktop.appearance"; +static constexpr const char* PORTAL_KEY = "color-scheme"; +} // namespace waybar + +using namespace Gio; + +auto fmt::formatter::format(waybar::Appearance c, format_context& ctx) const { + string_view name; + switch (c) { + case waybar::Appearance::LIGHT: + name = "light"; + break; + case waybar::Appearance::DARK: + name = "dark"; + break; + default: + name = "unknown"; + break; + } + return formatter::format(name, ctx); +} + +waybar::Portal::Portal() + : DBus::Proxy(DBus::Connection::get_sync(DBus::BusType::BUS_TYPE_SESSION), PORTAL_BUS_NAME, + PORTAL_OBJ_PATH, PORTAL_INTERFACE), + currentMode(Appearance::UNKNOWN) { + refreshAppearance(); +}; + +void waybar::Portal::refreshAppearance() { + auto params = Glib::Variant>::create( + {PORTAL_NAMESPACE, PORTAL_KEY}); + Glib::VariantBase response; + try { + response = call_sync(std::string(PORTAL_INTERFACE) + ".Read", params); + } catch (const Glib::Error& e) { + spdlog::info("Unable to receive desktop appearance: {}", std::string(e.what())); + return; + } + + // unfortunately, the response is triple-nested, with type (v>), + // so we have cast thrice. This is a variation from the freedesktop standard + // (it should only be doubly nested) but all implementations appear to do so. + // + // xdg-desktop-portal 1.17 will fix this issue with a new `ReadOne` method, + // but this version is not yet released. + // TODO(xdg-desktop-portal v1.17): switch to ReadOne + auto container = Glib::VariantBase::cast_dynamic(response); + Glib::VariantBase modev; + container.get_child(modev, 0); + auto mode = + Glib::VariantBase::cast_dynamic>>>(modev) + .get() + .get() + .get(); + auto newMode = Appearance(mode); + if (newMode == currentMode) { + return; + } + spdlog::info("Discovered appearance '{}'", newMode); + currentMode = newMode; + m_signal_appearance_changed.emit(currentMode); +} + +waybar::Appearance waybar::Portal::getAppearance() { return currentMode; }; + +void waybar::Portal::on_signal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, + const Glib::VariantContainerBase& parameters) { + spdlog::debug("Received signal {}", (std::string)signal_name); + if (signal_name != "SettingChanged" || parameters.get_n_children() != 3) { + return; + } + Glib::VariantBase nspcv, keyv, valuev; + parameters.get_child(nspcv, 0); + parameters.get_child(keyv, 1); + parameters.get_child(valuev, 2); + auto nspc = Glib::VariantBase::cast_dynamic>(nspcv).get(); + auto key = Glib::VariantBase::cast_dynamic>(keyv).get(); + if (nspc != PORTAL_NAMESPACE || key != PORTAL_KEY) { + return; + } + auto value = + Glib::VariantBase::cast_dynamic>>(valuev).get().get(); + auto newMode = Appearance(value); + if (newMode == currentMode) { + return; + } + spdlog::info("Received new appearance '{}'", newMode); + currentMode = newMode; + m_signal_appearance_changed.emit(currentMode); +} From 9bb2c01a44abdbabd9b99e9d3e4408780056893c Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Thu, 7 Sep 2023 13:43:59 +0000 Subject: [PATCH 046/364] clean up client.cpp --- include/client.hpp | 1 - src/client.cpp | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index 8faa7198..9ec29ef3 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -47,7 +47,6 @@ class Client { static void handleOutputDone(void *, struct zxdg_output_v1 *); static void handleOutputName(void *, struct zxdg_output_v1 *, const char *); static void handleOutputDescription(void *, struct zxdg_output_v1 *, const char *); - void handleAppearanceChanged(waybar::Appearance appearance); void handleMonitorAdded(Glib::RefPtr monitor); void handleMonitorRemoved(Glib::RefPtr monitor); void handleDeferredMonitorRemoval(Glib::RefPtr monitor); diff --git a/src/client.cpp b/src/client.cpp index 10073df0..cd0fa55b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -154,9 +154,7 @@ void waybar::Client::handleDeferredMonitorRemoval(Glib::RefPtr mon const std::string waybar::Client::getStyle(const std::string &style, std::optional appearance = std::nullopt) { std::optional css_file; - if (!style.empty()) { - css_file = style; - } else { + if (style.empty()) { std::vector search_files; switch (appearance.value_or(portal->getAppearance())) { case waybar::Appearance::LIGHT: @@ -170,6 +168,8 @@ const std::string waybar::Client::getStyle(const std::string &style, } search_files.push_back("style.css"); css_file = Config::findConfigPath(search_files); + } else { + css_file = style; } if (!css_file) { throw std::runtime_error("Missing required resource files"); From 6c3565c52061f86f0cdbbca116aa4222fb670dc7 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 7 Sep 2023 19:33:35 +0200 Subject: [PATCH 047/364] Add urgent icon Fixes #2476 --- 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 64c7a9ed..ccf75310 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -441,6 +441,13 @@ void Workspaces::sort_workspaces() { } std::string &Workspace::select_icon(std::map &icons_map) { + if (is_urgent()) { + auto urgent_icon_it = icons_map.find("urgent"); + if (urgent_icon_it != icons_map.end()) { + return urgent_icon_it->second; + } + } + if (active()) { auto active_icon_it = icons_map.find("active"); if (active_icon_it != icons_map.end()) { From 2837b720643da5663a63e9d20cd06b5796c3762e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 8 Sep 2023 14:11:02 -0500 Subject: [PATCH 048/364] fix: rename workspace active fix --- src/modules/hyprland/workspaces.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index ccf75310..82442d53 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -163,6 +163,9 @@ void Workspaces::onEvent(const std::string &ev) { std::string new_name = payload.substr(payload.find(',') + 1); for (auto &workspace : workspaces_) { if (workspace->id() == workspace_id) { + if (workspace->name() == active_workspace_name_) { + active_workspace_name_ = new_name; + } workspace->set_name(new_name); break; } From 587bd0cd628a3cc5c7a333acdba31a5503d1e2d4 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 8 Sep 2023 22:24:05 -0500 Subject: [PATCH 049/364] refactor: cleanup hyprland workspaces constructor --- include/modules/hyprland/workspaces.hpp | 2 ++ src/modules/hyprland/workspaces.cpp | 27 +++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 270c1e36..93faf494 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -80,6 +80,8 @@ class Workspaces : public AModule, public EventHandler { void create_workspace(Json::Value& value); void remove_workspace(std::string name); void set_urgent_workspace(std::string windowaddress); + void parse_config(const Json::Value& config); + void register_ipc(); bool all_outputs_ = false; bool show_special_ = false; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 82442d53..504a6caa 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -14,6 +14,20 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value : AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { + parse_config(config); + + box_.set_name("workspaces"); + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + event_box_.add(box_); + + register_ipc(); + + init(); +} + +auto Workspaces::parse_config(const Json::Value &config) -> void { Json::Value config_format = config["format"]; format_ = config_format.isString() ? config_format.asString() : "{name}"; @@ -43,18 +57,19 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value active_only_ = config_active_only.asBool(); } - box_.set_name("workspaces"); - if (!id.empty()) { - box_.get_style_context()->add_class(id); + auto config_sort_by = config_["sort-by"]; + if (config_sort_by.isString()) { + sort_by = config_sort_by.asString(); } - event_box_.add(box_); +} + +auto Workspaces::register_ipc() -> void { modulesReady = true; + if (!gIPC) { gIPC = std::make_unique(); } - init(); - gIPC->registerForIPC("workspace", this); gIPC->registerForIPC("createworkspace", this); gIPC->registerForIPC("destroyworkspace", this); From cbc12e544355e0df311c5e44216639970b7b72ab Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 8 Sep 2023 22:24:28 -0500 Subject: [PATCH 050/364] feat: hyprland workspaces add sort-by --- include/modules/hyprland/workspaces.hpp | 1 + man/waybar-hyprland-workspaces.5.scd | 8 +++ src/modules/hyprland/workspaces.cpp | 71 +++++++++++++++---------- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 93faf494..3b8de4ae 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -86,6 +86,7 @@ class Workspaces : public AModule, public EventHandler { bool all_outputs_ = false; bool show_special_ = false; bool active_only_ = false; + std::string sort_by = "default"; void fill_persistent_workspaces(); void create_persistent_workspaces(); diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 13764752..e0caf80f 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -36,6 +36,14 @@ Addressed by *hyprland/workspaces* default: false ++ If set to true, only the active workspace will be shown. +*sort-by*: ++ + typeof: string ++ + default: "default" ++ + If set to number, workspaces will sort by number. + If set to name, workspaces will sort by name. + If set to id, workspaces will sort by id. + If none of those, workspaces will sort with default behavior. + # FORMAT REPLACEMENTS *{id}*: id of workspace assigned by compositor diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 504a6caa..517de1be 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -421,36 +421,53 @@ void Workspace::update(const std::string &format, const std::string &icon) { void Workspaces::sort_workspaces() { std::sort(workspaces_.begin(), workspaces_.end(), - [](std::unique_ptr &a, std::unique_ptr &b) { - // normal -> named persistent -> named -> special -> named special + [&](std::unique_ptr &a, std::unique_ptr &b) { + // Helper comparisons + auto is_id_less = a->id() < b->id(); + auto is_name_less = a->name() < b->name(); + auto is_number_less = std::stoi(a->name()) < std::stoi(b->name()); - // both normal (includes numbered persistent) => sort by ID - if (a->id() > 0 && b->id() > 0) { - return a->id() < b->id(); - } - - // one normal, one special => normal first - if ((a->is_special()) ^ (b->is_special())) { - return b->is_special(); - } - - // only one normal, one named - if ((a->id() > 0) ^ (b->id() > 0)) { - return a->id() > 0; - } - - // both special - if (a->is_special() && b->is_special()) { - // if one is -99 => put it last - if (a->id() == -99 || b->id() == -99) { - return b->id() == -99; + if (sort_by == "number") { + try { + return is_number_less; + } catch (const std::invalid_argument &) { } - // both are 0 (not yet named persistents) / both are named specials (-98 <= ID <=-1) - return a->name() < b->name(); - } + } else if (sort_by == "name") { + return is_name_less; + } else if (sort_by == "id") { + return is_id_less; + } else { + // normal -> named persistent -> named -> special -> named special - // sort non-special named workspaces by name (ID <= -1377) - return a->name() < b->name(); + // both normal (includes numbered persistent) => sort by ID + if (a->id() > 0 && b->id() > 0) { + return is_id_less; + } + + // one normal, one special => normal first + if ((a->is_special()) ^ (b->is_special())) { + return b->is_special(); + } + + // only one normal, one named + if ((a->id() > 0) ^ (b->id() > 0)) { + return a->id() > 0; + } + + // both special + if (a->is_special() && b->is_special()) { + // if one is -99 => put it last + 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) + return is_name_less; + } + + // sort non-special named workspaces by name (ID <= -1377) + return is_name_less; + } }); for (size_t i = 0; i < workspaces_.size(); ++i) { From 65ba449460bea5cd3a7008fe3777a76a0952d453 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 8 Sep 2023 23:17:21 -0500 Subject: [PATCH 051/364] chore: update man page index --- man/waybar.5.scd.in | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index a8376697..92b365d9 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -273,28 +273,39 @@ Valid options for the (optional) "orientation" property are: "horizontal", "vert - *waybar-cpu(5)* - *waybar-custom(5)* - *waybar-disk(5)* +- *waybar-dwl-tags(5)* +- *waybar-gamemode(5)* +- *waybar-hyprland-language(5)* +- *waybar-hyprland-submap(5)* +- *waybar-hyprland-window(5)* +- *waybar-hyprland-workspaces(5)* - *waybar-idle-inhibitor(5)* - *waybar-image(5)* +- *waybar-inhibitor(5)* +- *waybar-jack(5)* - *waybar-keyboard-state(5)* - *waybar-memory(5)* - *waybar-mpd(5)* - *waybar-mpris(5)* - *waybar-network(5)* - *waybar-pulseaudio(5)* +- *waybar-river-layout(5)* - *waybar-river-mode(5)* - *waybar-river-tags(5)* - *waybar-river-window(5)* -- *waybar-river-layout(5)* +- *waybar-sndio(5)* - *waybar-states(5)* +- *waybar-sway-language(5)* - *waybar-sway-mode(5)* - *waybar-sway-scratchpad(5)* - *waybar-sway-window(5)* - *waybar-sway-workspaces(5)* +- *waybar-temperature(5)* +- *waybar-tray(5)* +- *waybar-upower(5)* - *waybar-wireplumber(5)* - *waybar-wlr-taskbar(5)* - *waybar-wlr-workspaces(5)* -- *waybar-temperature(5)* -- *waybar-tray(5)* # SEE ALSO From 8ea2626de8058348a36f752da4a2c1fafa05a538 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 09:32:55 -0500 Subject: [PATCH 052/364] refactor: sort-by enum hyprland --- include/modules/hyprland/workspaces.hpp | 4 +- include/util/enum.hpp | 28 ++++++++ src/modules/hyprland/workspaces.cpp | 92 +++++++++++++++---------- 3 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 include/util/enum.hpp diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 3b8de4ae..9cd17f88 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -11,6 +11,7 @@ #include "AModule.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" +#include "util/enum.hpp" namespace waybar::modules::hyprland { @@ -86,7 +87,8 @@ class Workspaces : public AModule, public EventHandler { bool all_outputs_ = false; bool show_special_ = false; bool active_only_ = false; - std::string sort_by = "default"; + util::EnumParser enum_parser_; + util::EnumParser::SORT_METHOD sort_by_ = util::EnumParser::SORT_METHOD::DEFAULT; void fill_persistent_workspaces(); void create_persistent_workspaces(); diff --git a/include/util/enum.hpp b/include/util/enum.hpp new file mode 100644 index 00000000..a1cb6f6c --- /dev/null +++ b/include/util/enum.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +namespace waybar::util { + +struct EnumParser { + EnumParser() {} + + enum SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; + + SORT_METHOD sortStringToEnum(const std::string& str) { + static const std::map enumMap = { + {"ID", ID}, {"NAME", NAME}, {"NUMBER", NUMBER}, {"DEFAULT", DEFAULT}}; + + auto it = enumMap.find(str); + if (it != enumMap.end()) { + return it->second; + } else { + throw std::invalid_argument("Invalid string representation for enum"); + } + } + + ~EnumParser() = default; +}; +} // namespace waybar::util diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 517de1be..9eda2c39 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -59,7 +59,14 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { auto config_sort_by = config_["sort-by"]; if (config_sort_by.isString()) { - sort_by = config_sort_by.asString(); + auto sort_by_str = config_sort_by.asString(); + try { + sort_by_ = enum_parser_.sortStringToEnum(sort_by_str); + } catch (const std::invalid_argument &e) { + // Handle the case where the string is not a valid enum representation. + sort_by_ = util::EnumParser::SORT_METHOD::DEFAULT; + g_warning("Invalid string representation for sort-by. Falling back to default sort method."); + } } } @@ -427,47 +434,56 @@ void Workspaces::sort_workspaces() { auto is_name_less = a->name() < b->name(); auto is_number_less = std::stoi(a->name()) < std::stoi(b->name()); - if (sort_by == "number") { - try { - return is_number_less; - } catch (const std::invalid_argument &) { - } - } else if (sort_by == "name") { - return is_name_less; - } else if (sort_by == "id") { - return is_id_less; - } else { - // normal -> named persistent -> named -> special -> named special - - // both normal (includes numbered persistent) => sort by ID - if (a->id() > 0 && b->id() > 0) { + switch (sort_by_) { + case util::EnumParser::SORT_METHOD::ID: return is_id_less; - } - - // one normal, one special => normal first - if ((a->is_special()) ^ (b->is_special())) { - return b->is_special(); - } - - // only one normal, one named - if ((a->id() > 0) ^ (b->id() > 0)) { - return a->id() > 0; - } - - // both special - if (a->is_special() && b->is_special()) { - // if one is -99 => put it last - 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) + case util::EnumParser::SORT_METHOD::NAME: return is_name_less; - } + case util::EnumParser::SORT_METHOD::NUMBER: + try { + return is_number_less; + } catch (const std::invalid_argument &) { + // Handle the exception if necessary. + break; + } + case util::EnumParser::SORT_METHOD::DEFAULT: + default: + // Handle the default case here. + // normal -> named persistent -> named -> special -> named special - // sort non-special named workspaces by name (ID <= -1377) - return is_name_less; + // both normal (includes numbered persistent) => sort by ID + if (a->id() > 0 && b->id() > 0) { + return is_id_less; + } + + // one normal, one special => normal first + if ((a->is_special()) ^ (b->is_special())) { + return b->is_special(); + } + + // only one normal, one named + if ((a->id() > 0) ^ (b->id() > 0)) { + return a->id() > 0; + } + + // both special + if (a->is_special() && b->is_special()) { + // if one is -99 => put it last + 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) + return is_name_less; + } + + // sort non-special named workspaces by name (ID <= -1377) + return is_name_less; + break; } + + // Return a default value if none of the cases match. + return is_name_less; // You can adjust this to your specific needs. }); for (size_t i = 0; i < workspaces_.size(); ++i) { From 8ce64ea784ca3345735cef7de9464583abd965dc Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 09:38:03 -0500 Subject: [PATCH 053/364] refactor: make parsing sort-by more lenient --- include/util/enum.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/util/enum.hpp b/include/util/enum.hpp index a1cb6f6c..dcf0e45c 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -12,10 +12,16 @@ struct EnumParser { enum SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; SORT_METHOD sortStringToEnum(const std::string& str) { + // Convert the input string to uppercase (make it lenient on config input) + std::string uppercaseStr; + for (char c : str) { + uppercaseStr += std::toupper(c); + } + static const std::map enumMap = { {"ID", ID}, {"NAME", NAME}, {"NUMBER", NUMBER}, {"DEFAULT", DEFAULT}}; - auto it = enumMap.find(str); + auto it = enumMap.find(uppercaseStr); if (it != enumMap.end()) { return it->second; } else { From 2b8c92e8fdbe98282a625741abf8d9b21f8b969f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 11:18:12 -0500 Subject: [PATCH 054/364] refactor: enum utility allow overriding --- include/modules/hyprland/workspaces.hpp | 10 ++++++++-- include/util/enum.hpp | 15 +++++++-------- src/modules/hyprland/workspaces.cpp | 12 ++++++------ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 9cd17f88..7c4d919e 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -87,8 +87,14 @@ class Workspaces : public AModule, public EventHandler { bool all_outputs_ = false; bool show_special_ = false; bool active_only_ = false; - util::EnumParser enum_parser_; - util::EnumParser::SORT_METHOD sort_by_ = util::EnumParser::SORT_METHOD::DEFAULT; + + enum SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; + util::EnumParser enum_parser_; + SORT_METHOD sort_by_ = SORT_METHOD::DEFAULT; + std::map sort_map_ = {{"ID", SORT_METHOD::ID}, + {"NAME", SORT_METHOD::NAME}, + {"NUMBER", SORT_METHOD::NUMBER}, + {"DEFAULT", SORT_METHOD::DEFAULT}}; void fill_persistent_workspaces(); void create_persistent_workspaces(); diff --git a/include/util/enum.hpp b/include/util/enum.hpp index dcf0e45c..7ee80694 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -1,26 +1,25 @@ #pragma once +#include #include #include +#include #include namespace waybar::util { +template struct EnumParser { EnumParser() {} - enum SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; - - SORT_METHOD sortStringToEnum(const std::string& str) { - // Convert the input string to uppercase (make it lenient on config input) + EnumType sortStringToEnum(const std::string& str, + const std::map& enumMap) { + // Convert the input string to uppercase std::string uppercaseStr; for (char c : str) { - uppercaseStr += std::toupper(c); + uppercaseStr += std::toupper(c); } - static const std::map enumMap = { - {"ID", ID}, {"NAME", NAME}, {"NUMBER", NUMBER}, {"DEFAULT", DEFAULT}}; - auto it = enumMap.find(uppercaseStr); if (it != enumMap.end()) { return it->second; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 9eda2c39..21a2a8f7 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -61,10 +61,10 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { if (config_sort_by.isString()) { auto sort_by_str = config_sort_by.asString(); try { - sort_by_ = enum_parser_.sortStringToEnum(sort_by_str); + sort_by_ = enum_parser_.sortStringToEnum(sort_by_str, sort_map_); } catch (const std::invalid_argument &e) { // Handle the case where the string is not a valid enum representation. - sort_by_ = util::EnumParser::SORT_METHOD::DEFAULT; + sort_by_ = SORT_METHOD::DEFAULT; g_warning("Invalid string representation for sort-by. Falling back to default sort method."); } } @@ -435,18 +435,18 @@ void Workspaces::sort_workspaces() { auto is_number_less = std::stoi(a->name()) < std::stoi(b->name()); switch (sort_by_) { - case util::EnumParser::SORT_METHOD::ID: + case SORT_METHOD::ID: return is_id_less; - case util::EnumParser::SORT_METHOD::NAME: + case SORT_METHOD::NAME: return is_name_less; - case util::EnumParser::SORT_METHOD::NUMBER: + case SORT_METHOD::NUMBER: try { return is_number_less; } catch (const std::invalid_argument &) { // Handle the exception if necessary. break; } - case util::EnumParser::SORT_METHOD::DEFAULT: + case SORT_METHOD::DEFAULT: default: // Handle the default case here. // normal -> named persistent -> named -> special -> named special From 3ae2fe3272782af0459e40f2605d88927fc86678 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 12:02:56 -0500 Subject: [PATCH 055/364] refactor: PR review cleanup --- include/modules/hyprland/workspaces.hpp | 2 +- include/util/enum.hpp | 21 ++++++++++----------- src/modules/hyprland/workspaces.cpp | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 7c4d919e..14b9ba0e 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -88,7 +88,7 @@ class Workspaces : public AModule, public EventHandler { bool show_special_ = false; bool active_only_ = false; - enum SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; + enum class SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; util::EnumParser enum_parser_; SORT_METHOD sort_by_ = SORT_METHOD::DEFAULT; std::map sort_map_ = {{"ID", SORT_METHOD::ID}, diff --git a/include/util/enum.hpp b/include/util/enum.hpp index 7ee80694..a4239bd2 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -12,20 +12,19 @@ template struct EnumParser { EnumParser() {} - EnumType sortStringToEnum(const std::string& str, - const std::map& enumMap) { + EnumType parseStringToEnum(const std::string& str, + const std::map& enumMap) { // Convert the input string to uppercase - std::string uppercaseStr; - for (char c : str) { - uppercaseStr += std::toupper(c); - } + std::string uppercaseStr = str; + std::transform(uppercaseStr.begin(), uppercaseStr.end(), uppercaseStr.begin(), + [](unsigned char c) { return std::toupper(c); }); + // Return enum match of string auto it = enumMap.find(uppercaseStr); - if (it != enumMap.end()) { - return it->second; - } else { - throw std::invalid_argument("Invalid string representation for enum"); - } + if (it != enumMap.end()) return it->second; + + // Throw error if it doesnt return + throw std::invalid_argument("Invalid string representation for enum"); } ~EnumParser() = default; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 21a2a8f7..91da69de 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -61,7 +61,7 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { if (config_sort_by.isString()) { auto sort_by_str = config_sort_by.asString(); try { - sort_by_ = enum_parser_.sortStringToEnum(sort_by_str, sort_map_); + sort_by_ = enum_parser_.parseStringToEnum(sort_by_str, sort_map_); } catch (const std::invalid_argument &e) { // Handle the case where the string is not a valid enum representation. sort_by_ = SORT_METHOD::DEFAULT; From 2fee12d930eda56310276e0ef80f10a70a093868 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 12:14:52 -0500 Subject: [PATCH 056/364] fix: enumparser capitalize everything to avoid issues --- include/util/enum.hpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/include/util/enum.hpp b/include/util/enum.hpp index a4239bd2..6f402614 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -12,12 +12,26 @@ template struct EnumParser { EnumParser() {} + // Helper function to capitalize a string + std::string capitalizeString(const std::string& str) { + std::string result = str; + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return std::toupper(c); }); + return result; + } + EnumType parseStringToEnum(const std::string& str, const std::map& enumMap) { // Convert the input string to uppercase - std::string uppercaseStr = str; - std::transform(uppercaseStr.begin(), uppercaseStr.end(), uppercaseStr.begin(), - [](unsigned char c) { return std::toupper(c); }); + std::string uppercaseStr = capitalizeString(str); + + // Capitalize the map keys before searching + std::map capitalizedEnumMap; + std::transform(enumMap.begin(), enumMap.end(), + std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()), + [this](const auto& pair) { + return std::make_pair(capitalizeString(pair.first), pair.second); + }); // Return enum match of string auto it = enumMap.find(uppercaseStr); From b8630968b262726b01c00a34a65907ca86eed784 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 13:23:17 -0500 Subject: [PATCH 057/364] refactor: move capitalize string helper --- include/util/enum.hpp | 20 ++++++-------------- include/util/string.hpp | 8 ++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/util/enum.hpp b/include/util/enum.hpp index 6f402614..951cace3 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -6,32 +6,24 @@ #include #include +#include "util/string.hpp" + namespace waybar::util { template struct EnumParser { EnumParser() {} - // Helper function to capitalize a string - std::string capitalizeString(const std::string& str) { - std::string result = str; - std::transform(result.begin(), result.end(), result.begin(), - [](unsigned char c) { return std::toupper(c); }); - return result; - } - EnumType parseStringToEnum(const std::string& str, const std::map& enumMap) { // Convert the input string to uppercase - std::string uppercaseStr = capitalizeString(str); + std::string uppercaseStr = capitalize(str); // Capitalize the map keys before searching std::map capitalizedEnumMap; - std::transform(enumMap.begin(), enumMap.end(), - std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()), - [this](const auto& pair) { - return std::make_pair(capitalizeString(pair.first), pair.second); - }); + std::transform( + enumMap.begin(), enumMap.end(), std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()), + [this](const auto& pair) { return std::make_pair(capitalize(pair.first), pair.second); }); // Return enum match of string auto it = enumMap.find(uppercaseStr); diff --git a/include/util/string.hpp b/include/util/string.hpp index 24a9b2b9..d06557c1 100644 --- a/include/util/string.hpp +++ b/include/util/string.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include const std::string WHITESPACE = " \n\r\t\f\v"; @@ -15,3 +16,10 @@ inline std::string rtrim(const std::string& s) { } inline std::string trim(const std::string& s) { return rtrim(ltrim(s)); } + +inline std::string capitalize(const std::string& str) { + std::string result = str; + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return std::toupper(c); }); + return result; +} From 79cf33b9f1643d8dca5809ae8ca208d234a8e071 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 17:48:36 -0500 Subject: [PATCH 058/364] refactor: enumparser create implementation file --- include/util/enum.hpp | 29 +++++----------------------- meson.build | 1 + src/util/enum.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 src/util/enum.cpp diff --git a/include/util/enum.hpp b/include/util/enum.hpp index 951cace3..681385fd 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -1,38 +1,19 @@ #pragma once -#include -#include #include #include #include -#include "util/string.hpp" - namespace waybar::util { template struct EnumParser { - EnumParser() {} + public: + EnumParser(); + ~EnumParser(); EnumType parseStringToEnum(const std::string& str, - const std::map& enumMap) { - // Convert the input string to uppercase - std::string uppercaseStr = capitalize(str); - - // Capitalize the map keys before searching - std::map capitalizedEnumMap; - std::transform( - enumMap.begin(), enumMap.end(), std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()), - [this](const auto& pair) { return std::make_pair(capitalize(pair.first), pair.second); }); - - // Return enum match of string - auto it = enumMap.find(uppercaseStr); - if (it != enumMap.end()) return it->second; - - // Throw error if it doesnt return - throw std::invalid_argument("Invalid string representation for enum"); - } - - ~EnumParser() = default; + const std::map& enumMap); }; + } // namespace waybar::util diff --git a/meson.build b/meson.build index e71807ec..b27bc05b 100644 --- a/meson.build +++ b/meson.build @@ -171,6 +171,7 @@ src_files = files( 'src/client.cpp', 'src/config.cpp', 'src/group.cpp', + 'src/util/enum.cpp', 'src/util/prepare_for_sleep.cpp', 'src/util/ustring_clen.cpp', 'src/util/sanitize_str.cpp', diff --git a/src/util/enum.cpp b/src/util/enum.cpp new file mode 100644 index 00000000..a29304c5 --- /dev/null +++ b/src/util/enum.cpp @@ -0,0 +1,45 @@ +#include "util/enum.hpp" + +#include // for std::transform +#include // for std::toupper +#include +#include +#include +#include + +#include "modules/hyprland/workspaces.hpp" +#include "util/string.hpp" + +namespace waybar::util { + +template +EnumParser::EnumParser() = default; + +template +EnumParser::~EnumParser() = default; + +template +EnumType EnumParser::parseStringToEnum(const std::string& str, + const std::map& enumMap) { + // Convert the input string to uppercase + std::string uppercaseStr = capitalize(str); + + // Capitalize the map keys before searching + std::map capitalizedEnumMap; + std::transform( + enumMap.begin(), enumMap.end(), std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()), + [this](const auto& pair) { return std::make_pair(capitalize(pair.first), pair.second); }); + + // Return enum match of string + auto it = capitalizedEnumMap.find(uppercaseStr); + if (it != capitalizedEnumMap.end()) return it->second; + + // Throw error if it doesn't return + throw std::invalid_argument("Invalid string representation for enum"); +} + +// Explicit instantiations for specific EnumType types you intend to use +// Add explicit instantiations for all relevant EnumType types +template struct EnumParser; + +} // namespace waybar::util From 60611e9c2b098b3a7a63417de3624f399e0b8c0b Mon Sep 17 00:00:00 2001 From: KanuX-14 Date: Sun, 10 Sep 2023 14:41:40 -0300 Subject: [PATCH 059/364] Fix battery not showing for some devices Adds 'bat-compatibility' boolean checking from configuration file. --- src/modules/battery.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 3ced890d..96dbdbfa 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -100,9 +100,11 @@ void waybar::modules::Battery::refreshBatteries() { } auto dir_name = node.path().filename(); auto bat_defined = config_["bat"].isString(); + bool bat_compatibility = config_["bat-compatibility"].asBool(); if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) && (fs::exists(node.path() / "capacity") || fs::exists(node.path() / "charge_now")) && - fs::exists(node.path() / "uevent") && fs::exists(node.path() / "status") && + fs::exists(node.path() / "uevent") && + (fs::exists(node.path() / "status") || bat_compatibility) && fs::exists(node.path() / "type")) { std::string type; std::ifstream(node.path() / "type") >> type; From 1ff4464b2f62c30bdfebffa71fc05fdc703ff828 Mon Sep 17 00:00:00 2001 From: KanuX-14 Date: Sun, 10 Sep 2023 15:16:43 -0300 Subject: [PATCH 060/364] Use adapter status if battery status is inexistent --- src/modules/battery.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 96dbdbfa..757a7ded 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -254,7 +254,13 @@ const std::tuple waybar::modules::Battery::g for (auto const& item : batteries_) { auto bat = item.first; std::string _status; - std::getline(std::ifstream(bat / "status"), _status); + + /* Check for adapter status if battery is not available */ + if(!std::ifstream(bat / "status")) { + std::getline(std::ifstream(adapter_ / "status"), _status); + } else { + std::getline(std::ifstream(bat / "status"), _status); + } // Some battery will report current and charge in μA/μAh. // Scale these by the voltage to get μW/μWh. From 28a2d15fef237019e3a55dc9e2d985a740166727 Mon Sep 17 00:00:00 2001 From: KanuX-14 Date: Tue, 12 Sep 2023 13:39:09 -0300 Subject: [PATCH 061/364] Update 'bat-compatibility' option to manual --- man/waybar-battery.5.scd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 52bc9f64..b13cee6e 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -100,6 +100,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y default: true ++ Option to disable tooltip on hover. +*bat-compatibility*: ++ + typeof: bool ++ + default: false ++ + Option to enable battery compatibility if not detected. + # FORMAT REPLACEMENTS *{capacity}*: Capacity in percentage From 69736d68aab96ad75e3980990a8a5d034c80afab Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Wed, 13 Sep 2023 18:20:13 +0200 Subject: [PATCH 062/364] Update workspaces.cpp Fix unchecked string to int conversion of workspace name (which can be a string) Closes #2501 --- src/modules/hyprland/workspaces.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index b7ffa5f8..f38b5050 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -439,7 +439,6 @@ void Workspaces::sort_workspaces() { // Helper comparisons auto is_id_less = a->id() < b->id(); auto is_name_less = a->name() < b->name(); - auto is_number_less = std::stoi(a->name()) < std::stoi(b->name()); switch (sort_by_) { case SORT_METHOD::ID: @@ -448,7 +447,7 @@ void Workspaces::sort_workspaces() { return is_name_less; case SORT_METHOD::NUMBER: try { - return is_number_less; + return std::stoi(a->name()) < std::stoi(b->name()); } catch (const std::invalid_argument &) { // Handle the exception if necessary. break; From 729564cc27a753aacb1bc553c89ca6857fbc4c5c Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Wed, 21 Dec 2022 17:44:11 +0100 Subject: [PATCH 063/364] Introduced separate load module The module provides the three system load averages. This is an improvement compared what you can do with the cpu module: cpu only provides the one minute sample and the state of the cpu module is derived from the cpu usage which messes up the formating of the load average. Also, at least on modern Linux systems, the load of a system takes much more than the cpu utilization into account and it should therefore live in a separate module. --- include/factory.hpp | 1 + include/modules/load.hpp | 29 +++++++++++++++++++ meson.build | 1 + src/factory.cpp | 3 ++ src/modules/load.cpp | 61 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+) create mode 100644 include/modules/load.hpp create mode 100644 src/modules/load.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 90d0ac1d..217f4122 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -38,6 +38,7 @@ #endif #if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD) #include "modules/cpu.hpp" +#include "modules/load.hpp" #endif #include "modules/idle_inhibitor.hpp" #if defined(HAVE_MEMORY_LINUX) || defined(HAVE_MEMORY_BSD) diff --git a/include/modules/load.hpp b/include/modules/load.hpp new file mode 100644 index 00000000..39df4131 --- /dev/null +++ b/include/modules/load.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include "AButton.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class Load : public AButton { + public: + Load(const std::string&, const Json::Value&); + ~Load() = default; + auto update() -> void; + + private: + std::tuple getLoad(); + + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index 9ccd83d8..fc6aa738 100644 --- a/meson.build +++ b/meson.build @@ -164,6 +164,7 @@ src_files = files( 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', 'src/modules/image.cpp', + 'src/modules/load.cpp', 'src/modules/temperature.cpp', 'src/modules/user.cpp', 'src/main.cpp', diff --git a/src/factory.cpp b/src/factory.cpp index 1d7a00b5..965c57d5 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -99,6 +99,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "cpu") { return new waybar::modules::Cpu(id, config_[name]); } + if (ref == "load") { + return new waybar::modules::Load(id, config_[name]); + } #endif if (ref == "clock") { return new waybar::modules::Clock(id, config_[name]); diff --git a/src/modules/load.cpp b/src/modules/load.cpp new file mode 100644 index 00000000..98bc1302 --- /dev/null +++ b/src/modules/load.cpp @@ -0,0 +1,61 @@ +#include "modules/load.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 + +waybar::modules::Load::Load(const std::string& id, const Json::Value& config) + : AButton(config, "load", id, "{load1}", 10) { + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +auto waybar::modules::Load::update() -> void { + // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both + auto [load1, load5, load15] = getLoad(); + if (tooltipEnabled()) { + auto tooltip = fmt::format("Load 1: {}\nLoad 5: {}\nLoad 15: {}", load1, load5, load15); + button_.set_tooltip_text(tooltip); + } + auto format = format_; + auto state = getState(load1); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + auto icons = std::vector{state}; + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("load1", load1)); + store.push_back(fmt::arg("load5", load5)); + store.push_back(fmt::arg("load15", load15)); + store.push_back(fmt::arg("icon1", getIcon(load1, icons))); + store.push_back(fmt::arg("icon5", getIcon(load5, icons))); + store.push_back(fmt::arg("icon15", getIcon(load15, icons))); + label_->set_markup(fmt::vformat(format, store)); + } + + // Call parent update + AButton::update(); +} + +std::tuple waybar::modules::Load::getLoad() { + double load[3]; + if (getloadavg(load, 3) != -1) { + double load1 = std::ceil(load[0] * 100.0) / 100.0; + double load5 = std::ceil(load[1] * 100.0) / 100.0; + double load15 = std::ceil(load[2] * 100.0) / 100.0; + return {load1, load5, load15}; + } + throw std::runtime_error("Can't get Cpu load"); +} From c36fe3a0041ad51fea33f92148e5a62cf2571923 Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Wed, 21 Dec 2022 17:45:53 +0100 Subject: [PATCH 064/364] Introduce cpu_frequency module --- include/factory.hpp | 1 + include/modules/cpu_frequency.hpp | 30 +++++++++++++ meson.build | 2 + src/factory.cpp | 3 ++ src/modules/cpu_frequency/common.cpp | 67 ++++++++++++++++++++++++++++ src/modules/cpu_frequency/linux.cpp | 47 +++++++++++++++++++ 6 files changed, 150 insertions(+) create mode 100644 include/modules/cpu_frequency.hpp create mode 100644 src/modules/cpu_frequency/common.cpp create mode 100644 src/modules/cpu_frequency/linux.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 217f4122..ff72749e 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -38,6 +38,7 @@ #endif #if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD) #include "modules/cpu.hpp" +#include "modules/cpu_frequency.hpp" #include "modules/load.hpp" #endif #include "modules/idle_inhibitor.hpp" diff --git a/include/modules/cpu_frequency.hpp b/include/modules/cpu_frequency.hpp new file mode 100644 index 00000000..84475722 --- /dev/null +++ b/include/modules/cpu_frequency.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include "AButton.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class CpuFrequency : public AButton { + public: + CpuFrequency(const std::string&, const Json::Value&); + ~CpuFrequency() = default; + auto update() -> void; + + private: + std::tuple getCpuFrequency(); + std::vector parseCpuFrequencies(); + + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index fc6aa738..a20748d1 100644 --- a/meson.build +++ b/meson.build @@ -190,6 +190,8 @@ if is_linux 'src/modules/battery.cpp', 'src/modules/cpu/common.cpp', 'src/modules/cpu/linux.cpp', + 'src/modules/cpu_frequency/common.cpp', + 'src/modules/cpu_frequency/linux.cpp', 'src/modules/memory/common.cpp', 'src/modules/memory/linux.cpp', ) diff --git a/src/factory.cpp b/src/factory.cpp index 965c57d5..b0b9af47 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -99,6 +99,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "cpu") { return new waybar::modules::Cpu(id, config_[name]); } + if (ref == "cpu_frequency") { + return new waybar::modules::CpuFrequency(id, config_[name]); + } if (ref == "load") { return new waybar::modules::Load(id, config_[name]); } diff --git a/src/modules/cpu_frequency/common.cpp b/src/modules/cpu_frequency/common.cpp new file mode 100644 index 00000000..13aa4d27 --- /dev/null +++ b/src/modules/cpu_frequency/common.cpp @@ -0,0 +1,67 @@ +#include "modules/cpu_frequency.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 + +waybar::modules::CpuFrequency::CpuFrequency(const std::string& id, const Json::Value& config) + : AButton(config, "cpu_frequency", id, "{avg_frequency}", 10) { + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +auto waybar::modules::CpuFrequency::update() -> void { + // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both + auto [max_frequency, min_frequency, avg_frequency] = getCpuFrequency(); + if (tooltipEnabled()) { + auto tooltip = + fmt::format("Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n", + min_frequency, avg_frequency, max_frequency); + button_.set_tooltip_text(tooltip); + } + auto format = format_; + auto state = getState(avg_frequency); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + auto icons = std::vector{state}; + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("icon", getIcon(avg_frequency, icons))); + store.push_back(fmt::arg("max_frequency", max_frequency)); + store.push_back(fmt::arg("min_frequency", min_frequency)); + store.push_back(fmt::arg("avg_frequency", avg_frequency)); + label_->set_markup(fmt::vformat(format, store)); + } + + // Call parent update + AButton::update(); +} + +std::tuple waybar::modules::CpuFrequency::getCpuFrequency() { + std::vector frequencies = parseCpuFrequencies(); + if (frequencies.empty()) { + return {0.f, 0.f, 0.f}; + } + auto [min, max] = std::minmax_element(std::begin(frequencies), std::end(frequencies)); + float avg_frequency = + std::accumulate(std::begin(frequencies), std::end(frequencies), 0.0) / frequencies.size(); + + // Round frequencies with double decimal precision to get GHz + float max_frequency = std::ceil(*max / 10.0) / 100.0; + float min_frequency = std::ceil(*min / 10.0) / 100.0; + avg_frequency = std::ceil(avg_frequency / 10.0) / 100.0; + + return {max_frequency, min_frequency, avg_frequency}; +} diff --git a/src/modules/cpu_frequency/linux.cpp b/src/modules/cpu_frequency/linux.cpp new file mode 100644 index 00000000..1f368789 --- /dev/null +++ b/src/modules/cpu_frequency/linux.cpp @@ -0,0 +1,47 @@ +#include + +#include "modules/cpu_frequency.hpp" + +std::vector waybar::modules::CpuFrequency::parseCpuFrequencies() { + const std::string file_path_ = "/proc/cpuinfo"; + std::ifstream info(file_path_); + if (!info.is_open()) { + throw std::runtime_error("Can't open " + file_path_); + } + std::vector frequencies; + std::string line; + while (getline(info, line)) { + if (line.substr(0, 7).compare("cpu MHz") != 0) { + continue; + } + + std::string frequency_str = line.substr(line.find(":") + 2); + float frequency = std::strtol(frequency_str.c_str(), nullptr, 10); + frequencies.push_back(frequency); + } + info.close(); + + if (frequencies.size() <= 0) { + std::string cpufreq_dir = "/sys/devices/system/cpu/cpufreq"; + if (std::filesystem::exists(cpufreq_dir)) { + std::vector frequency_files = {"/cpuinfo_min_freq", "/cpuinfo_max_freq"}; + for (auto& p : std::filesystem::directory_iterator(cpufreq_dir)) { + for (auto freq_file : frequency_files) { + std::string freq_file_path = p.path().string() + freq_file; + if (std::filesystem::exists(freq_file_path)) { + std::string freq_value; + std::ifstream freq(freq_file_path); + if (freq.is_open()) { + getline(freq, freq_value); + float frequency = std::strtol(freq_value.c_str(), nullptr, 10); + frequencies.push_back(frequency / 1000); + freq.close(); + } + } + } + } + } + } + + return frequencies; +} From 888adb57ec8c87b4a32a5f335e6116e1aba2c18e Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Wed, 21 Dec 2022 17:47:06 +0100 Subject: [PATCH 065/364] Introduce cpu_usage module --- include/factory.hpp | 1 + include/modules/cpu_usage.hpp | 32 ++++++++++ meson.build | 4 ++ src/factory.cpp | 3 + src/modules/cpu_usage/bsd.cpp | 102 +++++++++++++++++++++++++++++++ src/modules/cpu_usage/common.cpp | 78 +++++++++++++++++++++++ src/modules/cpu_usage/linux.cpp | 31 ++++++++++ 7 files changed, 251 insertions(+) create mode 100644 include/modules/cpu_usage.hpp create mode 100644 src/modules/cpu_usage/bsd.cpp create mode 100644 src/modules/cpu_usage/common.cpp create mode 100644 src/modules/cpu_usage/linux.cpp diff --git a/include/factory.hpp b/include/factory.hpp index ff72749e..cb25078d 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -39,6 +39,7 @@ #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" diff --git a/include/modules/cpu_usage.hpp b/include/modules/cpu_usage.hpp new file mode 100644 index 00000000..0e57124a --- /dev/null +++ b/include/modules/cpu_usage.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include "AButton.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class CpuUsage : public AButton { + public: + CpuUsage(const std::string&, const Json::Value&); + ~CpuUsage() = default; + auto update() -> void; + + private: + std::tuple, std::string> getCpuUsage(); + std::vector> parseCpuinfo(); + + std::vector> prev_times_; + + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index a20748d1..2a0922cb 100644 --- a/meson.build +++ b/meson.build @@ -192,6 +192,8 @@ if is_linux 'src/modules/cpu/linux.cpp', 'src/modules/cpu_frequency/common.cpp', 'src/modules/cpu_frequency/linux.cpp', + 'src/modules/cpu_usage/common.cpp', + 'src/modules/cpu_usage/linux.cpp', 'src/modules/memory/common.cpp', 'src/modules/memory/linux.cpp', ) @@ -201,6 +203,8 @@ elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd src_files += files( 'src/modules/cpu/bsd.cpp', 'src/modules/cpu/common.cpp', + 'src/modules/cpu_usage/bsd.cpp', + 'src/modules/cpu_usage/common.cpp', 'src/modules/memory/bsd.cpp', 'src/modules/memory/common.cpp', ) diff --git a/src/factory.cpp b/src/factory.cpp index b0b9af47..18b14427 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -102,6 +102,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "cpu_frequency") { return new waybar::modules::CpuFrequency(id, config_[name]); } + if (ref == "cpu_usage") { + return new waybar::modules::CpuUsage(id, config_[name]); + } if (ref == "load") { return new waybar::modules::Load(id, config_[name]); } diff --git a/src/modules/cpu_usage/bsd.cpp b/src/modules/cpu_usage/bsd.cpp new file mode 100644 index 00000000..c987a770 --- /dev/null +++ b/src/modules/cpu_usage/bsd.cpp @@ -0,0 +1,102 @@ +#include +// clang-format off +#include +#include +// clang-format on +#include // sysconf + +#include // NAN +#include // malloc + +#include "modules/cpu_usage.hpp" + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif + +#if defined(__NetBSD__) +typedef uint64_t cp_time_t; +#else +typedef long cp_time_t; +#endif +#if defined(__NetBSD__) || defined(__OpenBSD__) +typedef uint64_t pcp_time_t; +#else +typedef long pcp_time_t; +#endif + +std::vector> waybar::modules::CpuUsage::parseCpuinfo() { + cp_time_t sum_cp_time[CPUSTATES]; + size_t sum_sz = sizeof(sum_cp_time); + int ncpu = sysconf(_SC_NPROCESSORS_CONF); + size_t sz = CPUSTATES * (ncpu + 1) * sizeof(pcp_time_t); + pcp_time_t *cp_time = static_cast(malloc(sz)), *pcp_time = cp_time; +#if defined(__NetBSD__) + int mib[] = { + CTL_KERN, + KERN_CP_TIME, + }; + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), sum_cp_time, &sum_sz, NULL, 0)) { + throw std::runtime_error("sysctl kern.cp_time failed"); + } + for (int state = 0; state < CPUSTATES; state++) { + cp_time[state] = sum_cp_time[state]; + } + pcp_time += CPUSTATES; + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), pcp_time, &sz, NULL, 0)) { + throw std::runtime_error("sysctl kern.cp_time failed"); + } +#elif defined(__OpenBSD__) + { + int mib[] = { + CTL_KERN, + KERN_CPTIME, + }; + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), sum_cp_time, &sum_sz, NULL, 0)) { + throw std::runtime_error("sysctl kern.cp_time failed"); + } + } + for (int state = 0; state < CPUSTATES; state++) { + cp_time[state] = sum_cp_time[state]; + } + pcp_time = cp_time; + sz /= ncpu + 1; + { + int mib[] = { + CTL_KERN, + KERN_CPTIME2, + 0, + }; + for (int cpu = 0; cpu < ncpu; cpu++) { + mib[2] = cpu; + pcp_time += CPUSTATES; + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), pcp_time, &sz, NULL, 0)) { + throw std::runtime_error("sysctl kern.cp_time2 failed"); + } + } + } +#else + if (sysctlbyname("kern.cp_time", sum_cp_time, &sum_sz, NULL, 0)) { + throw std::runtime_error("sysctl kern.cp_time failed"); + } + for (int state = 0; state < CPUSTATES; state++) { + cp_time[state] = sum_cp_time[state]; + } + pcp_time += CPUSTATES; + if (sysctlbyname("kern.cp_times", pcp_time, &sz, NULL, 0)) { + throw std::runtime_error("sysctl kern.cp_times failed"); + } +#endif + std::vector> cpuinfo; + for (int cpu = 0; cpu < ncpu + 1; cpu++) { + pcp_time_t total = 0, *single_cp_time = &cp_time[cpu * CPUSTATES]; + for (int state = 0; state < CPUSTATES; state++) { + total += single_cp_time[state]; + } + cpuinfo.emplace_back(single_cp_time[CP_IDLE], total); + } + free(cp_time); + return cpuinfo; +} diff --git a/src/modules/cpu_usage/common.cpp b/src/modules/cpu_usage/common.cpp new file mode 100644 index 00000000..140373a0 --- /dev/null +++ b/src/modules/cpu_usage/common.cpp @@ -0,0 +1,78 @@ +#include "modules/cpu_usage.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 + +waybar::modules::CpuUsage::CpuUsage(const std::string& id, const Json::Value& config) + : AButton(config, "cpu_usage", id, "{usage}%", 10) { + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +auto waybar::modules::CpuUsage::update() -> void { + // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both + auto [cpu_usage, tooltip] = getCpuUsage(); + if (tooltipEnabled()) { + button_.set_tooltip_text(tooltip); + } + auto format = format_; + auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; + auto state = getState(total_usage); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + auto icons = std::vector{state}; + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("usage", total_usage)); + store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); + for (size_t i = 1; i < cpu_usage.size(); ++i) { + auto core_i = i - 1; + auto core_format = fmt::format("usage{}", core_i); + store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); + auto icon_format = fmt::format("icon{}", core_i); + store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); + } + label_->set_markup(fmt::vformat(format, store)); + } + + // Call parent update + AButton::update(); +} + +std::tuple, std::string> waybar::modules::CpuUsage::getCpuUsage() { + if (prev_times_.empty()) { + prev_times_ = parseCpuinfo(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + std::vector> curr_times = parseCpuinfo(); + std::string tooltip; + std::vector usage; + 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]; + 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); + if (i == 0) { + tooltip = fmt::format("Total: {}%", tmp); + } else { + tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp); + } + usage.push_back(tmp); + } + prev_times_ = curr_times; + return {usage, tooltip}; +} diff --git a/src/modules/cpu_usage/linux.cpp b/src/modules/cpu_usage/linux.cpp new file mode 100644 index 00000000..28930c1c --- /dev/null +++ b/src/modules/cpu_usage/linux.cpp @@ -0,0 +1,31 @@ +#include + +#include "modules/cpu_usage.hpp" + +std::vector> waybar::modules::CpuUsage::parseCpuinfo() { + const std::string data_dir_ = "/proc/stat"; + std::ifstream info(data_dir_); + if (!info.is_open()) { + throw std::runtime_error("Can't open " + data_dir_); + } + std::vector> cpuinfo; + std::string line; + while (getline(info, line)) { + if (line.substr(0, 3).compare("cpu") != 0) { + break; + } + std::stringstream sline(line.substr(5)); + std::vector times; + for (size_t time = 0; sline >> time; times.push_back(time)) + ; + + size_t idle_time = 0; + size_t total_time = 0; + if (times.size() >= 4) { + idle_time = times[3]; + total_time = std::accumulate(times.begin(), times.end(), 0); + } + cpuinfo.emplace_back(idle_time, total_time); + } + return cpuinfo; +} From 982ffde0027d3808858248929bc6317c766f5f44 Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Fri, 14 Apr 2023 01:25:13 +0200 Subject: [PATCH 066/364] Use labels instead of buttons --- include/modules/cpu_frequency.hpp | 4 ++-- include/modules/cpu_usage.hpp | 4 ++-- include/modules/load.hpp | 4 ++-- src/modules/cpu_frequency/common.cpp | 8 ++++---- src/modules/cpu_usage/common.cpp | 8 ++++---- src/modules/load.cpp | 10 +++++----- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/modules/cpu_frequency.hpp b/include/modules/cpu_frequency.hpp index 84475722..0b5bba0c 100644 --- a/include/modules/cpu_frequency.hpp +++ b/include/modules/cpu_frequency.hpp @@ -9,12 +9,12 @@ #include #include -#include "AButton.hpp" +#include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { -class CpuFrequency : public AButton { +class CpuFrequency : public ALabel { public: CpuFrequency(const std::string&, const Json::Value&); ~CpuFrequency() = default; diff --git a/include/modules/cpu_usage.hpp b/include/modules/cpu_usage.hpp index 0e57124a..3088c5a6 100644 --- a/include/modules/cpu_usage.hpp +++ b/include/modules/cpu_usage.hpp @@ -9,12 +9,12 @@ #include #include -#include "AButton.hpp" +#include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { -class CpuUsage : public AButton { +class CpuUsage : public ALabel { public: CpuUsage(const std::string&, const Json::Value&); ~CpuUsage() = default; diff --git a/include/modules/load.hpp b/include/modules/load.hpp index 39df4131..1d11370d 100644 --- a/include/modules/load.hpp +++ b/include/modules/load.hpp @@ -9,12 +9,12 @@ #include #include -#include "AButton.hpp" +#include "ALabel.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { -class Load : public AButton { +class Load : public ALabel { public: Load(const std::string&, const Json::Value&); ~Load() = default; diff --git a/src/modules/cpu_frequency/common.cpp b/src/modules/cpu_frequency/common.cpp index 13aa4d27..abed2aba 100644 --- a/src/modules/cpu_frequency/common.cpp +++ b/src/modules/cpu_frequency/common.cpp @@ -10,7 +10,7 @@ #endif waybar::modules::CpuFrequency::CpuFrequency(const std::string& id, const Json::Value& config) - : AButton(config, "cpu_frequency", id, "{avg_frequency}", 10) { + : ALabel(config, "cpu_frequency", id, "{avg_frequency}", 10) { thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); @@ -24,7 +24,7 @@ auto waybar::modules::CpuFrequency::update() -> void { auto tooltip = fmt::format("Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n", min_frequency, avg_frequency, max_frequency); - button_.set_tooltip_text(tooltip); + label_.set_tooltip_text(tooltip); } auto format = format_; auto state = getState(avg_frequency); @@ -42,11 +42,11 @@ auto waybar::modules::CpuFrequency::update() -> void { store.push_back(fmt::arg("max_frequency", max_frequency)); store.push_back(fmt::arg("min_frequency", min_frequency)); store.push_back(fmt::arg("avg_frequency", avg_frequency)); - label_->set_markup(fmt::vformat(format, store)); + label_.set_markup(fmt::vformat(format, store)); } // Call parent update - AButton::update(); + ALabel::update(); } std::tuple waybar::modules::CpuFrequency::getCpuFrequency() { diff --git a/src/modules/cpu_usage/common.cpp b/src/modules/cpu_usage/common.cpp index 140373a0..bd56cf1a 100644 --- a/src/modules/cpu_usage/common.cpp +++ b/src/modules/cpu_usage/common.cpp @@ -10,7 +10,7 @@ #endif waybar::modules::CpuUsage::CpuUsage(const std::string& id, const Json::Value& config) - : AButton(config, "cpu_usage", id, "{usage}%", 10) { + : ALabel(config, "cpu_usage", id, "{usage}%", 10) { thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); @@ -21,7 +21,7 @@ auto waybar::modules::CpuUsage::update() -> void { // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both auto [cpu_usage, tooltip] = getCpuUsage(); if (tooltipEnabled()) { - button_.set_tooltip_text(tooltip); + label_.set_tooltip_text(tooltip); } auto format = format_; auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; @@ -45,11 +45,11 @@ auto waybar::modules::CpuUsage::update() -> void { auto icon_format = fmt::format("icon{}", core_i); store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); } - label_->set_markup(fmt::vformat(format, store)); + label_.set_markup(fmt::vformat(format, store)); } // Call parent update - AButton::update(); + ALabel::update(); } std::tuple, std::string> waybar::modules::CpuUsage::getCpuUsage() { diff --git a/src/modules/load.cpp b/src/modules/load.cpp index 98bc1302..2ea646f3 100644 --- a/src/modules/load.cpp +++ b/src/modules/load.cpp @@ -10,7 +10,7 @@ #endif waybar::modules::Load::Load(const std::string& id, const Json::Value& config) - : AButton(config, "load", id, "{load1}", 10) { + : ALabel(config, "load", id, "{load1}", 10) { thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); @@ -22,7 +22,7 @@ auto waybar::modules::Load::update() -> void { auto [load1, load5, load15] = getLoad(); if (tooltipEnabled()) { auto tooltip = fmt::format("Load 1: {}\nLoad 5: {}\nLoad 15: {}", load1, load5, load15); - button_.set_tooltip_text(tooltip); + label_.set_tooltip_text(tooltip); } auto format = format_; auto state = getState(load1); @@ -42,11 +42,11 @@ auto waybar::modules::Load::update() -> void { store.push_back(fmt::arg("icon1", getIcon(load1, icons))); store.push_back(fmt::arg("icon5", getIcon(load5, icons))); store.push_back(fmt::arg("icon15", getIcon(load15, icons))); - label_->set_markup(fmt::vformat(format, store)); + label_.set_markup(fmt::vformat(format, store)); } // Call parent update - AButton::update(); + ALabel::update(); } std::tuple waybar::modules::Load::getLoad() { @@ -57,5 +57,5 @@ std::tuple waybar::modules::Load::getLoad() { double load15 = std::ceil(load[2] * 100.0) / 100.0; return {load1, load5, load15}; } - throw std::runtime_error("Can't get Cpu load"); + throw std::runtime_error("Can't get Load"); } From dce6a98f38f4bcc0c62d8f34b28f8e55b50bb0f5 Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Sat, 29 Jul 2023 14:52:59 +0200 Subject: [PATCH 067/364] Added changes made to the cpu module --- include/modules/cpu_frequency.hpp | 4 ++-- include/modules/cpu_usage.hpp | 4 ++-- include/modules/load.hpp | 4 ++-- src/modules/load.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/modules/cpu_frequency.hpp b/include/modules/cpu_frequency.hpp index 0b5bba0c..3331bd9c 100644 --- a/include/modules/cpu_frequency.hpp +++ b/include/modules/cpu_frequency.hpp @@ -17,8 +17,8 @@ namespace waybar::modules { class CpuFrequency : public ALabel { public: CpuFrequency(const std::string&, const Json::Value&); - ~CpuFrequency() = default; - auto update() -> void; + virtual ~CpuFrequency() = default; + auto update() -> void override; private: std::tuple getCpuFrequency(); diff --git a/include/modules/cpu_usage.hpp b/include/modules/cpu_usage.hpp index 3088c5a6..48f9194c 100644 --- a/include/modules/cpu_usage.hpp +++ b/include/modules/cpu_usage.hpp @@ -17,8 +17,8 @@ namespace waybar::modules { class CpuUsage : public ALabel { public: CpuUsage(const std::string&, const Json::Value&); - ~CpuUsage() = default; - auto update() -> void; + virtual ~CpuUsage() = default; + auto update() -> void override; private: std::tuple, std::string> getCpuUsage(); diff --git a/include/modules/load.hpp b/include/modules/load.hpp index 1d11370d..e4e91213 100644 --- a/include/modules/load.hpp +++ b/include/modules/load.hpp @@ -17,8 +17,8 @@ namespace waybar::modules { class Load : public ALabel { public: Load(const std::string&, const Json::Value&); - ~Load() = default; - auto update() -> void; + virtual ~Load() = default; + auto update() -> void override; private: std::tuple getLoad(); diff --git a/src/modules/load.cpp b/src/modules/load.cpp index 2ea646f3..9ee4b764 100644 --- a/src/modules/load.cpp +++ b/src/modules/load.cpp @@ -57,5 +57,5 @@ std::tuple waybar::modules::Load::getLoad() { double load15 = std::ceil(load[2] * 100.0) / 100.0; return {load1, load5, load15}; } - throw std::runtime_error("Can't get Load"); + throw std::runtime_error("Can't get system load"); } From c45f6681b37257cc6fdb63b0d610b809914f9194 Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Sat, 29 Jul 2023 15:28:26 +0200 Subject: [PATCH 068/364] cpu module: Reuse getCpuFrequency of cpu_frequency module --- include/modules/cpu.hpp | 2 -- include/modules/cpu_frequency.hpp | 6 ++-- src/modules/cpu/bsd.cpp | 10 ------- src/modules/cpu/common.cpp | 20 ++----------- src/modules/cpu/linux.cpp | 44 ---------------------------- src/modules/cpu_frequency/common.cpp | 4 +-- 6 files changed, 8 insertions(+), 78 deletions(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index a5235486..57331163 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -23,9 +23,7 @@ class Cpu : public ALabel { private: double getCpuLoad(); std::tuple, std::string> getCpuUsage(); - std::tuple getCpuFrequency(); std::vector> parseCpuinfo(); - std::vector parseCpuFrequencies(); std::vector> prev_times_; diff --git a/include/modules/cpu_frequency.hpp b/include/modules/cpu_frequency.hpp index 3331bd9c..49ca1b86 100644 --- a/include/modules/cpu_frequency.hpp +++ b/include/modules/cpu_frequency.hpp @@ -20,9 +20,11 @@ class CpuFrequency : public ALabel { virtual ~CpuFrequency() = default; auto update() -> void override; + // This is a static member because it is also used by the cpu module. + static std::tuple getCpuFrequency(); + private: - std::tuple getCpuFrequency(); - std::vector parseCpuFrequencies(); + static std::vector parseCpuFrequencies(); util::SleeperThread thread_; }; diff --git a/src/modules/cpu/bsd.cpp b/src/modules/cpu/bsd.cpp index 5eb767d9..96f2e51c 100644 --- a/src/modules/cpu/bsd.cpp +++ b/src/modules/cpu/bsd.cpp @@ -100,13 +100,3 @@ std::vector> waybar::modules::Cpu::parseCpuinfo() { free(cp_time); return cpuinfo; } - -std::vector waybar::modules::Cpu::parseCpuFrequencies() { - static std::vector frequencies; - if (frequencies.empty()) { - spdlog::warn( - "cpu/bsd: parseCpuFrequencies is not implemented, expect garbage in {*_frequency}"); - frequencies.push_back(NAN); - } - return frequencies; -} diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index 8fedf842..a27b7f52 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -1,4 +1,5 @@ #include "modules/cpu.hpp" +#include "modules/cpu_frequency.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 @@ -21,7 +22,7 @@ auto waybar::modules::Cpu::update() -> void { // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both auto cpu_load = getCpuLoad(); auto [cpu_usage, tooltip] = getCpuUsage(); - auto [max_frequency, min_frequency, avg_frequency] = getCpuFrequency(); + auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); if (tooltipEnabled()) { label_.set_tooltip_text(tooltip); } @@ -90,20 +91,3 @@ std::tuple, std::string> waybar::modules::Cpu::getCpuUsage prev_times_ = curr_times; return {usage, tooltip}; } - -std::tuple waybar::modules::Cpu::getCpuFrequency() { - std::vector frequencies = parseCpuFrequencies(); - if (frequencies.empty()) { - return {0.f, 0.f, 0.f}; - } - auto [min, max] = std::minmax_element(std::begin(frequencies), std::end(frequencies)); - float avg_frequency = - std::accumulate(std::begin(frequencies), std::end(frequencies), 0.0) / frequencies.size(); - - // Round frequencies with double decimal precision to get GHz - float max_frequency = std::ceil(*max / 10.0) / 100.0; - float min_frequency = std::ceil(*min / 10.0) / 100.0; - avg_frequency = std::ceil(avg_frequency / 10.0) / 100.0; - - return {max_frequency, min_frequency, avg_frequency}; -} diff --git a/src/modules/cpu/linux.cpp b/src/modules/cpu/linux.cpp index e9b18d70..88d71ee7 100644 --- a/src/modules/cpu/linux.cpp +++ b/src/modules/cpu/linux.cpp @@ -29,47 +29,3 @@ std::vector> waybar::modules::Cpu::parseCpuinfo() { } return cpuinfo; } - -std::vector waybar::modules::Cpu::parseCpuFrequencies() { - const std::string file_path_ = "/proc/cpuinfo"; - std::ifstream info(file_path_); - if (!info.is_open()) { - throw std::runtime_error("Can't open " + file_path_); - } - std::vector frequencies; - std::string line; - while (getline(info, line)) { - if (line.substr(0, 7).compare("cpu MHz") != 0) { - continue; - } - - std::string frequency_str = line.substr(line.find(":") + 2); - float frequency = std::strtol(frequency_str.c_str(), nullptr, 10); - frequencies.push_back(frequency); - } - info.close(); - - if (frequencies.size() <= 0) { - std::string cpufreq_dir = "/sys/devices/system/cpu/cpufreq"; - if (std::filesystem::exists(cpufreq_dir)) { - std::vector frequency_files = {"/cpuinfo_min_freq", "/cpuinfo_max_freq"}; - for (auto& p : std::filesystem::directory_iterator(cpufreq_dir)) { - for (auto freq_file : frequency_files) { - std::string freq_file_path = p.path().string() + freq_file; - if (std::filesystem::exists(freq_file_path)) { - std::string freq_value; - std::ifstream freq(freq_file_path); - if (freq.is_open()) { - getline(freq, freq_value); - float frequency = std::strtol(freq_value.c_str(), nullptr, 10); - frequencies.push_back(frequency / 1000); - freq.close(); - } - } - } - } - } - } - - return frequencies; -} diff --git a/src/modules/cpu_frequency/common.cpp b/src/modules/cpu_frequency/common.cpp index abed2aba..e47364ba 100644 --- a/src/modules/cpu_frequency/common.cpp +++ b/src/modules/cpu_frequency/common.cpp @@ -19,7 +19,7 @@ waybar::modules::CpuFrequency::CpuFrequency(const std::string& id, const Json::V auto waybar::modules::CpuFrequency::update() -> void { // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both - auto [max_frequency, min_frequency, avg_frequency] = getCpuFrequency(); + auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); if (tooltipEnabled()) { auto tooltip = fmt::format("Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n", @@ -50,7 +50,7 @@ auto waybar::modules::CpuFrequency::update() -> void { } std::tuple waybar::modules::CpuFrequency::getCpuFrequency() { - std::vector frequencies = parseCpuFrequencies(); + std::vector frequencies = CpuFrequency::parseCpuFrequencies(); if (frequencies.empty()) { return {0.f, 0.f, 0.f}; } From d1602e383c88d1f546b36704037133648b0e5ac4 Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Sat, 29 Jul 2023 15:56:51 +0200 Subject: [PATCH 069/364] cpu module: Reuse getCpuUsage of cpu_usage module --- include/modules/cpu.hpp | 2 - include/modules/cpu_usage.hpp | 6 +- meson.build | 2 - src/modules/cpu/bsd.cpp | 102 ------------------------------- src/modules/cpu/common.cpp | 28 +-------- src/modules/cpu/linux.cpp | 31 ---------- src/modules/cpu_usage/common.cpp | 14 ++--- 7 files changed, 13 insertions(+), 172 deletions(-) delete mode 100644 src/modules/cpu/bsd.cpp delete mode 100644 src/modules/cpu/linux.cpp diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 57331163..aff4c508 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -22,8 +22,6 @@ class Cpu : public ALabel { private: double getCpuLoad(); - std::tuple, std::string> getCpuUsage(); - std::vector> parseCpuinfo(); std::vector> prev_times_; diff --git a/include/modules/cpu_usage.hpp b/include/modules/cpu_usage.hpp index 48f9194c..4349f705 100644 --- a/include/modules/cpu_usage.hpp +++ b/include/modules/cpu_usage.hpp @@ -20,9 +20,11 @@ class CpuUsage : public ALabel { virtual ~CpuUsage() = default; auto update() -> void override; + // This is a static member because it is also used by the cpu module. + static std::tuple, std::string> getCpuUsage(std::vector>&); + private: - std::tuple, std::string> getCpuUsage(); - std::vector> parseCpuinfo(); + static std::vector> parseCpuinfo(); std::vector> prev_times_; diff --git a/meson.build b/meson.build index 2a0922cb..3bb79d43 100644 --- a/meson.build +++ b/meson.build @@ -189,7 +189,6 @@ if is_linux src_files += files( 'src/modules/battery.cpp', 'src/modules/cpu/common.cpp', - 'src/modules/cpu/linux.cpp', 'src/modules/cpu_frequency/common.cpp', 'src/modules/cpu_frequency/linux.cpp', 'src/modules/cpu_usage/common.cpp', @@ -201,7 +200,6 @@ 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') src_files += files( - 'src/modules/cpu/bsd.cpp', 'src/modules/cpu/common.cpp', 'src/modules/cpu_usage/bsd.cpp', 'src/modules/cpu_usage/common.cpp', diff --git a/src/modules/cpu/bsd.cpp b/src/modules/cpu/bsd.cpp deleted file mode 100644 index 96f2e51c..00000000 --- a/src/modules/cpu/bsd.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include -// clang-format off -#include -#include -// clang-format on -#include // sysconf - -#include // NAN -#include // malloc - -#include "modules/cpu.hpp" - -#if defined(__NetBSD__) || defined(__OpenBSD__) -#include -#else -#include -#endif - -#if defined(__NetBSD__) -typedef uint64_t cp_time_t; -#else -typedef long cp_time_t; -#endif -#if defined(__NetBSD__) || defined(__OpenBSD__) -typedef uint64_t pcp_time_t; -#else -typedef long pcp_time_t; -#endif - -std::vector> waybar::modules::Cpu::parseCpuinfo() { - cp_time_t sum_cp_time[CPUSTATES]; - size_t sum_sz = sizeof(sum_cp_time); - int ncpu = sysconf(_SC_NPROCESSORS_CONF); - size_t sz = CPUSTATES * (ncpu + 1) * sizeof(pcp_time_t); - pcp_time_t *cp_time = static_cast(malloc(sz)), *pcp_time = cp_time; -#if defined(__NetBSD__) - int mib[] = { - CTL_KERN, - KERN_CP_TIME, - }; - if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), sum_cp_time, &sum_sz, NULL, 0)) { - throw std::runtime_error("sysctl kern.cp_time failed"); - } - for (int state = 0; state < CPUSTATES; state++) { - cp_time[state] = sum_cp_time[state]; - } - pcp_time += CPUSTATES; - if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), pcp_time, &sz, NULL, 0)) { - throw std::runtime_error("sysctl kern.cp_time failed"); - } -#elif defined(__OpenBSD__) - { - int mib[] = { - CTL_KERN, - KERN_CPTIME, - }; - if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), sum_cp_time, &sum_sz, NULL, 0)) { - throw std::runtime_error("sysctl kern.cp_time failed"); - } - } - for (int state = 0; state < CPUSTATES; state++) { - cp_time[state] = sum_cp_time[state]; - } - pcp_time = cp_time; - sz /= ncpu + 1; - { - int mib[] = { - CTL_KERN, - KERN_CPTIME2, - 0, - }; - for (int cpu = 0; cpu < ncpu; cpu++) { - mib[2] = cpu; - pcp_time += CPUSTATES; - if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), pcp_time, &sz, NULL, 0)) { - throw std::runtime_error("sysctl kern.cp_time2 failed"); - } - } - } -#else - if (sysctlbyname("kern.cp_time", sum_cp_time, &sum_sz, NULL, 0)) { - throw std::runtime_error("sysctl kern.cp_time failed"); - } - for (int state = 0; state < CPUSTATES; state++) { - cp_time[state] = sum_cp_time[state]; - } - pcp_time += CPUSTATES; - if (sysctlbyname("kern.cp_times", pcp_time, &sz, NULL, 0)) { - throw std::runtime_error("sysctl kern.cp_times failed"); - } -#endif - std::vector> cpuinfo; - for (int cpu = 0; cpu < ncpu + 1; cpu++) { - pcp_time_t total = 0, *single_cp_time = &cp_time[cpu * CPUSTATES]; - for (int state = 0; state < CPUSTATES; state++) { - total += single_cp_time[state]; - } - cpuinfo.emplace_back(single_cp_time[CP_IDLE], total); - } - free(cp_time); - return cpuinfo; -} diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index a27b7f52..5259dcb9 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -1,5 +1,6 @@ #include "modules/cpu.hpp" #include "modules/cpu_frequency.hpp" +#include "modules/cpu_usage.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 @@ -21,7 +22,7 @@ waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config) auto waybar::modules::Cpu::update() -> void { // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both auto cpu_load = getCpuLoad(); - auto [cpu_usage, tooltip] = getCpuUsage(); + auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_); auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); if (tooltipEnabled()) { label_.set_tooltip_text(tooltip); @@ -66,28 +67,3 @@ double waybar::modules::Cpu::getCpuLoad() { } throw std::runtime_error("Can't get Cpu load"); } - -std::tuple, std::string> waybar::modules::Cpu::getCpuUsage() { - if (prev_times_.empty()) { - prev_times_ = parseCpuinfo(); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - std::vector> curr_times = parseCpuinfo(); - std::string tooltip; - std::vector usage; - 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]; - 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); - if (i == 0) { - tooltip = fmt::format("Total: {}%", tmp); - } else { - tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp); - } - usage.push_back(tmp); - } - prev_times_ = curr_times; - return {usage, tooltip}; -} diff --git a/src/modules/cpu/linux.cpp b/src/modules/cpu/linux.cpp deleted file mode 100644 index 88d71ee7..00000000 --- a/src/modules/cpu/linux.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include - -#include "modules/cpu.hpp" - -std::vector> waybar::modules::Cpu::parseCpuinfo() { - const std::string data_dir_ = "/proc/stat"; - std::ifstream info(data_dir_); - if (!info.is_open()) { - throw std::runtime_error("Can't open " + data_dir_); - } - std::vector> cpuinfo; - std::string line; - while (getline(info, line)) { - if (line.substr(0, 3).compare("cpu") != 0) { - break; - } - std::stringstream sline(line.substr(5)); - std::vector times; - for (size_t time = 0; sline >> time; times.push_back(time)) - ; - - size_t idle_time = 0; - size_t total_time = 0; - if (times.size() >= 4) { - idle_time = times[3]; - total_time = std::accumulate(times.begin(), times.end(), 0); - } - cpuinfo.emplace_back(idle_time, total_time); - } - return cpuinfo; -} diff --git a/src/modules/cpu_usage/common.cpp b/src/modules/cpu_usage/common.cpp index bd56cf1a..ffa54ae0 100644 --- a/src/modules/cpu_usage/common.cpp +++ b/src/modules/cpu_usage/common.cpp @@ -19,7 +19,7 @@ waybar::modules::CpuUsage::CpuUsage(const std::string& id, const Json::Value& co auto waybar::modules::CpuUsage::update() -> void { // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both - auto [cpu_usage, tooltip] = getCpuUsage(); + auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_); if (tooltipEnabled()) { label_.set_tooltip_text(tooltip); } @@ -52,17 +52,17 @@ auto waybar::modules::CpuUsage::update() -> void { ALabel::update(); } -std::tuple, std::string> waybar::modules::CpuUsage::getCpuUsage() { - if (prev_times_.empty()) { - prev_times_ = parseCpuinfo(); +std::tuple, std::string> waybar::modules::CpuUsage::getCpuUsage(std::vector>& prev_times) { + if (prev_times.empty()) { + prev_times = CpuUsage::parseCpuinfo(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - std::vector> curr_times = parseCpuinfo(); + std::vector> curr_times = CpuUsage::parseCpuinfo(); std::string tooltip; std::vector usage; 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]; + auto [prev_idle, prev_total] = prev_times[i]; 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); @@ -73,6 +73,6 @@ std::tuple, std::string> waybar::modules::CpuUsage::getCpu } usage.push_back(tmp); } - prev_times_ = curr_times; + prev_times = curr_times; return {usage, tooltip}; } From 8d7341da6e9a5186dcba8f993123c2d4d151bd99 Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Sat, 29 Jul 2023 16:02:36 +0200 Subject: [PATCH 070/364] cpu module: Reuse getLoad of load module --- include/modules/cpu.hpp | 2 -- include/modules/load.hpp | 4 +++- src/modules/cpu/common.cpp | 13 +++---------- src/modules/load.cpp | 2 +- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index aff4c508..7f78c165 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -21,8 +21,6 @@ class Cpu : public ALabel { auto update() -> void override; private: - double getCpuLoad(); - std::vector> prev_times_; util::SleeperThread thread_; diff --git a/include/modules/load.hpp b/include/modules/load.hpp index e4e91213..2c4ce610 100644 --- a/include/modules/load.hpp +++ b/include/modules/load.hpp @@ -20,8 +20,10 @@ class Load : public ALabel { virtual ~Load() = default; auto update() -> void override; + // This is a static member because it is also used by the cpu module. + static std::tuple getLoad(); + private: - std::tuple getLoad(); util::SleeperThread thread_; }; diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index 5259dcb9..8373d79e 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -1,6 +1,7 @@ #include "modules/cpu.hpp" #include "modules/cpu_frequency.hpp" #include "modules/cpu_usage.hpp" +#include "modules/load.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 @@ -21,7 +22,7 @@ waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config) auto waybar::modules::Cpu::update() -> void { // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both - auto cpu_load = getCpuLoad(); + auto [load1, load5, load15] = Load::getLoad(); auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_); auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); if (tooltipEnabled()) { @@ -40,7 +41,7 @@ auto waybar::modules::Cpu::update() -> void { event_box_.show(); auto icons = std::vector{state}; fmt::dynamic_format_arg_store store; - store.push_back(fmt::arg("load", cpu_load)); + store.push_back(fmt::arg("load", load1)); store.push_back(fmt::arg("usage", total_usage)); store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); store.push_back(fmt::arg("max_frequency", max_frequency)); @@ -59,11 +60,3 @@ auto waybar::modules::Cpu::update() -> void { // Call parent update ALabel::update(); } - -double waybar::modules::Cpu::getCpuLoad() { - double load[1]; - if (getloadavg(load, 1) != -1) { - return std::ceil(load[0] * 100.0) / 100.0; - } - throw std::runtime_error("Can't get Cpu load"); -} diff --git a/src/modules/load.cpp b/src/modules/load.cpp index 9ee4b764..69a37b4e 100644 --- a/src/modules/load.cpp +++ b/src/modules/load.cpp @@ -19,7 +19,7 @@ waybar::modules::Load::Load(const std::string& id, const Json::Value& config) auto waybar::modules::Load::update() -> void { // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both - auto [load1, load5, load15] = getLoad(); + auto [load1, load5, load15] = Load::getLoad(); if (tooltipEnabled()) { auto tooltip = fmt::format("Load 1: {}\nLoad 5: {}\nLoad 15: {}", load1, load5, load15); label_.set_tooltip_text(tooltip); From 93d66a92589dd3c6f52fd7c81e38db65a233945f Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Sat, 29 Jul 2023 16:27:46 +0200 Subject: [PATCH 071/364] Moved cpu/common.cpp to cpu.cpp --- meson.build | 4 ++-- src/modules/{cpu/common.cpp => cpu.cpp} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/modules/{cpu/common.cpp => cpu.cpp} (100%) diff --git a/meson.build b/meson.build index 3bb79d43..f4af7c67 100644 --- a/meson.build +++ b/meson.build @@ -188,7 +188,7 @@ if is_linux add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp') src_files += files( 'src/modules/battery.cpp', - 'src/modules/cpu/common.cpp', + 'src/modules/cpu.cpp', 'src/modules/cpu_frequency/common.cpp', 'src/modules/cpu_frequency/linux.cpp', 'src/modules/cpu_usage/common.cpp', @@ -200,7 +200,7 @@ 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') src_files += files( - 'src/modules/cpu/common.cpp', + 'src/modules/cpu.cpp', 'src/modules/cpu_usage/bsd.cpp', 'src/modules/cpu_usage/common.cpp', 'src/modules/memory/bsd.cpp', diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu.cpp similarity index 100% rename from src/modules/cpu/common.cpp rename to src/modules/cpu.cpp From 91b66291039a6dbdbb79e6fc4b5fde0e8e141fbd Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Wed, 13 Sep 2023 21:59:42 +0200 Subject: [PATCH 072/364] Fixed format errors --- src/modules/cpu.cpp | 1 + src/modules/cpu_usage/common.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/cpu.cpp b/src/modules/cpu.cpp index 8373d79e..0703eaf7 100644 --- a/src/modules/cpu.cpp +++ b/src/modules/cpu.cpp @@ -1,4 +1,5 @@ #include "modules/cpu.hpp" + #include "modules/cpu_frequency.hpp" #include "modules/cpu_usage.hpp" #include "modules/load.hpp" diff --git a/src/modules/cpu_usage/common.cpp b/src/modules/cpu_usage/common.cpp index ffa54ae0..4e36f48e 100644 --- a/src/modules/cpu_usage/common.cpp +++ b/src/modules/cpu_usage/common.cpp @@ -52,7 +52,8 @@ auto waybar::modules::CpuUsage::update() -> void { ALabel::update(); } -std::tuple, std::string> waybar::modules::CpuUsage::getCpuUsage(std::vector>& prev_times) { +std::tuple, std::string> waybar::modules::CpuUsage::getCpuUsage( + std::vector>& prev_times) { if (prev_times.empty()) { prev_times = CpuUsage::parseCpuinfo(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); From d5203e5b379077da262834f11fa30bfd6436c293 Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Wed, 13 Sep 2023 23:12:35 +0200 Subject: [PATCH 073/364] Fixed cpu module: Provide stub implementation for parseCpuFrequencies --- meson.build | 2 ++ src/factory.cpp | 2 ++ src/modules/cpu_frequency/bsd.cpp | 15 +++++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 src/modules/cpu_frequency/bsd.cpp diff --git a/meson.build b/meson.build index f4af7c67..0dab053e 100644 --- a/meson.build +++ b/meson.build @@ -201,6 +201,8 @@ elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd add_project_arguments('-DHAVE_MEMORY_BSD', language: 'cpp') src_files += files( 'src/modules/cpu.cpp', + 'src/modules/cpu_frequency/bsd.cpp', + 'src/modules/cpu_frequency/common.cpp', 'src/modules/cpu_usage/bsd.cpp', 'src/modules/cpu_usage/common.cpp', 'src/modules/memory/bsd.cpp', diff --git a/src/factory.cpp b/src/factory.cpp index 18b14427..d9d09ed1 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -99,9 +99,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "cpu") { return new waybar::modules::Cpu(id, config_[name]); } +#if defined(HAVE_CPU_LINUX) if (ref == "cpu_frequency") { return new waybar::modules::CpuFrequency(id, config_[name]); } +#endif if (ref == "cpu_usage") { return new waybar::modules::CpuUsage(id, config_[name]); } diff --git a/src/modules/cpu_frequency/bsd.cpp b/src/modules/cpu_frequency/bsd.cpp new file mode 100644 index 00000000..8cbe4f85 --- /dev/null +++ b/src/modules/cpu_frequency/bsd.cpp @@ -0,0 +1,15 @@ +#include + +#include // NAN + +#include "modules/cpu_frequency.hpp" + +std::vector waybar::modules::CpuFrequency::parseCpuFrequencies() { + static std::vector frequencies; + if (frequencies.empty()) { + spdlog::warn( + "cpu/bsd: parseCpuFrequencies is not implemented, expect garbage in {*_frequency}"); + frequencies.push_back(NAN); + } + return frequencies; +} From 80a34eec81254c63d2dd9ebe3f4fa18e0a763fb9 Mon Sep 17 00:00:00 2001 From: Mann mit Hut Date: Wed, 13 Sep 2023 23:25:39 +0200 Subject: [PATCH 074/364] Fixed formatting again --- src/modules/cpu_frequency/bsd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/cpu_frequency/bsd.cpp b/src/modules/cpu_frequency/bsd.cpp index 8cbe4f85..c837c1fd 100644 --- a/src/modules/cpu_frequency/bsd.cpp +++ b/src/modules/cpu_frequency/bsd.cpp @@ -1,6 +1,6 @@ #include -#include // NAN +#include // NAN #include "modules/cpu_frequency.hpp" From 14820e5d18f8ae44defb2873220af33d3515fddd Mon Sep 17 00:00:00 2001 From: Christopher Shore <113217432+chrisjshore@users.noreply.github.com> Date: Sun, 17 Sep 2023 16:32:19 -0400 Subject: [PATCH 075/364] fix clock to work with fmt 10.x --- 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 3d6b8919..27b7da5e 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -175,7 +175,7 @@ auto waybar::modules::Clock::update() -> void { tz, date::local_days(shiftedDay) + (now.get_local_time() - date::floor(now.get_local_time())))}; - label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now)); + label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now.get_local_time().time_since_epoch())); if (tooltipEnabled()) { const std::string tz_text{(is_timezoned_list_in_tooltip_) ? timezones_text(now.get_sys_time()) From 7c28ffc856a3466269574eae4384269b84c21727 Mon Sep 17 00:00:00 2001 From: Evan Overman Date: Mon, 18 Sep 2023 14:55:50 -0700 Subject: [PATCH 076/364] add indefinite `sleep()` function to `SleeperThread` --- include/util/sleeper_thread.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/util/sleeper_thread.hpp b/include/util/sleeper_thread.hpp index 861d5f1f..3d8c05d1 100644 --- a/include/util/sleeper_thread.hpp +++ b/include/util/sleeper_thread.hpp @@ -58,6 +58,12 @@ class SleeperThread { bool isRunning() const { return do_run_; } + auto sleep() { + std::unique_lock lk(mutex_); + CancellationGuard cancel_lock; + return condvar_.wait(lk); + } + auto sleep_for(std::chrono::system_clock::duration dur) { std::unique_lock lk(mutex_); CancellationGuard cancel_lock; From bf371f70d1e7ea5a38388661d662ee849f46f08e Mon Sep 17 00:00:00 2001 From: Evan Overman Date: Mon, 18 Sep 2023 14:56:14 -0700 Subject: [PATCH 077/364] add `waitingWorker()` to `Custom` --- include/modules/custom.hpp | 1 + src/modules/custom.cpp | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/include/modules/custom.hpp b/include/modules/custom.hpp index a6024a84..c9992695 100644 --- a/include/modules/custom.hpp +++ b/include/modules/custom.hpp @@ -22,6 +22,7 @@ class Custom : public ALabel { private: void delayWorker(); void continuousWorker(); + void waitingWorker(); void parseOutputRaw(); void parseOutputJson(); void handleEvent(); diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 5a246aff..6cbb5e1e 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -11,11 +11,13 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id, fp_(nullptr), pid_(-1) { dp.emit(); - if (interval_.count() > 0) { + if (!config_["signal"].empty() && config_["interval"].empty()) { + waitingWorker(); + } else if (interval_.count() > 0) { delayWorker(); } else if (config_["exec"].isString()) { continuousWorker(); - } + } } waybar::modules::Custom::~Custom() { @@ -92,6 +94,26 @@ void waybar::modules::Custom::continuousWorker() { }; } +void waybar::modules::Custom::waitingWorker() { + thread_ = [this] { + bool can_update = true; + if (config_["exec-if"].isString()) { + output_ = util::command::execNoRead(config_["exec-if"].asString()); + if (output_.exit_code != 0) { + can_update = false; + dp.emit(); + } + } + if (can_update) { + if (config_["exec"].isString()) { + output_ = util::command::exec(config_["exec"].asString()); + } + dp.emit(); + } + thread_.sleep(); + }; +} + void waybar::modules::Custom::refresh(int sig) { if (sig == SIGRTMIN + config_["signal"].asInt()) { thread_.wake_up(); From 388c024298bb81ee33052d61e69a72995c2b32ec Mon Sep 17 00:00:00 2001 From: idm1try Date: Tue, 19 Sep 2023 18:50:39 +0500 Subject: [PATCH 078/364] fix(backlight): wrong percentage numbers for device apple-panel-bl --- flake.nix | 1 + result | 1 + src/modules/backlight.cpp | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) create mode 120000 result diff --git a/flake.nix b/flake.nix index 24fcae9f..15e70b63 100644 --- a/flake.nix +++ b/flake.nix @@ -16,6 +16,7 @@ inherit (nixpkgs) lib; genSystems = lib.genAttrs [ "x86_64-linux" + "aarch64-linux" ]; pkgsFor = genSystems (system: diff --git a/result b/result new file mode 120000 index 00000000..a8f7089b --- /dev/null +++ b/result @@ -0,0 +1 @@ +/nix/store/0pz53p83i16kv8ff5pwxhpp7zvmxapm9-waybar-0.9.22+date=2023-09-13_dirty \ No newline at end of file diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 27871048..40c63f07 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -244,7 +244,7 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last, check_nn(name); const char *actual_brightness_attr = - strncmp(name, "amdgpu_bl", 9) == 0 ? "brightness" : "actual_brightness"; + strncmp(name, "amdgpu_bl", 9) == 0 ? "brightness" : 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"); From 954bea36f4f7f9205e563d49c283f9e40d43af38 Mon Sep 17 00:00:00 2001 From: idm1try Date: Tue, 19 Sep 2023 18:52:00 +0500 Subject: [PATCH 079/364] chore: remove result dir by nix --- result | 1 - 1 file changed, 1 deletion(-) delete mode 120000 result diff --git a/result b/result deleted file mode 120000 index a8f7089b..00000000 --- a/result +++ /dev/null @@ -1 +0,0 @@ -/nix/store/0pz53p83i16kv8ff5pwxhpp7zvmxapm9-waybar-0.9.22+date=2023-09-13_dirty \ No newline at end of file From f14fe96e199af4bd8050c304ae5ea6c300ba28df Mon Sep 17 00:00:00 2001 From: Evan Overman Date: Tue, 19 Sep 2023 14:52:48 -0700 Subject: [PATCH 080/364] add info to `interval` and `signal` in manpage for custom modules --- man/waybar-custom.5.scd | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index aeb419f9..a038e355 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -34,7 +34,8 @@ Addressed by *custom/* typeof: integer ++ The interval (in seconds) in which the information gets polled. ++ Use *once* if you want to execute the module only on startup. ++ - You can update it manually with a signal. If no *interval* is defined, it is assumed that the out script loops it self. + You can update it manually with a signal. If no *interval* or *signal* is defined, it is assumed that the out script loops it self. ++ + If a *signal* is defined then the script will run once on startup and will will only update with a signal. *restart-interval*: ++ typeof: integer ++ @@ -45,7 +46,8 @@ Addressed by *custom/* *signal*: ++ typeof: integer ++ The signal number used to update the module. ++ - The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*. + The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*. ++ + If no interval is defined then a signal will be the only way to update the module. *format*: ++ typeof: string ++ From 81ffeebfb1d7a761c7069627dd2462388043487c Mon Sep 17 00:00:00 2001 From: idm1try Date: Thu, 21 Sep 2023 09:03:13 +0500 Subject: [PATCH 081/364] fix: style Co-authored-by: Alexis Rouillard --- src/modules/backlight.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 40c63f07..dff10743 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -244,7 +244,7 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last, check_nn(name); const char *actual_brightness_attr = - strncmp(name, "amdgpu_bl", 9) == 0 ? "brightness" : strcmp(name, "apple-panel-bl") == 0 ? "brightness" : "actual_brightness"; + 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"); From 3e2761e81f2415eaaf3cd7108bc317619fcbc4d0 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Fri, 22 Sep 2023 16:37:26 -0300 Subject: [PATCH 082/364] feat: dynamically assign windows to workspaces Co-authored-by: Gabriel Fox --- include/modules/hyprland/workspaces.hpp | 35 ++++- src/modules/hyprland/workspaces.cpp | 175 ++++++++++++++++++++++-- 2 files changed, 200 insertions(+), 10 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 14b9ba0e..d010cb08 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -3,8 +3,11 @@ #include #include +#include +#include #include #include +#include #include #include @@ -13,13 +16,16 @@ #include "modules/hyprland/backend.hpp" #include "util/enum.hpp" +using WindowAddress = std::string; +using mywindowtype = std::string; namespace waybar::modules::hyprland { class Workspaces; class Workspace { public: - explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager); + explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager, + const Json::Value& clients_json = Json::Value::nullRef); std::string& select_icon(std::map& icons_map); Gtk::Button& button() { return button_; }; @@ -40,6 +46,21 @@ class Workspace { void set_visible(bool value = true) { is_visible_ = value; }; void set_windows(uint value) { windows_ = value; }; void set_name(std::string value) { name_ = value; }; + bool contains_window(WindowAddress addr) { return window_map_.contains(addr); } + void insert_window(WindowAddress addr, mywindowtype window_repr) { + window_map_.emplace(addr, window_repr); + }; + void remove_window(WindowAddress addr) { window_map_.erase(addr); } + void initialize_window_map(const Json::Value& clients_data); + + bool on_window_opened(WindowAddress& addr, std::string& workspace_name, + const Json::Value& clients_data); + bool on_window_opened(WindowAddress& addr, std::string& workspace_name, std::string& window_class, + std::string& window_title); + + bool on_window_closed(WindowAddress& addr); + bool on_window_moved(WindowAddress& addr, std::string& workspace_name, + const Json::Value& clients_data); void update(const std::string& format, const std::string& icon); @@ -56,6 +77,8 @@ class Workspace { bool is_urgent_ = false; bool is_visible_ = false; + std::map window_map_; + Gtk::Button button_; Gtk::Box content_; Gtk::Label label_; @@ -77,13 +100,19 @@ class Workspaces : public AModule, public EventHandler { private: void onEvent(const std::string&) override; void update_window_count(); + void initialize_window_maps(); void sort_workspaces(); - void create_workspace(Json::Value& value); + void create_workspace(Json::Value& workspace_data, + const Json::Value& clients_data = Json::Value::nullRef); void remove_workspace(std::string name); void set_urgent_workspace(std::string windowaddress); void parse_config(const Json::Value& config); void register_ipc(); + void on_window_opened(std::string payload); + void on_window_closed(std::string payload); + void on_window_moved(std::string payload); + bool all_outputs_ = false; bool show_special_ = false; bool active_only_ = false; @@ -103,6 +132,8 @@ class Workspaces : public AModule, public EventHandler { std::string format_; std::map icons_map_; + std::map window_rewrite_rules_; + std::string format_window_separator_; bool with_icon_; uint64_t monitor_id_; std::string active_workspace_name_; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index f38b5050..4176851a 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -1,5 +1,6 @@ #include "modules/hyprland/workspaces.hpp" +#include #include #include @@ -68,6 +69,18 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { g_warning("Invalid string representation for sort-by. Falling back to default sort method."); } } + + Json::Value format_window_separator = config["format-window-separator"]; + format_window_separator_ = + format_window_separator.isString() ? format_window_separator.asString() : " "; + + Json::Value window_rewrite_map = config["window-rewrite-map"]; + + if (window_rewrite_map.isObject()) { + for (std::string &name : window_rewrite_map.getMemberNames()) { + window_rewrite_rules_.emplace(name, window_rewrite_map[name].asString()); + } + } } auto Workspaces::register_ipc() -> void { @@ -175,8 +188,15 @@ void Workspaces::onEvent(const std::string &ev) { } else { workspaces_to_remove_.push_back(workspace); } - } else if (eventName == "openwindow" || eventName == "closewindow" || eventName == "movewindow") { + } else if (eventName == "openwindow") { update_window_count(); + on_window_opened(payload); + } else if (eventName == "closewindow") { + update_window_count(); + on_window_closed(payload); + } else if (eventName == "movewindow") { + update_window_count(); + on_window_moved(payload); } else if (eventName == "urgent") { set_urgent_workspace(payload); } else if (eventName == "renameworkspace") { @@ -197,6 +217,71 @@ void Workspaces::onEvent(const std::string &ev) { dp.emit(); } +void Workspaces::on_window_opened(std::string payload) { + size_t last_comma_idx = 0; + size_t next_comma_idx = payload.find(','); + std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx); + + last_comma_idx = next_comma_idx; + next_comma_idx = payload.find(',', next_comma_idx + 1); + std::string workspace_name = + payload.substr(last_comma_idx + 1, next_comma_idx - last_comma_idx - 1); + + last_comma_idx = next_comma_idx; + next_comma_idx = payload.find(',', next_comma_idx + 1); + std::string window_class = + payload.substr(last_comma_idx + 1, next_comma_idx - last_comma_idx - 1); + + std::string window_title = payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx); + + fmt::println("> Inserting window [{}] [{}] [{}] [{}]", window_address, workspace_name, + window_class, window_title); + + for (auto &workspace : workspaces_) { + if (workspace->on_window_opened(window_address, workspace_name, window_class, window_title)) { + break; + } + } + + fmt::println("<"); +} + +void Workspaces::on_window_closed(std::string addr) { + fmt::println("> Removing window [{}]", addr); + for (auto &workspace : workspaces_) { + if (workspace->on_window_closed(addr)) { + break; + } + } + fmt::println("<"); +} + +void Workspaces::on_window_moved(std::string payload) { + size_t last_comma_idx = 0; + size_t next_comma_idx = payload.find(','); + std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx); + + std::string workspace_name = + payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx); + + int changes = 0; + + const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); + + fmt::println(">> Moving window [{}] [{}]", window_address, workspace_name); + + for (auto &workspace : workspaces_) { + if (workspace->on_window_moved(window_address, workspace_name, clients_json)) { + changes++; + if (changes == 2) { + break; + } + } + } + + fmt::println("<<"); +} + void Workspaces::update_window_count() { const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : workspaces_) { @@ -215,22 +300,84 @@ void Workspaces::update_window_count() { } } -void Workspaces::create_workspace(Json::Value &value) { +void Workspaces::initialize_window_maps() { + Json::Value clients_data = gIPC->getSocket1JsonReply("clients"); + for (auto &workspace : workspaces_) { + workspace->initialize_window_map(clients_data); + } +} + +void Workspace::initialize_window_map(const Json::Value &clients_data) { + window_map_.clear(); + for (auto client : clients_data) { + if (client["workspace"]["id"].asInt() == id()) { + WindowAddress client_address = client["address"].asString(); + insert_window(client_address, client["class"].asString()); + } + } +} + +bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, + const Json::Value &clients_data) { + if (workspace_name == name()) { + fmt::println("\tInserting on workspace {}", id()); + for (auto client : clients_data) { + auto client_address = client["address"].asString().substr(2, addr.length()); + if (client_address == addr) { + std::string window_class = client["class"].asString(); + insert_window(addr, window_class); + return true; + } + } + fmt::println("\tERROR on workspace {}", id()); + return false; + } else { + return false; + } +} + +bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, + std::string &window_class, std::string &window_title) { + if (workspace_name == name()) { + fmt::println("\tInserting on workspace {}", id()); + insert_window(addr, window_class); + return true; + } else { + return false; + } +} + +bool Workspace::on_window_closed(WindowAddress &addr) { + if (window_map_.contains(addr)) { + fmt::println("\tRemoving on workspace {}", id()); + remove_window(addr); + return true; + } else { + return false; + } +} + +bool Workspace::on_window_moved(WindowAddress &addr, std::string &workspace_name, + const Json::Value &clients_data) { + return on_window_opened(addr, workspace_name, clients_data) || on_window_closed(addr); +} + +void Workspaces::create_workspace(Json::Value &workspace_data, const Json::Value &clients_data) { // replace the existing persistent workspace if it exists auto workspace = std::find_if( workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr const &x) { - auto name = value["name"].asString(); + auto name = workspace_data["name"].asString(); return x->is_persistent() && ((name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name()); }); if (workspace != workspaces_.end()) { // replace workspace, but keep persistent flag workspaces_.erase(workspace); - value["persistent"] = true; + workspace_data["persistent"] = true; } // create new workspace - workspaces_.emplace_back(std::make_unique(value, *this)); + workspaces_.emplace_back(std::make_unique(workspace_data, *this, clients_data)); Gtk::Button &new_workspace_button = workspaces_.back()->button(); box_.pack_start(new_workspace_button, false, false); sort_workspaces(); @@ -351,10 +498,12 @@ void Workspaces::init() { create_persistent_workspaces(); const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); + const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); + for (Json::Value workspace_json : workspaces_json) { if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && (!workspace_json["name"].asString().starts_with("special") || show_special())) { - create_workspace(workspace_json); + create_workspace(workspace_json, clients_json); } } @@ -371,7 +520,8 @@ Workspaces::~Workspaces() { std::lock_guard lg(mutex_); } -Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager) +Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager, + const Json::Value &clients_data) : workspace_manager_(workspace_manager), id_(workspace_data["id"].asInt()), name_(workspace_data["name"].asString()), @@ -396,6 +546,8 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma button_.set_relief(Gtk::RELIEF_NONE); content_.set_center_widget(label_); button_.add(content_); + + initialize_window_map(clients_data); } void add_or_remove_class(const Glib::RefPtr &context, bool condition, @@ -429,8 +581,15 @@ void Workspace::update(const std::string &format, const std::string &icon) { add_or_remove_class(style_context, is_urgent(), "urgent"); add_or_remove_class(style_context, is_visible(), "visible"); + std::string first_letters; + + for (auto &[_pid, window_repr] : window_map_) { + first_letters.append(window_repr.substr(0, 1)); + } + label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), - fmt::arg("name", name()), fmt::arg("icon", icon))); + fmt::arg("name", name()), fmt::arg("icon", icon), + fmt::arg("windows", first_letters))); } void Workspaces::sort_workspaces() { From b9d5912a4f7d862ee64c8beea40cdc49665afc80 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Fri, 22 Sep 2023 18:18:02 -0300 Subject: [PATCH 083/364] feat: rewrite window classes feat: cache window class rewrite resolution Co-authored-by: Gabriel Fox --- include/modules/hyprland/workspaces.hpp | 14 ++++--- src/modules/hyprland/workspaces.cpp | 55 ++++++++++++++----------- src/util/rewrite_string.cpp | 3 +- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index d010cb08..528bf933 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -17,7 +18,6 @@ #include "util/enum.hpp" using WindowAddress = std::string; -using mywindowtype = std::string; namespace waybar::modules::hyprland { class Workspaces; @@ -47,9 +47,7 @@ class Workspace { void set_windows(uint value) { windows_ = value; }; void set_name(std::string value) { name_ = value; }; bool contains_window(WindowAddress addr) { return window_map_.contains(addr); } - void insert_window(WindowAddress addr, mywindowtype window_repr) { - window_map_.emplace(addr, window_repr); - }; + void insert_window(WindowAddress addr, std::string window_repr); void remove_window(WindowAddress addr) { window_map_.erase(addr); } void initialize_window_map(const Json::Value& clients_data); @@ -77,7 +75,7 @@ class Workspace { bool is_urgent_ = false; bool is_visible_ = false; - std::map window_map_; + std::map window_map_; Gtk::Button button_; Gtk::Box content_; @@ -97,6 +95,9 @@ class Workspaces : public AModule, public EventHandler { auto get_bar_output() const -> std::string { return bar_.output->name; } + std::string get_rewrite(std::string window_class); + std::string& get_window_separator() { return format_window_separator_; } + private: void onEvent(const std::string&) override; void update_window_count(); @@ -132,7 +133,8 @@ class Workspaces : public AModule, public EventHandler { std::string format_; std::map icons_map_; - std::map window_rewrite_rules_; + Json::Value window_rewrite_rules_; + std::map regex_cache_; std::string format_window_separator_; bool with_icon_; uint64_t monitor_id_; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 4176851a..43cd7774 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -9,6 +9,8 @@ #include #include +#include "util/rewrite_string.hpp" + namespace waybar::modules::hyprland { Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) @@ -74,13 +76,7 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { format_window_separator_ = format_window_separator.isString() ? format_window_separator.asString() : " "; - Json::Value window_rewrite_map = config["window-rewrite-map"]; - - if (window_rewrite_map.isObject()) { - for (std::string &name : window_rewrite_map.getMemberNames()) { - window_rewrite_rules_.emplace(name, window_rewrite_map[name].asString()); - } - } + window_rewrite_rules_ = config["window-rewrite"]; } auto Workspaces::register_ipc() -> void { @@ -234,26 +230,19 @@ void Workspaces::on_window_opened(std::string payload) { std::string window_title = payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx); - fmt::println("> Inserting window [{}] [{}] [{}] [{}]", window_address, workspace_name, - window_class, window_title); - for (auto &workspace : workspaces_) { if (workspace->on_window_opened(window_address, workspace_name, window_class, window_title)) { break; } } - - fmt::println("<"); } void Workspaces::on_window_closed(std::string addr) { - fmt::println("> Removing window [{}]", addr); for (auto &workspace : workspaces_) { if (workspace->on_window_closed(addr)) { break; } } - fmt::println("<"); } void Workspaces::on_window_moved(std::string payload) { @@ -268,8 +257,6 @@ void Workspaces::on_window_moved(std::string payload) { const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); - fmt::println(">> Moving window [{}] [{}]", window_address, workspace_name); - for (auto &workspace : workspaces_) { if (workspace->on_window_moved(window_address, workspace_name, clients_json)) { changes++; @@ -278,8 +265,6 @@ void Workspaces::on_window_moved(std::string payload) { } } } - - fmt::println("<<"); } void Workspaces::update_window_count() { @@ -317,10 +302,13 @@ void Workspace::initialize_window_map(const Json::Value &clients_data) { } } +void Workspace::insert_window(WindowAddress addr, std::string window_class) { + window_map_.emplace(addr, workspace_manager_.get_rewrite(window_class)); +}; + bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, const Json::Value &clients_data) { if (workspace_name == name()) { - fmt::println("\tInserting on workspace {}", id()); for (auto client : clients_data) { auto client_address = client["address"].asString().substr(2, addr.length()); if (client_address == addr) { @@ -329,7 +317,6 @@ bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_nam return true; } } - fmt::println("\tERROR on workspace {}", id()); return false; } else { return false; @@ -339,7 +326,6 @@ bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_nam bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, std::string &window_class, std::string &window_title) { if (workspace_name == name()) { - fmt::println("\tInserting on workspace {}", id()); insert_window(addr, window_class); return true; } else { @@ -349,7 +335,6 @@ bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_nam bool Workspace::on_window_closed(WindowAddress &addr) { if (window_map_.contains(addr)) { - fmt::println("\tRemoving on workspace {}", id()); remove_window(addr); return true; } else { @@ -581,15 +566,22 @@ void Workspace::update(const std::string &format, const std::string &icon) { add_or_remove_class(style_context, is_urgent(), "urgent"); add_or_remove_class(style_context, is_visible(), "visible"); - std::string first_letters; + std::string windows; + auto window_separator = workspace_manager_.get_window_separator(); + + bool is_not_first = false; for (auto &[_pid, window_repr] : window_map_) { - first_letters.append(window_repr.substr(0, 1)); + if (is_not_first) { + windows.append(window_separator); + } + is_not_first = true; + windows.append(window_repr); } label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), fmt::arg("name", name()), fmt::arg("icon", icon), - fmt::arg("windows", first_letters))); + fmt::arg("windows", windows))); } void Workspaces::sort_workspaces() { @@ -748,4 +740,17 @@ void Workspaces::set_urgent_workspace(std::string windowaddress) { } } +std::string Workspaces::get_rewrite(std::string window_class) { + if (regex_cache_.contains(window_class)) { + return regex_cache_[window_class]; + } + + std::string window_class_rewrite = + waybar::util::rewriteString(window_class, window_rewrite_rules_); + + regex_cache_.emplace(window_class, window_class_rewrite); + + return window_class_rewrite; +} + } // namespace waybar::modules::hyprland diff --git a/src/util/rewrite_string.cpp b/src/util/rewrite_string.cpp index 40c71e99..ec3e1673 100644 --- a/src/util/rewrite_string.cpp +++ b/src/util/rewrite_string.cpp @@ -1,5 +1,6 @@ #include "util/rewrite_string.hpp" +#include #include #include @@ -17,7 +18,7 @@ std::string rewriteString(const std::string& value, const Json::Value& rules) { try { // malformated regexes will cause an exception. // in this case, log error and try the next rule. - const std::regex rule{it.key().asString()}; + const std::regex rule{it.key().asString(), std::regex_constants::icase}; if (std::regex_match(value, rule)) { res = std::regex_replace(res, rule, it->asString()); } From fbe544984c0df828a1f6f3bbd6fb07eb3468dba1 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Fri, 22 Sep 2023 18:38:18 -0300 Subject: [PATCH 084/364] fix: ipc vs json window addr format mismatch feat: ignore empty windows Co-authored-by: Gabriel Fox --- src/modules/hyprland/workspaces.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 43cd7774..e6edc4ac 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -296,20 +296,32 @@ void Workspace::initialize_window_map(const Json::Value &clients_data) { window_map_.clear(); for (auto client : clients_data) { if (client["workspace"]["id"].asInt() == id()) { + // substr(2, ...) is necessary because Hyprland's JSON follows this format: + // 0x{ADDR} + // While Hyprland's IPC follows this format: + // {ADDR} WindowAddress client_address = client["address"].asString(); + client_address = client_address.substr(2, client_address.length() - 2); insert_window(client_address, client["class"].asString()); } } } void Workspace::insert_window(WindowAddress addr, std::string window_class) { - window_map_.emplace(addr, workspace_manager_.get_rewrite(window_class)); + auto window_repr = workspace_manager_.get_rewrite(window_class); + if (!window_repr.empty()) { + window_map_.emplace(addr, window_repr); + } }; bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, const Json::Value &clients_data) { if (workspace_name == name()) { for (auto client : clients_data) { + // substr(2, ...) is necessary because Hyprland's JSON follows this format: + // 0x{ADDR} + // While Hyprland's IPC follows this format: + // {ADDR} auto client_address = client["address"].asString().substr(2, addr.length()); if (client_address == addr) { std::string window_class = client["class"].asString(); From adbc9d95de7e26ea8e518b72cc707d924e4dd00c Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Fri, 22 Sep 2023 19:05:34 -0300 Subject: [PATCH 085/364] feat: optional default icon for 0-match classes Co-authored-by: Gabriel Fox --- include/modules/hyprland/workspaces.hpp | 1 + include/util/rewrite_string.hpp | 4 +++- src/modules/hyprland/workspaces.cpp | 12 ++++++++++- src/util/rewrite_string.cpp | 27 +++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 528bf933..75465994 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -136,6 +136,7 @@ class Workspaces : public AModule, public EventHandler { Json::Value window_rewrite_rules_; std::map regex_cache_; std::string format_window_separator_; + std::string window_rewrite_default_; bool with_icon_; uint64_t monitor_id_; std::string active_workspace_name_; diff --git a/include/util/rewrite_string.hpp b/include/util/rewrite_string.hpp index 2ab39ad8..3352a47a 100644 --- a/include/util/rewrite_string.hpp +++ b/include/util/rewrite_string.hpp @@ -5,4 +5,6 @@ namespace waybar::util { std::string rewriteString(const std::string&, const Json::Value&); -} +std::string rewriteStringOnce(const std::string& value, const Json::Value& rules, + bool& matched_any); +} // namespace waybar::util diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index e6edc4ac..526129e9 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -77,6 +77,10 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { format_window_separator.isString() ? format_window_separator.asString() : " "; window_rewrite_rules_ = config["window-rewrite"]; + + Json::Value window_rewrite_default = config["window-rewrite-default"]; + window_rewrite_default_ = + window_rewrite_default.isString() ? window_rewrite_default.asString() : "?"; } auto Workspaces::register_ipc() -> void { @@ -757,8 +761,14 @@ std::string Workspaces::get_rewrite(std::string window_class) { return regex_cache_[window_class]; } + bool matched_any; + std::string window_class_rewrite = - waybar::util::rewriteString(window_class, window_rewrite_rules_); + waybar::util::rewriteStringOnce(window_class, window_rewrite_rules_, matched_any); + + if (!matched_any) { + window_class_rewrite = window_rewrite_default_; + } regex_cache_.emplace(window_class, window_class_rewrite); diff --git a/src/util/rewrite_string.cpp b/src/util/rewrite_string.cpp index ec3e1673..475f8a3d 100644 --- a/src/util/rewrite_string.cpp +++ b/src/util/rewrite_string.cpp @@ -30,4 +30,31 @@ std::string rewriteString(const std::string& value, const Json::Value& rules) { return res; } + +std::string rewriteStringOnce(const std::string& value, const Json::Value& rules, + bool& matched_any) { + if (!rules.isObject()) { + return value; + } + + matched_any = false; + + std::string res = value; + + for (auto it = rules.begin(); it != rules.end(); ++it) { + if (it.key().isString() && it->isString()) { + try { + const std::regex rule{it.key().asString(), std::regex_constants::icase}; + if (std::regex_match(value, rule)) { + matched_any = true; + return std::regex_replace(res, rule, it->asString()); + } + } catch (const std::regex_error& e) { + spdlog::error("Invalid rule {}: {}", it.key().asString(), e.what()); + } + } + } + + return value; +} } // namespace waybar::util From 6663ca3d754525a6e0e059f61b291d62a4e7af37 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Fri, 22 Sep 2023 20:41:38 -0300 Subject: [PATCH 086/364] chore: document new properties --- man/waybar-hyprland-workspaces.5.scd | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 99d21804..02be176d 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -21,6 +21,21 @@ Addressed by *hyprland/workspaces* typeof: array ++ Based on the workspace id and state, the corresponding icon gets selected. See *icons*. +*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. + +*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. + *show-special*: ++ typeof: bool ++ default: false ++ @@ -103,6 +118,19 @@ Additional to workspace name matching, the following *format-icons* can be set. } ``` +``` +"hyprland/workspaces": { + "format": "{name}\n{windows}", + "format-window-separator": "\n", + "window-rewrite-default": "", + "window-rewrite": { + "firefox": "", + "foot": "", + "code": "󰨞", + } +} +``` + # Style - *#workspaces* From 258ab8b1477a15c037c5b4a9157ab9b67c598955 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Fri, 22 Sep 2023 21:12:42 -0300 Subject: [PATCH 087/364] refactor: take window representation directly from old workspace on movewindow event --- include/modules/hyprland/workspaces.hpp | 9 ++-- src/modules/hyprland/workspaces.cpp | 60 ++++++++++++------------- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 75465994..1a01758e 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -48,17 +48,14 @@ class Workspace { void set_name(std::string value) { name_ = value; }; bool contains_window(WindowAddress addr) { return window_map_.contains(addr); } void insert_window(WindowAddress addr, std::string window_repr); - void remove_window(WindowAddress addr) { window_map_.erase(addr); } + std::string remove_window(WindowAddress addr); void initialize_window_map(const Json::Value& clients_data); - bool on_window_opened(WindowAddress& addr, std::string& workspace_name, - const Json::Value& clients_data); + bool on_window_opened(WindowAddress& addr, std::string& workspace_name, std::string window_repr); bool on_window_opened(WindowAddress& addr, std::string& workspace_name, std::string& window_class, std::string& window_title); - bool on_window_closed(WindowAddress& addr); - bool on_window_moved(WindowAddress& addr, std::string& workspace_name, - const Json::Value& clients_data); + std::optional on_window_closed(WindowAddress& addr); void update(const std::string& format, const std::string& icon); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 526129e9..9ed8c5aa 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "util/rewrite_string.hpp" @@ -257,16 +258,23 @@ void Workspaces::on_window_moved(std::string payload) { std::string workspace_name = payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx); - int changes = 0; - - const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); + std::string window_repr; + // Take the window's representation from the old workspace... for (auto &workspace : workspaces_) { - if (workspace->on_window_moved(window_address, workspace_name, clients_json)) { - changes++; - if (changes == 2) { - break; - } + try { + window_repr = workspace->on_window_closed(window_address).value(); + break; + } catch (const std::bad_optional_access &e) { + // window was not found in this workspace + continue; + } + } + + // ...and add it to the new workspace + for (auto &workspace : workspaces_) { + if (workspace->on_window_opened(window_address, workspace_name, window_repr)) { + break; } } } @@ -318,22 +326,18 @@ void Workspace::insert_window(WindowAddress addr, std::string window_class) { } }; +std::string Workspace::remove_window(WindowAddress addr) { + std::string window_repr = window_map_[addr]; + window_map_.erase(addr); + + return window_repr; +} + bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, - const Json::Value &clients_data) { + std::string window_repr) { if (workspace_name == name()) { - for (auto client : clients_data) { - // substr(2, ...) is necessary because Hyprland's JSON follows this format: - // 0x{ADDR} - // While Hyprland's IPC follows this format: - // {ADDR} - auto client_address = client["address"].asString().substr(2, addr.length()); - if (client_address == addr) { - std::string window_class = client["class"].asString(); - insert_window(addr, window_class); - return true; - } - } - return false; + window_map_.emplace(addr, window_repr); + return true; } else { return false; } @@ -349,20 +353,14 @@ bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_nam } } -bool Workspace::on_window_closed(WindowAddress &addr) { +std::optional Workspace::on_window_closed(WindowAddress &addr) { if (window_map_.contains(addr)) { - remove_window(addr); - return true; + return remove_window(addr); } else { - return false; + return {}; } } -bool Workspace::on_window_moved(WindowAddress &addr, std::string &workspace_name, - const Json::Value &clients_data) { - return on_window_opened(addr, workspace_name, clients_data) || on_window_closed(addr); -} - void Workspaces::create_workspace(Json::Value &workspace_data, const Json::Value &clients_data) { // replace the existing persistent workspace if it exists auto workspace = std::find_if( From d37954322a6102d3137ec40d2631cd8c3f63ab2c Mon Sep 17 00:00:00 2001 From: woojiq Date: Sat, 23 Sep 2023 21:55:18 +0300 Subject: [PATCH 088/364] fix(hyprland): use workspace `name` as default icon Wlr and Sway modules use the workspace `name` as the default icon if no icon is provided. This adds the same behavior for the `hyprland/workspace` module. Closes https://github.com/Alexays/Waybar/issues/2533 --- src/modules/hyprland/workspaces.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index f38b5050..8083ae88 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -549,7 +549,8 @@ std::string &Workspace::select_icon(std::map &icons_ma if (default_icon_it != icons_map.end()) { return default_icon_it->second; } - return icons_map[""]; + + return name_; } auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { From 6e48b236a18606d46c0ffd9e053579ac45f4d6cb Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 25 Sep 2023 17:12:51 -0300 Subject: [PATCH 089/364] fix: workspace special wasn't removed fixes #2505 Co-authored-by: Gabriel Fox --- src/modules/hyprland/workspaces.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index f38b5050..331403d7 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -238,8 +238,10 @@ void Workspaces::create_workspace(Json::Value &value) { } void Workspaces::remove_workspace(std::string name) { - auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(), - [&](std::unique_ptr &x) { return x->name() == name; }); + auto workspace = + std::find_if(workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr &x) { + return (name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name(); + }); if (workspace == workspaces_.end()) { // happens when a workspace on another monitor is destroyed From 9a3238c20b3367a308e6d9d143916db1a64f5e00 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 25 Sep 2023 17:13:26 -0300 Subject: [PATCH 090/364] chore: avoid the creation and deletion of doubled-special workspaces see hyprwm/Hyprland#3424 for more info Co-authored-by: Gabriel Fox --- src/modules/hyprland/workspaces.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 331403d7..14534000 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -134,6 +134,14 @@ auto Workspaces::update() -> void { AModule::update(); } +bool isDoubleSpecial(std::string &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::onEvent(const std::string &ev) { std::lock_guard lock(mutex_); std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>')); @@ -143,15 +151,16 @@ void Workspaces::onEvent(const std::string &ev) { active_workspace_name_ = payload; } else if (eventName == "destroyworkspace") { - workspaces_to_remove_.push_back(payload); - + if (!isDoubleSpecial(payload)) { + workspaces_to_remove_.push_back(payload); + } } else if (eventName == "createworkspace") { const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (Json::Value workspace_json : workspaces_json) { std::string name = workspace_json["name"].asString(); if (name == payload && (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (show_special() || !name.starts_with("special"))) { + (show_special() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { workspaces_to_create_.push_back(workspace_json); break; } From 1b98a04c93657e03f9d3776cbe4cdb5ad9504e2a Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 2 Oct 2023 12:33:28 -0300 Subject: [PATCH 091/364] chore: lint unrelated files so the CI passes --- src/modules/backlight.cpp | 4 +++- src/modules/battery.cpp | 2 +- src/modules/custom.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index dff10743..b3ca85fc 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -244,7 +244,9 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last, check_nn(name); const char *actual_brightness_attr = - strncmp(name, "amdgpu_bl", 9) == 0 || strcmp(name, "apple-panel-bl") == 0 ? "brightness" : "actual_brightness"; + 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"); diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 757a7ded..70268c8a 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -256,7 +256,7 @@ const std::tuple waybar::modules::Battery::g std::string _status; /* Check for adapter status if battery is not available */ - if(!std::ifstream(bat / "status")) { + if (!std::ifstream(bat / "status")) { std::getline(std::ifstream(adapter_ / "status"), _status); } else { std::getline(std::ifstream(bat / "status"), _status); diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 6cbb5e1e..4889b7a3 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -17,7 +17,7 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id, delayWorker(); } else if (config_["exec"].isString()) { continuousWorker(); - } + } } waybar::modules::Custom::~Custom() { From 1eb077779943a77f6a249fb2049ca74b111ea933 Mon Sep 17 00:00:00 2001 From: Joerg Weislogel Date: Sat, 7 Oct 2023 12:50:24 +0200 Subject: [PATCH 092/364] add function to cava module to hide on silence --- include/modules/cava.hpp | 1 + src/modules/cava.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/modules/cava.hpp b/include/modules/cava.hpp index d4da2b77..76a459c0 100644 --- a/include/modules/cava.hpp +++ b/include/modules/cava.hpp @@ -36,6 +36,7 @@ class Cava final : public ALabel { std::chrono::seconds fetch_input_delay_{4}; std::chrono::seconds suspend_silence_delay_{0}; bool silence_{false}; + bool hide_on_silence_{false}; int sleep_counter_{0}; // Cava method void pause_resume(); diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index be9bef4e..6ef6bf0b 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -64,6 +64,7 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) prm_.noise_reduction = config_["noise_reduction"].asDouble(); 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(); // Make cava parameters configuration plan_ = new cava_plan{}; @@ -174,10 +175,13 @@ auto waybar::modules::Cava::update() -> void { } label_.set_markup(text_); + label_.show(); ALabel::update(); } - } else + } else { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); + if (hide_on_silence_) label_.hide(); + } } auto waybar::modules::Cava::doAction(const std::string& name) -> void { From c16e791cdc2a9018f90823706b42d0696950225f Mon Sep 17 00:00:00 2001 From: SWarrener Date: Sun, 8 Oct 2023 16:24:50 +0100 Subject: [PATCH 093/364] Adding options to allow disk outputs to always be in a specific unit --- include/modules/disk.hpp | 3 +++ src/modules/disk.cpp | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/include/modules/disk.hpp b/include/modules/disk.hpp index 2a307c9e..1b4f3176 100644 --- a/include/modules/disk.hpp +++ b/include/modules/disk.hpp @@ -20,6 +20,9 @@ class Disk : public ALabel { private: util::SleeperThread thread_; std::string path_; + std::string unit_; + + float calc_specific_divisor(const std::string divisor); }; } // namespace waybar::modules diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index eb4d902f..ae1e5cf5 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -11,6 +11,9 @@ waybar::modules::Disk::Disk(const std::string& id, const Json::Value& config) if (config["path"].isString()) { path_ = config["path"].asString(); } + if (config["unit"].isString()) { + unit_ = config["unit"].asString(); + } } auto waybar::modules::Disk::update() -> void { @@ -43,6 +46,13 @@ auto waybar::modules::Disk::update() -> void { return; } + float specific_free, specific_used, specific_total, divisor; + + divisor = calc_specific_divisor(unit_); + specific_free = (stats.f_bavail * stats.f_frsize)/divisor; + specific_used = ((stats.f_blocks - stats.f_bfree) * stats.f_frsize)/divisor; + specific_total = (stats.f_blocks * stats.f_frsize)/divisor; + auto free = pow_format(stats.f_bavail * stats.f_frsize, "B", true); auto used = pow_format((stats.f_blocks - stats.f_bfree) * stats.f_frsize, "B", true); auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true); @@ -62,7 +72,8 @@ auto waybar::modules::Disk::update() -> void { 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), fmt::arg("percentage_used", percentage_used), fmt::arg("total", total), - fmt::arg("path", path_))); + fmt::arg("path", path_), fmt::arg("specific_free", specific_free), + fmt::arg("specific_used", specific_used), fmt::arg("specific_total", specific_total))); } if (tooltipEnabled()) { @@ -74,8 +85,31 @@ auto waybar::modules::Disk::update() -> void { fmt::runtime(tooltip_format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used), fmt::arg("percentage_used", percentage_used), fmt::arg("total", total), - fmt::arg("path", path_))); + fmt::arg("path", path_), fmt::arg("specific_free", specific_free), + fmt::arg("specific_used", specific_used), fmt::arg("specific_total", specific_total))); } // Call parent update ALabel::update(); } + +float waybar::modules::Disk::calc_specific_divisor(std::string divisor) { + if (divisor == "kB") { + return 1000.0; + } else if (divisor == "kiB") { + return 1024.0; + } else if (divisor == "MB") { + return 1000.0 * 1000.0; + } else if (divisor == "MiB") { + return 1024.0 * 1024.0; + } else if (divisor == "GB") { + return 1000.0 * 1000.0 * 1000.0; + } else if (divisor == "GiB") { + return 1024.0 * 1024.0 * 1024.0; + } else if (divisor == "TB") { + return 1000.0 * 1000.0 * 1000.0 * 1000.0; + } else if (divisor == "TiB") { + return 1024.0 * 1024.0 * 1024.0 * 1024.0; + } else { //default to Bytes if it is anything that we don't recongnise + return 1.0; + } +} \ No newline at end of file From ae92d7d186e4058eccfe2f9fae5529eccba43cf7 Mon Sep 17 00:00:00 2001 From: SWarrener Date: Sun, 8 Oct 2023 16:26:06 +0100 Subject: [PATCH 094/364] Updating man page with details of options to force specific units in disk size output --- man/waybar-disk.5.scd | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index 8d9b8191..732ee0f9 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -85,20 +85,30 @@ Addressed by *disk* default: "{used} out of {total} used ({percentage_used}%)" ++ The format of the information displayed in the tooltip. +*unit*: ++ + typeof: string ++ + Use with specific_free, specific_used, and speciifc_total to force calculation to always be in a certain unit. Accepts kB, kiB, MB, Mib, GB, GiB, TB, TiB. + # FORMAT REPLACEMENTS *{percentage_used}*: Percentage of disk in use. *{percentage_free}*: Percentage of free disk space -*{total}*: Total amount of space on the disk, partition or mountpoint. +*{total}*: Total amount of space on the disk, partition or mountpoint. Automatically selects unit based on size remaining. -*{used}*: Amount of used disk space. +*{used}*: Amount of used disk space. Automatically selects unit based on size remaining. -*{free}*: Amount of available disk space for normal users. +*{free}*: Amount of available disk space for normal users. Automatically selects unit based on size remaining. *{path}*: The path specified in the configuration. +*{specific_total}*: Total amount of space on the disk, partition or mountpoint in a specific unit. Deaults to bytes. + +*{specific_used}*: Amount of used disk space in a specific unit. Deaults to bytes. + +*{specific_free}*: Amount of available disk space for normal users in a specific unit. Deaults to bytes. + # EXAMPLES ``` @@ -108,6 +118,14 @@ Addressed by *disk* } ``` +``` +"disk": { + "interval": 30, + "format": "{specific_free:0.2f} GB out of {specific_total:0.2f} GB available. Alternatively {free} out of {total} available", + "unit": "GB" + // 1434.25 GB out of 2000.00 GB available. Alternatively 1.4TiB out of 1.9TiB available. +} + # STYLE - *#disk* From 32d326ca4a78b987b7d5d15a425471ef61f7802f Mon Sep 17 00:00:00 2001 From: SWarrener Date: Sun, 8 Oct 2023 16:34:27 +0100 Subject: [PATCH 095/364] Some missing backticks --- man/waybar-disk.5.scd | 1 + 1 file changed, 1 insertion(+) diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index 732ee0f9..444f8a2b 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -125,6 +125,7 @@ Addressed by *disk* "unit": "GB" // 1434.25 GB out of 2000.00 GB available. Alternatively 1.4TiB out of 1.9TiB available. } +``` # STYLE From 1af02e0a6708cee1206d5468b8eb4fc5140e471d Mon Sep 17 00:00:00 2001 From: Akshett Rai Jindal Date: Mon, 9 Oct 2023 11:48:49 +0530 Subject: [PATCH 096/364] fix(modules/mpris): fix on-*-click typos In all other places, the norm is to use `on-click-(middle|right)` but in the mpris module, `on-(middle|right)-click` was being used which caused clicks to malfunction if set to some custom commands --- man/waybar-mpris.5.scd | 4 ++-- src/modules/mpris/mpris.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 2e445696..35d7bd6f 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -126,12 +126,12 @@ The *mpris* module displays currently playing media via libplayerctl. default: play-pause ++ Overwrite default action toggles. -*on-middle-click*: ++ +*on-click-middle*: ++ typeof: string ++ default: previous track ++ Overwrite default action toggles. -*on-right-click*: ++ +*on-click-right*: ++ typeof: string ++ default: next track ++ Overwrite default action toggles. diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index aa425489..140bc785 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -587,13 +587,13 @@ bool Mpris::handleToggle(GdkEventButton* const& e) { playerctl_player_play_pause(player, &error); break; case 2: // middle-click - if (config_["on-middle-click"].isString()) { + if (config_["on-click-middle"].isString()) { return ALabel::handleToggle(e); } playerctl_player_previous(player, &error); break; case 3: // right-click - if (config_["on-right-click"].isString()) { + if (config_["on-click-right"].isString()) { return ALabel::handleToggle(e); } playerctl_player_next(player, &error); From b2e88347b32993df7fe9bed38088d94f9f8f5624 Mon Sep 17 00:00:00 2001 From: Joerg Weislogel Date: Mon, 9 Oct 2023 17:05:12 +0200 Subject: [PATCH 097/364] added description for configuration option hide-on-silence in man file --- man/waybar-cava.5.scd | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/man/waybar-cava.5.scd b/man/waybar-cava.5.scd index 5d62572e..88e736e5 100644 --- a/man/waybar-cava.5.scd +++ b/man/waybar-cava.5.scd @@ -35,7 +35,7 @@ libcava lives in: |[ *framerate* :[ integer :[ 30 -:[ rames per second. Is used as a replacement for *interval* +:[ Frames per second. Is used as a replacement for *interval* |[ *autosens* :[ integer :[ 1 @@ -60,6 +60,10 @@ libcava lives in: :[ integer :[ 5 :[ Seconds with no input before cava main thread goes to sleep mode +|[ *hide_on_silence* +:[ bool +:[ false +:[ Hides the widget if no input (after sleep_timer elapsed) |[ *method* :[ string :[ pulse From 8d057e6f96beaf5429e3c251f32f46135787f715 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 9 Oct 2023 13:53:00 -0300 Subject: [PATCH 098/364] refactor: separate regex rule matching and caching in separate class --- include/modules/hyprland/workspaces.hpp | 13 +++-- include/util/regex_collection.hpp | 37 +++++++++++++ meson.build | 3 +- src/modules/hyprland/workspaces.cpp | 61 ++++++++++++---------- src/util/regex_collection.cpp | 69 +++++++++++++++++++++++++ src/util/rewrite_string.cpp | 28 ---------- 6 files changed, 149 insertions(+), 62 deletions(-) create mode 100644 include/util/regex_collection.hpp create mode 100644 src/util/regex_collection.cpp diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 1a01758e..67fa6fc8 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -16,8 +16,10 @@ #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; @@ -47,7 +49,7 @@ class Workspace { void set_windows(uint value) { windows_ = value; }; void set_name(std::string value) { name_ = value; }; bool contains_window(WindowAddress addr) { return window_map_.contains(addr); } - void insert_window(WindowAddress addr, std::string window_repr); + void insert_window(WindowAddress addr, std::string window_class, std::string window_title); std::string remove_window(WindowAddress addr); void initialize_window_map(const Json::Value& clients_data); @@ -92,7 +94,7 @@ class Workspaces : public AModule, public EventHandler { auto get_bar_output() const -> std::string { return bar_.output->name; } - std::string get_rewrite(std::string window_class); + std::string get_rewrite(std::string window_class, std::string window_title); std::string& get_window_separator() { return format_window_separator_; } private: @@ -129,11 +131,12 @@ class Workspaces : public AModule, public EventHandler { bool persistent_created_ = false; std::string format_; + std::map icons_map_; - Json::Value window_rewrite_rules_; - std::map regex_cache_; + util::RegexCollection window_rewrite_rules_; + bool any_window_rewrite_rule_uses_title_ = false; std::string format_window_separator_; - std::string window_rewrite_default_; + bool with_icon_; uint64_t monitor_id_; std::string active_workspace_name_; diff --git a/include/util/regex_collection.hpp b/include/util/regex_collection.hpp new file mode 100644 index 00000000..267bb726 --- /dev/null +++ b/include/util/regex_collection.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include +#include +#include + +namespace waybar::util { + +struct Rule { + std::regex rule; + std::string repr; + int priority; +}; + +int default_priority_function(std::string& key); + +class RegexCollection { + private: + std::vector rules; + std::map regex_cache; + std::string default_repr; + + std::string& find_match(std::string& value, bool& matched_any); + + public: + RegexCollection() = default; + RegexCollection(const Json::Value& map, std::string default_repr = "", + std::function priority_function = default_priority_function); + ~RegexCollection() = default; + + std::string& get(std::string& value, bool& matched_any); + std::string& get(std::string& value); +}; + +} // namespace waybar::util \ No newline at end of file diff --git a/meson.build b/meson.build index 9ccd83d8..15bab87c 100644 --- a/meson.build +++ b/meson.build @@ -177,7 +177,8 @@ 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/gtk_icon.cpp', + 'src/util/regex_collection.cpp' ) inc_dirs = ['include'] diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 277a83ea..c3ddb4e5 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -1,19 +1,34 @@ #include "modules/hyprland/workspaces.hpp" -#include #include #include #include #include #include -#include #include -#include "util/rewrite_string.hpp" +#include "util/regex_collection.hpp" namespace waybar::modules::hyprland { +int window_rewrite_priority_function(std::string &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 has_title = window_rule.find("title") != std::string::npos; + bool has_class = window_rule.find("class") != std::string::npos; + + if (has_title && has_class) { + return 3; + } else if (has_title) { + return 2; + } else if (has_class) { + return 1; + } else { + return 0; + } +} + Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, false), bar_(bar), @@ -77,11 +92,14 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { format_window_separator_ = format_window_separator.isString() ? format_window_separator.asString() : " "; - window_rewrite_rules_ = config["window-rewrite"]; + Json::Value window_rewrite = config["window-rewrite"]; - Json::Value window_rewrite_default = config["window-rewrite-default"]; - window_rewrite_default_ = - window_rewrite_default.isString() ? window_rewrite_default.asString() : "?"; + Json::Value window_rewrite_default_config = config["window-rewrite-default"]; + std::string window_rewrite_default = + window_rewrite_default_config.isString() ? window_rewrite_default_config.asString() : "?"; + + window_rewrite_rules_ = util::RegexCollection(window_rewrite, window_rewrite_default, + window_rewrite_priority_function); } auto Workspaces::register_ipc() -> void { @@ -323,13 +341,14 @@ void Workspace::initialize_window_map(const Json::Value &clients_data) { // {ADDR} WindowAddress client_address = client["address"].asString(); client_address = client_address.substr(2, client_address.length() - 2); - insert_window(client_address, client["class"].asString()); + insert_window(client_address, client["class"].asString(), client["title"].asString()); } } } -void Workspace::insert_window(WindowAddress addr, std::string window_class) { - auto window_repr = workspace_manager_.get_rewrite(window_class); +void Workspace::insert_window(WindowAddress addr, std::string window_class, + std::string window_title) { + auto window_repr = workspace_manager_.get_rewrite(window_class, window_title); if (!window_repr.empty()) { window_map_.emplace(addr, window_repr); } @@ -355,7 +374,7 @@ bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_nam bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, std::string &window_class, std::string &window_title) { if (workspace_name == name()) { - insert_window(addr, window_class); + insert_window(addr, window_class, window_title); return true; } else { return false; @@ -766,23 +785,9 @@ void Workspaces::set_urgent_workspace(std::string windowaddress) { } } -std::string Workspaces::get_rewrite(std::string window_class) { - if (regex_cache_.contains(window_class)) { - return regex_cache_[window_class]; - } - - bool matched_any; - - std::string window_class_rewrite = - waybar::util::rewriteStringOnce(window_class, window_rewrite_rules_, matched_any); - - if (!matched_any) { - window_class_rewrite = window_rewrite_default_; - } - - regex_cache_.emplace(window_class, window_class_rewrite); - - return window_class_rewrite; +std::string Workspaces::get_rewrite(std::string window_class, std::string window_title) { + std::string window_repr_key = fmt::format("class<{}> title<{}>", window_class, window_title); + return window_rewrite_rules_.get(window_repr_key); } } // namespace waybar::modules::hyprland diff --git a/src/util/regex_collection.cpp b/src/util/regex_collection.cpp new file mode 100644 index 00000000..df0879c1 --- /dev/null +++ b/src/util/regex_collection.cpp @@ -0,0 +1,69 @@ +#include "util/regex_collection.hpp" + +#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) { + if (!map.isObject()) { + spdlog::warn("Mapping is not an object"); + return; + } + + for (auto it = map.begin(); it != map.end(); ++it) { + if (it.key().isString() && it->isString()) { + std::string key = it.key().asString(); + int priority = priority_function(key); + try { + const std::regex rule{key, std::regex_constants::icase}; + rules.emplace_back(rule, it->asString(), priority); + } catch (const std::regex_error& e) { + spdlog::error("Invalid rule '{}': {}", key, e.what()); + } + } + } + + 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) { + for (auto& rule : rules) { + if (std::regex_search(value, rule.rule)) { + matched_any = true; + return rule.repr; + } + } + + return value; +} + +std::string& RegexCollection::get(std::string& value, bool& matched_any) { + if (regex_cache.contains(value)) { + return regex_cache[value]; + } + + // std::string repr = + // waybar::util::find_match(value, window_rewrite_rules_, matched_any); + + std::string repr = find_match(value, matched_any); + + if (!matched_any) { + repr = default_repr; + } + + regex_cache.emplace(value, repr); + + return regex_cache[value]; // Necessary in order to return a reference to the heap +} + +std::string& RegexCollection::get(std::string& value) { + bool matched_any = false; + return get(value, matched_any); +} + +} // namespace waybar::util \ No newline at end of file diff --git a/src/util/rewrite_string.cpp b/src/util/rewrite_string.cpp index 475f8a3d..3f6ae4ca 100644 --- a/src/util/rewrite_string.cpp +++ b/src/util/rewrite_string.cpp @@ -1,6 +1,5 @@ #include "util/rewrite_string.hpp" -#include #include #include @@ -30,31 +29,4 @@ std::string rewriteString(const std::string& value, const Json::Value& rules) { return res; } - -std::string rewriteStringOnce(const std::string& value, const Json::Value& rules, - bool& matched_any) { - if (!rules.isObject()) { - return value; - } - - matched_any = false; - - std::string res = value; - - for (auto it = rules.begin(); it != rules.end(); ++it) { - if (it.key().isString() && it->isString()) { - try { - const std::regex rule{it.key().asString(), std::regex_constants::icase}; - if (std::regex_match(value, rule)) { - matched_any = true; - return std::regex_replace(res, rule, it->asString()); - } - } catch (const std::regex_error& e) { - spdlog::error("Invalid rule {}: {}", it.key().asString(), e.what()); - } - } - } - - return value; -} } // namespace waybar::util From fad43d4b1676390dd6ec86da73c6ca89ae08a84f Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 9 Oct 2023 14:42:53 -0300 Subject: [PATCH 099/364] feat: listen to windowtitle IPC event condiationally to update window rules --- include/modules/hyprland/workspaces.hpp | 4 ++- src/modules/hyprland/workspaces.cpp | 40 +++++++++++++++++++++---- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 67fa6fc8..1ab90457 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -48,7 +48,7 @@ class Workspace { void set_visible(bool value = true) { is_visible_ = value; }; void set_windows(uint value) { windows_ = value; }; void set_name(std::string value) { name_ = value; }; - bool contains_window(WindowAddress addr) { return window_map_.contains(addr); } + bool contains_window(WindowAddress addr) const { return window_map_.contains(addr); } void insert_window(WindowAddress addr, std::string window_class, std::string window_title); std::string remove_window(WindowAddress addr); void initialize_window_map(const Json::Value& clients_data); @@ -113,6 +113,8 @@ class Workspaces : public AModule, public EventHandler { void on_window_closed(std::string payload); void on_window_moved(std::string payload); + int window_rewrite_priority_function(std::string& window_rule); + bool all_outputs_ = false; bool show_special_ = false; bool active_only_ = false; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index c3ddb4e5..ebd0e67b 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -12,15 +12,17 @@ namespace waybar::modules::hyprland { -int window_rewrite_priority_function(std::string &window_rule) { +int Workspaces::window_rewrite_priority_function(std::string &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 has_title = window_rule.find("title") != std::string::npos; bool has_class = window_rule.find("class") != std::string::npos; if (has_title && has_class) { + any_window_rewrite_rule_uses_title_ = true; return 3; } else if (has_title) { + any_window_rewrite_rule_uses_title_ = true; return 2; } else if (has_class) { return 1; @@ -98,8 +100,10 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { std::string window_rewrite_default = window_rewrite_default_config.isString() ? window_rewrite_default_config.asString() : "?"; - window_rewrite_rules_ = util::RegexCollection(window_rewrite, window_rewrite_default, - window_rewrite_priority_function); + window_rewrite_rules_ = util::RegexCollection( + window_rewrite, window_rewrite_default, [this](std::string &window_rule) { + return this->window_rewrite_priority_function(window_rule); + }); } auto Workspaces::register_ipc() -> void { @@ -119,6 +123,13 @@ auto Workspaces::register_ipc() -> void { gIPC->registerForIPC("closewindow", this); gIPC->registerForIPC("movewindow", this); gIPC->registerForIPC("urgent", this); + + if (any_window_rewrite_rule_uses_title_) { + spdlog::info( + "Registering for Hyprland's 'windowtitle' events because a user-defined window " + "rewrite rule uses the 'title' field."); + gIPC->registerForIPC("windowtitle", this); + } } auto Workspaces::update() -> void { @@ -240,6 +251,25 @@ void Workspaces::onEvent(const std::string &ev) { break; } } + } else if (eventName == "windowtitle") { + auto window_workspace = + std::find_if(workspaces_.begin(), workspaces_.end(), + [payload](auto &workspace) { return workspace->contains_window(payload); }); + + if (window_workspace != workspaces_.end()) { + Json::Value clients_data = gIPC->getSocket1JsonReply("clients"); + std::string json_window_address = fmt::format("0x{}", payload); + + auto client = std::find_if(clients_data.begin(), clients_data.end(), + [json_window_address](auto &client) { + return client["address"].asString() == json_window_address; + }); + + if (!client->empty()) { + (*window_workspace) + ->insert_window(payload, (*client)["class"].asString(), (*client)["title"].asString()); + } + } } dp.emit(); @@ -350,7 +380,7 @@ void Workspace::insert_window(WindowAddress addr, std::string window_class, std::string window_title) { auto window_repr = workspace_manager_.get_rewrite(window_class, window_title); if (!window_repr.empty()) { - window_map_.emplace(addr, window_repr); + window_map_[addr] = window_repr; } }; @@ -364,7 +394,7 @@ std::string Workspace::remove_window(WindowAddress addr) { bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, std::string window_repr) { if (workspace_name == name()) { - window_map_.emplace(addr, window_repr); + window_map_[addr] = window_repr; return true; } else { return false; From 387e54498e60736eea136551aaa2dd78f096eedc Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 9 Oct 2023 14:46:57 -0300 Subject: [PATCH 100/364] docs: document new regex collection class --- include/util/regex_collection.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/util/regex_collection.hpp b/include/util/regex_collection.hpp index 267bb726..14f28611 100644 --- a/include/util/regex_collection.hpp +++ b/include/util/regex_collection.hpp @@ -16,6 +16,14 @@ struct Rule { int default_priority_function(std::string& key); +/* A collection of regexes and strings, with a default string to return if no regexes. + * When a regex is matched, the corresponding string is returned. + * All regexes that are matched are cached, so that the regexes are only + * evaluated once against a given string. + * Regexes may be given a higher priority than others, so that they are matched + * first. The priority function is given the regex string, and should return a + * higher number for higher priority regexes. + */ class RegexCollection { private: std::vector rules; From f9a7ecf3a9a82bb4f48da3df87e5fd64631395fa Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 9 Oct 2023 14:49:38 -0300 Subject: [PATCH 101/364] feat: optimize cache usage when window titles aren't involved --- src/modules/hyprland/workspaces.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index ebd0e67b..b8dc5e00 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -816,7 +816,12 @@ void Workspaces::set_urgent_workspace(std::string windowaddress) { } std::string Workspaces::get_rewrite(std::string window_class, std::string window_title) { - std::string window_repr_key = fmt::format("class<{}> title<{}>", window_class, window_title); + std::string window_repr_key; + if (any_window_rewrite_rule_uses_title_) { + window_repr_key = fmt::format("class<{}> title<{}>", window_class, window_title); + } else { + window_repr_key = fmt::format("class<{}>", window_class); + } return window_rewrite_rules_.get(window_repr_key); } From 592d5645a5d684cab18fe3e0688ac24329acfe26 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 9 Oct 2023 14:58:21 -0300 Subject: [PATCH 102/364] docs: include new feature in manual --- man/waybar-hyprland-workspaces.5.scd | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 02be176d..6bbc1407 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -25,6 +25,7 @@ Addressed by *hyprland/workspaces* 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 ++ @@ -124,8 +125,10 @@ Additional to workspace name matching, the following *format-icons* can be set. "format-window-separator": "\n", "window-rewrite-default": "", "window-rewrite": { - "firefox": "", - "foot": "", + "title<.*youtube.*>": "", // Windows whose titles contain "youtube" + "class": "", // Windows whose classes are "firefox" + "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": "󰨞", } } From f8340d88be67ed4492eca45dc24667fa5393c4c2 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 9 Oct 2023 15:26:07 -0300 Subject: [PATCH 103/364] chore: lint unrelated file this file was edited in #2558 but not linted --- src/modules/disk.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index ae1e5cf5..ef257b72 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -49,9 +49,9 @@ auto waybar::modules::Disk::update() -> void { float specific_free, specific_used, specific_total, divisor; divisor = calc_specific_divisor(unit_); - specific_free = (stats.f_bavail * stats.f_frsize)/divisor; - specific_used = ((stats.f_blocks - stats.f_bfree) * stats.f_frsize)/divisor; - specific_total = (stats.f_blocks * stats.f_frsize)/divisor; + specific_free = (stats.f_bavail * stats.f_frsize) / divisor; + specific_used = ((stats.f_blocks - stats.f_bfree) * stats.f_frsize) / divisor; + specific_total = (stats.f_blocks * stats.f_frsize) / divisor; auto free = pow_format(stats.f_bavail * stats.f_frsize, "B", true); auto used = pow_format((stats.f_blocks - stats.f_bfree) * stats.f_frsize, "B", true); @@ -72,7 +72,7 @@ auto waybar::modules::Disk::update() -> void { 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), fmt::arg("percentage_used", percentage_used), fmt::arg("total", total), - fmt::arg("path", path_), fmt::arg("specific_free", specific_free), + fmt::arg("path", path_), fmt::arg("specific_free", specific_free), fmt::arg("specific_used", specific_used), fmt::arg("specific_total", specific_total))); } @@ -85,7 +85,7 @@ auto waybar::modules::Disk::update() -> void { fmt::runtime(tooltip_format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used), fmt::arg("percentage_used", percentage_used), fmt::arg("total", total), - fmt::arg("path", path_), fmt::arg("specific_free", specific_free), + fmt::arg("path", path_), fmt::arg("specific_free", specific_free), fmt::arg("specific_used", specific_used), fmt::arg("specific_total", specific_total))); } // Call parent update @@ -109,7 +109,7 @@ float waybar::modules::Disk::calc_specific_divisor(std::string divisor) { return 1000.0 * 1000.0 * 1000.0 * 1000.0; } else if (divisor == "TiB") { return 1024.0 * 1024.0 * 1024.0 * 1024.0; - } else { //default to Bytes if it is anything that we don't recongnise + } else { // default to Bytes if it is anything that we don't recongnise return 1.0; } } \ No newline at end of file From c995bafa7a32587f814d79220b3acc58fa8292c7 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Wed, 11 Oct 2023 10:59:33 -0300 Subject: [PATCH 104/364] feat: add option to ignore workspaces based on regex --- include/modules/hyprland/workspaces.hpp | 5 +++ src/modules/hyprland/workspaces.cpp | 50 ++++++++++++++++++++----- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 1a01758e..544fa1d2 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -94,6 +95,7 @@ class Workspaces : public AModule, public EventHandler { std::string get_rewrite(std::string window_class); std::string& get_window_separator() { return format_window_separator_; } + bool is_workspace_ignored(std::string& workspace_name); private: void onEvent(const std::string&) override; @@ -140,6 +142,9 @@ class Workspaces : public AModule, public EventHandler { std::vector> workspaces_; std::vector workspaces_to_create_; std::vector workspaces_to_remove_; + + std::vector ignore_workspaces_; + std::mutex mutex_; const Bar& bar_; Gtk::Box box_; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 277a83ea..7936add4 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -73,6 +73,23 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { } } + Json::Value ignore_workspaces = config["ignore-workspaces"]; + if (ignore_workspaces.isArray()) { + for (Json::Value &workspace_regex : ignore_workspaces) { + if (workspace_regex.isString()) { + std::string rule_string = workspace_regex.asString(); + try { + const std::regex rule{rule_string, std::regex_constants::icase}; + ignore_workspaces_.emplace_back(rule); + } catch (const std::regex_error &e) { + spdlog::error("Invalid rule {}: {}", rule_string, e.what()); + } + } else { + spdlog::error("Not a string: '{}'", workspace_regex); + } + } + } + Json::Value format_window_separator = config["format-window-separator"]; format_window_separator_ = format_window_separator.isString() ? format_window_separator.asString() : " "; @@ -156,6 +173,17 @@ bool isDoubleSpecial(std::string &workspace_name) { return workspace_name.find("special:special:") != std::string::npos; } +bool Workspaces::is_workspace_ignored(std::string &name) { + for (auto &rule : ignore_workspaces_) { + if (std::regex_match(name, rule)) { + return true; + break; + } + } + + return false; +} + void Workspaces::onEvent(const std::string &ev) { std::lock_guard lock(mutex_); std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>')); @@ -170,16 +198,18 @@ void Workspaces::onEvent(const std::string &ev) { } } else if (eventName == "createworkspace") { const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); - for (Json::Value workspace_json : workspaces_json) { - std::string name = workspace_json["name"].asString(); - if (name == payload && - (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (show_special() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { - workspaces_to_create_.push_back(workspace_json); - break; + + if (!is_workspace_ignored(payload)) { + for (Json::Value workspace_json : workspaces_json) { + std::string name = workspace_json["name"].asString(); + if (name == payload && + (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && + (show_special() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { + workspaces_to_create_.push_back(workspace_json); + break; + } } } - } else if (eventName == "focusedmon") { active_workspace_name_ = payload.substr(payload.find(',') + 1); @@ -511,8 +541,10 @@ void Workspaces::init() { const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); for (Json::Value workspace_json : workspaces_json) { + std::string workspace_name = workspace_json["name"].asString(); if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (!workspace_json["name"].asString().starts_with("special") || show_special())) { + (!workspace_name.starts_with("special") || show_special()) && + !is_workspace_ignored(workspace_name)) { create_workspace(workspace_json, clients_json); } } From ceeb5bf8bd0ae683263eb1e9c026337c57ef4c02 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Wed, 11 Oct 2023 11:03:18 -0300 Subject: [PATCH 105/364] docs: include new ignore-workspaces example and documentation --- man/waybar-hyprland-workspaces.5.scd | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 02be176d..cdbb0fd2 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -51,6 +51,11 @@ Addressed by *hyprland/workspaces* default: false ++ If set to true, only the active workspace will be shown. +*ignore-workspaces*: ++ + typeof: array ++ + default: [] ++ + Regexes to match against workspaces names. If there's a match, the workspace will not be shown. This takes precedence over *show-special*, *all-outputs* and *active-only*. + *sort-by*: ++ typeof: string ++ default: "default" ++ @@ -131,6 +136,14 @@ Additional to workspace name matching, the following *format-icons* can be set. } ``` +"hyprland/workspaces": { + // Formatting omitted for brevity + "ignore-workspaces": [ + "(special:)?chrome-sharing-indicator" + ] +} +``` + # Style - *#workspaces* From ee652677a6fed6b4579e2eec3e93adac138db914 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Wed, 11 Oct 2023 11:40:49 -0300 Subject: [PATCH 106/364] feat: ignore windows with empty classes or titles (if any rule uses title) --- include/modules/hyprland/workspaces.hpp | 2 ++ src/modules/hyprland/workspaces.cpp | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 1ab90457..9f259614 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -97,6 +97,8 @@ class Workspaces : public AModule, public EventHandler { std::string get_rewrite(std::string window_class, std::string window_title); std::string& get_window_separator() { return format_window_separator_; } + bool window_rewrite_config_uses_title() const { return any_window_rewrite_rule_uses_title_; } + private: void onEvent(const std::string&) override; void update_window_count(); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index b8dc5e00..3be2acf2 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -124,7 +124,7 @@ auto Workspaces::register_ipc() -> void { gIPC->registerForIPC("movewindow", this); gIPC->registerForIPC("urgent", this); - if (any_window_rewrite_rule_uses_title_) { + if (window_rewrite_config_uses_title()) { spdlog::info( "Registering for Hyprland's 'windowtitle' events because a user-defined window " "rewrite rule uses the 'title' field."); @@ -378,7 +378,13 @@ void Workspace::initialize_window_map(const Json::Value &clients_data) { void Workspace::insert_window(WindowAddress addr, std::string window_class, std::string window_title) { + if (window_class.empty() && + (!workspace_manager_.window_rewrite_config_uses_title() || window_title.empty())) { + return; + } + auto window_repr = workspace_manager_.get_rewrite(window_class, window_title); + if (!window_repr.empty()) { window_map_[addr] = window_repr; } @@ -817,7 +823,7 @@ void Workspaces::set_urgent_workspace(std::string windowaddress) { std::string Workspaces::get_rewrite(std::string window_class, std::string window_title) { std::string window_repr_key; - if (any_window_rewrite_rule_uses_title_) { + if (window_rewrite_config_uses_title()) { window_repr_key = fmt::format("class<{}> title<{}>", window_class, window_title); } else { window_repr_key = fmt::format("class<{}>", window_class); From c59264d6b4447663e3886f79741b531bffd4b74c Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Thu, 12 Oct 2023 17:30:32 -0300 Subject: [PATCH 107/364] fix: clang < 16 can't emplace back struct with no constructor --- include/util/regex_collection.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/util/regex_collection.hpp b/include/util/regex_collection.hpp index 14f28611..5ea2882e 100644 --- a/include/util/regex_collection.hpp +++ b/include/util/regex_collection.hpp @@ -12,6 +12,12 @@ struct Rule { std::regex rule; std::string repr; int priority; + + // Fix for Clang < 16 + // 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) {} }; int default_priority_function(std::string& key); From bbb7fb0c8266c13ebb50e79224045ef1a8e16439 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sat, 14 Oct 2023 13:08:44 -0300 Subject: [PATCH 108/364] refactor: don't use a group's box directly in bar --- include/bar.hpp | 3 ++- include/group.hpp | 6 ++++-- src/bar.cpp | 17 ++++++++++++----- src/group.cpp | 4 +++- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index ee4a1d5e..d2cbffa0 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -12,6 +12,7 @@ #include #include "AModule.hpp" +#include "group.hpp" #include "xdg-output-unstable-v1-client-protocol.h" namespace waybar { @@ -101,7 +102,7 @@ class Bar { private: void onMap(GdkEventAny *); auto setupWidgets() -> void; - void getModules(const Factory &, const std::string &, Gtk::Box *); + void getModules(const Factory &, const std::string &, waybar::Group *); void setupAltFormatKeyForModule(const std::string &module_name); void setupAltFormatKeyForModuleList(const char *module_list_name); void setMode(const bar_mode &); diff --git a/include/group.hpp b/include/group.hpp index 60e31c96..ee9d282b 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -5,8 +5,6 @@ #include #include "AModule.hpp" -#include "bar.hpp" -#include "factory.hpp" namespace waybar { @@ -16,6 +14,10 @@ class Group : public AModule { ~Group() = default; auto update() -> void override; operator Gtk::Widget&() override; + + virtual Gtk::Box& getBox(); + + protected: Gtk::Box box; }; diff --git a/src/bar.cpp b/src/bar.cpp index 30cf7fad..9b3a12f3 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -740,7 +740,7 @@ void waybar::Bar::handleSignal(int signal) { } void waybar::Bar::getModules(const Factory& factory, const std::string& pos, - Gtk::Box* group = nullptr) { + waybar::Group* group = nullptr) { auto module_list = group ? config[pos]["modules"] : config[pos]; if (module_list.isArray()) { for (const auto& name : module_list) { @@ -753,10 +753,17 @@ 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 parent = group ? group : &this->box_; - auto vertical = parent->get_orientation() == Gtk::ORIENTATION_VERTICAL; + // auto parent = group ? group : &this->box_; + // auto vertical = parent->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->box); + getModules(factory, ref, group_module); module = group_module; } else { module = factory.makeModule(ref); @@ -765,7 +772,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) { - group->pack_start(*module, false, false); + group->getBox().pack_start(*module, false, false); } else { if (pos == "modules-left") { modules_left_.emplace_back(module_sp); diff --git a/src/group.cpp b/src/group.cpp index 548fb0da..54d53e7e 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -35,6 +35,8 @@ auto Group::update() -> void { // noop } -Group::operator Gtk::Widget&() { return box; } +Gtk::Box& Group::getBox() { return box; } + +Group::operator Gtk::Widget&() { return getBox(); } } // namespace waybar From 5246ab15cbbdb92bf490b70d1c326465f6910d38 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sat, 14 Oct 2023 17:17:19 -0300 Subject: [PATCH 109/364] feat: add drawer bool option to group --- include/group.hpp | 10 +++++++- src/bar.cpp | 9 +++---- src/group.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index ee9d282b..a9ecf066 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -5,20 +5,28 @@ #include #include "AModule.hpp" +#include "gtkmm/revealer.h" namespace waybar { class Group : public AModule { public: Group(const std::string&, const std::string&, const Json::Value&, bool); - ~Group() = default; + virtual ~Group() = default; auto update() -> void override; operator Gtk::Widget&() override; virtual Gtk::Box& getBox(); + void addWidget(Gtk::Widget& widget); + + bool hangleMouseHover(GdkEventCrossing* const& e); protected: Gtk::Box box; + Gtk::Box revealer_box; + Gtk::Revealer revealer; + bool is_first_widget = true; + bool is_drawer = false; }; } // namespace waybar diff --git a/src/bar.cpp b/src/bar.cpp index 9b3a12f3..415466b0 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -756,11 +756,8 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, // auto parent = group ? group : &this->box_; // auto vertical = parent->get_orientation() == Gtk::ORIENTATION_VERTICAL; - auto vertical = ( - group ? - 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); @@ -772,7 +769,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) { - group->getBox().pack_start(*module, false, false); + group->addWidget(*module); } else { if (pos == "modules-left") { modules_left_.emplace_back(module_sp); diff --git a/src/group.cpp b/src/group.cpp index 54d53e7e..300d59fb 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -1,15 +1,20 @@ #include "group.hpp" #include +#include #include +#include "gdkmm/device.h" +#include "gtkmm/widget.h" + namespace waybar { Group::Group(const std::string& name, const std::string& id, const Json::Value& config, bool vertical) - : AModule(config, name, id, false, false), - box{vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} { + : 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_); if (!id.empty()) { box.get_style_context()->add_class(id); @@ -29,14 +34,62 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& } else { throw std::runtime_error("Invalid orientation value: " + orientation); } + + if (!config_["drawer"].empty() && config_["drawer"].asBool()) { + is_drawer = true; + revealer.set_transition_type(Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_UP); + revealer.set_transition_duration(500); + revealer.set_reveal_child(false); + + revealer.get_style_context()->add_class("drawer"); + + revealer.add(revealer_box); + box.pack_start(revealer); + + revealer.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK); + revealer.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::hangleMouseHover)); + revealer.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::hangleMouseHover)); + } +} + +bool Group::hangleMouseHover(GdkEventCrossing* const& e) { + spdlog::info("Mouse hover event"); + + switch (e->type) { + case GDK_ENTER_NOTIFY: + spdlog::info("Mouse enter event"); + revealer.set_reveal_child(true); + break; + case GDK_LEAVE_NOTIFY: + spdlog::info("Mouse leave event"); + revealer.set_reveal_child(false); + break; + default: + spdlog::warn("Unhandled mouse hover event type: {}", (int)e->type); + break; + } + + return true; } auto Group::update() -> void { // noop } -Gtk::Box& Group::getBox() { return box; } +Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; } -Group::operator Gtk::Widget&() { return getBox(); } +void Group::addWidget(Gtk::Widget& widget) { + widget.set_has_tooltip(false); + spdlog::info("Adding widget to group {}. Is first? {}", name_, is_first_widget); + getBox().pack_start(widget, false, false); + if (is_first_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::hangleMouseHover)); + widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::hangleMouseHover)); + } + is_first_widget = false; +} + +Group::operator Gtk::Widget&() { return box; } } // namespace waybar From fad858782c5c4dab5c8bcdcf31cfd10dd448bca6 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sat, 14 Oct 2023 18:03:27 -0300 Subject: [PATCH 110/364] feat: improve drawer configuration --- include/group.hpp | 1 + src/bar.cpp | 3 --- src/group.cpp | 63 +++++++++++++++++++++++++++++++++++------------ 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index a9ecf066..03b2632f 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -27,6 +27,7 @@ class Group : public AModule { Gtk::Revealer revealer; bool is_first_widget = true; bool is_drawer = false; + std::string add_class_to_drawer_children; }; } // namespace waybar diff --git a/src/bar.cpp b/src/bar.cpp index 415466b0..d0a187c6 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -753,9 +753,6 @@ 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 parent = group ? group : &this->box_; - // auto vertical = parent->get_orientation() == Gtk::ORIENTATION_VERTICAL; - auto vertical = (group ? group->getBox().get_orientation() : box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL; diff --git a/src/group.cpp b/src/group.cpp index 300d59fb..240216c7 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -1,7 +1,6 @@ #include "group.hpp" #include -#include #include @@ -10,6 +9,22 @@ namespace waybar { +const Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical, bool left_to_right) { + if (is_vertical) { + if (left_to_right) { + return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_DOWN; + } else { + 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; + } + } +} + Group::Group(const std::string& name, const std::string& id, const Json::Value& config, bool vertical) : AModule(config, name, id, true, true), @@ -35,10 +50,24 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& throw std::runtime_error("Invalid orientation value: " + orientation); } - if (!config_["drawer"].empty() && config_["drawer"].asBool()) { + if (config_["drawer"].isObject()) { is_drawer = true; - revealer.set_transition_type(Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_UP); - revealer.set_transition_duration(500); + + 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 = + (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); + + auto transition_type = getPreferredTransitionType(vertical, left_to_right); + + revealer.set_transition_type(transition_type); + revealer.set_transition_duration(transition_duration); revealer.set_reveal_child(false); revealer.get_style_context()->add_class("drawer"); @@ -53,19 +82,14 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& } bool Group::hangleMouseHover(GdkEventCrossing* const& e) { - spdlog::info("Mouse hover event"); - switch (e->type) { case GDK_ENTER_NOTIFY: - spdlog::info("Mouse enter event"); revealer.set_reveal_child(true); break; case GDK_LEAVE_NOTIFY: - spdlog::info("Mouse leave event"); revealer.set_reveal_child(false); break; default: - spdlog::warn("Unhandled mouse hover event type: {}", (int)e->type); break; } @@ -79,14 +103,21 @@ auto Group::update() -> void { Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; } void Group::addWidget(Gtk::Widget& widget) { - widget.set_has_tooltip(false); - spdlog::info("Adding widget to group {}. Is first? {}", name_, is_first_widget); - getBox().pack_start(widget, false, false); - if (is_first_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::hangleMouseHover)); - widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::hangleMouseHover)); + if (is_drawer) { + getBox().pack_start(widget, false, false); + + if (is_first_widget) { + // Necessary because of GTK's hitbox detection + widget.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK); + widget.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::hangleMouseHover)); + widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::hangleMouseHover)); + } else { + widget.get_style_context()->add_class(add_class_to_drawer_children); + } + } else { + getBox().pack_start(widget, false, false); } + is_first_widget = false; } From 5e44cb6ba200aa86f5929a35db047119e91d3ed0 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sat, 14 Oct 2023 18:30:27 -0300 Subject: [PATCH 111/364] refactor: move signal handler adding into separate method fix: typo in handleMouseHover method name --- include/group.hpp | 4 +++- src/group.cpp | 16 +++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index 03b2632f..67cf4385 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -19,7 +19,7 @@ class Group : public AModule { virtual Gtk::Box& getBox(); void addWidget(Gtk::Widget& widget); - bool hangleMouseHover(GdkEventCrossing* const& e); + bool handleMouseHover(GdkEventCrossing* const& e); protected: Gtk::Box box; @@ -28,6 +28,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); }; } // namespace waybar diff --git a/src/group.cpp b/src/group.cpp index 240216c7..11e28d85 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -75,13 +75,11 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& revealer.add(revealer_box); box.pack_start(revealer); - revealer.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK); - revealer.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::hangleMouseHover)); - revealer.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::hangleMouseHover)); + addHoverHandlerTo(revealer); } } -bool Group::hangleMouseHover(GdkEventCrossing* const& e) { +bool Group::handleMouseHover(GdkEventCrossing* const& e) { switch (e->type) { case GDK_ENTER_NOTIFY: revealer.set_reveal_child(true); @@ -96,6 +94,12 @@ bool Group::hangleMouseHover(GdkEventCrossing* const& e) { return true; } +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)); +} + auto Group::update() -> void { // noop } @@ -108,9 +112,7 @@ void Group::addWidget(Gtk::Widget& widget) { if (is_first_widget) { // Necessary because of GTK's hitbox detection - widget.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK); - widget.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::hangleMouseHover)); - widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::hangleMouseHover)); + addHoverHandlerTo(widget); } else { widget.get_style_context()->add_class(add_class_to_drawer_children); } From 5a380da3bb5adcd9dd3a3e1d073f830b0c9df722 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sat, 14 Oct 2023 18:39:42 -0300 Subject: [PATCH 112/364] chore: remove redundant else statement --- src/group.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/group.cpp b/src/group.cpp index 11e28d85..3eceacc1 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -107,17 +107,15 @@ auto Group::update() -> void { Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; } void Group::addWidget(Gtk::Widget& widget) { - if (is_drawer) { - getBox().pack_start(widget, false, false); + getBox().pack_start(widget, false, false); + if (is_drawer) { if (is_first_widget) { // Necessary because of GTK's hitbox detection addHoverHandlerTo(widget); } else { widget.get_style_context()->add_class(add_class_to_drawer_children); } - } else { - getBox().pack_start(widget, false, false); } is_first_widget = false; From 8f32d102ae3cbf9628f38206cdda806916088a3c Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sat, 14 Oct 2023 18:50:45 -0300 Subject: [PATCH 113/364] docs: include group drawer documentation --- man/waybar.5.scd.in | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 92b365d9..d932307c 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -240,7 +240,7 @@ Valid options for the "rotate" property are: 0, 90, 180 and 270. ## Grouping modules -Module groups allow stacking modules in the direction orthogonal to the bar direction. When the bar is positioned on the top or bottom of the screen, modules in a group are stacked vertically. Likewise, when positioned on the left or right, modules in a group are stacked horizontally. +Module groups allow stacking modules in any direction. By default, when the bar is positioned on the top or bottom of the screen, modules in a group are stacked vertically. Likewise, when positioned on the left or right, modules in a group are stacked horizontally. This can be changed with the "orientation" property. A module group is defined by specifying a module named "group/some-group-name". The group must also be configured with a list of contained modules. Example: @@ -263,6 +263,43 @@ A module group is defined by specifying a module named "group/some-group-name". Valid options for the (optional) "orientation" property are: "horizontal", "vertical", "inherit", and "orthogonal" (default). +### Group Drawers + +A group may hide all but one element, showing them only on mouse hover. In order to configure this, you can use the `drawer` property, whose value is an object with the following properties: + +*transition-duration*: ++ + typeof: integer ++ + default: 500 ++ + Defines the duration of the transition animation in milliseconds. + +*children-class*: ++ + typeof: string ++ + default: "hidden" ++ + Defines the CSS class to be applied to the hidden elements. + +*transition-left-to-right*: ++ + typeof: bool ++ + default: true ++ + Defines the direction of the transition animation. If true, the hidden elements will slide from left to right. If false, they will slide from right to left. + When the bar is vertical, it reads as top-to-bottom. + +``` +"group/power": { + "orientation": "inherit", + "drawer": { + "transition-duration": 500, + "children-class": "not-power", + "transition-left-to-right": false, + }, + "modules": [ + "custom/power", // First element is the "group leader" and won't ever be hidden + "custom/quit", + "custom/lock", + "custom/reboot", + ] +}, +``` + # SUPPORTED MODULES - *waybar-backlight(5)* From 05b97e9ec2015c57db44f0149b8b43f8d98bfe62 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sat, 14 Oct 2023 22:13:01 -0300 Subject: [PATCH 114/364] fix: add hover handler to every element in the group drawer --- src/group.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/group.cpp b/src/group.cpp index 3eceacc1..cad36e51 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -110,10 +110,9 @@ void Group::addWidget(Gtk::Widget& widget) { getBox().pack_start(widget, false, false); if (is_drawer) { - if (is_first_widget) { - // Necessary because of GTK's hitbox detection - addHoverHandlerTo(widget); - } else { + // Necessary because of GTK's hitbox detection + addHoverHandlerTo(widget); + if (!is_first_widget) { widget.get_style_context()->add_class(add_class_to_drawer_children); } } From ff9b6c94691237e96d5fc163865ddfa54030ac1b Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sat, 14 Oct 2023 22:14:13 -0300 Subject: [PATCH 115/364] docs: fix weird man limitation --- man/waybar.5.scd.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index d932307c..9c3220f8 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -263,7 +263,7 @@ A module group is defined by specifying a module named "group/some-group-name". Valid options for the (optional) "orientation" property are: "horizontal", "vertical", "inherit", and "orthogonal" (default). -### Group Drawers +## Group Drawers A group may hide all but one element, showing them only on mouse hover. In order to configure this, you can use the `drawer` property, whose value is an object with the following properties: From 64d7fae03afea2e67eb13b71c295e970f4790b4d Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sun, 15 Oct 2023 10:16:49 -0300 Subject: [PATCH 116/364] refactor: move pulseaudio handling to separate class --- include/modules/pulseaudio.hpp | 35 +---- include/util/audio_backend.hpp | 90 ++++++++++++ meson.build | 3 +- src/modules/pulseaudio.cpp | 249 ++++---------------------------- src/util/audio_backend.cpp | 251 +++++++++++++++++++++++++++++++++ 5 files changed, 377 insertions(+), 251 deletions(-) create mode 100644 include/util/audio_backend.hpp create mode 100644 src/util/audio_backend.cpp diff --git a/include/modules/pulseaudio.hpp b/include/modules/pulseaudio.hpp index d0b17e47..eead664f 100644 --- a/include/modules/pulseaudio.hpp +++ b/include/modules/pulseaudio.hpp @@ -1,54 +1,27 @@ #pragma once #include -#include -#include #include #include +#include #include "ALabel.hpp" +#include "util/audio_backend.hpp" namespace waybar::modules { class Pulseaudio : public ALabel { public: Pulseaudio(const std::string&, const Json::Value&); - virtual ~Pulseaudio(); + virtual ~Pulseaudio() = default; auto update() -> void override; private: - static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*); - static void contextStateCb(pa_context*, void*); - static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*); - 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*); - bool handleScroll(GdkEventScroll* e) override; const std::vector getPulseIcon() const; - pa_threaded_mainloop* mainloop_; - pa_mainloop_api* mainloop_api_; - pa_context* context_; - // SINK - uint32_t sink_idx_{0}; - uint16_t volume_; - pa_cvolume pa_volume_; - bool muted_; - std::string port_name_; - std::string form_factor_; - std::string desc_; - std::string monitor_; - std::string current_sink_name_; - bool current_sink_running_; - // SOURCE - uint32_t source_idx_{0}; - uint16_t source_volume_; - bool source_muted_; - std::string source_port_name_; - std::string source_desc_; - std::string default_source_name_; + std::shared_ptr backend = nullptr; }; } // namespace waybar::modules diff --git a/include/util/audio_backend.hpp b/include/util/audio_backend.hpp new file mode 100644 index 00000000..9435a842 --- /dev/null +++ b/include/util/audio_backend.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace waybar::util { + +enum class ChangeType : char { Increase, Decrease }; + +void noop(); + +class AudioBackend { + private: + static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*); + static void contextStateCb(pa_context*, void*); + static void sinkInfoCb(pa_context*, const pa_sink_info*, int, void*); + 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*); + + pa_threaded_mainloop* mainloop_; + pa_mainloop_api* mainloop_api_; + pa_context* context_; + pa_cvolume pa_volume_; + + // SINK + uint32_t sink_idx_{0}; + uint16_t volume_; + bool muted_; + std::string port_name_; + std::string form_factor_; + std::string desc_; + std::string monitor_; + std::string current_sink_name_; + bool current_sink_running_; + // SOURCE + uint32_t source_idx_{0}; + uint16_t source_volume_; + bool source_muted_; + std::string source_port_name_; + std::string source_desc_; + std::string default_source_name_; + + std::vector ignored_sinks_; + + std::function on_updated_cb_ = noop; + + /* Hack to keep constructor inaccessible but still public. + * This is required to be able to use std::make_shared. + * It is important to keep this class only accessible via a reference-counted + * 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 {}; + + public: + static std::shared_ptr getInstance(std::function on_updated_cb = noop); + + AudioBackend(std::function on_updated_cb, private_constructor_tag tag); + ~AudioBackend(); + + void changeVolume(ChangeType change_type, double step = 1, int max_volume = 100); + + void setIgnoredSinks(const Json::Value& config); + + std::string getSinkPortName() const { return port_name_; } + std::string getFormFactor() const { return form_factor_; } + std::string getSinkDesc() const { return desc_; } + std::string getMonitor() const { return monitor_; } + std::string getCurrentSinkName() const { return current_sink_name_; } + bool getCurrentSinkRunning() const { return current_sink_running_; } + uint16_t getSinkVolume() const { return volume_; } + bool getSinkMuted() const { return muted_; } + uint16_t getSourceVolume() const { return source_volume_; } + bool getSourceMuted() const { return source_muted_; } + std::string getSourcePortName() const { return source_port_name_; } + std::string getSourceDesc() const { return source_desc_; } + std::string getDefaultSourceName() const { return default_source_name_; } + + bool isBluetooth(); +}; + +} // namespace waybar::util \ No newline at end of file diff --git a/meson.build b/meson.build index 15bab87c..e85785ad 100644 --- a/meson.build +++ b/meson.build @@ -178,7 +178,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/audio_backend.cpp' ) inc_dirs = ['include'] diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index d35e2983..d7dc80d3 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -1,74 +1,12 @@ #include "modules/pulseaudio.hpp" waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config) - : ALabel(config, "pulseaudio", id, "{volume}%"), - mainloop_(nullptr), - mainloop_api_(nullptr), - context_(nullptr), - sink_idx_(0), - volume_(0), - muted_(false), - source_idx_(0), - source_volume_(0), - source_muted_(false) { - mainloop_ = pa_threaded_mainloop_new(); - if (mainloop_ == nullptr) { - throw std::runtime_error("pa_mainloop_new() failed."); - } - 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); - if (pa_threaded_mainloop_start(mainloop_) < 0) { - throw std::runtime_error("pa_mainloop_run() failed."); - } - pa_threaded_mainloop_unlock(mainloop_); + : 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)); -} -waybar::modules::Pulseaudio::~Pulseaudio() { - pa_context_disconnect(context_); - mainloop_api_->quit(mainloop_api_, 0); - pa_threaded_mainloop_stop(mainloop_); - pa_threaded_mainloop_free(mainloop_); -} - -void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) { - auto pa = static_cast(data); - switch (pa_context_get_state(c)) { - case PA_CONTEXT_TERMINATED: - pa->mainloop_api_->quit(pa->mainloop_api_, 0); - break; - case PA_CONTEXT_READY: - pa_context_get_server_info(c, serverInfoCb, data); - pa_context_set_subscribe_callback(c, subscribeCb, data); - pa_context_subscribe(c, - static_cast( - static_cast(PA_SUBSCRIPTION_MASK_SERVER) | - static_cast(PA_SUBSCRIPTION_MASK_SINK) | - static_cast(PA_SUBSCRIPTION_MASK_SINK_INPUT) | - static_cast(PA_SUBSCRIPTION_MASK_SOURCE) | - static_cast(PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)), - nullptr, nullptr); - break; - case PA_CONTEXT_FAILED: - pa->mainloop_api_->quit(pa->mainloop_api_, 1); - break; - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - default: - break; - } + backend = util::AudioBackend::getInstance([this] { this->dp.emit(); }); + backend->setIgnoredSinks(config_["ignored-sinks"]); } bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { @@ -81,9 +19,6 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { if (dir == SCROLL_DIR::NONE) { return true; } - double volume_tick = static_cast(PA_VOLUME_NORM) / 100; - pa_volume_t change = volume_tick; - pa_cvolume pa_volume = pa_volume_; int max_volume = 100; double step = 1; // isDouble returns true for integers as well, just in case @@ -91,152 +26,24 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { step = config_["scroll-step"].asDouble(); } if (config_["max-volume"].isInt()) { - max_volume = std::min(config_["max-volume"].asInt(), static_cast(PA_VOLUME_UI_MAX)); + max_volume = config_["max-volume"].asInt(); } - if (dir == SCROLL_DIR::UP) { - if (volume_ < max_volume) { - if (volume_ + step > max_volume) { - change = round((max_volume - volume_) * volume_tick); - } else { - change = round(step * volume_tick); - } - pa_cvolume_inc(&pa_volume, change); - } - } else if (dir == SCROLL_DIR::DOWN) { - if (volume_ > 0) { - if (volume_ - step < 0) { - change = round(volume_ * volume_tick); - } else { - change = round(step * volume_tick); - } - pa_cvolume_dec(&pa_volume, change); - } - } - pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); + 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; } -/* - * Called when an event we subscribed to occurs. - */ -void waybar::modules::Pulseaudio::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; - if (operation != PA_SUBSCRIPTION_EVENT_CHANGE) { - return; - } - if (facility == PA_SUBSCRIPTION_EVENT_SERVER) { - pa_context_get_server_info(context, serverInfoCb, data); - } else if (facility == PA_SUBSCRIPTION_EVENT_SINK) { - pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data); - } else if (facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT) { - pa_context_get_sink_info_list(context, sinkInfoCb, data); - } else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) { - pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data); - } else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) { - pa_context_get_source_info_list(context, sourceInfoCb, data); - } -} - -/* - * Called in response to a volume change request - */ -void waybar::modules::Pulseaudio::volumeModifyCb(pa_context *c, int success, void *data) { - auto pa = static_cast(data); - if (success != 0) { - pa_context_get_sink_info_by_index(pa->context_, pa->sink_idx_, sinkInfoCb, data); - } -} - -/* - * Called when the requested source information is ready. - */ -void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i, - int /*eol*/, void *data) { - auto pa = static_cast(data); - if (i != nullptr && pa->default_source_name_ == i->name) { - auto source_volume = static_cast(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM}; - pa->source_volume_ = std::round(source_volume * 100.0F); - pa->source_idx_ = i->index; - pa->source_muted_ = i->mute != 0; - pa->source_desc_ = i->description; - pa->source_port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; - pa->dp.emit(); - } -} - -/* - * Called when the requested sink information is ready. - */ -void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, - int /*eol*/, void *data) { - if (i == nullptr) return; - - auto pa = static_cast(data); - - if (pa->config_["ignored-sinks"].isArray()) { - for (const auto &ignored_sink : pa->config_["ignored-sinks"]) { - if (ignored_sink.asString() == i->description) { - return; - } - } - } - - if (pa->current_sink_name_ == i->name) { - if (i->state != PA_SINK_RUNNING) { - pa->current_sink_running_ = false; - } else { - pa->current_sink_running_ = true; - } - } - - if (!pa->current_sink_running_ && i->state == PA_SINK_RUNNING) { - pa->current_sink_name_ = i->name; - pa->current_sink_running_ = true; - } - - if (pa->current_sink_name_ == i->name) { - pa->pa_volume_ = i->volume; - float volume = static_cast(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM}; - pa->sink_idx_ = i->index; - pa->volume_ = std::round(volume * 100.0F); - pa->muted_ = i->mute != 0; - pa->desc_ = i->description; - pa->monitor_ = i->monitor_source_name; - pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; - if (auto ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) { - pa->form_factor_ = ff; - } else { - pa->form_factor_ = ""; - } - pa->dp.emit(); - } -} - -/* - * Called when the requested information on the server is ready. This is - * used to find the default PulseAudio sink. - */ -void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i, - void *data) { - auto pa = static_cast(data); - pa->current_sink_name_ = i->default_sink_name; - pa->default_source_name_ = i->default_source_name; - - pa_context_get_sink_info_list(context, sinkInfoCb, data); - pa_context_get_source_info_list(context, sourceInfoCb, data); -} - static const std::array ports = { "headphone", "speaker", "hdmi", "headset", "hands-free", "portable", "car", "hifi", "phone", }; const std::vector waybar::modules::Pulseaudio::getPulseIcon() const { - std::vector res = {current_sink_name_, default_source_name_}; - std::string nameLC = port_name_ + form_factor_; + std::vector res = {backend->getCurrentSinkName(), 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) { @@ -250,17 +57,16 @@ const std::vector waybar::modules::Pulseaudio::getPulseIcon() const auto waybar::modules::Pulseaudio::update() -> void { auto format = format_; std::string tooltip_format; + auto sink_volume = backend->getSinkVolume(); if (!alt_) { std::string format_name = "format"; - if (monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio - monitor_.find("a2dp-sink") != std::string::npos || // PipeWire - monitor_.find("bluez") != std::string::npos) { + if (backend->isBluetooth()) { format_name = format_name + "-bluetooth"; label_.get_style_context()->add_class("bluetooth"); } else { label_.get_style_context()->remove_class("bluetooth"); } - if (muted_) { + if (backend->getSinkMuted()) { // Check muted bluetooth format exist, otherwise fallback to default muted format if (format_name != "format" && !config_[format_name + "-muted"].isString()) { format_name = "format"; @@ -272,7 +78,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(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()) { @@ -281,7 +87,7 @@ auto waybar::modules::Pulseaudio::update() -> void { } // TODO: find a better way to split source/sink std::string format_source = "{volume}%"; - if (source_muted_) { + if (backend->getSourceMuted()) { label_.get_style_context()->add_class("source-muted"); if (config_["format-source-muted"].isString()) { format_source = config_["format-source-muted"].asString(); @@ -292,11 +98,16 @@ auto waybar::modules::Pulseaudio::update() -> void { format_source = config_["format-source"].asString(); } } - format_source = fmt::format(fmt::runtime(format_source), fmt::arg("volume", source_volume_)); + + 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", desc_), fmt::arg("volume", volume_), - fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_), - fmt::arg("source_desc", source_desc_), fmt::arg("icon", getIcon(volume_, getPulseIcon()))); + 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 { @@ -310,12 +121,12 @@ auto waybar::modules::Pulseaudio::update() -> void { } if (!tooltip_format.empty()) { label_.set_tooltip_text(fmt::format( - fmt::runtime(tooltip_format), fmt::arg("desc", desc_), fmt::arg("volume", volume_), - fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_), - fmt::arg("source_desc", source_desc_), - fmt::arg("icon", getIcon(volume_, getPulseIcon())))); + fmt::runtime(tooltip_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())))); } else { - label_.set_tooltip_text(desc_); + label_.set_tooltip_text(sink_desc); } } diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp new file mode 100644 index 00000000..c230c1c9 --- /dev/null +++ b/src/util/audio_backend.cpp @@ -0,0 +1,251 @@ +#include "util/audio_backend.hpp" + +#include +#include +#include + +#include +#include +#include + +namespace waybar::util { + +void noop() {} + +AudioBackend::AudioBackend(std::function on_updated_cb, private_constructor_tag tag) + : 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) { + mainloop_ = pa_threaded_mainloop_new(); + if (mainloop_ == nullptr) { + throw std::runtime_error("pa_mainloop_new() failed."); + } + 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); + if (pa_threaded_mainloop_start(mainloop_) < 0) { + throw std::runtime_error("pa_mainloop_run() failed."); + } + pa_threaded_mainloop_unlock(mainloop_); +} + +AudioBackend::~AudioBackend() { + if (context_ != nullptr) { + pa_context_disconnect(context_); + } + + if (mainloop_ != nullptr) { + mainloop_api_->quit(mainloop_api_, 0); + pa_threaded_mainloop_stop(mainloop_); + pa_threaded_mainloop_free(mainloop_); + } +} + +std::shared_ptr AudioBackend::getInstance(std::function on_updated_cb) { + private_constructor_tag tag; + return std::make_shared(on_updated_cb, tag); +} + +void AudioBackend::contextStateCb(pa_context *c, void *data) { + auto backend = static_cast(data); + switch (pa_context_get_state(c)) { + case PA_CONTEXT_TERMINATED: + backend->mainloop_api_->quit(backend->mainloop_api_, 0); + break; + case PA_CONTEXT_READY: + pa_context_get_server_info(c, serverInfoCb, data); + pa_context_set_subscribe_callback(c, subscribeCb, data); + pa_context_subscribe(c, + static_cast( + static_cast(PA_SUBSCRIPTION_MASK_SERVER) | + static_cast(PA_SUBSCRIPTION_MASK_SINK) | + static_cast(PA_SUBSCRIPTION_MASK_SINK_INPUT) | + static_cast(PA_SUBSCRIPTION_MASK_SOURCE) | + static_cast(PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)), + nullptr, nullptr); + break; + case PA_CONTEXT_FAILED: + backend->mainloop_api_->quit(backend->mainloop_api_, 1); + break; + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + default: + break; + } +} + +/* + * Called when an event we subscribed to occurs. + */ +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; + if (operation != PA_SUBSCRIPTION_EVENT_CHANGE) { + return; + } + if (facility == PA_SUBSCRIPTION_EVENT_SERVER) { + pa_context_get_server_info(context, serverInfoCb, data); + } else if (facility == PA_SUBSCRIPTION_EVENT_SINK) { + pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data); + } else if (facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT) { + pa_context_get_sink_info_list(context, sinkInfoCb, data); + } else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) { + pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data); + } else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) { + pa_context_get_source_info_list(context, sourceInfoCb, data); + } +} + +/* + * Called in response to a volume change request + */ +void AudioBackend::volumeModifyCb(pa_context *c, int success, void *data) { + auto backend = static_cast(data); + if (success != 0) { + pa_context_get_sink_info_by_index(backend->context_, backend->sink_idx_, sinkInfoCb, data); + } +} + +/* + * Called when the requested sink information is ready. + */ +void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, int /*eol*/, + void *data) { + if (i == nullptr) return; + + auto backend = static_cast(data); + + if (!backend->ignored_sinks_.empty()) { + for (const auto &ignored_sink : backend->ignored_sinks_) { + if (ignored_sink == i->description) { + return; + } + } + } + + if (backend->current_sink_name_ == i->name) { + if (i->state != PA_SINK_RUNNING) { + backend->current_sink_running_ = false; + } else { + backend->current_sink_running_ = true; + } + } + + if (!backend->current_sink_running_ && i->state == PA_SINK_RUNNING) { + backend->current_sink_name_ = i->name; + backend->current_sink_running_ = true; + } + + 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}; + 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 (auto ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) { + backend->form_factor_ = ff; + } else { + backend->form_factor_ = ""; + } + backend->on_updated_cb_(); + } +} + +/* + * Called when the requested source information is ready. + */ +void AudioBackend::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i, int /*eol*/, + void *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); + backend->source_idx_ = i->index; + backend->source_muted_ = i->mute != 0; + backend->source_desc_ = i->description; + backend->source_port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; + backend->on_updated_cb_(); + } +} + +/* + * Called when the requested information on the server is ready. This is + * 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); + backend->current_sink_name_ = i->default_sink_name; + backend->default_source_name_ = i->default_source_name; + + pa_context_get_sink_info_list(context, sinkInfoCb, data); + pa_context_get_source_info_list(context, sourceInfoCb, data); +} + +void AudioBackend::changeVolume(ChangeType change_type, double step, int max_volume) { + double volume_tick = static_cast(PA_VOLUME_NORM) / 100; + pa_volume_t change = volume_tick; + pa_cvolume pa_volume = pa_volume_; + + max_volume = std::min(max_volume, static_cast(PA_VOLUME_UI_MAX)); + + if (change_type == ChangeType::Increase) { + if (volume_ < max_volume) { + if (volume_ + step > max_volume) { + change = round((max_volume - volume_) * volume_tick); + } else { + change = round(step * volume_tick); + } + pa_cvolume_inc(&pa_volume, change); + } + } else if (change_type == ChangeType::Decrease) { + if (volume_ > 0) { + if (volume_ - step < 0) { + change = round(volume_ * volume_tick); + } else { + change = round(step * volume_tick); + } + pa_cvolume_dec(&pa_volume, change); + } + } + pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); +} + +bool AudioBackend::isBluetooth() { + return monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio + monitor_.find("a2dp-sink") != std::string::npos || // PipeWire + monitor_.find("bluez") != std::string::npos; +} + +void AudioBackend::setIgnoredSinks(const Json::Value &config) { + if (config.isArray()) { + for (const auto &ignored_sink : config) { + if (ignored_sink.isString()) { + ignored_sinks_.push_back(ignored_sink.asString()); + } + } + } +} + +} // namespace waybar::util \ No newline at end of file From c9e129cda2c5dd7fd3f22509d06e4fb853ce1938 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sun, 15 Oct 2023 11:08:30 -0300 Subject: [PATCH 117/364] feat: allow setting volume directly --- include/util/audio_backend.hpp | 3 ++- src/util/audio_backend.cpp | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/util/audio_backend.hpp b/include/util/audio_backend.hpp index 9435a842..a0c36ea1 100644 --- a/include/util/audio_backend.hpp +++ b/include/util/audio_backend.hpp @@ -66,7 +66,8 @@ class AudioBackend { AudioBackend(std::function on_updated_cb, private_constructor_tag tag); ~AudioBackend(); - void changeVolume(ChangeType change_type, double step = 1, int max_volume = 100); + void changeVolume(uint16_t volume, uint16_t max_volume = 100); + void changeVolume(ChangeType change_type, double step = 1, uint16_t max_volume = 100); void setIgnoredSinks(const Json::Value& config); diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index c230c1c9..ddb2ab6f 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -203,12 +204,22 @@ void AudioBackend::serverInfoCb(pa_context *context, const pa_server_info *i, vo pa_context_get_source_info_list(context, sourceInfoCb, data); } -void AudioBackend::changeVolume(ChangeType change_type, double step, int max_volume) { +void AudioBackend::changeVolume(uint16_t volume, uint16_t max_volume) { + double volume_tick = static_cast(PA_VOLUME_NORM) / 100; + pa_cvolume pa_volume = pa_volume_; + + volume = std::min(volume, max_volume); + pa_cvolume_set(&pa_volume, pa_volume_.channels, volume * volume_tick); + + pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); +} + +void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t max_volume) { double volume_tick = static_cast(PA_VOLUME_NORM) / 100; pa_volume_t change = volume_tick; pa_cvolume pa_volume = pa_volume_; - max_volume = std::min(max_volume, static_cast(PA_VOLUME_UI_MAX)); + max_volume = std::min(max_volume, static_cast(PA_VOLUME_UI_MAX)); if (change_type == ChangeType::Increase) { if (volume_ < max_volume) { From 442a4b0da09d2b86fcefa73b87cf5d5d0dc87e85 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sun, 15 Oct 2023 11:32:05 -0300 Subject: [PATCH 118/364] feat: pulseaudio slider module --- include/ASlider.hpp | 19 ++++++ include/modules/pulseaudio_slider.hpp | 27 +++++++++ include/util/audio_backend.hpp | 2 +- man/waybar-pulseaudio-slider.5.scd | 83 +++++++++++++++++++++++++++ meson.build | 3 + src/ASlider.cpp | 34 +++++++++++ src/factory.cpp | 7 +++ src/modules/pulseaudio_slider.cpp | 45 +++++++++++++++ src/util/audio_backend.cpp | 3 +- 9 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 include/ASlider.hpp create mode 100644 include/modules/pulseaudio_slider.hpp create mode 100644 man/waybar-pulseaudio-slider.5.scd create mode 100644 src/ASlider.cpp create mode 100644 src/modules/pulseaudio_slider.cpp diff --git a/include/ASlider.hpp b/include/ASlider.hpp new file mode 100644 index 00000000..44cde507 --- /dev/null +++ b/include/ASlider.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "AModule.hpp" +#include "gtkmm/scale.h" + +namespace waybar { + +class ASlider : public AModule { + public: + ASlider(const Json::Value& config, const std::string& name, const std::string& id); + virtual void onValueChanged(); + + protected: + bool vertical_ = false; + int min_ = 0, max_ = 100, curr_ = 50; + Gtk::Scale scale_; +}; + +} // namespace waybar \ No newline at end of file diff --git a/include/modules/pulseaudio_slider.hpp b/include/modules/pulseaudio_slider.hpp new file mode 100644 index 00000000..3ef44684 --- /dev/null +++ b/include/modules/pulseaudio_slider.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "ASlider.hpp" +#include "util/audio_backend.hpp" +namespace waybar::modules { + +enum class PulseaudioSliderTarget { + Sink, + Source, +}; + +class PulseaudioSlider : public ASlider { + public: + PulseaudioSlider(const std::string&, const Json::Value&); + virtual ~PulseaudioSlider() = default; + + void update() override; + void onValueChanged() override; + + private: + std::shared_ptr backend = nullptr; + PulseaudioSliderTarget target = PulseaudioSliderTarget::Sink; +}; + +} // namespace waybar::modules \ No newline at end of file diff --git a/include/util/audio_backend.hpp b/include/util/audio_backend.hpp index a0c36ea1..1a882cc5 100644 --- a/include/util/audio_backend.hpp +++ b/include/util/audio_backend.hpp @@ -66,7 +66,7 @@ class AudioBackend { AudioBackend(std::function on_updated_cb, private_constructor_tag tag); ~AudioBackend(); - void changeVolume(uint16_t volume, uint16_t max_volume = 100); + void changeVolume(uint16_t volume, uint16_t min_volume = 0, uint16_t max_volume = 100); void changeVolume(ChangeType change_type, double step = 1, uint16_t max_volume = 100); void setIgnoredSinks(const Json::Value& config); diff --git a/man/waybar-pulseaudio-slider.5.scd b/man/waybar-pulseaudio-slider.5.scd new file mode 100644 index 00000000..8ecc040e --- /dev/null +++ b/man/waybar-pulseaudio-slider.5.scd @@ -0,0 +1,83 @@ +waybar-pulseaudio-slider(5) + +# NAME + +waybar - pulseaudio slider module + +# DESCRIPTION + +The *pulseaudio slider* module displays and controls the current volume of the default sink or source as a bar. + +The volume can be controlled by dragging the slider accross the bar, or clicking on a specific position. + +# CONFIGURATION + +*min*: ++ + typeof: int ++ + default: 0 ++ + The minimum volume value the slider should display and set. + +*max*: ++ + typeof: int ++ + default: 100 ++ + The maximum volume value the slider should display and set. + +*orientation*: ++ + typeof: string ++ + default: horizontal ++ + The orientation of the slider. Can be either `horizontal` or `vertical`. + +# EXAMPLES + +``` +"modules-right": [ + "pulseaudio-slider", +], +"pulseaudio/slider": { + "min": 0, + "max": 100, + "orientation": "horizontal" +} +``` + +# STYLE + +The slider is a component with multiple CSS Nodes, of which the following are exposed: + +*#pulseaudio-slider*: ++ + Controls the style of the box *around* the slider and bar. + +*#pulseaudio-slider slider*: ++ + Controls the style of the slider handle. + +*#pulseaudio-slider trough*: ++ + Controls the style of the part of the bar that has not been filled. + +*#pulseaudio-slider highlight*: ++ + Controls the style of the part of the bar that has been filled. + +## STYLE EXAMPLE + +``` +#pulseaudio-slider slider { + min-height: 0px; + min-width: 0px; + opacity: 0; + background-image: none; + border: none; + box-shadow: none; +} + +#pulseaudio-slider trough { + min-height: 80px; + min-width: 10px; + border-radius: 5px; + background-color: black; +} + +#pulseaudio-slider highlight { + min-width: 10px; + border-radius: 5px; + background-color: green; +} +``` diff --git a/meson.build b/meson.build index e85785ad..d2d3e17a 100644 --- a/meson.build +++ b/meson.build @@ -166,6 +166,7 @@ src_files = files( 'src/modules/image.cpp', 'src/modules/temperature.cpp', 'src/modules/user.cpp', + 'src/ASlider.cpp', 'src/main.cpp', 'src/bar.cpp', 'src/client.cpp', @@ -274,6 +275,7 @@ endif if libpulse.found() add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp') src_files += 'src/modules/pulseaudio.cpp' + src_files += 'src/modules/pulseaudio_slider.cpp' endif if libjack.found() @@ -441,6 +443,7 @@ if scdoc.found() 'waybar-mpris.5.scd', 'waybar-network.5.scd', 'waybar-pulseaudio.5.scd', + 'waybar-pulseaudio-slider.5.scd', 'waybar-river-mode.5.scd', 'waybar-river-tags.5.scd', 'waybar-river-window.5.scd', diff --git a/src/ASlider.cpp b/src/ASlider.cpp new file mode 100644 index 00000000..a5e3889c --- /dev/null +++ b/src/ASlider.cpp @@ -0,0 +1,34 @@ +#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_.set_name(name); + if (!id.empty()) { + scale_.get_style_context()->add_class(id); + } + event_box_.add(scale_); + scale_.signal_value_changed().connect(sigc::mem_fun(*this, &ASlider::onValueChanged)); + + if (config_["min"].isUInt()) { + min_ = config_["min"].asUInt(); + } + + if (config_["max"].isUInt()) { + max_ = config_["max"].asUInt(); + } + + scale_.set_inverted(vertical_); + scale_.set_draw_value(false); + scale_.set_adjustment(Gtk::Adjustment::create(curr_, min_, max_ + 1, 1, 1, 1)); +} + +void ASlider::onValueChanged() {} + +} // namespace waybar \ No newline at end of file diff --git a/src/factory.cpp b/src/factory.cpp index 1d7a00b5..0358b9db 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -1,5 +1,9 @@ #include "factory.hpp" +#ifdef HAVE_LIBPULSE +#include "modules/pulseaudio_slider.hpp" +#endif + waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {} waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { @@ -136,6 +140,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "pulseaudio") { return new waybar::modules::Pulseaudio(id, config_[name]); } + if (ref == "pulseaudio/slider") { + return new waybar::modules::PulseaudioSlider(id, config_[name]); + } #endif #ifdef HAVE_LIBMPDCLIENT if (ref == "mpd") { diff --git a/src/modules/pulseaudio_slider.cpp b/src/modules/pulseaudio_slider.cpp new file mode 100644 index 00000000..edd92670 --- /dev/null +++ b/src/modules/pulseaudio_slider.cpp @@ -0,0 +1,45 @@ +#include "modules/pulseaudio_slider.hpp" + +namespace waybar::modules { + +PulseaudioSlider::PulseaudioSlider(const std::string& id, const Json::Value& config) + : ASlider(config, "pulseaudio-slider", id) { + backend = util::AudioBackend::getInstance([this] { this->dp.emit(); }); + backend->setIgnoredSinks(config_["ignored-sinks"]); + + if (config_["target"].isString()) { + std::string target = config_["target"].asString(); + if (target == "sink") { + this->target = PulseaudioSliderTarget::Sink; + } else if (target == "source") { + this->target = PulseaudioSliderTarget::Source; + } + } +} + +void PulseaudioSlider::update() { + switch (target) { + case PulseaudioSliderTarget::Sink: + if (backend->getSinkMuted()) { + scale_.set_value(min_); + } else { + scale_.set_value(backend->getSinkVolume()); + } + break; + + case PulseaudioSliderTarget::Source: + if (backend->getSourceMuted()) { + scale_.set_value(min_); + } else { + scale_.set_value(backend->getSourceVolume()); + } + break; + } +} + +void PulseaudioSlider::onValueChanged() { + uint16_t volume = scale_.get_value(); + backend->changeVolume(volume, min_, max_); +} + +} // namespace waybar::modules \ No newline at end of file diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index ddb2ab6f..4600d80c 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -204,11 +204,12 @@ void AudioBackend::serverInfoCb(pa_context *context, const pa_server_info *i, vo pa_context_get_source_info_list(context, sourceInfoCb, data); } -void AudioBackend::changeVolume(uint16_t volume, uint16_t max_volume) { +void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t max_volume) { double volume_tick = static_cast(PA_VOLUME_NORM) / 100; pa_cvolume pa_volume = pa_volume_; volume = std::min(volume, max_volume); + volume = std::max(volume, min_volume); pa_cvolume_set(&pa_volume, pa_volume_.channels, volume * volume_tick); pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); From c3779dd16ea3c095c87f65207067c0a0aa03d53b Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sun, 15 Oct 2023 16:14:06 -0300 Subject: [PATCH 119/364] refactor: move backlight backend out of backlight module --- include/modules/backlight.hpp | 45 +---- include/util/audio_backend.hpp | 10 +- include/util/backend_common.hpp | 10 + include/util/backlight_backend.hpp | 91 +++++++++ meson.build | 1 + src/modules/backlight.cpp | 298 ++++------------------------- src/util/audio_backend.cpp | 5 +- src/util/backlight_backend.cpp | 276 ++++++++++++++++++++++++++ 8 files changed, 419 insertions(+), 317 deletions(-) create mode 100644 include/util/backend_common.hpp create mode 100644 include/util/backlight_backend.hpp create mode 100644 src/util/backlight_backend.cpp diff --git a/include/modules/backlight.hpp b/include/modules/backlight.hpp index ade4bc78..110cd434 100644 --- a/include/modules/backlight.hpp +++ b/include/modules/backlight.hpp @@ -1,14 +1,14 @@ #pragma once +#include #include #include #include #include #include "ALabel.hpp" -#include "giomm/dbusproxy.h" +#include "util/backlight_backend.hpp" #include "util/json.hpp" -#include "util/sleeper_thread.hpp" struct udev; struct udev_device; @@ -16,54 +16,17 @@ struct udev_device; namespace waybar::modules { class Backlight : public ALabel { - class BacklightDev { - public: - BacklightDev() = default; - BacklightDev(std::string name, int actual, int max, bool powered); - std::string_view name() const; - int get_actual() const; - void set_actual(int actual); - int get_max() const; - void set_max(int max); - bool get_powered() const; - void set_powered(bool powered); - friend inline bool operator==(const BacklightDev &lhs, const BacklightDev &rhs) { - return lhs.name_ == rhs.name_ && lhs.actual_ == rhs.actual_ && lhs.max_ == rhs.max_; - } - - private: - std::string name_; - int actual_ = 1; - int max_ = 1; - bool powered_ = true; - }; - public: Backlight(const std::string &, const Json::Value &); - virtual ~Backlight(); + virtual ~Backlight() = default; auto update() -> void override; - private: - template - static const BacklightDev *best_device(ForwardIt first, ForwardIt last, std::string_view); - 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 handleScroll(GdkEventScroll *e) override; const std::string preferred_device_; - static constexpr int EPOLL_MAX_EVENTS = 16; - std::optional previous_best_; std::string previous_format_; - std::mutex udev_thread_mutex_; - std::vector devices_; - // thread must destruct before shared data - util::SleeperThread udev_thread_; - - Glib::RefPtr login_proxy_; + util::BacklightBackend backend; }; } // namespace waybar::modules diff --git a/include/util/audio_backend.hpp b/include/util/audio_backend.hpp index 1a882cc5..9d043612 100644 --- a/include/util/audio_backend.hpp +++ b/include/util/audio_backend.hpp @@ -10,12 +10,10 @@ #include #include +#include "util/backend_common.hpp" + namespace waybar::util { -enum class ChangeType : char { Increase, Decrease }; - -void noop(); - class AudioBackend { private: static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*); @@ -50,7 +48,7 @@ class AudioBackend { std::vector ignored_sinks_; - std::function on_updated_cb_ = noop; + std::function on_updated_cb_ = NOOP; /* Hack to keep constructor inaccessible but still public. * This is required to be able to use std::make_shared. @@ -61,7 +59,7 @@ class AudioBackend { struct private_constructor_tag {}; public: - static std::shared_ptr getInstance(std::function on_updated_cb = noop); + static std::shared_ptr getInstance(std::function on_updated_cb = NOOP); AudioBackend(std::function on_updated_cb, private_constructor_tag tag); ~AudioBackend(); diff --git a/include/util/backend_common.hpp b/include/util/backend_common.hpp new file mode 100644 index 00000000..dda6ac57 --- /dev/null +++ b/include/util/backend_common.hpp @@ -0,0 +1,10 @@ +#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 diff --git a/include/util/backlight_backend.hpp b/include/util/backlight_backend.hpp new file mode 100644 index 00000000..1f7bddc8 --- /dev/null +++ b/include/util/backlight_backend.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "giomm/dbusproxy.h" +#include "util/backend_common.hpp" +#include "util/sleeper_thread.hpp" + +#define GET_BEST_DEVICE(varname, backend, preferred_device) \ + decltype((backend).devices_) __devices; \ + { \ + std::scoped_lock lock((backend).udev_thread_mutex_); \ + __devices = (backend).devices_; \ + } \ + auto varname = (backend).best_device(__devices.cbegin(), __devices.cend(), preferred_device); + +namespace waybar::util { + +class BacklightDevice { + public: + BacklightDevice() = default; + BacklightDevice(std::string name, int actual, int max, bool powered); + + std::string name() const; + int get_actual() const; + void set_actual(int actual); + int get_max() const; + void set_max(int max); + bool get_powered() const; + void set_powered(bool powered); + friend inline bool operator==(const BacklightDevice &lhs, const BacklightDevice &rhs) { + return lhs.name_ == rhs.name_ && lhs.actual_ == rhs.actual_ && lhs.max_ == rhs.max_; + } + + private: + std::string name_; + int actual_ = 1; + int max_ = 1; + bool powered_ = true; +}; + +class BacklightBackend { + public: + BacklightBackend(std::chrono::milliseconds interval, std::function on_updated_cb = NOOP); + + // const inline BacklightDevice *get_best_device(std::string_view preferred_device); + const BacklightDevice *get_previous_best_device(); + + void set_previous_best_device(const BacklightDevice *device); + + void set_brightness(std::string preferred_device, int brightness); + void set_brightness(std::string preferred_device, ChangeType change_type, double step); + + 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); + + std::vector devices_; + std::mutex udev_thread_mutex_; + + private: + void set_brightness_internal(std::string device_name, int brightness, int max_brightness); + + std::function on_updated_cb_; + std::chrono::milliseconds polling_interval_; + + std::optional previous_best_; + // thread must destruct before shared data + util::SleeperThread udev_thread_; + + Glib::RefPtr login_proxy_; + + static constexpr int EPOLL_MAX_EVENTS = 16; +}; + +} // namespace waybar::util \ No newline at end of file diff --git a/meson.build b/meson.build index d2d3e17a..3e8951c5 100644 --- a/meson.build +++ b/meson.build @@ -301,6 +301,7 @@ 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/util/backlight_backend.cpp' endif if libevdev.found() and (is_linux or libepoll.found()) and libinput.found() and (is_linux or libinotify.found()) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index b3ca85fc..759bbd13 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -9,179 +10,26 @@ #include #include -namespace { -class FileDescriptor { - public: - explicit FileDescriptor(int fd) : fd_(fd) {} - FileDescriptor(const FileDescriptor &other) = delete; - FileDescriptor(FileDescriptor &&other) noexcept = delete; - FileDescriptor &operator=(const FileDescriptor &other) = delete; - FileDescriptor &operator=(FileDescriptor &&other) noexcept = delete; - ~FileDescriptor() { - if (fd_ != -1) { - if (close(fd_) != 0) { - fmt::print(stderr, "Failed to close fd: {}\n", errno); - } - } - } - int get() const { return fd_; } - - private: - int fd_; -}; - -struct UdevDeleter { - void operator()(udev *ptr) { udev_unref(ptr); } -}; - -struct UdevDeviceDeleter { - void operator()(udev_device *ptr) { udev_device_unref(ptr); } -}; - -struct UdevEnumerateDeleter { - void operator()(udev_enumerate *ptr) { udev_enumerate_unref(ptr); } -}; - -struct UdevMonitorDeleter { - void operator()(udev_monitor *ptr) { udev_monitor_unref(ptr); } -}; - -void check_eq(int rc, int expected, const char *message = "eq, rc was: ") { - if (rc != expected) { - throw std::runtime_error(fmt::format(fmt::runtime(message), rc)); - } -} - -void check_neq(int rc, int bad_rc, const char *message = "neq, rc was: ") { - if (rc == bad_rc) { - throw std::runtime_error(fmt::format(fmt::runtime(message), rc)); - } -} - -void check0(int rc, const char *message = "rc wasn't 0") { check_eq(rc, 0, message); } - -void check_gte(int rc, int gte, const char *message = "rc was: ") { - if (rc < gte) { - throw std::runtime_error(fmt::format(fmt::runtime(message), rc)); - } -} - -void check_nn(const void *ptr, const char *message = "ptr was null") { - if (ptr == nullptr) { - throw std::runtime_error(message); - } -} -} // namespace - -waybar::modules::Backlight::BacklightDev::BacklightDev(std::string name, int actual, int max, - bool powered) - : name_(std::move(name)), actual_(actual), max_(max), powered_(powered) {} - -std::string_view waybar::modules::Backlight::BacklightDev::name() const { return name_; } - -int waybar::modules::Backlight::BacklightDev::get_actual() const { return actual_; } - -void waybar::modules::Backlight::BacklightDev::set_actual(int actual) { actual_ = actual; } - -int waybar::modules::Backlight::BacklightDev::get_max() const { return max_; } - -void waybar::modules::Backlight::BacklightDev::set_max(int max) { max_ = max; } - -bool waybar::modules::Backlight::BacklightDev::get_powered() const { return powered_; } - -void waybar::modules::Backlight::BacklightDev::set_powered(bool powered) { powered_ = powered; } +#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), - preferred_device_(config["device"].isString() ? config["device"].asString() : "") { - // Get initial state - { - 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()); - if (devices_.empty()) { - throw std::runtime_error("No backlight found"); - } - dp.emit(); - } + 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)); - - // 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"); - - udev_thread_ = [this] { - std::unique_ptr udev{udev_new()}; - check_nn(udev.get(), "Udev new failed"); - - std::unique_ptr mon{ - udev_monitor_new_from_netlink(udev.get(), "udev")}; - check_nn(mon.get(), "udev monitor new failed"); - check_gte(udev_monitor_filter_add_match_subsystem_devtype(mon.get(), "backlight", nullptr), 0, - "udev failed to add monitor filter: "); - udev_monitor_enable_receiving(mon.get()); - - auto udev_fd = udev_monitor_get_fd(mon.get()); - - auto epoll_fd = FileDescriptor{epoll_create1(EPOLL_CLOEXEC)}; - check_neq(epoll_fd.get(), -1, "epoll init failed: "); - epoll_event ctl_event{}; - ctl_event.events = EPOLLIN; - ctl_event.data.fd = udev_fd; - - check0(epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, ctl_event.data.fd, &ctl_event), - "epoll_ctl failed: {}"); - epoll_event events[EPOLL_MAX_EVENTS]; - - while (udev_thread_.isRunning()) { - const int event_count = epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, - std::chrono::milliseconds{interval_}.count()); - if (!udev_thread_.isRunning()) { - break; - } - decltype(devices_) devices; - { - std::scoped_lock lock(udev_thread_mutex_); - devices = devices_; - } - for (int i = 0; i < event_count; ++i) { - const auto &event = events[i]; - 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()); - } - - // Refresh state if timed out - if (event_count == 0) { - enumerate_devices(devices.begin(), devices.end(), std::back_inserter(devices), udev.get()); - } - { - std::scoped_lock lock(udev_thread_mutex_); - devices_ = devices; - } - dp.emit(); - } - }; } -waybar::modules::Backlight::~Backlight() = default; - auto waybar::modules::Backlight::update() -> void { - decltype(devices_) devices; - { - std::scoped_lock lock(udev_thread_mutex_); - devices = devices_; - } + GET_BEST_DEVICE(best, backend, preferred_device_); - const auto best = best_device(devices.cbegin(), devices.cend(), preferred_device_); + const auto previous_best_device = backend.get_previous_best_device(); if (best != nullptr) { - if (previous_best_.has_value() && previous_best_.value() == *best && + if (previous_best_device != nullptr && *previous_best_device == *best && !previous_format_.empty() && previous_format_ == format_) { return; } @@ -211,84 +59,16 @@ auto waybar::modules::Backlight::update() -> void { event_box_.hide(); } } else { - if (!previous_best_.has_value()) { + if (previous_best_device == nullptr) { return; } label_.set_markup(""); } - previous_best_ = best == nullptr ? std::nullopt : std::optional{*best}; + backend.set_previous_best_device(best); previous_format_ = format_; - // Call parent update ALabel::update(); } -template -const waybar::modules::Backlight::BacklightDev *waybar::modules::Backlight::best_device( - ForwardIt first, ForwardIt last, 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) { - return &(*found); - } - - const auto max = std::max_element( - first, last, [](const auto &l, const auto &r) { return l.get_max() < r.get_max(); }); - - return max == last ? nullptr : &(*max); -} - -template -void waybar::modules::Backlight::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 = BacklightDev{name, actual_int, max_int, power_bool}; - ++inserter; - } -} - -template -void waybar::modules::Backlight::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()); - } -} - bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { // Check if the user has set a custom command for scrolling if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { @@ -296,14 +76,31 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { } // Fail fast if the proxy could not be initialized - if (!login_proxy_) { + if (!backend.is_login_proxy_initialized()) { return true; } // Check scroll direction auto dir = AModule::getScrollDir(e); - if (dir == SCROLL_DIR::NONE) { - return true; + + util::ChangeType ct; + + switch (dir) { + case SCROLL_DIR::UP: + [[fallthrough]]; + case SCROLL_DIR::RIGHT: + ct = util::ChangeType::Increase; + break; + + case SCROLL_DIR::DOWN: + [[fallthrough]]; + case SCROLL_DIR::LEFT: + ct = util::ChangeType::Decrease; + break; + + case SCROLL_DIR::NONE: + return true; + break; } // Get scroll step @@ -313,38 +110,7 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { step = config_["scroll-step"].asDouble(); } - // Get the best device - decltype(devices_) devices; - { - std::scoped_lock lock(udev_thread_mutex_); - devices = devices_; - } - const auto best = best_device(devices.cbegin(), devices.cend(), preferred_device_); - - if (best == nullptr) { - return true; - } - - // Compute the absolute step - const auto abs_step = static_cast(round(step * best->get_max() / 100.0f)); - - // Compute the new value - int new_value = best->get_actual(); - - if (dir == SCROLL_DIR::UP) { - new_value += abs_step; - } else if (dir == SCROLL_DIR::DOWN) { - new_value -= abs_step; - } - - // Clamp the value - new_value = std::clamp(new_value, 0, best->get_max()); - - // Set the new value - auto call_args = Glib::VariantContainerBase( - g_variant_new("(ssu)", "backlight", std::string(best->name()).c_str(), new_value)); - - login_proxy_->call_sync("SetBrightness", call_args); + backend.set_brightness(preferred_device_, ct, step); return true; } diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 4600d80c..eb2cfaff 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -11,8 +11,6 @@ namespace waybar::util { -void noop() {} - AudioBackend::AudioBackend(std::function on_updated_cb, private_constructor_tag tag) : mainloop_(nullptr), mainloop_api_(nullptr), @@ -208,8 +206,7 @@ void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t m double volume_tick = static_cast(PA_VOLUME_NORM) / 100; pa_cvolume pa_volume = pa_volume_; - volume = std::min(volume, max_volume); - volume = std::max(volume, min_volume); + volume = std::clamp(volume, min_volume, max_volume); pa_cvolume_set(&pa_volume, pa_volume_.channels, volume * volume_tick); pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp new file mode 100644 index 00000000..7123ee3a --- /dev/null +++ b/src/util/backlight_backend.cpp @@ -0,0 +1,276 @@ +#include "util/backlight_backend.hpp" + +#include +#include +#include + +#include + +namespace { +class FileDescriptor { + public: + explicit FileDescriptor(int fd) : fd_(fd) {} + FileDescriptor(const FileDescriptor &other) = delete; + FileDescriptor(FileDescriptor &&other) noexcept = delete; + FileDescriptor &operator=(const FileDescriptor &other) = delete; + FileDescriptor &operator=(FileDescriptor &&other) noexcept = delete; + ~FileDescriptor() { + if (fd_ != -1) { + if (close(fd_) != 0) { + fmt::print(stderr, "Failed to close fd: {}\n", errno); + } + } + } + int get() const { return fd_; } + + private: + int fd_; +}; + +struct UdevDeleter { + void operator()(udev *ptr) { udev_unref(ptr); } +}; + +struct UdevDeviceDeleter { + void operator()(udev_device *ptr) { udev_device_unref(ptr); } +}; + +struct UdevEnumerateDeleter { + void operator()(udev_enumerate *ptr) { udev_enumerate_unref(ptr); } +}; + +struct UdevMonitorDeleter { + void operator()(udev_monitor *ptr) { udev_monitor_unref(ptr); } +}; + +void check_eq(int rc, int expected, const char *message = "eq, rc was: ") { + if (rc != expected) { + throw std::runtime_error(fmt::format(fmt::runtime(message), rc)); + } +} + +void check_neq(int rc, int bad_rc, const char *message = "neq, rc was: ") { + if (rc == bad_rc) { + throw std::runtime_error(fmt::format(fmt::runtime(message), rc)); + } +} + +void check0(int rc, const char *message = "rc wasn't 0") { check_eq(rc, 0, message); } + +void check_gte(int rc, int gte, const char *message = "rc was: ") { + if (rc < gte) { + throw std::runtime_error(fmt::format(fmt::runtime(message), rc)); + } +} + +void check_nn(const void *ptr, const char *message = "ptr was null") { + if (ptr == nullptr) { + throw std::runtime_error(message); + } +} + +} // namespace + +namespace waybar::util { + +BacklightDevice::BacklightDevice(std::string name, int actual, int max, bool powered) + : name_(name), actual_(actual), max_(max), powered_(powered) {} + +std::string BacklightDevice::name() const { return name_; } + +int BacklightDevice::get_actual() const { return actual_; } + +void BacklightDevice::set_actual(int actual) { actual_ = actual; } + +int BacklightDevice::get_max() const { return max_; } + +void BacklightDevice::set_max(int max) { max_ = max; } + +bool BacklightDevice::get_powered() const { return powered_; } + +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_({}) { + 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()); + if (devices_.empty()) { + throw std::runtime_error("No backlight found"); + } + + // 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"); + + udev_thread_ = [this] { + std::unique_ptr udev{udev_new()}; + check_nn(udev.get(), "Udev new failed"); + + std::unique_ptr mon{ + udev_monitor_new_from_netlink(udev.get(), "udev")}; + check_nn(mon.get(), "udev monitor new failed"); + check_gte(udev_monitor_filter_add_match_subsystem_devtype(mon.get(), "backlight", nullptr), 0, + "udev failed to add monitor filter: "); + udev_monitor_enable_receiving(mon.get()); + + auto udev_fd = udev_monitor_get_fd(mon.get()); + + auto epoll_fd = FileDescriptor{epoll_create1(EPOLL_CLOEXEC)}; + check_neq(epoll_fd.get(), -1, "epoll init failed: "); + epoll_event ctl_event{}; + ctl_event.events = EPOLLIN; + ctl_event.data.fd = udev_fd; + + check0(epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, ctl_event.data.fd, &ctl_event), + "epoll_ctl failed: {}"); + epoll_event events[EPOLL_MAX_EVENTS]; + + while (udev_thread_.isRunning()) { + const int event_count = + epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, this->polling_interval_.count()); + if (!udev_thread_.isRunning()) { + break; + } + decltype(devices_) devices; + { + std::scoped_lock lock(udev_thread_mutex_); + devices = devices_; + } + for (int i = 0; i < event_count; ++i) { + const auto &event = events[i]; + 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()); + } + + // Refresh state if timed out + if (event_count == 0) { + enumerate_devices(devices.begin(), devices.end(), std::back_inserter(devices), udev.get()); + } + { + std::scoped_lock lock(udev_thread_mutex_); + devices_ = devices; + } + this->on_updated_cb_(); + } + }; +} + +template +const BacklightDevice *BacklightBackend::best_device(ForwardIt first, ForwardIt last, + 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) { + return &(*found); + } + + const auto max = std::max_element( + first, last, [](const auto &l, const auto &r) { return l.get_max() < r.get_max(); }); + + return max == last ? nullptr : &(*max); +} + +const BacklightDevice *BacklightBackend::get_previous_best_device() { + return previous_best_.has_value() ? &(*previous_best_) : nullptr; +} + +void BacklightBackend::set_previous_best_device(const BacklightDevice *device) { + if (device == nullptr) { + previous_best_ = std::nullopt; + } else { + previous_best_ = std::optional{*device}; + } +} + +void BacklightBackend::set_brightness(std::string preferred_device, int brightness) { + GET_BEST_DEVICE(best, (*this), preferred_device); + + if (best != nullptr) { + set_brightness_internal(best->name(), brightness, best->get_max()); + } +} + +void BacklightBackend::set_brightness(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 int new_brightness = change_type == ChangeType::Increase ? best->get_actual() + abs_step + : best->get_actual() - abs_step; + set_brightness_internal(best->name(), new_brightness, max); + } +} + +void BacklightBackend::set_brightness_internal(std::string device_name, int brightness, + int max_brightness) { + brightness = std::clamp(brightness, 0, max_brightness); + + auto call_args = Glib::VariantContainerBase( + g_variant_new("(ssu)", "backlight", device_name.c_str(), brightness)); + + login_proxy_->call_sync("SetBrightness", call_args); +} + +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 From 11d7ca1d73abad29f4bff4e2bc513b41349f3376 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sun, 15 Oct 2023 17:42:19 -0300 Subject: [PATCH 120/364] feat: backlight slider --- include/modules/backlight_slider.hpp | 24 ++++++++ include/util/backlight_backend.hpp | 4 +- man/waybar-backlight-slider.5.scd | 88 ++++++++++++++++++++++++++++ meson.build | 2 + src/factory.cpp | 7 +++ src/modules/backlight_slider.cpp | 23 ++++++++ src/util/backlight_backend.cpp | 16 ++++- 7 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 include/modules/backlight_slider.hpp create mode 100644 man/waybar-backlight-slider.5.scd create mode 100644 src/modules/backlight_slider.cpp diff --git a/include/modules/backlight_slider.hpp b/include/modules/backlight_slider.hpp new file mode 100644 index 00000000..437c53c4 --- /dev/null +++ b/include/modules/backlight_slider.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include "ASlider.hpp" +#include "util/backlight_backend.hpp" + +namespace waybar::modules { + +class BacklightSlider : public ASlider { + public: + BacklightSlider(const std::string&, const Json::Value&); + virtual ~BacklightSlider() = default; + + void update() override; + void onValueChanged() override; + + private: + std::chrono::milliseconds interval_; + std::string preferred_device_; + util::BacklightBackend backend; +}; + +} // namespace waybar::modules \ No newline at end of file diff --git a/include/util/backlight_backend.hpp b/include/util/backlight_backend.hpp index 1f7bddc8..8dcb8958 100644 --- a/include/util/backlight_backend.hpp +++ b/include/util/backlight_backend.hpp @@ -56,9 +56,11 @@ class BacklightBackend { void set_previous_best_device(const BacklightDevice *device); - void set_brightness(std::string preferred_device, int brightness); void set_brightness(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); + template static void upsert_device(ForwardIt first, ForwardIt last, Inserter inserter, udev_device *dev); diff --git a/man/waybar-backlight-slider.5.scd b/man/waybar-backlight-slider.5.scd new file mode 100644 index 00000000..55004d08 --- /dev/null +++ b/man/waybar-backlight-slider.5.scd @@ -0,0 +1,88 @@ +waybar-backlight-slider(5) + +# NAME + +waybar - backlight slider module + +# DESCRIPTION + +The *backlight slider* module displays and controls the current brightness of the default or preferred device. + +The brightness can be controlled by dragging the slider accross the bar, or clicking on a specific position. + +# CONFIGURATION + +*min*: ++ + typeof: int ++ + default: 0 ++ + The minimum volume value the slider should display and set. + +*max*: ++ + typeof: int ++ + default: 100 ++ + The maximum volume value the slider should display and set. + +*orientation*: ++ + typeof: string ++ + default: horizontal ++ + The orientation of the slider. Can be either `horizontal` or `vertical`. + +*device*: ++ + typeof: string ++ + The name of the preferred device to control. If left empty, a device will be chosen automatically. + +# EXAMPLES + +``` +"modules-right": [ + "backlight-slider", +], +"backlight/slider": { + "min": 0, + "max": 100, + "orientation": "horizontal", + "device": "intel_backlight" +} +``` + +# STYLE + +The slider is a component with multiple CSS Nodes, of which the following are exposed: + +*#backlight-slider*: ++ + Controls the style of the box *around* the slider and bar. + +*#backlight-slider slider*: ++ + Controls the style of the slider handle. + +*#backlight-slider trough*: ++ + Controls the style of the part of the bar that has not been filled. + +*#backlight-slider highlight*: ++ + Controls the style of the part of the bar that has been filled. + +## STYLE EXAMPLE + +``` +#backlight-slider slider { + min-height: 0px; + min-width: 0px; + opacity: 0; + background-image: none; + border: none; + box-shadow: none; +} + +#backlight-slider trough { + min-height: 80px; + min-width: 10px; + border-radius: 5px; + background-color: black; +} + +#backlight-slider highlight { + min-width: 10px; + border-radius: 5px; + background-color: red; +} +``` diff --git a/meson.build b/meson.build index 3e8951c5..656cac98 100644 --- a/meson.build +++ b/meson.build @@ -301,6 +301,7 @@ 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' endif @@ -429,6 +430,7 @@ if scdoc.found() man_files = [ main_manpage_path, 'waybar-backlight.5.scd', + 'waybar-backlight-slider.5.scd', 'waybar-battery.5.scd', 'waybar-cava.5.scd', 'waybar-clock.5.scd', diff --git a/src/factory.cpp b/src/factory.cpp index 0358b9db..6f8d7b40 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -4,6 +4,10 @@ #include "modules/pulseaudio_slider.hpp" #endif +#ifdef HAVE_LIBUDEV +#include "modules/backlight_slider.hpp" +#endif + waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {} waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { @@ -130,6 +134,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "backlight") { return new waybar::modules::Backlight(id, config_[name]); } + if (ref == "backlight/slider") { + return new waybar::modules::BacklightSlider(id, config_[name]); + } #endif #ifdef HAVE_LIBEVDEV if (ref == "keyboard-state") { diff --git a/src/modules/backlight_slider.cpp b/src/modules/backlight_slider.cpp new file mode 100644 index 00000000..6269dddb --- /dev/null +++ b/src/modules/backlight_slider.cpp @@ -0,0 +1,23 @@ +#include "modules/backlight_slider.hpp" + +#include "ASlider.hpp" + +namespace waybar::modules { + +BacklightSlider::BacklightSlider(const std::string& id, const Json::Value& config) + : ASlider(config, "backlight-slider", id), + interval_(config_["interval"].isUInt() ? config_["interval"].asUInt() : 1000), + preferred_device_(config["device"].isString() ? config["device"].asString() : ""), + backend(interval_, [this] { this->dp.emit(); }) {} + +void BacklightSlider::update() { + uint16_t brightness = backend.get_scaled_brightness(preferred_device_); + scale_.set_value(brightness); +} + +void BacklightSlider::onValueChanged() { + auto brightness = scale_.get_value(); + backend.set_scaled_brightness(preferred_device_, brightness); +} + +} // namespace waybar::modules \ No newline at end of file diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index 7123ee3a..1512103c 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -188,11 +188,13 @@ void BacklightBackend::set_previous_best_device(const BacklightDevice *device) { } } -void BacklightBackend::set_brightness(std::string preferred_device, int brightness) { +void BacklightBackend::set_scaled_brightness(std::string preferred_device, int brightness) { GET_BEST_DEVICE(best, (*this), preferred_device); if (best != nullptr) { - set_brightness_internal(best->name(), brightness, best->get_max()); + const auto max = best->get_max(); + const auto abs_val = static_cast(round(brightness * max / 100.0f)); + set_brightness_internal(best->name(), abs_val, best->get_max()); } } @@ -221,6 +223,16 @@ 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) { + GET_BEST_DEVICE(best, (*this), preferred_device); + + if (best != nullptr) { + return best->get_actual() * 100 / best->get_max(); + } + + return 0; +} + template void BacklightBackend::upsert_device(ForwardIt first, ForwardIt last, Inserter inserter, udev_device *dev) { From fd3710d869435d7bdf032e08084186cdbe135e3e Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sun, 15 Oct 2023 17:49:45 -0300 Subject: [PATCH 121/364] chore: suppress compiler warning --- src/modules/backlight.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 759bbd13..4ae511eb 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -83,7 +83,9 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { // Check scroll direction auto dir = AModule::getScrollDir(e); - util::ChangeType ct; + // No worries, it will always be set because of the switch below. This is purely to suppress a + // warning + util::ChangeType ct = util::ChangeType::Increase; switch (dir) { case SCROLL_DIR::UP: From ecbcf242d5e751f686fb127cd6759480e1dedab1 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sun, 15 Oct 2023 17:50:41 -0300 Subject: [PATCH 122/364] feat: allow unmuting by moving the pulseaudio slider --- include/util/audio_backend.hpp | 6 +++++ src/modules/pulseaudio_slider.cpp | 37 +++++++++++++++++++++++++++++++ src/util/audio_backend.cpp | 20 +++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/include/util/audio_backend.hpp b/include/util/audio_backend.hpp index 9d043612..8d9b6f2f 100644 --- a/include/util/audio_backend.hpp +++ b/include/util/audio_backend.hpp @@ -83,6 +83,12 @@ class AudioBackend { std::string getSourceDesc() const { return source_desc_; } std::string getDefaultSourceName() const { return default_source_name_; } + void toggleSinkMute(); + void toggleSinkMute(bool); + + void toggleSourceMute(); + void toggleSourceMute(bool); + bool isBluetooth(); }; diff --git a/src/modules/pulseaudio_slider.cpp b/src/modules/pulseaudio_slider.cpp index edd92670..bf85584e 100644 --- a/src/modules/pulseaudio_slider.cpp +++ b/src/modules/pulseaudio_slider.cpp @@ -38,7 +38,44 @@ void PulseaudioSlider::update() { } void PulseaudioSlider::onValueChanged() { + bool is_mute = false; + + switch (target) { + case PulseaudioSliderTarget::Sink: + if (backend->getSinkMuted()) { + is_mute = true; + } + break; + + case PulseaudioSliderTarget::Source: + if (backend->getSourceMuted()) { + is_mute = true; + } + break; + } + uint16_t volume = scale_.get_value(); + + if (is_mute) { + // Avoid setting sink/source to volume 0 if the user muted if via another mean. + if (volume == 0) { + return; + } + + // If the sink/source is mute, but the user clicked the slider, unmute it! + else { + switch (target) { + case PulseaudioSliderTarget::Sink: + backend->toggleSinkMute(false); + break; + + case PulseaudioSliderTarget::Source: + backend->toggleSourceMute(false); + break; + } + } + } + backend->changeVolume(volume, min_, max_); } diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index eb2cfaff..7eef1448 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -241,6 +241,26 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); } +void AudioBackend::toggleSinkMute() { + muted_ = !muted_; + pa_context_set_sink_mute_by_index(context_, sink_idx_, muted_, nullptr, nullptr); +} + +void AudioBackend::toggleSinkMute(bool mute) { + muted_ = mute; + pa_context_set_sink_mute_by_index(context_, sink_idx_, muted_, nullptr, nullptr); +} + +void AudioBackend::toggleSourceMute() { + source_muted_ = !muted_; + pa_context_set_source_mute_by_index(context_, source_idx_, 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); +} + bool AudioBackend::isBluetooth() { return monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio monitor_.find("a2dp-sink") != std::string::npos || // PipeWire From 9d316de15a2051be613eefd1931039ffef934e91 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Sun, 15 Oct 2023 18:12:31 -0300 Subject: [PATCH 123/364] fix: avoid compiling audio_backend if pulse is not available --- meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 656cac98..b06786ba 100644 --- a/meson.build +++ b/meson.build @@ -179,8 +179,7 @@ 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/audio_backend.cpp' + 'src/util/regex_collection.cpp' ) inc_dirs = ['include'] @@ -276,6 +275,7 @@ 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' endif if libjack.found() From 67c0c9a66c04afe0407ea84725fca69d473d8828 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 16 Oct 2023 19:07:00 -0300 Subject: [PATCH 124/364] fix: delay window creation to await for hyprland to create a workspace --- include/modules/hyprland/workspaces.hpp | 37 ++++++- src/modules/hyprland/workspaces.cpp | 139 ++++++++++++++++-------- 2 files changed, 129 insertions(+), 47 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index c5851cad..263a56ce 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "AModule.hpp" @@ -25,6 +26,35 @@ namespace waybar::modules::hyprland { class Workspaces; +class CreateWindow { + public: + CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_repr); + CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_class, std::string window_title); + CreateWindow(Json::Value& client_data); + + int increment_time_spent_uncreated(); + bool is_empty(Workspaces& workspace_manager); + bool repr_is_ready() const { return std::holds_alternative(window_); } + std::string repr(Workspaces& workspace_manager); + + std::string workspace_name() const { return workspace_name_; } + WindowAddress addr() const { return window_address_; } + + private: + + void clear_addr(); + + using Repr = std::string; + using ClassAndTitle = std::pair; + + std::variant window_; + + WindowAddress window_address_; + std::string workspace_name_; + int time_spent_uncreated_ = 0; + +}; + class Workspace { public: explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager, @@ -50,13 +80,11 @@ class Workspace { void set_windows(uint value) { windows_ = value; }; void set_name(std::string value) { name_ = value; }; bool contains_window(WindowAddress addr) const { return window_map_.contains(addr); } - void insert_window(WindowAddress addr, std::string window_class, std::string window_title); + void insert_window(CreateWindow create_window_paylod); std::string remove_window(WindowAddress addr); void initialize_window_map(const Json::Value& clients_data); - bool on_window_opened(WindowAddress& addr, std::string& workspace_name, std::string window_repr); - bool on_window_opened(WindowAddress& addr, std::string& workspace_name, std::string& window_class, - std::string& window_title); + bool on_window_opened(CreateWindow create_window_paylod); std::optional on_window_closed(WindowAddress& addr); @@ -149,6 +177,7 @@ class Workspaces : public AModule, public EventHandler { std::vector> workspaces_; std::vector workspaces_to_create_; std::vector workspaces_to_remove_; + std::vector windows_to_create_; std::vector ignore_workspaces_; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index b3faeac1..b6c37a7a 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "util/regex_collection.hpp" @@ -191,6 +193,28 @@ auto Workspaces::update() -> void { } workspace->update(format_, workspace_icon); } + + std::vector not_created; + + for (auto &window_payload : windows_to_create_) { + bool created = false; + for (auto &workspace : workspaces_) { + if (workspace->on_window_opened(window_payload)) { + created = true; + break; + } + } + if (!created) { + static const int WINDOW_CREATION_TIMEOUT = 2; + if (window_payload.increment_time_spent_uncreated() < WINDOW_CREATION_TIMEOUT) { + not_created.push_back(window_payload); + } + } + } + + windows_to_create_.clear(); + windows_to_create_ = not_created; + AModule::update(); } @@ -297,7 +321,7 @@ void Workspaces::onEvent(const std::string &ev) { if (!client->empty()) { (*window_workspace) - ->insert_window(payload, (*client)["class"].asString(), (*client)["title"].asString()); + ->insert_window({*client}); } } } @@ -322,11 +346,7 @@ void Workspaces::on_window_opened(std::string payload) { std::string window_title = payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx); - for (auto &workspace : workspaces_) { - if (workspace->on_window_opened(window_address, workspace_name, window_class, window_title)) { - break; - } - } + windows_to_create_.push_back({workspace_name, window_address, window_class, window_title}); } void Workspaces::on_window_closed(std::string addr) { @@ -359,11 +379,7 @@ void Workspaces::on_window_moved(std::string payload) { } // ...and add it to the new workspace - for (auto &workspace : workspaces_) { - if (workspace->on_window_opened(window_address, workspace_name, window_repr)) { - break; - } - } + windows_to_create_.push_back({workspace_name, window_address, window_repr}); } void Workspaces::update_window_count() { @@ -395,28 +411,14 @@ void Workspace::initialize_window_map(const Json::Value &clients_data) { window_map_.clear(); for (auto client : clients_data) { if (client["workspace"]["id"].asInt() == id()) { - // substr(2, ...) is necessary because Hyprland's JSON follows this format: - // 0x{ADDR} - // While Hyprland's IPC follows this format: - // {ADDR} - WindowAddress client_address = client["address"].asString(); - client_address = client_address.substr(2, client_address.length() - 2); - insert_window(client_address, client["class"].asString(), client["title"].asString()); + insert_window({client}); } } } -void Workspace::insert_window(WindowAddress addr, std::string window_class, - std::string window_title) { - if (window_class.empty() && - (!workspace_manager_.window_rewrite_config_uses_title() || window_title.empty())) { - return; - } - - auto window_repr = workspace_manager_.get_rewrite(window_class, window_title); - - if (!window_repr.empty()) { - window_map_[addr] = window_repr; +void Workspace::insert_window(CreateWindow create_window_paylod) { + if (!create_window_paylod.is_empty(workspace_manager_)) { + window_map_[create_window_paylod.addr()] = create_window_paylod.repr(workspace_manager_); } }; @@ -427,20 +429,9 @@ std::string Workspace::remove_window(WindowAddress addr) { return window_repr; } -bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, - std::string window_repr) { - if (workspace_name == name()) { - window_map_[addr] = window_repr; - return true; - } else { - return false; - } -} - -bool Workspace::on_window_opened(WindowAddress &addr, std::string &workspace_name, - std::string &window_class, std::string &window_title) { - if (workspace_name == name()) { - insert_window(addr, window_class, window_title); +bool Workspace::on_window_opened(CreateWindow create_window_paylod) { + if (create_window_paylod.workspace_name() == name()) { + insert_window(create_window_paylod); return true; } else { return false; @@ -863,4 +854,66 @@ std::string Workspaces::get_rewrite(std::string window_class, std::string window return window_rewrite_rules_.get(window_repr_key); } +CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_repr) + : window_(window_repr), + window_address_(window_address), + workspace_name_(workspace_name) { + clear_addr(); +} + +CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_class, std::string window_title) + : window_(std::make_pair(window_class, window_title)), + window_address_(window_address), + workspace_name_(workspace_name) { + clear_addr(); +} + +CreateWindow::CreateWindow(Json::Value& client_data) { + window_address_ = client_data["address"].asString(); + workspace_name_ = client_data["workspace"]["name"].asString(); + window_ = std::make_pair(client_data["class"].asString(), client_data["title"].asString()); + clear_addr(); +} + +std::string CreateWindow::repr(Workspaces& workspace_manager) { + if (std::holds_alternative(window_)) { + return std::get(window_); + } else if (std::holds_alternative(window_)) { + auto [window_class, window_title] = std::get(window_); + return workspace_manager.get_rewrite(window_class, window_title); + } else { + // Unreachable + return ""; + } +} + +bool CreateWindow::is_empty(Workspaces& workspace_manager) { + if (std::holds_alternative(window_)) { + return std::get(window_).empty(); + } else if (std::holds_alternative(window_)) { + auto [window_class, window_title] = std::get(window_); + return ( + window_class.empty() && + (!workspace_manager.window_rewrite_config_uses_title() || window_title.empty()) + ); + } else { + // Unreachable + return true; + } +} + +int CreateWindow::increment_time_spent_uncreated() { + return time_spent_uncreated_++; +} + +void CreateWindow::clear_addr() { + // substr(2, ...) is necessary because Hyprland's JSON follows this format: + // 0x{ADDR} + // While Hyprland's IPC follows this format: + // {ADDR} + if (window_address_.starts_with("0x")) { + window_address_ = window_address_.substr(2, window_address_.length() - 2); + } +} + } // namespace waybar::modules::hyprland From e70a67d9583ab9cce2a1186c29ce44e4369e9e6f Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 16 Oct 2023 19:14:50 -0300 Subject: [PATCH 125/364] chore: lint chore: swap push_back to emplace_back --- include/modules/hyprland/workspaces.hpp | 5 ++- src/modules/hyprland/workspaces.cpp | 44 +++++++++++-------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 263a56ce..37fb7d09 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -29,7 +29,8 @@ class Workspaces; class CreateWindow { public: CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_repr); - CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_class, std::string window_title); + CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_class, + std::string window_title); CreateWindow(Json::Value& client_data); int increment_time_spent_uncreated(); @@ -41,7 +42,6 @@ class CreateWindow { WindowAddress addr() const { return window_address_; } private: - void clear_addr(); using Repr = std::string; @@ -52,7 +52,6 @@ class CreateWindow { WindowAddress window_address_; std::string workspace_name_; int time_spent_uncreated_ = 0; - }; class Workspace { diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index b6c37a7a..5ccbd52e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -320,8 +320,7 @@ void Workspaces::onEvent(const std::string &ev) { }); if (!client->empty()) { - (*window_workspace) - ->insert_window({*client}); + (*window_workspace)->insert_window({*client}); } } } @@ -346,7 +345,8 @@ void Workspaces::on_window_opened(std::string payload) { std::string window_title = payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx); - windows_to_create_.push_back({workspace_name, window_address, window_class, window_title}); + windows_to_create_.emplace_back( + CreateWindow(workspace_name, window_address, window_class, window_title)); } void Workspaces::on_window_closed(std::string addr) { @@ -379,7 +379,7 @@ void Workspaces::on_window_moved(std::string payload) { } // ...and add it to the new workspace - windows_to_create_.push_back({workspace_name, window_address, window_repr}); + windows_to_create_.emplace_back(CreateWindow(workspace_name, window_address, window_repr)); } void Workspaces::update_window_count() { @@ -854,28 +854,28 @@ std::string Workspaces::get_rewrite(std::string window_class, std::string window return window_rewrite_rules_.get(window_repr_key); } -CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_repr) - : window_(window_repr), - window_address_(window_address), - workspace_name_(workspace_name) { - clear_addr(); +CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_address, + std::string window_repr) + : window_(window_repr), window_address_(window_address), workspace_name_(workspace_name) { + clear_addr(); } -CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_class, std::string window_title) - : window_(std::make_pair(window_class, window_title)), - window_address_(window_address), - workspace_name_(workspace_name) { - clear_addr(); +CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_address, + std::string window_class, std::string window_title) + : window_(std::make_pair(window_class, window_title)), + window_address_(window_address), + workspace_name_(workspace_name) { + clear_addr(); } -CreateWindow::CreateWindow(Json::Value& client_data) { +CreateWindow::CreateWindow(Json::Value &client_data) { window_address_ = client_data["address"].asString(); workspace_name_ = client_data["workspace"]["name"].asString(); window_ = std::make_pair(client_data["class"].asString(), client_data["title"].asString()); clear_addr(); } -std::string CreateWindow::repr(Workspaces& workspace_manager) { +std::string CreateWindow::repr(Workspaces &workspace_manager) { if (std::holds_alternative(window_)) { return std::get(window_); } else if (std::holds_alternative(window_)) { @@ -887,24 +887,20 @@ std::string CreateWindow::repr(Workspaces& workspace_manager) { } } -bool CreateWindow::is_empty(Workspaces& workspace_manager) { +bool CreateWindow::is_empty(Workspaces &workspace_manager) { if (std::holds_alternative(window_)) { return std::get(window_).empty(); } else if (std::holds_alternative(window_)) { auto [window_class, window_title] = std::get(window_); - return ( - window_class.empty() && - (!workspace_manager.window_rewrite_config_uses_title() || window_title.empty()) - ); + return (window_class.empty() && + (!workspace_manager.window_rewrite_config_uses_title() || window_title.empty())); } else { // Unreachable return true; } } -int CreateWindow::increment_time_spent_uncreated() { - return time_spent_uncreated_++; -} +int CreateWindow::increment_time_spent_uncreated() { return time_spent_uncreated_++; } void CreateWindow::clear_addr() { // substr(2, ...) is necessary because Hyprland's JSON follows this format: From 6ddd283d0f16ec65c04ee4955270220fb0aef2c7 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 16 Oct 2023 20:48:52 -0300 Subject: [PATCH 126/364] fix: special workspaces weren't registering windows because of the special qualifier prefix --- include/modules/hyprland/workspaces.hpp | 1 + src/modules/hyprland/workspaces.cpp | 26 +++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 37fb7d09..c76ac267 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -43,6 +43,7 @@ class CreateWindow { private: void clear_addr(); + void clear_workspace_name(); using Repr = std::string; using ClassAndTitle = std::pair; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 5ccbd52e..e26fe757 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -858,6 +858,7 @@ CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_addr std::string window_repr) : window_(window_repr), window_address_(window_address), workspace_name_(workspace_name) { clear_addr(); + clear_workspace_name(); } CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_address, @@ -866,6 +867,7 @@ CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_addr window_address_(window_address), workspace_name_(workspace_name) { clear_addr(); + clear_workspace_name(); } CreateWindow::CreateWindow(Json::Value &client_data) { @@ -873,6 +875,7 @@ CreateWindow::CreateWindow(Json::Value &client_data) { workspace_name_ = client_data["workspace"]["name"].asString(); window_ = std::make_pair(client_data["class"].asString(), client_data["title"].asString()); clear_addr(); + clear_workspace_name(); } std::string CreateWindow::repr(Workspaces &workspace_manager) { @@ -907,8 +910,27 @@ void CreateWindow::clear_addr() { // 0x{ADDR} // While Hyprland's IPC follows this format: // {ADDR} - if (window_address_.starts_with("0x")) { - window_address_ = window_address_.substr(2, window_address_.length() - 2); + static const std::string ADDR_PREFIX = "0x"; + static const int ADDR_PREFIX_LEN = ADDR_PREFIX.length(); + + if (window_address_.starts_with(ADDR_PREFIX)) { + window_address_ = + window_address_.substr(ADDR_PREFIX_LEN, window_address_.length() - ADDR_PREFIX_LEN); + } +} + +void CreateWindow::clear_workspace_name() { + // 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 (workspace_name_.starts_with(SPECIAL_QUALIFIER_PREFIX)) { + workspace_name_ = workspace_name_.substr( + SPECIAL_QUALIFIER_PREFIX_LEN, workspace_name_.length() - SPECIAL_QUALIFIER_PREFIX_LEN); } } From 208928ded5534c05ab8afeb6c0575f17347b5964 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 17 Oct 2023 19:29:06 +0200 Subject: [PATCH 127/364] fix: lint --- include/modules/cpu_usage.hpp | 3 ++- include/modules/load.hpp | 1 - src/modules/clock.cpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/modules/cpu_usage.hpp b/include/modules/cpu_usage.hpp index 4349f705..c93a1734 100644 --- a/include/modules/cpu_usage.hpp +++ b/include/modules/cpu_usage.hpp @@ -21,7 +21,8 @@ class CpuUsage : public ALabel { auto update() -> void override; // This is a static member because it is also used by the cpu module. - static std::tuple, std::string> getCpuUsage(std::vector>&); + static std::tuple, std::string> getCpuUsage( + std::vector>&); private: static std::vector> parseCpuinfo(); diff --git a/include/modules/load.hpp b/include/modules/load.hpp index 2c4ce610..c4c06d26 100644 --- a/include/modules/load.hpp +++ b/include/modules/load.hpp @@ -24,7 +24,6 @@ class Load : public ALabel { static std::tuple getLoad(); private: - util::SleeperThread thread_; }; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 27b7da5e..4c4bcc43 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -175,7 +175,8 @@ auto waybar::modules::Clock::update() -> void { tz, date::local_days(shiftedDay) + (now.get_local_time() - date::floor(now.get_local_time())))}; - label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now.get_local_time().time_since_epoch())); + label_.set_markup( + fmt::format(locale_, fmt::runtime(format_), now.get_local_time().time_since_epoch())); if (tooltipEnabled()) { const std::string tz_text{(is_timezoned_list_in_tooltip_) ? timezones_text(now.get_sys_time()) From 5319cb6e106080c4aa5ddedf41913b2104f60b45 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 17 Oct 2023 20:12:24 +0200 Subject: [PATCH 128/364] fix: upower hidded on start --- src/modules/upower/upower.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index 1262d0a1..f2bc621d 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -289,7 +289,12 @@ auto UPower::update() -> void { std::lock_guard guard(m_Mutex); // Don't update widget if the UPower service isn't running - if (!upowerRunning) return; + if (!upowerRunning) { + if (hideIfEmpty) { + event_box_.set_visible(false); + } + return; + } UpDeviceKind kind; UpDeviceState state; From 871f9a12696b4b9fe0e696cc64f987e73ba29dbb Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 18 Oct 2023 08:45:56 +0200 Subject: [PATCH 129/364] fix: revert clock --- src/modules/clock.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 4c4bcc43..3d6b8919 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -175,8 +175,7 @@ auto waybar::modules::Clock::update() -> void { tz, date::local_days(shiftedDay) + (now.get_local_time() - date::floor(now.get_local_time())))}; - label_.set_markup( - fmt::format(locale_, fmt::runtime(format_), now.get_local_time().time_since_epoch())); + label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now)); if (tooltipEnabled()) { const std::string tz_text{(is_timezoned_list_in_tooltip_) ? timezones_text(now.get_sys_time()) From 193040c41ee996ff3dd1b8d8c5574620d479da64 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Wed, 18 Oct 2023 19:04:09 -0300 Subject: [PATCH 130/364] feat: attempt to move windows out of the create window payload before taking them from workspaces --- include/modules/hyprland/workspaces.hpp | 2 ++ src/modules/hyprland/workspaces.cpp | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index c76ac267..0a834f11 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -41,6 +41,8 @@ class CreateWindow { std::string workspace_name() const { return workspace_name_; } WindowAddress addr() const { return window_address_; } + void move_to_worksace(std::string& new_workspace_name); + private: void clear_addr(); void clear_workspace_name(); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index e26fe757..416e06f3 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -367,6 +367,15 @@ void Workspaces::on_window_moved(std::string payload) { std::string window_repr; + // If the window was still queued to be created, just change its destination + // and exit + for (auto &window : windows_to_create_) { + if (window.addr() == window_address) { + window.move_to_worksace(workspace_name); + return; + } + } + // Take the window's representation from the old workspace... for (auto &workspace : workspaces_) { try { @@ -379,7 +388,9 @@ void Workspaces::on_window_moved(std::string payload) { } // ...and add it to the new workspace - windows_to_create_.emplace_back(CreateWindow(workspace_name, window_address, window_repr)); + if (!window_repr.empty()) { + windows_to_create_.emplace_back(CreateWindow(workspace_name, window_address, window_repr)); + } } void Workspaces::update_window_count() { @@ -934,4 +945,8 @@ void CreateWindow::clear_workspace_name() { } } +void CreateWindow::move_to_worksace(std::string &new_workspace_name) { + workspace_name_ = new_workspace_name; +} + } // namespace waybar::modules::hyprland From e845db84adf37d2b37a657c7ec38c3e304791ab8 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Wed, 18 Oct 2023 19:06:36 -0300 Subject: [PATCH 131/364] feat: avoid recreating workspaces --- src/modules/hyprland/workspaces.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 416e06f3..5c009202 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -458,17 +458,16 @@ std::optional Workspace::on_window_closed(WindowAddress &addr) { } void Workspaces::create_workspace(Json::Value &workspace_data, const Json::Value &clients_data) { - // replace the existing persistent workspace if it exists + // avoid recreating existing workspaces auto workspace = std::find_if( workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr const &x) { auto name = workspace_data["name"].asString(); return x->is_persistent() && ((name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name()); }); + if (workspace != workspaces_.end()) { - // replace workspace, but keep persistent flag - workspaces_.erase(workspace); - workspace_data["persistent"] = true; + return; } // create new workspace From 846842be8089da4ea7a936d39148f723a41b0faa Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Thu, 19 Oct 2023 10:45:00 -0300 Subject: [PATCH 132/364] feat: emit dispatcher when any window is created during update --- 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 5c009202..a01dd2f0 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -194,6 +194,7 @@ auto Workspaces::update() -> void { workspace->update(format_, workspace_icon); } + bool any_window_created = false; std::vector not_created; for (auto &window_payload : windows_to_create_) { @@ -201,6 +202,7 @@ auto Workspaces::update() -> void { for (auto &workspace : workspaces_) { if (workspace->on_window_opened(window_payload)) { created = true; + any_window_created = true; break; } } @@ -212,6 +214,10 @@ auto Workspaces::update() -> void { } } + if (any_window_created) { + dp.emit(); + } + windows_to_create_.clear(); windows_to_create_ = not_created; From e14a3b86874bf3ca121645fc61743ed74742f3c8 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Thu, 19 Oct 2023 21:19:08 -0300 Subject: [PATCH 133/364] fix: fill persistent workspaces' windows at init --- include/modules/hyprland/workspaces.hpp | 2 +- src/modules/hyprland/workspaces.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 0a834f11..556a5c24 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -162,7 +162,7 @@ class Workspaces : public AModule, public EventHandler { {"DEFAULT", SORT_METHOD::DEFAULT}}; void fill_persistent_workspaces(); - void create_persistent_workspaces(); + void create_persistent_workspaces(const Json::Value& clients_data); std::vector persistent_workspaces_to_create_; bool persistent_created_ = false; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index a01dd2f0..7685de8a 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -562,7 +562,7 @@ void Workspaces::fill_persistent_workspaces() { } } -void Workspaces::create_persistent_workspaces() { +void Workspaces::create_persistent_workspaces(const Json::Value &clients_data) { for (const std::string &workspace_name : persistent_workspaces_to_create_) { Json::Value new_workspace; try { @@ -577,7 +577,7 @@ void Workspaces::create_persistent_workspaces() { new_workspace["windows"] = 0; new_workspace["persistent"] = true; - create_workspace(new_workspace); + create_workspace(new_workspace, clients_data); } } @@ -596,12 +596,12 @@ void Workspaces::init() { monitor_id_ = (*current_monitor)["id"].asInt(); } - fill_persistent_workspaces(); - create_persistent_workspaces(); - const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); + fill_persistent_workspaces(); + create_persistent_workspaces(clients_json); + for (Json::Value workspace_json : workspaces_json) { std::string workspace_name = workspace_json["name"].asString(); if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && From a0d2d95b41a00e8e1fad57f3367ca81b3eea73b9 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Thu, 19 Oct 2023 21:21:55 -0300 Subject: [PATCH 134/364] chore: remove unused function --- include/modules/hyprland/workspaces.hpp | 1 - src/modules/hyprland/workspaces.cpp | 7 ------- 2 files changed, 8 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 556a5c24..6a0c2634 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -134,7 +134,6 @@ class Workspaces : public AModule, public EventHandler { private: void onEvent(const std::string&) override; void update_window_count(); - void initialize_window_maps(); void sort_workspaces(); void create_workspace(Json::Value& workspace_data, const Json::Value& clients_data = Json::Value::nullRef); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 7685de8a..6ffdf237 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -417,13 +417,6 @@ void Workspaces::update_window_count() { } } -void Workspaces::initialize_window_maps() { - Json::Value clients_data = gIPC->getSocket1JsonReply("clients"); - for (auto &workspace : workspaces_) { - workspace->initialize_window_map(clients_data); - } -} - void Workspace::initialize_window_map(const Json::Value &clients_data) { window_map_.clear(); for (auto client : clients_data) { From a41225c4e083bacf4d69c7018ed02792576eedc3 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Thu, 19 Oct 2023 21:23:00 -0300 Subject: [PATCH 135/364] Revert "fix: fill persistent workspaces' windows at init" This reverts commit e14a3b86874bf3ca121645fc61743ed74742f3c8. --- include/modules/hyprland/workspaces.hpp | 2 +- src/modules/hyprland/workspaces.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 6a0c2634..dde564df 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -161,7 +161,7 @@ class Workspaces : public AModule, public EventHandler { {"DEFAULT", SORT_METHOD::DEFAULT}}; void fill_persistent_workspaces(); - void create_persistent_workspaces(const Json::Value& clients_data); + void create_persistent_workspaces(); std::vector persistent_workspaces_to_create_; bool persistent_created_ = false; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 6ffdf237..42e112d2 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -555,7 +555,7 @@ void Workspaces::fill_persistent_workspaces() { } } -void Workspaces::create_persistent_workspaces(const Json::Value &clients_data) { +void Workspaces::create_persistent_workspaces() { for (const std::string &workspace_name : persistent_workspaces_to_create_) { Json::Value new_workspace; try { @@ -570,7 +570,7 @@ void Workspaces::create_persistent_workspaces(const Json::Value &clients_data) { new_workspace["windows"] = 0; new_workspace["persistent"] = true; - create_workspace(new_workspace, clients_data); + create_workspace(new_workspace); } } @@ -589,12 +589,12 @@ void Workspaces::init() { monitor_id_ = (*current_monitor)["id"].asInt(); } + fill_persistent_workspaces(); + create_persistent_workspaces(); + const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); - fill_persistent_workspaces(); - create_persistent_workspaces(clients_json); - for (Json::Value workspace_json : workspaces_json) { std::string workspace_name = workspace_json["name"].asString(); if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && From 7d5577a2ed89cc98cf2b1e6f81d53b24f737e74e Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Thu, 19 Oct 2023 21:28:28 -0300 Subject: [PATCH 136/364] feat: create persistent workspaces after regular ones at init feat: avoid recreating duplicate workspaces --- src/modules/hyprland/workspaces.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 42e112d2..4facacfb 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -458,11 +458,11 @@ std::optional Workspace::on_window_closed(WindowAddress &addr) { void Workspaces::create_workspace(Json::Value &workspace_data, const Json::Value &clients_data) { // avoid recreating existing workspaces + auto workspace_name = workspace_data["name"].asString(); auto workspace = std::find_if( - workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr const &x) { - auto name = workspace_data["name"].asString(); - return x->is_persistent() && - ((name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name()); + workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr const &w) { + return (workspace_name.starts_with("special:") && workspace_name.substr(8) == w->name()) || + workspace_name == w->name(); }); if (workspace != workspaces_.end()) { @@ -589,9 +589,6 @@ void Workspaces::init() { monitor_id_ = (*current_monitor)["id"].asInt(); } - fill_persistent_workspaces(); - create_persistent_workspaces(); - const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); @@ -604,6 +601,9 @@ void Workspaces::init() { } } + fill_persistent_workspaces(); + create_persistent_workspaces(); + update_window_count(); sort_workspaces(); From 6829ed1bb4d598ff29099f466a81d05f3a1dd30f Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 20 Oct 2023 08:25:28 +0200 Subject: [PATCH 137/364] Different interfaces have different index --- 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 5eef1661..d203ec7b 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -656,7 +656,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { checking route id **/ if (!is_del_event && - ((net->ifid_ == -1) || (priority < net->route_priority) || (net->ifid_ != temp_idx))) { + ((net->ifid_ == -1) || (priority < net->route_priority))) { // Clear if's state for the case were there is a higher priority // route on a different interface. net->clearIface(); From b8afde043cbbcbe36574c28ab422c23740f7dc77 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 20 Oct 2023 23:57:51 +0200 Subject: [PATCH 138/364] sleeper_thread: allow interrupting sleep() This keeps the function consistent with sleep_until() and sleep_for() which both can be interrupted. This is relevant to allow an update via a "signal" without an "interval" in a custom module. --- include/util/sleeper_thread.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/util/sleeper_thread.hpp b/include/util/sleeper_thread.hpp index 3d8c05d1..80acf169 100644 --- a/include/util/sleeper_thread.hpp +++ b/include/util/sleeper_thread.hpp @@ -61,7 +61,7 @@ class SleeperThread { auto sleep() { std::unique_lock lk(mutex_); CancellationGuard cancel_lock; - return condvar_.wait(lk); + return condvar_.wait(lk, [this] { return signal_ || !do_run_; }); } auto sleep_for(std::chrono::system_clock::duration dur) { From 8c57756556ce28dc688e54e939806fb14f7a0a1f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 20 Oct 2023 22:34:56 +0200 Subject: [PATCH 139/364] util: add scope_guard This custom small implementation avoids adding an extra dependency like Boost.ScopeExit --- include/util/scope_guard.hpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 include/util/scope_guard.hpp diff --git a/include/util/scope_guard.hpp b/include/util/scope_guard.hpp new file mode 100644 index 00000000..5c717704 --- /dev/null +++ b/include/util/scope_guard.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace waybar::util { + +template +class scope_guard { + public: + explicit scope_guard(Func&& exit_function) : f{std::forward(exit_function)} {} + scope_guard(const scope_guard&) = delete; + scope_guard(scope_guard&&) = default; + scope_guard& operator=(const scope_guard&) = delete; + scope_guard& operator=(scope_guard&&) = default; + ~scope_guard() { f(); } + + private: + Func f; +}; + +} // namespace waybar::util From a73669be6a2bf7e8c592b4a38a445b03e5c4cd4d Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 20 Oct 2023 22:36:44 +0200 Subject: [PATCH 140/364] modules/upower: use smart pointer to avoid memory leak --- include/modules/upower/upower_tooltip.hpp | 3 ++- src/modules/upower/upower_tooltip.cpp | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/modules/upower/upower_tooltip.hpp b/include/modules/upower/upower_tooltip.hpp index 05e9dcb3..bc99abed 100644 --- a/include/modules/upower/upower_tooltip.hpp +++ b/include/modules/upower/upower_tooltip.hpp @@ -2,6 +2,7 @@ #include +#include #include #include "gtkmm/box.h" @@ -16,7 +17,7 @@ class UPowerTooltip : public Gtk::Window { const std::string getDeviceIcon(UpDeviceKind& kind); - Gtk::Box* contentBox; + std::unique_ptr contentBox; uint iconSize; uint tooltipSpacing; diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp index 45544bbc..1a653f85 100644 --- a/src/modules/upower/upower_tooltip.cpp +++ b/src/modules/upower/upower_tooltip.cpp @@ -9,11 +9,10 @@ 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_) { - contentBox = new Gtk::Box(Gtk::ORIENTATION_VERTICAL); - // Sets the Tooltip Padding contentBox->set_margin_top(tooltipPadding); contentBox->set_margin_bottom(tooltipPadding); From 89e85db7902dbb7cc18c241e458155866b99ce2c Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 20 Oct 2023 22:37:47 +0200 Subject: [PATCH 141/364] ALabel: make use of std::chrono::max() instead of magic number --- src/ALabel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 4d8b2218..c87e3228 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -12,7 +12,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st : 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(100000000) + ? std::chrono::seconds::max() : std::chrono::seconds( config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)), default_format_(format_) { From a0b63d6b1eb7ae9525abe705578470cfbf74801f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 20 Oct 2023 22:39:10 +0200 Subject: [PATCH 142/364] modules: use scope_exit for deletion to make code more robust --- src/modules/bluetooth.cpp | 8 +++++++- src/modules/sni/host.cpp | 16 ++++++++++++---- src/modules/sni/watcher.cpp | 8 +++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 9e207507..6b6cb7ef 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -6,12 +6,19 @@ #include #include +#include "util/scope_guard.hpp" + namespace { using GDBusManager = std::unique_ptr; auto generateManager() -> GDBusManager { GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error) { + g_error_free(error); + } + }); GDBusObjectManager* manager = g_dbus_object_manager_client_new_for_bus_sync( G_BUS_TYPE_SYSTEM, GDBusObjectManagerClientFlags::G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, @@ -19,7 +26,6 @@ auto generateManager() -> GDBusManager { if (error) { spdlog::error("g_dbus_object_manager_client_new_for_bus_sync() failed: {}", error->message); - g_error_free(error); } auto destructor = [](GDBusObjectManager* manager) { diff --git a/src/modules/sni/host.cpp b/src/modules/sni/host.cpp index 0bbd4d2f..136c2941 100644 --- a/src/modules/sni/host.cpp +++ b/src/modules/sni/host.cpp @@ -2,6 +2,8 @@ #include +#include "util/scope_guard.hpp" + namespace waybar::modules::SNI { Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar, @@ -57,17 +59,20 @@ void Host::nameVanished(const Glib::RefPtr& conn, const G void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error != nullptr) { + g_error_free(error); + } + }); SnWatcher* watcher = sn_watcher_proxy_new_finish(res, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { spdlog::error("Host: {}", error->message); - g_error_free(error); return; } auto host = static_cast(data); host->watcher_ = watcher; if (error != nullptr) { spdlog::error("Host: {}", error->message); - g_error_free(error); return; } sn_watcher_call_register_host(host->watcher_, host->object_path_.c_str(), host->cancellable_, @@ -76,16 +81,19 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error != nullptr) { + g_error_free(error); + } + }); sn_watcher_call_register_host_finish(SN_WATCHER(src), res, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { spdlog::error("Host: {}", error->message); - g_error_free(error); return; } auto host = static_cast(data); if (error != nullptr) { spdlog::error("Host: {}", error->message); - g_error_free(error); return; } g_signal_connect(host->watcher_, "item-registered", G_CALLBACK(&Host::itemRegistered), data); diff --git a/src/modules/sni/watcher.cpp b/src/modules/sni/watcher.cpp index dfd076ef..22434709 100644 --- a/src/modules/sni/watcher.cpp +++ b/src/modules/sni/watcher.cpp @@ -2,6 +2,8 @@ #include +#include "util/scope_guard.hpp" + using namespace waybar::modules::SNI; Watcher::Watcher() @@ -29,6 +31,11 @@ Watcher::~Watcher() { void Watcher::busAcquired(const Glib::RefPtr& conn, Glib::ustring name) { GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error) { + g_error_free(error); + } + }); g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(watcher_), conn->gobj(), "/StatusNotifierWatcher", &error); if (error != nullptr) { @@ -36,7 +43,6 @@ void Watcher::busAcquired(const Glib::RefPtr& conn, Glib: if (error->code != 2) { spdlog::error("Watcher: {}", error->message); } - g_error_free(error); return; } g_signal_connect_swapped(watcher_, "handle-register-item", From ae748b26441712b93578889db0a5f1be5471893c Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 20 Oct 2023 22:41:53 +0200 Subject: [PATCH 143/364] modules+util: fix actual (potential) memory leaks --- include/modules/upower/upower.hpp | 2 +- src/modules/cpu_usage/bsd.cpp | 7 ++++++- src/modules/custom.cpp | 8 +++++++- src/modules/mpris/mpris.cpp | 30 ++++++++++++++++++++---------- src/modules/upower/upower.cpp | 8 ++++---- src/util/prepare_for_sleep.cpp | 3 +-- 6 files changed, 39 insertions(+), 19 deletions(-) diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index 446d1f53..9c1a1830 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -69,7 +69,7 @@ class UPower : public AModule { UpDevice *displayDevice; guint login1_id; GDBusConnection *login1_connection; - UPowerTooltip *upower_tooltip; + std::unique_ptr upower_tooltip; std::string lastStatus; bool showAltText; bool upowerRunning; diff --git a/src/modules/cpu_usage/bsd.cpp b/src/modules/cpu_usage/bsd.cpp index c987a770..663bc0a7 100644 --- a/src/modules/cpu_usage/bsd.cpp +++ b/src/modules/cpu_usage/bsd.cpp @@ -9,6 +9,7 @@ #include // malloc #include "modules/cpu_usage.hpp" +#include "util/scope_guard.hpp" #if defined(__NetBSD__) || defined(__OpenBSD__) #include @@ -33,6 +34,11 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( int ncpu = sysconf(_SC_NPROCESSORS_CONF); size_t sz = CPUSTATES * (ncpu + 1) * sizeof(pcp_time_t); pcp_time_t *cp_time = static_cast(malloc(sz)), *pcp_time = cp_time; + waybar::util::scope_guard cp_time_deleter([cp_time]() { + if (cp_time) { + free(cp_time); + } + }); #if defined(__NetBSD__) int mib[] = { CTL_KERN, @@ -97,6 +103,5 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( } cpuinfo.emplace_back(single_cp_time[CP_IDLE], total); } - free(cp_time); return cpuinfo; } diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 4889b7a3..d0a39802 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -2,6 +2,8 @@ #include +#include "util/scope_guard.hpp" + waybar::modules::Custom::Custom(const std::string& name, const std::string& id, const Json::Value& config) : ALabel(config, "custom-" + name, id, "{}"), @@ -57,6 +59,11 @@ void waybar::modules::Custom::continuousWorker() { } thread_ = [this, cmd] { char* buff = nullptr; + waybar::util::scope_guard buff_deleter([buff]() { + if (buff) { + free(buff); + } + }); size_t len = 0; if (getline(&buff, &len, fp_) == -1) { int exit_code = 1; @@ -90,7 +97,6 @@ void waybar::modules::Custom::continuousWorker() { output_ = {0, output}; dp.emit(); } - free(buff); }; } diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 140bc785..685b1881 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -6,6 +6,8 @@ #include #include +#include "util/scope_guard.hpp" + extern "C" { #include } @@ -117,6 +119,11 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error) { + g_error_free(error); + } + }); manager = playerctl_player_manager_new(&error); if (error) { throw std::runtime_error(fmt::format("unable to create MPRIS client: {}", error->message)); @@ -136,9 +143,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } else { GList* players = playerctl_list_players(&error); if (error) { - auto e = fmt::format("unable to list players: {}", error->message); - g_error_free(error); - throw std::runtime_error(e); + throw std::runtime_error(fmt::format("unable to list players: {}", error->message)); } for (auto p = players; p != NULL; p = p->next) { @@ -410,8 +415,7 @@ auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlaye return; } - GError* error = nullptr; - mpris->player = playerctl_player_new_from_name(player_name, &error); + mpris->player = playerctl_player_new_from_name(player_name, NULL); 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", @@ -478,6 +482,11 @@ auto Mpris::getPlayerInfo() -> std::optional { } GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error) { + g_error_free(error); + } + }); char* player_status = nullptr; auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED; @@ -487,9 +496,7 @@ auto Mpris::getPlayerInfo() -> std::optional { if (player_name == "playerctld") { GList* players = playerctl_list_players(&error); if (error) { - auto e = fmt::format("unable to list players: {}", error->message); - g_error_free(error); - throw std::runtime_error(e); + throw std::runtime_error(fmt::format("unable to list players: {}", error->message)); } // > get the list of players [..] in order of activity // https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249 @@ -568,12 +575,16 @@ auto Mpris::getPlayerInfo() -> std::optional { errorexit: spdlog::error("mpris[{}]: {}", info.name, error->message); - g_error_free(error); return std::nullopt; } bool Mpris::handleToggle(GdkEventButton* const& e) { GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error) { + g_error_free(error); + } + }); auto info = getPlayerInfo(); if (!info) return false; @@ -603,7 +614,6 @@ bool Mpris::handleToggle(GdkEventButton* const& e) { if (error) { spdlog::error("mpris[{}]: error running builtin on-click action: {}", (*info).name, error->message); - g_error_free(error); return false; } return true; diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index f2bc621d..e3b3981a 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -63,7 +63,7 @@ UPower::UPower(const std::string& id, const Json::Value& config) box_.set_has_tooltip(tooltip_enabled); if (tooltip_enabled) { // Sets the window to use when showing the tooltip - upower_tooltip = new UPowerTooltip(iconSize, tooltip_spacing, tooltip_padding); + 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)); } @@ -72,14 +72,13 @@ UPower::UPower(const std::string& id, const Json::Value& config) G_BUS_NAME_WATCHER_FLAGS_AUTO_START, upowerAppear, upowerDisappear, this, NULL); - GError* error = NULL; - client = up_client_new_full(NULL, &error); + 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, &error); + 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 { @@ -99,6 +98,7 @@ UPower::UPower(const std::string& id, const Json::Value& config) } 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); diff --git a/src/util/prepare_for_sleep.cpp b/src/util/prepare_for_sleep.cpp index 218c1e29..661285a2 100644 --- a/src/util/prepare_for_sleep.cpp +++ b/src/util/prepare_for_sleep.cpp @@ -7,8 +7,7 @@ namespace { class PrepareForSleep { private: PrepareForSleep() { - GError *error = NULL; - login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); if (!login1_connection) { spdlog::warn("Unable to connect to the SYSTEM Bus!..."); } else { From 6dd2cfba34e98a291e5ce82ee877b445de03347b Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 21 Oct 2023 11:57:11 +0200 Subject: [PATCH 144/364] ci: Lint header files with hpp file ending --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d11d2ccc..f931babb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,5 +10,5 @@ jobs: - uses: DoozyX/clang-format-lint-action@v0.13 with: source: '.' - extensions: 'h,cpp,c' + extensions: 'hpp,h,cpp,c' clangFormatVersion: 12 From f598e348c4705cbd2335aec59afb13143b9bc235 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 21 Oct 2023 12:50:56 +0200 Subject: [PATCH 145/364] ci: Update Linter job --- .github/workflows/lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f931babb..5504dc32 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,8 +7,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: DoozyX/clang-format-lint-action@v0.13 + - uses: DoozyX/clang-format-lint-action@v0.16.2 with: source: '.' extensions: 'hpp,h,cpp,c' - clangFormatVersion: 12 + clangFormatVersion: 16 From ed65d9cdbdfb8842c1ec1c7bb74f660945f3674b Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 21 Oct 2023 16:52:23 +0200 Subject: [PATCH 146/364] General cleanup in hyprland/workspaces --- include/modules/hyprland/window.hpp | 2 +- include/modules/hyprland/workspaces.hpp | 50 +++++----- src/modules/hyprland/workspaces.cpp | 127 ++++++++++++------------ 3 files changed, 92 insertions(+), 87 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index c9f0be03..ea4d83b2 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -14,7 +14,7 @@ namespace waybar::modules::hyprland { class Window : public waybar::AAppIconLabel, public EventHandler { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); - virtual ~Window(); + ~Window() override; auto update() -> void override; diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index dde564df..6ca2877b 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -26,12 +26,13 @@ namespace waybar::modules::hyprland { class Workspaces; -class CreateWindow { +class WorkspaceWindow { public: - CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_repr); - CreateWindow(std::string workspace_name, WindowAddress window_address, std::string window_class, - std::string window_title); - CreateWindow(Json::Value& client_data); + WorkspaceWindow(std::string workspace_name, WindowAddress window_address, + std::string window_repr); + WorkspaceWindow(std::string workspace_name, WindowAddress window_address, + std::string window_class, std::string window_title); + WorkspaceWindow(Json::Value const& client_data); int increment_time_spent_uncreated(); bool is_empty(Workspaces& workspace_manager); @@ -49,18 +50,18 @@ class CreateWindow { using Repr = std::string; using ClassAndTitle = std::pair; - std::variant window_; WindowAddress window_address_; std::string workspace_name_; + int time_spent_uncreated_ = 0; }; class Workspace { public: explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager, - const Json::Value& clients_json = Json::Value::nullRef); + const Json::Value& clients_data = Json::Value::nullRef); std::string& select_icon(std::map& icons_map); Gtk::Button& button() { return button_; }; @@ -74,21 +75,20 @@ class Workspace { bool is_empty() const { return windows_ == 0; }; bool is_urgent() const { return is_urgent_; }; - auto handle_clicked(GdkEventButton* bt) -> bool; + bool handle_clicked(GdkEventButton* bt) const; void set_active(bool value = true) { active_ = value; }; void set_persistent(bool value = true) { is_persistent_ = value; }; void set_urgent(bool value = true) { is_urgent_ = value; }; void set_visible(bool value = true) { is_visible_ = value; }; void set_windows(uint value) { windows_ = value; }; - void set_name(std::string value) { name_ = value; }; - bool contains_window(WindowAddress addr) const { return window_map_.contains(addr); } - void insert_window(CreateWindow create_window_paylod); - std::string remove_window(WindowAddress addr); + void set_name(std::string const& value) { name_ = value; }; + bool contains_window(WindowAddress const& addr) const { return window_map_.contains(addr); } + void insert_window(WorkspaceWindow create_window_paylod); + std::string remove_window(WindowAddress const& addr); void initialize_window_map(const Json::Value& clients_data); - bool on_window_opened(CreateWindow create_window_paylod); - - std::optional on_window_closed(WindowAddress& addr); + bool on_window_opened(WorkspaceWindow const& create_window_paylod); + std::optional on_window_closed(WindowAddress const& addr); void update(const std::string& format, const std::string& icon); @@ -132,21 +132,21 @@ class Workspaces : public AModule, public EventHandler { bool window_rewrite_config_uses_title() const { return any_window_rewrite_rule_uses_title_; } private: - void onEvent(const std::string&) override; + void onEvent(const std::string& e) override; void update_window_count(); void sort_workspaces(); - void create_workspace(Json::Value& workspace_data, - const Json::Value& clients_data = Json::Value::nullRef); - void remove_workspace(std::string name); - void set_urgent_workspace(std::string windowaddress); + void create_workspace(Json::Value const& workspace_data, + Json::Value const& clients_data = Json::Value::nullRef); + void remove_workspace(std::string const& name); + void set_urgent_workspace(std::string const& windowaddress); void parse_config(const Json::Value& config); void register_ipc(); - void on_window_opened(std::string payload); - void on_window_closed(std::string payload); - void on_window_moved(std::string payload); + void on_window_opened(std::string const& payload); + void on_window_closed(std::string const& payload); + void on_window_moved(std::string const& payload); - int window_rewrite_priority_function(std::string& window_rule); + int window_rewrite_priority_function(std::string const& window_rule); bool all_outputs_ = false; bool show_special_ = false; @@ -178,7 +178,7 @@ class Workspaces : public AModule, public EventHandler { std::vector> workspaces_; std::vector workspaces_to_create_; std::vector workspaces_to_remove_; - std::vector windows_to_create_; + std::vector windows_to_create_; std::vector ignore_workspaces_; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 4facacfb..d9760e3e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -14,23 +14,28 @@ namespace waybar::modules::hyprland { -int Workspaces::window_rewrite_priority_function(std::string &window_rule) { +namespace { +auto constexpr WINDOW_CREATION_TIMEOUT = 2; +} + +int Workspaces::window_rewrite_priority_function(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 has_title = window_rule.find("title") != std::string::npos; - bool has_class = window_rule.find("class") != std::string::npos; + bool const has_title = window_rule.find("title") != std::string::npos; + bool const has_class = window_rule.find("class") != std::string::npos; if (has_title && has_class) { any_window_rewrite_rule_uses_title_ = true; return 3; - } else if (has_title) { + } + if (has_title) { any_window_rewrite_rule_uses_title_ = true; return 2; - } else if (has_class) { - return 1; - } else { - return 0; } + if (has_class) { + return 1; + } + return 0; } Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) @@ -51,7 +56,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value } auto Workspaces::parse_config(const Json::Value &config) -> void { - Json::Value config_format = config["format"]; + const Json::Value &config_format = config["format"]; format_ = config_format.isString() ? config_format.asString() : "{name}"; with_icon_ = format_.find("{icon}") != std::string::npos; @@ -109,13 +114,13 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { } } - Json::Value format_window_separator = config["format-window-separator"]; + const Json::Value &format_window_separator = config["format-window-separator"]; format_window_separator_ = format_window_separator.isString() ? format_window_separator.asString() : " "; - Json::Value window_rewrite = config["window-rewrite"]; + const Json::Value &window_rewrite = config["window-rewrite"]; - Json::Value window_rewrite_default_config = config["window-rewrite-default"]; + const Json::Value &window_rewrite_default_config = config["window-rewrite-default"]; std::string window_rewrite_default = window_rewrite_default_config.isString() ? window_rewrite_default_config.asString() : "?"; @@ -152,13 +157,13 @@ auto Workspaces::register_ipc() -> void { } auto Workspaces::update() -> void { - for (std::string workspace_to_remove : workspaces_to_remove_) { + for (const std::string &workspace_to_remove : workspaces_to_remove_) { remove_workspace(workspace_to_remove); } workspaces_to_remove_.clear(); - for (Json::Value &workspace_to_create : workspaces_to_create_) { + for (Json::Value const &workspace_to_create : workspaces_to_create_) { create_workspace(workspace_to_create); } @@ -195,7 +200,7 @@ auto Workspaces::update() -> void { } bool any_window_created = false; - std::vector not_created; + std::vector not_created; for (auto &window_payload : windows_to_create_) { bool created = false; @@ -207,7 +212,6 @@ auto Workspaces::update() -> void { } } if (!created) { - static const int WINDOW_CREATION_TIMEOUT = 2; if (window_payload.increment_time_spent_uncreated() < WINDOW_CREATION_TIMEOUT) { not_created.push_back(window_payload); } @@ -334,7 +338,7 @@ void Workspaces::onEvent(const std::string &ev) { dp.emit(); } -void Workspaces::on_window_opened(std::string payload) { +void Workspaces::on_window_opened(std::string const &payload) { size_t last_comma_idx = 0; size_t next_comma_idx = payload.find(','); std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx); @@ -351,11 +355,10 @@ void Workspaces::on_window_opened(std::string payload) { std::string window_title = payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx); - windows_to_create_.emplace_back( - CreateWindow(workspace_name, window_address, window_class, window_title)); + windows_to_create_.emplace_back(workspace_name, window_address, window_class, window_title); } -void Workspaces::on_window_closed(std::string addr) { +void Workspaces::on_window_closed(std::string const &addr) { for (auto &workspace : workspaces_) { if (workspace->on_window_closed(addr)) { break; @@ -363,7 +366,7 @@ void Workspaces::on_window_closed(std::string addr) { } } -void Workspaces::on_window_moved(std::string payload) { +void Workspaces::on_window_moved(std::string const &payload) { size_t last_comma_idx = 0; size_t next_comma_idx = payload.find(','); std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx); @@ -395,7 +398,7 @@ void Workspaces::on_window_moved(std::string payload) { // ...and add it to the new workspace if (!window_repr.empty()) { - windows_to_create_.emplace_back(CreateWindow(workspace_name, window_address, window_repr)); + windows_to_create_.emplace_back(workspace_name, window_address, window_repr); } } @@ -426,37 +429,35 @@ void Workspace::initialize_window_map(const Json::Value &clients_data) { } } -void Workspace::insert_window(CreateWindow create_window_paylod) { +void Workspace::insert_window(WorkspaceWindow create_window_paylod) { if (!create_window_paylod.is_empty(workspace_manager_)) { window_map_[create_window_paylod.addr()] = create_window_paylod.repr(workspace_manager_); } }; -std::string Workspace::remove_window(WindowAddress addr) { +std::string Workspace::remove_window(WindowAddress const &addr) { std::string window_repr = window_map_[addr]; window_map_.erase(addr); - return window_repr; } -bool Workspace::on_window_opened(CreateWindow create_window_paylod) { +bool Workspace::on_window_opened(WorkspaceWindow const &create_window_paylod) { if (create_window_paylod.workspace_name() == name()) { insert_window(create_window_paylod); return true; - } else { - return false; } + return false; } -std::optional Workspace::on_window_closed(WindowAddress &addr) { +std::optional Workspace::on_window_closed(WindowAddress const &addr) { if (window_map_.contains(addr)) { return remove_window(addr); - } else { - return {}; } + return {}; } -void Workspaces::create_workspace(Json::Value &workspace_data, const Json::Value &clients_data) { +void Workspaces::create_workspace(Json::Value const &workspace_data, + Json::Value const &clients_data) { // avoid recreating existing workspaces auto workspace_name = workspace_data["name"].asString(); auto workspace = std::find_if( @@ -477,7 +478,7 @@ void Workspaces::create_workspace(Json::Value &workspace_data, const Json::Value new_workspace_button.show_all(); } -void Workspaces::remove_workspace(std::string name) { +void Workspaces::remove_workspace(std::string const &name) { auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr &x) { return (name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name(); @@ -816,7 +817,7 @@ std::string &Workspace::select_icon(std::map &icons_ma return name_; } -auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { +bool Workspace::handle_clicked(GdkEventButton *bt) const { try { if (id() > 0) { // normal or numbered persistent gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); @@ -834,7 +835,7 @@ auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { return false; } -void Workspaces::set_urgent_workspace(std::string windowaddress) { +void Workspaces::set_urgent_workspace(std::string const &windowaddress) { const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); int workspace_id = -1; @@ -863,58 +864,62 @@ std::string Workspaces::get_rewrite(std::string window_class, std::string window return window_rewrite_rules_.get(window_repr_key); } -CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_address, - std::string window_repr) - : window_(window_repr), window_address_(window_address), workspace_name_(workspace_name) { +WorkspaceWindow::WorkspaceWindow(std::string workspace_name, WindowAddress window_address, + std::string window_repr) + : window_(std::move(window_repr)), + window_address_(std::move(window_address)), + workspace_name_(std::move(workspace_name)) { clear_addr(); clear_workspace_name(); } -CreateWindow::CreateWindow(std::string workspace_name, WindowAddress window_address, - std::string window_class, std::string window_title) - : window_(std::make_pair(window_class, window_title)), - window_address_(window_address), - workspace_name_(workspace_name) { +WorkspaceWindow::WorkspaceWindow(std::string workspace_name, WindowAddress window_address, + std::string window_class, std::string window_title) + : window_(std::make_pair(std::move(window_class), std::move(window_title))), + window_address_(std::move(window_address)), + workspace_name_(std::move(workspace_name)) { clear_addr(); clear_workspace_name(); } -CreateWindow::CreateWindow(Json::Value &client_data) { - window_address_ = client_data["address"].asString(); - workspace_name_ = client_data["workspace"]["name"].asString(); - window_ = std::make_pair(client_data["class"].asString(), client_data["title"].asString()); +WorkspaceWindow::WorkspaceWindow(Json::Value const &client_data) + : window_(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), + window_address_(client_data["address"].asString()), + workspace_name_(client_data["workspace"]["name"].asString()) { clear_addr(); clear_workspace_name(); } -std::string CreateWindow::repr(Workspaces &workspace_manager) { +std::string WorkspaceWindow::repr(Workspaces &workspace_manager) { if (std::holds_alternative(window_)) { return std::get(window_); - } else if (std::holds_alternative(window_)) { + } + if (std::holds_alternative(window_)) { auto [window_class, window_title] = std::get(window_); return workspace_manager.get_rewrite(window_class, window_title); - } else { - // Unreachable - return ""; } + // Unreachable + spdlog::error("WorkspaceWindow::repr: Unreachable"); + throw std::runtime_error("WorkspaceWindow::repr: Unreachable"); } -bool CreateWindow::is_empty(Workspaces &workspace_manager) { +bool WorkspaceWindow::is_empty(Workspaces &workspace_manager) { if (std::holds_alternative(window_)) { return std::get(window_).empty(); - } else if (std::holds_alternative(window_)) { + } + if (std::holds_alternative(window_)) { auto [window_class, window_title] = std::get(window_); return (window_class.empty() && (!workspace_manager.window_rewrite_config_uses_title() || window_title.empty())); - } else { - // Unreachable - return true; } + // Unreachable + spdlog::error("WorkspaceWindow::is_empty: Unreachable"); + throw std::runtime_error("WorkspaceWindow::is_empty: Unreachable"); } -int CreateWindow::increment_time_spent_uncreated() { return time_spent_uncreated_++; } +int WorkspaceWindow::increment_time_spent_uncreated() { return time_spent_uncreated_++; } -void CreateWindow::clear_addr() { +void WorkspaceWindow::clear_addr() { // substr(2, ...) is necessary because Hyprland's JSON follows this format: // 0x{ADDR} // While Hyprland's IPC follows this format: @@ -928,7 +933,7 @@ void CreateWindow::clear_addr() { } } -void CreateWindow::clear_workspace_name() { +void WorkspaceWindow::clear_workspace_name() { // 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 @@ -943,7 +948,7 @@ void CreateWindow::clear_workspace_name() { } } -void CreateWindow::move_to_worksace(std::string &new_workspace_name) { +void WorkspaceWindow::move_to_worksace(std::string &new_workspace_name) { workspace_name_ = new_workspace_name; } From 7576611782f70f3fffc7ce78692332b7a5675ec0 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 21 Oct 2023 17:06:02 +0200 Subject: [PATCH 147/364] formatting --- src/modules/network.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index d203ec7b..654afbe8 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -655,8 +655,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { higher priority. Disable router -> RTA_GATEWAY -> up new router -> set higher priority added checking route id **/ - if (!is_del_event && - ((net->ifid_ == -1) || (priority < net->route_priority))) { + if (!is_del_event && ((net->ifid_ == -1) || (priority < net->route_priority))) { // Clear if's state for the case were there is a higher priority // route on a different interface. net->clearIface(); From d0cab2a3679cf8d9814b64e9fb243a083136e283 Mon Sep 17 00:00:00 2001 From: Avishek Sen Date: Sat, 21 Oct 2023 21:15:25 +0530 Subject: [PATCH 148/364] docs: fix typos and improve language coherence --- man/waybar-backlight-slider.5.scd | 2 +- man/waybar-backlight.5.scd | 8 ++++---- man/waybar-battery.5.scd | 12 ++++++------ man/waybar-bluetooth.5.scd | 12 ++++++------ man/waybar-cava.5.scd | 24 ++++++++++++------------ man/waybar-clock.5.scd | 6 +++--- man/waybar-cpu.5.scd | 26 +++++++++++++------------- man/waybar-custom.5.scd | 12 ++++++------ man/waybar-disk.5.scd | 16 ++++++++-------- man/waybar-dwl-tags.5.scd | 2 +- man/waybar-gamemode.5.scd | 4 ++-- man/waybar-hyprland-submap.5.scd | 4 ++-- man/waybar-hyprland-workspaces.5.scd | 8 ++++---- man/waybar-idle-inhibitor.5.scd | 10 +++++----- man/waybar-image.5.scd | 2 +- man/waybar-inhibitor.5.scd | 4 ++-- man/waybar-jack.5.scd | 8 ++++---- man/waybar-keyboard-state.5.scd | 4 ++-- man/waybar-memory.5.scd | 4 ++-- man/waybar-mpd.5.scd | 6 +++--- man/waybar-mpris.5.scd | 6 +++--- man/waybar-network.5.scd | 8 ++++---- man/waybar-pulseaudio-slider.5.scd | 2 +- man/waybar-pulseaudio.5.scd | 12 ++++++------ man/waybar-river-layout.5.scd | 4 ++-- man/waybar-river-mode.5.scd | 4 ++-- man/waybar-river-tags.5.scd | 2 +- man/waybar-river-window.5.scd | 4 ++-- man/waybar-sndio.5.scd | 6 +++--- man/waybar-sway-mode.5.scd | 4 ++-- man/waybar-sway-window.5.scd | 8 ++++---- man/waybar-sway-workspaces.5.scd | 14 +++++++------- man/waybar-temperature.5.scd | 6 +++--- man/waybar-wireplumber.5.scd | 6 +++--- man/waybar-wlr-taskbar.5.scd | 4 ++-- man/waybar-wlr-workspaces.5.scd | 8 ++++---- man/waybar.5.scd.in | 16 ++++++++-------- 37 files changed, 144 insertions(+), 144 deletions(-) diff --git a/man/waybar-backlight-slider.5.scd b/man/waybar-backlight-slider.5.scd index 55004d08..cd5b5184 100644 --- a/man/waybar-backlight-slider.5.scd +++ b/man/waybar-backlight-slider.5.scd @@ -8,7 +8,7 @@ waybar - backlight slider module The *backlight slider* module displays and controls the current brightness of the default or preferred device. -The brightness can be controlled by dragging the slider accross the bar, or clicking on a specific position. +The brightness can be controlled by dragging the slider across the bar or clicking on a specific position. # CONFIGURATION diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index cbadb8b9..7db18a20 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -26,7 +26,7 @@ The *backlight* module displays the current backlight level. *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -46,11 +46,11 @@ The *backlight* module displays the current backlight level. *on-click-middle*: ++ typeof: string ++ - Command to execute when middle-clicked on the module using mousewheel. + Command to execute when middle-clicked on the module using mouse scroll wheel. *on-click-right*: ++ typeof: string ++ - Command to execute when the module is right clicked. + Command to execute when the module is right-clicked. *on-update*: ++ typeof: string ++ @@ -75,7 +75,7 @@ The *backlight* module displays the current backlight level. *scroll-step*: ++ typeof: float ++ default: 1.0 ++ - The speed in which to change the brightness when scrolling. + The speed at which to change the brightness when scrolling. # EXAMPLE: diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index b13cee6e..7827f4a8 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -25,7 +25,7 @@ The *battery* module displays the current capacity and state (eg. charging) of y *design-capacity*: ++ typeof: bool ++ default: false ++ - Option to use the battery design capacity instead of it's current maximal capacity. + Option to use the battery design capacity instead of its current maximal capacity. *interval*: ++ typeof: integer ++ @@ -57,7 +57,7 @@ The *battery* module displays the current capacity and state (eg. charging) of y *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -77,7 +77,7 @@ The *battery* module displays the current capacity and state (eg. charging) of y *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ @@ -126,7 +126,7 @@ The three arguments are: # CUSTOM FORMATS -The *battery* module allows one to define custom formats based on up to two factors. The best fitting format will be selected. +The *battery* module allows one to define custom formats based on up to two factors. The best-fitting format will be selected. *format-*: With *states*, a custom format can be set depending on the capacity of your battery. @@ -137,8 +137,8 @@ The *battery* module allows one to define custom formats based on up to two fact # STATES - Every entry (*state*) consists of a ** (typeof: *string*) and a ** (typeof: *integer*). -- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the ** of the state. Each class gets activated when the current capacity is equal or below the configured **. -- Also each state can have its own *format*. Those con be configured via *format-*. Or if you want to differentiate a bit more even as *format--*. For more information see *custom-formats*. +- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the ** of the state. Each class gets activated when the current capacity is equal to or below the configured **. +- Also each state can have its own *format*. Those can be configured via *format-*. Or if you want to differentiate a bit more even as *format--*. For more information see *custom-formats*. diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 4dff9bf1..1fdd984b 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -14,7 +14,7 @@ Addressed by *bluetooth* *controller*: ++ typeof: string ++ - Use the controller with the defined alias. Otherwise a random controller is used. Recommended to define when there is more than 1 controller available to the system. + Use the controller with the defined alias. Otherwise, a random controller is used. Recommended to define when there is more than 1 controller available to the system. *format-device-preference*: ++ typeof: array ++ @@ -44,7 +44,7 @@ Addressed by *bluetooth* *format-no-controller*: ++ typeof: string ++ - This format is used when no bluetooth controller could be found + This format is used when no bluetooth controller can be found *format-icons*: ++ typeof: array/object ++ @@ -62,7 +62,7 @@ Addressed by *bluetooth* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -78,7 +78,7 @@ Addressed by *bluetooth* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-scroll-up*: ++ typeof: string ++ @@ -119,7 +119,7 @@ Addressed by *bluetooth* *tooltip-format-no-controller*: ++ typeof: string ++ - This format is used when no bluetooth controller could be found + This format is used when no bluetooth controller can be found *tooltip-format-enumerate-connected*: ++ typeof: string ++ @@ -146,7 +146,7 @@ Addressed by *bluetooth* *{device_alias}*: Alias of the displayed device. *{device_enumerate}*: Show a list of all connected devices, each on a separate line. Define the format of each device with the *tooltip-format-enumerate-connected* ++ -and/or *tooltip-format-enumerate-connected-battery* config options. Can only be used in the tooltip related format options. +and/or *tooltip-format-enumerate-connected-battery* config options. Can only be used in the tooltip-related format options. # EXPERIMENTAL BATTERY PERCENTAGE FEATURE diff --git a/man/waybar-cava.5.scd b/man/waybar-cava.5.scd index 88e736e5..cf75441b 100644 --- a/man/waybar-cava.5.scd +++ b/man/waybar-cava.5.scd @@ -95,19 +95,19 @@ libcava lives in: |[ *monstercat* :[ bool :[ false -:[ Disables or enables the so-called "Monstercat smoothing" with of without "waves" +:[ Disables or enables the so-called "Monstercat smoothing" with or without "waves" |[ *waves* :[ bool :[ false -:[ Disables or enables the so-called "Monstercat smoothing" with of without "waves" +:[ Disables or enables the so-called "Monstercat smoothing" with or without "waves" |[ *noise_reduction* :[ double :[ 0.77 -:[ Range between 0 - 1. The raw visualization is very noisy, this factor adjust the integral and gravity filters to keep the signal smooth. 1 - will be very slow and smooth, 0 - will be fast but noisy +:[ Range between 0 - 1. The raw visualization is very noisy, this factor adjusts the integral and gravity filters to keep the signal smooth. 1 - will be very slow and smooth, 0 - will be fast but noisy |[ *input_delay* :[ integer :[ 2 -:[ Sets the delay before fetching audio source thread start working. On author machine Waybar starts much faster then pipewire audio server, and without a little delay cava module fails due to pipewire is not ready +:[ Sets the delay before fetching audio source thread start working. On author's machine, Waybar starts much faster than pipewire audio server, and without a little delay cava module fails because pipewire is not ready |[ *ascii_max_range* :[ integer :[ 7 @@ -124,14 +124,14 @@ libcava lives in: Configuration can be provided as: - The only cava configuration file which is provided through *cava_config*. The rest configuration can be skipped - Without cava configuration file. In such case cava should be configured through provided list of the configuration option -- Mix. When provided both And cava configuration file And configuration options. In such case waybar applies configuration file first then overrides particular options by the provided list of configuration options +- Mix. When provided both And cava configuration file And configuration options. In such case, waybar applies configuration file first and then overrides particular options by the provided list of configuration options # ACTIONS [- *String* :- *Action* |[ *mode* -:< Switch main cava thread and fetching audio source thread from/to pause/resume +:< Switch main cava thread and fetch audio source thread from/to pause/resume # DEPENDENCIES @@ -142,16 +142,16 @@ Configuration can be provided as: . On start Waybar throws an exception "error while loading shared libraries: libcava.so: cannot open shared object file: No such file or directory". It might happen when libcava for some reason hasn't been registered in the system. sudo ldconfig should help -. Waybar is starting but cava module doesn't react on the music - 1. In such case for at first need to make sure usual cava application is working as well +. Waybar is starting but cava module doesn't react to the music + 1. In such cases at first need to make sure usual cava application is working as well 2. If so, need to comment all configuration options. Uncomment cava_config and provide the path to the working cava config - 3. You might set too huge or too small input_delay. Try to setup to 4 seconds, restart waybar and check again 4 seconds past. Usual even on weak machines it should be enough - 4. You might accidentally switched action mode to pause mode + 3. You might set too huge or too small input_delay. Try to setup to 4 seconds, restart waybar, and check again 4 seconds past. Usual even on weak machines it should be enough + 4. You might accidentally switch action mode to pause mode # RISING ISSUES -For clear understanding: this module is a cava API's consumer. So for any bugs related to cava engine you should contact to Cava upstream(https://github.com/karlstav/cava) ++ -with the one Exception. Cava upstream doesn't provide cava as a shared library. For that this module author made a fork libcava(https://github.com/LukashonakV/cava). ++ +For clear understanding: this module is a cava API's consumer. So for any bugs related to cava engine you should contact Cava upstream(https://github.com/karlstav/cava) ++ +with the one Exception. Cava upstream doesn't provide cava as a shared library. For that, this module author made a fork libcava(https://github.com/LukashonakV/cava). ++ So the order is: . cava upstream . libcava upstream. diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 0e855afa..dc26c270 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -63,7 +63,7 @@ $XDG_CONFIG_HOME/waybar/config ++ |[ *on-click-right* :[ string :[ -:[ Command to execute when you right clicked on the module +:[ Command to execute when you right-click on the module |[ *on-scroll-up* :[ string :[ @@ -147,7 +147,7 @@ View all valid format options in *strftime(3)* or have a look * typeof: integer ++ The interval (in seconds) in which the information gets polled. ++ Use *once* if you want to execute the module only on startup. ++ - You can update it manually with a signal. If no *interval* or *signal* is defined, it is assumed that the out script loops it self. ++ - If a *signal* is defined then the script will run once on startup and will will only update with a signal. + You can update it manually with a signal. If no *interval* or *signal* is defined, it is assumed that the out script loops itself. ++ + If a *signal* is defined then the script will run once on startup and will only update with a signal. *restart-interval*: ++ typeof: integer ++ The restart interval (in seconds). ++ Can't be used with the *interval* option, so only with continuous scripts. ++ - Once the script exit, it'll be re-executed after the *restart-interval*. + Once the script exits, it'll be re-executed after the *restart-interval*. *signal*: ++ typeof: integer ++ @@ -68,7 +68,7 @@ Addressed by *custom/* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -84,7 +84,7 @@ Addressed by *custom/* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ @@ -136,7 +136,7 @@ $text\\n$tooltip\\n$class* *{}*: Output of the script. -*{percentage}* Percentage which can be set via a json return-type. +*{percentage}* Percentage which can be set via a json return type. *{icon}*: An icon from 'format-icons' according to percentage. diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index 444f8a2b..d466bddf 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -33,7 +33,7 @@ Addressed by *disk* *states*: ++ typeof: object ++ - A number of disk utilization states which get activated on certain percentage thresholds (percentage_used). See *waybar-states(5)*. + A number of disk utilization states that get activated on certain percentage thresholds (percentage_used). See *waybar-states(5)*. *max-length*: ++ typeof: integer ++ @@ -41,7 +41,7 @@ Addressed by *disk* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -57,7 +57,7 @@ Addressed by *disk* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ @@ -87,7 +87,7 @@ Addressed by *disk* *unit*: ++ typeof: string ++ - Use with specific_free, specific_used, and speciifc_total to force calculation to always be in a certain unit. Accepts kB, kiB, MB, Mib, GB, GiB, TB, TiB. + 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. # FORMAT REPLACEMENTS @@ -95,7 +95,7 @@ Addressed by *disk* *{percentage_free}*: Percentage of free disk space -*{total}*: Total amount of space on the disk, partition or mountpoint. Automatically selects unit based on size remaining. +*{total}*: Total amount of space on the disk, partition, or mountpoint. Automatically selects unit based on size remaining. *{used}*: Amount of used disk space. Automatically selects unit based on size remaining. @@ -103,11 +103,11 @@ Addressed by *disk* *{path}*: The path specified in the configuration. -*{specific_total}*: Total amount of space on the disk, partition or mountpoint in a specific unit. Deaults to bytes. +*{specific_total}*: Total amount of space on the disk, partition, or mountpoint in a specific unit. Defaults to bytes. -*{specific_used}*: Amount of used disk space in a specific unit. Deaults to bytes. +*{specific_used}*: Amount of used disk space in a specific unit. Defaults to bytes. -*{specific_free}*: Amount of available disk space for normal users in a specific unit. Deaults to bytes. +*{specific_free}*: Amount of available disk space for normal users in a specific unit. Defaults to bytes. # EXAMPLES diff --git a/man/waybar-dwl-tags.5.scd b/man/waybar-dwl-tags.5.scd index c9f1162d..07c94be9 100644 --- a/man/waybar-dwl-tags.5.scd +++ b/man/waybar-dwl-tags.5.scd @@ -24,7 +24,7 @@ Addressed by *dwl/tags* *disable-click*: ++ typeof: bool ++ default: false ++ - If set to false, you can left click to set focused tag. Right click to toggle tag focus. If set to true this behaviour is disabled. + If set to false, you can left-click to set focused tag. Right-click to toggle tag focus. If set to true this behaviour is disabled. # EXAMPLE diff --git a/man/waybar-gamemode.5.scd b/man/waybar-gamemode.5.scd index 257c9c91..492e9850 100644 --- a/man/waybar-gamemode.5.scd +++ b/man/waybar-gamemode.5.scd @@ -65,11 +65,11 @@ Feral Gamemode optimizations. *{glyph}*: The string icon glyph to use instead. -*{count}*: The amount of games running with gamemode optimizations. +*{count}*: The number of games running with gamemode optimizations. # TOOLTIP FORMAT REPLACEMENTS -*{count}*: The amount of games running with gamemode optimizations. +*{count}*: The number of games running with gamemode optimizations. # EXAMPLES diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index 51c23cb9..3f8d6280 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -27,7 +27,7 @@ Addressed by *hyprland/submap* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -43,7 +43,7 @@ Addressed by *hyprland/submap* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 8fecfa10..278b2e12 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -19,13 +19,13 @@ Addressed by *hyprland/workspaces* *format-icons*: ++ typeof: array ++ - Based on the workspace id and state, the corresponding icon gets selected. See *icons*. + Based on the workspace ID and state, the corresponding icon gets selected. See *icons*. *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. + Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching. *window-rewrite-default*: typeof: string ++ @@ -45,7 +45,7 @@ Addressed by *hyprland/workspaces* *all-outputs*: ++ typeof: bool ++ default: false ++ - If set to false workspaces group will be shown only in assigned output. Otherwise all workspace groups are shown. + If set to false workspaces group will be shown only in assigned output. Otherwise, all workspace groups are shown. *active-only*: ++ typeof: bool ++ @@ -55,7 +55,7 @@ Addressed by *hyprland/workspaces* *ignore-workspaces*: ++ typeof: array ++ default: [] ++ - Regexes to match against workspaces names. If there's a match, the workspace will not be shown. This takes precedence over *show-special*, *all-outputs* and *active-only*. + Regexes to match against workspaces names. If there's a match, the workspace will not be shown. This takes precedence over *show-special*, *all-outputs*, and *active-only*. *sort-by*: ++ typeof: string ++ diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index 7be3b568..287def1a 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -6,8 +6,8 @@ waybar - idle_inhibitor module # DESCRIPTION -The *idle_inhibitor* module can inhibiting the idle behavior such as screen blanking, locking, and -screensaving, also known as "presentation mode". +The *idle_inhibitor* module can inhibit the idle behavior such as screen blanking, locking, and +screensaver, also known as "presentation mode". # CONFIGURATION @@ -29,7 +29,7 @@ screensaving, also known as "presentation mode". *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -45,7 +45,7 @@ screensaving, also known as "presentation mode". *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ @@ -70,7 +70,7 @@ screensaving, also known as "presentation mode". *timeout*: ++ typeof: double ++ - The number of minutes the inhibit should last. + The number of minutes the inhibition should last. *tooltip*: ++ typeof: bool ++ diff --git a/man/waybar-image.5.scd b/man/waybar-image.5.scd index 401d0cd2..1671e711 100644 --- a/man/waybar-image.5.scd +++ b/man/waybar-image.5.scd @@ -66,7 +66,7 @@ The *image* module displays an image from a path. # SCRIPT OUTPUT -Similar to the *custom* module, output values of the script is *newline* separated. +Similar to the *custom* module, output values of the script are *newline* separated. The following is the output format: ``` diff --git a/man/waybar-inhibitor.5.scd b/man/waybar-inhibitor.5.scd index bf37d351..1233eb7d 100644 --- a/man/waybar-inhibitor.5.scd +++ b/man/waybar-inhibitor.5.scd @@ -33,7 +33,7 @@ See *systemd-inhibit*(1) for more information. *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -49,7 +49,7 @@ See *systemd-inhibit*(1) for more information. *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ diff --git a/man/waybar-jack.5.scd b/man/waybar-jack.5.scd index e04314a9..3af71b61 100644 --- a/man/waybar-jack.5.scd +++ b/man/waybar-jack.5.scd @@ -27,7 +27,7 @@ Addressed by *jack* *format-xrun*: ++ typeof: string ++ - This format is used for one polling interval, when the JACK server reports an xrun. + This format is used for one polling interval when the JACK server reports an xrun. *realtime*: ++ typeof: bool ++ @@ -59,7 +59,7 @@ Addressed by *jack* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -75,7 +75,7 @@ Addressed by *jack* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ @@ -87,7 +87,7 @@ Addressed by *jack* *{bufsize}*: The size of the JACK buffer. -*{samplerate}*: The samplerate at which the JACK server is running. +*{samplerate}*: The sample rate at which the JACK server is running. *{latency}*: The duration, in ms, of the current buffer size. diff --git a/man/waybar-keyboard-state.5.scd b/man/waybar-keyboard-state.5.scd index 23804443..9ecc5515 100644 --- a/man/waybar-keyboard-state.5.scd +++ b/man/waybar-keyboard-state.5.scd @@ -13,7 +13,7 @@ You must be a member of the input group to use this module. # CONFIGURATION *interval*: ++ - Deprecated, this module use event loop now, the interval has no effect. + Deprecated, this module uses event loop now, the interval has no effect. typeof: integer ++ default: 1 ++ The interval, in seconds, to poll the keyboard state. @@ -51,7 +51,7 @@ You must be a member of the input group to use this module. *binding-keys*: ++ typeof: array ++ default: [58, 69, 70] ++ - Customize the key to trigger this module, the key number can be find in /usr/include/linux/input-event-codes.h or running sudo libinput debug-events --show-keycodes. + Customize the key to trigger this module, the key number can be found in /usr/include/linux/input-event-codes.h or running sudo libinput debug-events --show-keycodes. # FORMAT REPLACEMENTS diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index 77e00638..55c74b0b 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -41,7 +41,7 @@ Addressed by *memory* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -57,7 +57,7 @@ Addressed by *memory* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index 1dde8f79..ffef0fef 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -99,7 +99,7 @@ Addressed by *mpd* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -115,7 +115,7 @@ Addressed by *mpd* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ @@ -182,7 +182,7 @@ Addressed by *mpd* *{queueLength}*: The length of the current queue. -*{stateIcon}*: The icon corresponding the playing or paused status of the player (see *state-icons* option) +*{stateIcon}*: The icon corresponding to the playing or paused status of the player (see *state-icons* option) *{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option) diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 35d7bd6f..137293bd 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -13,7 +13,7 @@ The *mpris* module displays currently playing media via libplayerctl. *player*: ++ typeof: string ++ default: playerctld ++ - Name of the MPRIS player to attach to. Using the default value always follows the currenly active player. + Name of the MPRIS player to attach to. Using the default value always follows the currently active player. *ignored-players*: ++ typeof: []string ++ @@ -97,7 +97,7 @@ The *mpris* module displays currently playing media via libplayerctl. *enable-tooltip-len-limits*: ++ typeof: bool ++ default: false ++ - Option to enable the length limits for the tooltip as well. By default the tooltip ignores all length limits. + Option to enable the length limits for the tooltip as well. By default, the tooltip ignores all length limits. *ellipsis*: ++ typeof: string ++ @@ -114,7 +114,7 @@ The *mpris* module displays currently playing media via libplayerctl. *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index 0367aaa4..08c86d3d 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -14,7 +14,7 @@ Addressed by *network* *interface*: ++ typeof: string ++ - Use the defined interface instead of auto detection. Accepts wildcard. + Use the defined interface instead of auto-detection. Accepts wildcard. *interval*: ++ typeof: integer ++ @@ -41,7 +41,7 @@ Addressed by *network* *format-linked*: ++ typeof: string ++ - This format is used when a linked interface with no ip address is displayed. + This format is used when a linked interface with no IP address is displayed. *format-disconnected*: ++ typeof: string ++ @@ -66,7 +66,7 @@ Addressed by *network* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -82,7 +82,7 @@ Addressed by *network* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ diff --git a/man/waybar-pulseaudio-slider.5.scd b/man/waybar-pulseaudio-slider.5.scd index 8ecc040e..fc1da1c4 100644 --- a/man/waybar-pulseaudio-slider.5.scd +++ b/man/waybar-pulseaudio-slider.5.scd @@ -8,7 +8,7 @@ waybar - pulseaudio slider module The *pulseaudio slider* module displays and controls the current volume of the default sink or source as a bar. -The volume can be controlled by dragging the slider accross the bar, or clicking on a specific position. +The volume can be controlled by dragging the slider across the bar or clicking on a specific position. # CONFIGURATION diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index bdb9c993..e04245ee 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -8,7 +8,7 @@ waybar - pulseaudio module The *pulseaudio* module displays the current volume reported by PulseAudio. -Additionally you can control the volume by scrolling *up* or *down* while the cursor is over the module. +Additionally, you can control the volume by scrolling *up* or *down* while the cursor is over the module. # CONFIGURATION @@ -36,7 +36,7 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu *format-icons*: ++ typeof: array ++ - Based on the current port-name and volume, the corresponding icon gets selected. The order is *low* to *high*. See *Icons*. + Based on the current port name and volume, the corresponding icon gets selected. The order is *low* to *high*. See *Icons*. *rotate*: ++ typeof: integer ++ @@ -52,7 +52,7 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -61,7 +61,7 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu *scroll-step*: ++ typeof: float ++ default: 1.0 ++ - The speed in which to change the volume when scrolling. + The speed at which to change the volume when scrolling. *on-click*: ++ typeof: string ++ @@ -73,7 +73,7 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ @@ -107,7 +107,7 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu *ignored-sinks*: ++ typeof: array ++ - Sinks in this list will not be shown as the active sink by Waybar. Entries should be the sink's description field. + Sinks in this list will not be shown as active sink by Waybar. Entries should be the sink's description field. # FORMAT REPLACEMENTS diff --git a/man/waybar-river-layout.5.scd b/man/waybar-river-layout.5.scd index 2a1206ce..f6f682d0 100644 --- a/man/waybar-river-layout.5.scd +++ b/man/waybar-river-layout.5.scd @@ -29,7 +29,7 @@ Addressed by *river/layout* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -45,7 +45,7 @@ Addressed by *river/layout* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. # EXAMPLE diff --git a/man/waybar-river-mode.5.scd b/man/waybar-river-mode.5.scd index 8dfb0ec6..aea6e205 100644 --- a/man/waybar-river-mode.5.scd +++ b/man/waybar-river-mode.5.scd @@ -27,7 +27,7 @@ Addressed by *river/mode* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -43,7 +43,7 @@ Addressed by *river/mode* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ diff --git a/man/waybar-river-tags.5.scd b/man/waybar-river-tags.5.scd index b117546d..f0b2b84e 100644 --- a/man/waybar-river-tags.5.scd +++ b/man/waybar-river-tags.5.scd @@ -24,7 +24,7 @@ Addressed by *river/tags* *disable-click*: ++ typeof: bool ++ default: false ++ - If set to false, you can left click to set focused tag. Right click to toggle tag focus. If set to true this behaviour is disabled. + If set to false, you can left-click to set focused tag. Right-click to toggle tag focus. If set to true this behaviour is disabled. # EXAMPLE diff --git a/man/waybar-river-window.5.scd b/man/waybar-river-window.5.scd index 2f01e5c3..9c202b2a 100644 --- a/man/waybar-river-window.5.scd +++ b/man/waybar-river-window.5.scd @@ -27,7 +27,7 @@ Addressed by *river/window* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -43,7 +43,7 @@ Addressed by *river/window* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. # EXAMPLES diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd index 01471392..1bb0484a 100644 --- a/man/waybar-sndio.5.scd +++ b/man/waybar-sndio.5.scd @@ -28,7 +28,7 @@ cursor is over the module, and clicking on the module toggles mute. *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -37,7 +37,7 @@ cursor is over the module, and clicking on the module toggles mute. *scroll-step*: ++ typeof: int ++ default: 5 ++ - The speed in which to change the volume when scrolling. + The speed at which to change the volume when scrolling. *on-click*: ++ typeof: string ++ @@ -50,7 +50,7 @@ cursor is over the module, and clicking on the module toggles mute. *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index 2aca7b0c..87e70adf 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -27,7 +27,7 @@ Addressed by *sway/mode* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -43,7 +43,7 @@ Addressed by *sway/mode* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index ef137873..9b793f32 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -27,7 +27,7 @@ Addressed by *sway/window* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -43,7 +43,7 @@ Addressed by *sway/window* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ @@ -78,7 +78,7 @@ Addressed by *sway/window* *offscreen-css-text*: ++ typeof: string ++ - Only effective when both all-outputs and offscreen-style are true. On screens currently not focused, show the given text along with that workspaces styles. + Only effective when both all-outputs and offscreen-style are true. On screens currently not focused, show the given text along with that workspace styles. *show-focused-workspace-name*: ++ typeof: bool ++ @@ -106,7 +106,7 @@ Addressed by *sway/window* *{app_id}*: The app_id of the focused window. *{shell}*: The shell of the focused window. It's 'xwayland' when the window is -running through xwayland, otherwise it's 'xdg-shell'. +running through xwayland, otherwise, it's 'xdg-shell'. # REWRITE RULES diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 2441a936..cdb653f9 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -63,7 +63,7 @@ Addressed by *sway/workspaces* *persistent-workspaces*: ++ typeof: json (see below) ++ default: empty ++ - Lists workspaces that should always be shown, even when non existent + Lists workspaces that should always be shown, even when non-existent *on-update*: ++ typeof: string ++ @@ -98,11 +98,11 @@ warp-on-scroll: ++ Additional to workspace name matching, the following *format-icons* can be set. -- *default*: Will be shown, when no string matches is found. +- *default*: Will be shown, when no string matches are found. - *urgent*: Will be shown, when workspace is flagged as urgent - *focused*: Will be shown, when workspace is focused -- *persistent*: Will be shown, when workspace is persistent one. -- *high-priority-named*: Icons by names will be shown always for that workspaces, independent by state. +- *persistent*: Will be shown, when workspace is persistent. +- *high-priority-named*: Icons by names will be shown always for those workspaces, independent by state. # PERSISTENT WORKSPACES @@ -113,9 +113,9 @@ an empty list denoting all outputs. ``` "sway/workspaces": { "persistent-workspaces": { - "3": [], // Always show a workspace with name '3', on all outputs if it does not exists - "4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exists - "5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exists + "3": [], // Always show a workspace with name '3', on all outputs if it does not exist + "4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exist + "5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exist } } ``` diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 8cb7367c..1d6e7d2e 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -67,7 +67,7 @@ Addressed by *temperature* *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -75,7 +75,7 @@ Addressed by *temperature* *on-click*: ++ typeof: string ++ - Command to execute when you clicked on the module. + Command to execute when you click on the module. *on-click-middle*: ++ typeof: string ++ @@ -83,7 +83,7 @@ Addressed by *temperature* *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ diff --git a/man/waybar-wireplumber.5.scd b/man/waybar-wireplumber.5.scd index 4d13b4f1..5424deb6 100644 --- a/man/waybar-wireplumber.5.scd +++ b/man/waybar-wireplumber.5.scd @@ -43,7 +43,7 @@ The *wireplumber* module displays the current volume reported by WirePlumber. *min-length*: ++ typeof: integer ++ - The minimum length in characters the module should take up. + The minimum length in characters the module should accept. *align*: ++ typeof: float ++ @@ -52,7 +52,7 @@ The *wireplumber* module displays the current volume reported by WirePlumber. *scroll-step*: ++ typeof: float ++ default: 1.0 ++ - The speed in which to change the volume when scrolling. + The speed at which to change the volume when scrolling. *on-click*: ++ typeof: string ++ @@ -64,7 +64,7 @@ The *wireplumber* module displays the current volume reported by WirePlumber. *on-click-right*: ++ typeof: string ++ - Command to execute when you right clicked on the module. + Command to execute when you right-click on the module. *on-update*: ++ typeof: string ++ diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 6c724b08..af1ba97f 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -16,7 +16,7 @@ Addressed by *wlr/taskbar* *all-outputs*: ++ typeof: bool ++ default: false ++ - If set to false applications on the waybar's current output will be shown. Otherwise all applications are shown. + If set to false applications on the waybar's current output will be shown. Otherwise, all applications are shown. *format*: ++ typeof: string ++ @@ -89,7 +89,7 @@ Addressed by *wlr/taskbar* *{icon}*: The icon of the application. -*{name}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id} +*{name}*: The application name as in desktop file if appropriate desktop files are found, otherwise same as {app_id} *{title}*: The title of the application. diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-wlr-workspaces.5.scd index 1c9f2d89..62d3f636 100644 --- a/man/waybar-wlr-workspaces.5.scd +++ b/man/waybar-wlr-workspaces.5.scd @@ -30,17 +30,17 @@ Addressed by *wlr/workspaces* typeof: bool ++ default: true ++ Should workspaces be sorted by coordinates. ++ - Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first. If both are false - sort by id will be performed. + Note that if both *sort-by-name* and *sort-by-coordinates* are true sort-by name will be first. If both are false - sort by id will be performed. *sort-by-number*: ++ typeof: bool ++ default: false ++ - If set to true, workspace names will be sorted numerically. Takes presedence over any other sort-by option. + If set to true, workspace names will be sorted numerically. Takes precedence over any other sort-by option. *all-outputs*: ++ typeof: bool ++ default: false ++ - If set to false workspaces group will be shown only in assigned output. Otherwise all workspace groups are shown. + If set to false workspaces group will be shown only in assigned output. Otherwise, all workspace groups are shown. *active-only*: ++ typeof: bool ++ @@ -61,7 +61,7 @@ Addressed by *wlr/workspaces* # ICONS -Additional to workspace name matching, the following *format-icons* can be set. +In addition to workspace name matching, the following *format-icons* can be set. - *default*: Will be shown, when no string match is found. - *active*: Will be shown, when workspace is active diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 9c3220f8..e85033d4 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -17,7 +17,7 @@ Valid locations for this file are: - *@sysconfdir@/xdg/waybar/config* A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config -Also a minimal example configuration can be found on the at the bottom of this man page. +Also, a minimal example configuration can be found at the bottom of this man page. # BAR CONFIGURATION @@ -30,7 +30,7 @@ Also a minimal example configuration can be found on the at the bottom of this m *output* ++ typeof: string|array ++ Specifies on which screen this bar will be displayed. Exclamation mark(*!*) can be used to exclude specific output. - Output specification follows sway's and can either be the output port such as "HDMI-A-1" or a string consisting of the make, model and serial such as "Some Company ABC123 0x00000000". See *sway-output(5)* for details. + Output specification follows sway's and can either be the output port such as "HDMI-A-1" or a string consisting of the make, model, and serial such as "Some Company ABC123 0x00000000". See *sway-output(5)* for details. In an array, star '*\**' can be used at the end to accept all outputs, in case all previous entries are exclusions. *position* ++ @@ -68,7 +68,7 @@ Also a minimal example configuration can be found on the at the bottom of this m *spacing* ++ typeof: integer ++ - Size of gaps in between of the different modules. + Size of gaps in between the different modules. *name* ++ typeof: string ++ @@ -89,7 +89,7 @@ Also a minimal example configuration can be found on the at the bottom of this m default: *press* Defines the timing of modifier key to reset the bar visibility. To reset the visibility of the bar with the press of the modifier key use *press*. - Use *release* to reset the visibility upon the release of the modifier key and only if no other action happened while the key was pressed. This prevents hiding the bar when the modifier is used to switch a workspace, change binding mode or start a keybinding. + Use *release* to reset the visibility upon the release of the modifier key and only if no other action happened while the key was pressed. This prevents hiding the bar when the modifier is used to switch a workspace, change binding mode, or start a keybinding. *exclusive* ++ typeof: bool ++ @@ -118,11 +118,11 @@ Also a minimal example configuration can be found on the at the bottom of this m typeof: bool ++ default: false ++ Option to subscribe to the Sway IPC bar configuration and visibility events and control waybar with *swaymsg bar* commands. ++ - Requires *bar_id* value from sway configuration to be either passed with the *-b* commandline argument or specified with the *id* option. + Requires *bar_id* value from sway configuration to be either passed with the *-b* command line argument or specified with the *id* option. *id* ++ typeof: string ++ - *bar_id* for the Sway IPC. Use this if you need to override the value passed with the *-b bar_id* commandline argument for the specific bar instance. + *bar_id* for the Sway IPC. Use this if you need to override the value passed with the *-b bar_id* command line argument for the specific bar instance. *include* ++ typeof: string|array ++ @@ -142,7 +142,7 @@ e.g. # MULTIPLE INSTANCES OF A MODULE If you want to have a second instance of a module, you can suffix it by a '#' and a custom name. -For example if you want a second battery module, you can add *"battery#bat2"* to your modules. +For example, if you want a second battery module, you can add *"battery#bat2"* to your modules. To configure the newly added module, you then also add a module configuration with the same name. This could then look something like this *(this is an incomplete example)*: @@ -236,7 +236,7 @@ When positioning Waybar on the left or right side of the screen, sometimes it's } ``` -Valid options for the "rotate" property are: 0, 90, 180 and 270. +Valid options for the "rotate" property are: 0, 90, 180, and 270. ## Grouping modules From 2d614c68f585bd7c0c5df37d1f5017b57e33365b Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 21 Oct 2023 18:15:22 +0200 Subject: [PATCH 149/364] code review --- include/modules/hyprland/workspaces.hpp | 18 ++++++------- src/modules/hyprland/workspaces.cpp | 34 ++++++++++++------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 6ca2877b..f2ca74ed 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -26,13 +26,13 @@ namespace waybar::modules::hyprland { class Workspaces; -class WorkspaceWindow { +class WindowCreationPayload { public: - WorkspaceWindow(std::string workspace_name, WindowAddress window_address, - std::string window_repr); - WorkspaceWindow(std::string workspace_name, WindowAddress window_address, - std::string window_class, std::string window_title); - WorkspaceWindow(Json::Value const& client_data); + 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 increment_time_spent_uncreated(); bool is_empty(Workspaces& workspace_manager); @@ -83,11 +83,11 @@ class Workspace { void set_windows(uint value) { windows_ = value; }; void set_name(std::string const& value) { name_ = value; }; bool contains_window(WindowAddress const& addr) const { return window_map_.contains(addr); } - void insert_window(WorkspaceWindow create_window_paylod); + void insert_window(WindowCreationPayload create_window_paylod); std::string remove_window(WindowAddress const& addr); void initialize_window_map(const Json::Value& clients_data); - bool on_window_opened(WorkspaceWindow const& create_window_paylod); + bool on_window_opened(WindowCreationPayload const& create_window_paylod); std::optional on_window_closed(WindowAddress const& addr); void update(const std::string& format, const std::string& icon); @@ -178,7 +178,7 @@ class Workspaces : public AModule, public EventHandler { std::vector> workspaces_; std::vector workspaces_to_create_; std::vector workspaces_to_remove_; - std::vector windows_to_create_; + std::vector windows_to_create_; std::vector ignore_workspaces_; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index d9760e3e..e70ad46e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -14,10 +14,6 @@ namespace waybar::modules::hyprland { -namespace { -auto constexpr WINDOW_CREATION_TIMEOUT = 2; -} - int Workspaces::window_rewrite_priority_function(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 @@ -200,7 +196,7 @@ auto Workspaces::update() -> void { } bool any_window_created = false; - std::vector not_created; + std::vector not_created; for (auto &window_payload : windows_to_create_) { bool created = false; @@ -212,6 +208,7 @@ auto Workspaces::update() -> void { } } if (!created) { + static auto const WINDOW_CREATION_TIMEOUT = 2; if (window_payload.increment_time_spent_uncreated() < WINDOW_CREATION_TIMEOUT) { not_created.push_back(window_payload); } @@ -429,7 +426,7 @@ void Workspace::initialize_window_map(const Json::Value &clients_data) { } } -void Workspace::insert_window(WorkspaceWindow create_window_paylod) { +void Workspace::insert_window(WindowCreationPayload create_window_paylod) { if (!create_window_paylod.is_empty(workspace_manager_)) { window_map_[create_window_paylod.addr()] = create_window_paylod.repr(workspace_manager_); } @@ -441,7 +438,7 @@ std::string Workspace::remove_window(WindowAddress const &addr) { return window_repr; } -bool Workspace::on_window_opened(WorkspaceWindow const &create_window_paylod) { +bool Workspace::on_window_opened(WindowCreationPayload const &create_window_paylod) { if (create_window_paylod.workspace_name() == name()) { insert_window(create_window_paylod); return true; @@ -864,8 +861,8 @@ std::string Workspaces::get_rewrite(std::string window_class, std::string window return window_rewrite_rules_.get(window_repr_key); } -WorkspaceWindow::WorkspaceWindow(std::string workspace_name, WindowAddress window_address, - std::string window_repr) +WindowCreationPayload::WindowCreationPayload(std::string workspace_name, + WindowAddress window_address, std::string window_repr) : window_(std::move(window_repr)), window_address_(std::move(window_address)), workspace_name_(std::move(workspace_name)) { @@ -873,8 +870,9 @@ WorkspaceWindow::WorkspaceWindow(std::string workspace_name, WindowAddress windo clear_workspace_name(); } -WorkspaceWindow::WorkspaceWindow(std::string workspace_name, WindowAddress window_address, - std::string window_class, std::string window_title) +WindowCreationPayload::WindowCreationPayload(std::string workspace_name, + WindowAddress window_address, std::string window_class, + std::string window_title) : window_(std::make_pair(std::move(window_class), std::move(window_title))), window_address_(std::move(window_address)), workspace_name_(std::move(workspace_name)) { @@ -882,7 +880,7 @@ WorkspaceWindow::WorkspaceWindow(std::string workspace_name, WindowAddress windo clear_workspace_name(); } -WorkspaceWindow::WorkspaceWindow(Json::Value const &client_data) +WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data) : window_(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), window_address_(client_data["address"].asString()), workspace_name_(client_data["workspace"]["name"].asString()) { @@ -890,7 +888,7 @@ WorkspaceWindow::WorkspaceWindow(Json::Value const &client_data) clear_workspace_name(); } -std::string WorkspaceWindow::repr(Workspaces &workspace_manager) { +std::string WindowCreationPayload::repr(Workspaces &workspace_manager) { if (std::holds_alternative(window_)) { return std::get(window_); } @@ -903,7 +901,7 @@ std::string WorkspaceWindow::repr(Workspaces &workspace_manager) { throw std::runtime_error("WorkspaceWindow::repr: Unreachable"); } -bool WorkspaceWindow::is_empty(Workspaces &workspace_manager) { +bool WindowCreationPayload::is_empty(Workspaces &workspace_manager) { if (std::holds_alternative(window_)) { return std::get(window_).empty(); } @@ -917,9 +915,9 @@ bool WorkspaceWindow::is_empty(Workspaces &workspace_manager) { throw std::runtime_error("WorkspaceWindow::is_empty: Unreachable"); } -int WorkspaceWindow::increment_time_spent_uncreated() { return time_spent_uncreated_++; } +int WindowCreationPayload::increment_time_spent_uncreated() { return time_spent_uncreated_++; } -void WorkspaceWindow::clear_addr() { +void WindowCreationPayload::clear_addr() { // substr(2, ...) is necessary because Hyprland's JSON follows this format: // 0x{ADDR} // While Hyprland's IPC follows this format: @@ -933,7 +931,7 @@ void WorkspaceWindow::clear_addr() { } } -void WorkspaceWindow::clear_workspace_name() { +void WindowCreationPayload::clear_workspace_name() { // 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 @@ -948,7 +946,7 @@ void WorkspaceWindow::clear_workspace_name() { } } -void WorkspaceWindow::move_to_worksace(std::string &new_workspace_name) { +void WindowCreationPayload::move_to_worksace(std::string &new_workspace_name) { workspace_name_ = new_workspace_name; } From acc911737d9ea3a9a7b7f763a01c2873046c9f49 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 21 Oct 2023 18:53:53 +0200 Subject: [PATCH 150/364] update window count inside the on_window_* functions --- src/modules/hyprland/workspaces.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index e70ad46e..29a8868e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -289,13 +289,10 @@ void Workspaces::onEvent(const std::string &ev) { workspaces_to_remove_.push_back(workspace); } } else if (eventName == "openwindow") { - update_window_count(); on_window_opened(payload); } else if (eventName == "closewindow") { - update_window_count(); on_window_closed(payload); } else if (eventName == "movewindow") { - update_window_count(); on_window_moved(payload); } else if (eventName == "urgent") { set_urgent_workspace(payload); @@ -336,6 +333,7 @@ void Workspaces::onEvent(const std::string &ev) { } void Workspaces::on_window_opened(std::string const &payload) { + update_window_count(); size_t last_comma_idx = 0; size_t next_comma_idx = payload.find(','); std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx); @@ -356,6 +354,7 @@ void Workspaces::on_window_opened(std::string const &payload) { } void Workspaces::on_window_closed(std::string const &addr) { + update_window_count(); for (auto &workspace : workspaces_) { if (workspace->on_window_closed(addr)) { break; @@ -364,6 +363,7 @@ void Workspaces::on_window_closed(std::string const &addr) { } void Workspaces::on_window_moved(std::string const &payload) { + update_window_count(); size_t last_comma_idx = 0; size_t next_comma_idx = payload.find(','); std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx); From dab1493644582fb36ecdcb233fc7bfad6ec9fb40 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:33:55 +0200 Subject: [PATCH 151/364] cleanup onEvent, dont use try/catch for flow control --- include/modules/hyprland/workspaces.hpp | 17 ++- src/modules/hyprland/workspaces.cpp | 169 ++++++++++++++---------- 2 files changed, 111 insertions(+), 75 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index f2ca74ed..53bc55fc 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -88,7 +88,7 @@ class Workspace { void initialize_window_map(const Json::Value& clients_data); bool on_window_opened(WindowCreationPayload const& create_window_paylod); - std::optional on_window_closed(WindowAddress const& addr); + std::optional close_window(WindowAddress const& addr); void update(const std::string& format, const std::string& icon); @@ -127,7 +127,7 @@ class Workspaces : public AModule, public EventHandler { std::string get_rewrite(std::string window_class, std::string window_title); std::string& get_window_separator() { return format_window_separator_; } - bool is_workspace_ignored(std::string& workspace_name); + bool is_workspace_ignored(std::string const& workspace_name); bool window_rewrite_config_uses_title() const { return any_window_rewrite_rule_uses_title_; } @@ -142,10 +142,23 @@ class Workspaces : public AModule, public EventHandler { void parse_config(const Json::Value& config); void register_ipc(); + // workspace events + void on_workspace_activated(std::string const& payload); + void on_workspace_destroyed(std::string const& payload); + void on_workspace_created(std::string const& payload); + void on_workspace_moved(std::string const& payload); + void on_workspace_renamed(std::string const& payload); + + // monitor events + void on_monitor_focused(std::string const& payload); + + // window events void on_window_opened(std::string const& payload); void on_window_closed(std::string const& payload); void on_window_moved(std::string const& payload); + void on_window_title_event(std::string const& payload); + int window_rewrite_priority_function(std::string const& window_rule); bool all_outputs_ = false; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 29a8868e..aa84b454 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -225,7 +225,7 @@ auto Workspaces::update() -> void { AModule::update(); } -bool isDoubleSpecial(std::string &workspace_name) { +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. @@ -233,7 +233,7 @@ bool isDoubleSpecial(std::string &workspace_name) { return workspace_name.find("special:special:") != std::string::npos; } -bool Workspaces::is_workspace_ignored(std::string &name) { +bool Workspaces::is_workspace_ignored(std::string const &name) { for (auto &rule : ignore_workspaces_) { if (std::regex_match(name, rule)) { return true; @@ -250,44 +250,15 @@ void Workspaces::onEvent(const std::string &ev) { std::string payload = ev.substr(eventName.size() + 2); if (eventName == "workspace") { - active_workspace_name_ = payload; - + on_workspace_activated(payload); } else if (eventName == "destroyworkspace") { - if (!isDoubleSpecial(payload)) { - workspaces_to_remove_.push_back(payload); - } + on_workspace_destroyed(payload); } else if (eventName == "createworkspace") { - const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); - - if (!is_workspace_ignored(payload)) { - for (Json::Value workspace_json : workspaces_json) { - std::string name = workspace_json["name"].asString(); - if (name == payload && - (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (show_special() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { - workspaces_to_create_.push_back(workspace_json); - break; - } - } - } + on_workspace_created(payload); } else if (eventName == "focusedmon") { - active_workspace_name_ = payload.substr(payload.find(',') + 1); - + on_monitor_focused(payload); } else if (eventName == "moveworkspace" && !all_outputs()) { - std::string workspace = payload.substr(0, payload.find(',')); - std::string new_output = payload.substr(payload.find(',') + 1); - if (bar_.output->name == new_output) { // TODO: implement this better - const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); - for (Json::Value workspace_json : workspaces_json) { - std::string name = workspace_json["name"].asString(); - if (name == workspace && bar_.output->name == workspace_json["monitor"].asString()) { - workspaces_to_create_.push_back(workspace_json); - break; - } - } - } else { - workspaces_to_remove_.push_back(workspace); - } + on_workspace_moved(payload); } else if (eventName == "openwindow") { on_window_opened(payload); } else if (eventName == "closewindow") { @@ -297,41 +268,76 @@ void Workspaces::onEvent(const std::string &ev) { } else if (eventName == "urgent") { set_urgent_workspace(payload); } else if (eventName == "renameworkspace") { - std::string workspace_id_str = payload.substr(0, payload.find(',')); - int workspace_id = workspace_id_str == "special" ? -99 : std::stoi(workspace_id_str); - std::string new_name = payload.substr(payload.find(',') + 1); - for (auto &workspace : workspaces_) { - if (workspace->id() == workspace_id) { - if (workspace->name() == active_workspace_name_) { - active_workspace_name_ = new_name; - } - workspace->set_name(new_name); - break; - } - } + on_workspace_renamed(payload); } else if (eventName == "windowtitle") { - auto window_workspace = - std::find_if(workspaces_.begin(), workspaces_.end(), - [payload](auto &workspace) { return workspace->contains_window(payload); }); - - if (window_workspace != workspaces_.end()) { - Json::Value clients_data = gIPC->getSocket1JsonReply("clients"); - std::string json_window_address = fmt::format("0x{}", payload); - - auto client = std::find_if(clients_data.begin(), clients_data.end(), - [json_window_address](auto &client) { - return client["address"].asString() == json_window_address; - }); - - if (!client->empty()) { - (*window_workspace)->insert_window({*client}); - } - } + on_window_title_event(payload); } dp.emit(); } +void Workspaces::on_workspace_activated(std::string const &payload) { + active_workspace_name_ = payload; +} + +void Workspaces::on_workspace_destroyed(std::string const &payload) { + if (!isDoubleSpecial(payload)) { + workspaces_to_remove_.push_back(payload); + } +} + +void Workspaces::on_workspace_created(std::string const &payload) { + const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); + + if (!is_workspace_ignored(payload)) { + for (Json::Value workspace_json : workspaces_json) { + std::string name = workspace_json["name"].asString(); + if (name == payload && + (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && + (show_special() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { + workspaces_to_create_.push_back(workspace_json); + break; + } + } + } +} + +void Workspaces::on_workspace_moved(std::string const &payload) { + std::string workspace = payload.substr(0, payload.find(',')); + std::string new_output = payload.substr(payload.find(',') + 1); + if (bar_.output->name == new_output) { // TODO: implement this better + const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); + for (Json::Value workspace_json : workspaces_json) { + std::string name = workspace_json["name"].asString(); + if (name == workspace && bar_.output->name == workspace_json["monitor"].asString()) { + workspaces_to_create_.push_back(workspace_json); + break; + } + } + } else { + workspaces_to_remove_.push_back(workspace); + } +} + +void Workspaces::on_workspace_renamed(std::string const &payload) { + std::string workspace_id_str = payload.substr(0, payload.find(',')); + int workspace_id = workspace_id_str == "special" ? -99 : std::stoi(workspace_id_str); + std::string new_name = payload.substr(payload.find(',') + 1); + for (auto &workspace : workspaces_) { + if (workspace->id() == workspace_id) { + if (workspace->name() == active_workspace_name_) { + active_workspace_name_ = new_name; + } + workspace->set_name(new_name); + break; + } + } +} + +void Workspaces::on_monitor_focused(std::string const &payload) { + active_workspace_name_ = payload.substr(payload.find(',') + 1); +} + void Workspaces::on_window_opened(std::string const &payload) { update_window_count(); size_t last_comma_idx = 0; @@ -356,7 +362,7 @@ void Workspaces::on_window_opened(std::string const &payload) { void Workspaces::on_window_closed(std::string const &addr) { update_window_count(); for (auto &workspace : workspaces_) { - if (workspace->on_window_closed(addr)) { + if (workspace->close_window(addr)) { break; } } @@ -384,12 +390,9 @@ void Workspaces::on_window_moved(std::string const &payload) { // Take the window's representation from the old workspace... for (auto &workspace : workspaces_) { - try { - window_repr = workspace->on_window_closed(window_address).value(); + if (auto window_addr = workspace->close_window(window_address); window_addr != std::nullopt) { + window_repr = window_addr.value(); break; - } catch (const std::bad_optional_access &e) { - // window was not found in this workspace - continue; } } @@ -399,6 +402,26 @@ void Workspaces::on_window_moved(std::string const &payload) { } } +void Workspaces::on_window_title_event(std::string const &payload) { + auto window_workspace = + std::find_if(workspaces_.begin(), workspaces_.end(), + [payload](auto &workspace) { return workspace->contains_window(payload); }); + + if (window_workspace != workspaces_.end()) { + Json::Value clients_data = gIPC->getSocket1JsonReply("clients"); + std::string json_window_address = fmt::format("0x{}", payload); + + auto client = + std::find_if(clients_data.begin(), clients_data.end(), [json_window_address](auto &client) { + return client["address"].asString() == json_window_address; + }); + + if (!client->empty()) { + (*window_workspace)->insert_window({*client}); + } + } +} + void Workspaces::update_window_count() { const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : workspaces_) { @@ -446,11 +469,11 @@ bool Workspace::on_window_opened(WindowCreationPayload const &create_window_payl return false; } -std::optional Workspace::on_window_closed(WindowAddress const &addr) { +std::optional Workspace::close_window(WindowAddress const &addr) { if (window_map_.contains(addr)) { return remove_window(addr); } - return {}; + return std::nullopt; } void Workspaces::create_workspace(Json::Value const &workspace_data, From 5963bf6ace601678615f9feb381e0721bcfd7804 Mon Sep 17 00:00:00 2001 From: Rehan Date: Sat, 21 Oct 2023 17:39:45 -0400 Subject: [PATCH 152/364] modules/mpris: change default interval value to 0 --- man/waybar-mpris.5.scd | 1 + src/modules/mpris/mpris.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 35d7bd6f..6e147f37 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -21,6 +21,7 @@ The *mpris* module displays currently playing media via libplayerctl. *interval*: ++ typeof: integer ++ + default: 0 ++ Refresh MPRIS information on a timer. *format*: ++ diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 685b1881..02138af7 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -20,7 +20,7 @@ namespace waybar::modules::mpris { const std::string DEFAULT_FORMAT = "{player} ({status}): {dynamic}"; Mpris::Mpris(const std::string& id, const Json::Value& config) - : ALabel(config, "mpris", id, DEFAULT_FORMAT, 5, false, true), + : ALabel(config, "mpris", id, DEFAULT_FORMAT, 0, false, true), tooltip_(DEFAULT_FORMAT), artist_len_(-1), album_len_(-1), From 2d33c20231a5baf3a427837ceead9046d5152f96 Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Sun, 22 Oct 2023 09:44:46 +0200 Subject: [PATCH 153/364] Revert "Fix potential memory leaks" --- include/modules/upower/upower.hpp | 2 +- include/modules/upower/upower_tooltip.hpp | 3 +-- include/util/scope_guard.hpp | 21 ---------------- src/ALabel.cpp | 2 +- src/modules/bluetooth.cpp | 8 +----- src/modules/cpu_usage/bsd.cpp | 7 +----- src/modules/custom.cpp | 8 +----- src/modules/mpris/mpris.cpp | 30 ++++++++--------------- src/modules/sni/host.cpp | 16 +++--------- src/modules/sni/watcher.cpp | 8 +----- src/modules/upower/upower.cpp | 8 +++--- src/modules/upower/upower_tooltip.cpp | 3 ++- src/util/prepare_for_sleep.cpp | 3 ++- 13 files changed, 29 insertions(+), 90 deletions(-) delete mode 100644 include/util/scope_guard.hpp diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index 9c1a1830..446d1f53 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -69,7 +69,7 @@ class UPower : public AModule { UpDevice *displayDevice; guint login1_id; GDBusConnection *login1_connection; - std::unique_ptr upower_tooltip; + UPowerTooltip *upower_tooltip; std::string lastStatus; bool showAltText; bool upowerRunning; diff --git a/include/modules/upower/upower_tooltip.hpp b/include/modules/upower/upower_tooltip.hpp index bc99abed..05e9dcb3 100644 --- a/include/modules/upower/upower_tooltip.hpp +++ b/include/modules/upower/upower_tooltip.hpp @@ -2,7 +2,6 @@ #include -#include #include #include "gtkmm/box.h" @@ -17,7 +16,7 @@ class UPowerTooltip : public Gtk::Window { const std::string getDeviceIcon(UpDeviceKind& kind); - std::unique_ptr contentBox; + Gtk::Box* contentBox; uint iconSize; uint tooltipSpacing; diff --git a/include/util/scope_guard.hpp b/include/util/scope_guard.hpp deleted file mode 100644 index 5c717704..00000000 --- a/include/util/scope_guard.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -namespace waybar::util { - -template -class scope_guard { - public: - explicit scope_guard(Func&& exit_function) : f{std::forward(exit_function)} {} - scope_guard(const scope_guard&) = delete; - scope_guard(scope_guard&&) = default; - scope_guard& operator=(const scope_guard&) = delete; - scope_guard& operator=(scope_guard&&) = default; - ~scope_guard() { f(); } - - private: - Func f; -}; - -} // namespace waybar::util diff --git a/src/ALabel.cpp b/src/ALabel.cpp index c87e3228..4d8b2218 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -12,7 +12,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st : 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(100000000) : std::chrono::seconds( config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)), default_format_(format_) { diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 6b6cb7ef..9e207507 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -6,19 +6,12 @@ #include #include -#include "util/scope_guard.hpp" - namespace { using GDBusManager = std::unique_ptr; auto generateManager() -> GDBusManager { GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { - if (error) { - g_error_free(error); - } - }); GDBusObjectManager* manager = g_dbus_object_manager_client_new_for_bus_sync( G_BUS_TYPE_SYSTEM, GDBusObjectManagerClientFlags::G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, @@ -26,6 +19,7 @@ auto generateManager() -> GDBusManager { if (error) { spdlog::error("g_dbus_object_manager_client_new_for_bus_sync() failed: {}", error->message); + g_error_free(error); } auto destructor = [](GDBusObjectManager* manager) { diff --git a/src/modules/cpu_usage/bsd.cpp b/src/modules/cpu_usage/bsd.cpp index 663bc0a7..c987a770 100644 --- a/src/modules/cpu_usage/bsd.cpp +++ b/src/modules/cpu_usage/bsd.cpp @@ -9,7 +9,6 @@ #include // malloc #include "modules/cpu_usage.hpp" -#include "util/scope_guard.hpp" #if defined(__NetBSD__) || defined(__OpenBSD__) #include @@ -34,11 +33,6 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( int ncpu = sysconf(_SC_NPROCESSORS_CONF); size_t sz = CPUSTATES * (ncpu + 1) * sizeof(pcp_time_t); pcp_time_t *cp_time = static_cast(malloc(sz)), *pcp_time = cp_time; - waybar::util::scope_guard cp_time_deleter([cp_time]() { - if (cp_time) { - free(cp_time); - } - }); #if defined(__NetBSD__) int mib[] = { CTL_KERN, @@ -103,5 +97,6 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( } cpuinfo.emplace_back(single_cp_time[CP_IDLE], total); } + free(cp_time); return cpuinfo; } diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index d0a39802..4889b7a3 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -2,8 +2,6 @@ #include -#include "util/scope_guard.hpp" - waybar::modules::Custom::Custom(const std::string& name, const std::string& id, const Json::Value& config) : ALabel(config, "custom-" + name, id, "{}"), @@ -59,11 +57,6 @@ void waybar::modules::Custom::continuousWorker() { } thread_ = [this, cmd] { char* buff = nullptr; - waybar::util::scope_guard buff_deleter([buff]() { - if (buff) { - free(buff); - } - }); size_t len = 0; if (getline(&buff, &len, fp_) == -1) { int exit_code = 1; @@ -97,6 +90,7 @@ void waybar::modules::Custom::continuousWorker() { output_ = {0, output}; dp.emit(); } + free(buff); }; } diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 685b1881..140bc785 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -6,8 +6,6 @@ #include #include -#include "util/scope_guard.hpp" - extern "C" { #include } @@ -119,11 +117,6 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { - if (error) { - g_error_free(error); - } - }); manager = playerctl_player_manager_new(&error); if (error) { throw std::runtime_error(fmt::format("unable to create MPRIS client: {}", error->message)); @@ -143,7 +136,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } else { GList* players = playerctl_list_players(&error); if (error) { - throw std::runtime_error(fmt::format("unable to list players: {}", error->message)); + auto e = fmt::format("unable to list players: {}", error->message); + g_error_free(error); + throw std::runtime_error(e); } for (auto p = players; p != NULL; p = p->next) { @@ -415,7 +410,8 @@ auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlaye return; } - mpris->player = playerctl_player_new_from_name(player_name, NULL); + GError* error = nullptr; + mpris->player = playerctl_player_new_from_name(player_name, &error); 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", @@ -482,11 +478,6 @@ auto Mpris::getPlayerInfo() -> std::optional { } GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { - if (error) { - g_error_free(error); - } - }); char* player_status = nullptr; auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED; @@ -496,7 +487,9 @@ auto Mpris::getPlayerInfo() -> std::optional { if (player_name == "playerctld") { GList* players = playerctl_list_players(&error); if (error) { - throw std::runtime_error(fmt::format("unable to list players: {}", error->message)); + auto e = fmt::format("unable to list players: {}", error->message); + g_error_free(error); + throw std::runtime_error(e); } // > get the list of players [..] in order of activity // https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249 @@ -575,16 +568,12 @@ auto Mpris::getPlayerInfo() -> std::optional { errorexit: spdlog::error("mpris[{}]: {}", info.name, error->message); + g_error_free(error); return std::nullopt; } bool Mpris::handleToggle(GdkEventButton* const& e) { GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { - if (error) { - g_error_free(error); - } - }); auto info = getPlayerInfo(); if (!info) return false; @@ -614,6 +603,7 @@ bool Mpris::handleToggle(GdkEventButton* const& e) { if (error) { spdlog::error("mpris[{}]: error running builtin on-click action: {}", (*info).name, error->message); + g_error_free(error); return false; } return true; diff --git a/src/modules/sni/host.cpp b/src/modules/sni/host.cpp index 136c2941..0bbd4d2f 100644 --- a/src/modules/sni/host.cpp +++ b/src/modules/sni/host.cpp @@ -2,8 +2,6 @@ #include -#include "util/scope_guard.hpp" - namespace waybar::modules::SNI { Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar, @@ -59,20 +57,17 @@ void Host::nameVanished(const Glib::RefPtr& conn, const G void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { - if (error != nullptr) { - g_error_free(error); - } - }); SnWatcher* watcher = sn_watcher_proxy_new_finish(res, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { spdlog::error("Host: {}", error->message); + g_error_free(error); return; } auto host = static_cast(data); host->watcher_ = watcher; if (error != nullptr) { spdlog::error("Host: {}", error->message); + g_error_free(error); return; } sn_watcher_call_register_host(host->watcher_, host->object_path_.c_str(), host->cancellable_, @@ -81,19 +76,16 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { - if (error != nullptr) { - g_error_free(error); - } - }); sn_watcher_call_register_host_finish(SN_WATCHER(src), res, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { spdlog::error("Host: {}", error->message); + g_error_free(error); return; } auto host = static_cast(data); if (error != nullptr) { spdlog::error("Host: {}", error->message); + g_error_free(error); return; } g_signal_connect(host->watcher_, "item-registered", G_CALLBACK(&Host::itemRegistered), data); diff --git a/src/modules/sni/watcher.cpp b/src/modules/sni/watcher.cpp index 22434709..dfd076ef 100644 --- a/src/modules/sni/watcher.cpp +++ b/src/modules/sni/watcher.cpp @@ -2,8 +2,6 @@ #include -#include "util/scope_guard.hpp" - using namespace waybar::modules::SNI; Watcher::Watcher() @@ -31,11 +29,6 @@ Watcher::~Watcher() { void Watcher::busAcquired(const Glib::RefPtr& conn, Glib::ustring name) { GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { - if (error) { - g_error_free(error); - } - }); g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(watcher_), conn->gobj(), "/StatusNotifierWatcher", &error); if (error != nullptr) { @@ -43,6 +36,7 @@ void Watcher::busAcquired(const Glib::RefPtr& conn, Glib: if (error->code != 2) { spdlog::error("Watcher: {}", error->message); } + g_error_free(error); return; } g_signal_connect_swapped(watcher_, "handle-register-item", diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index e3b3981a..f2bc621d 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -63,7 +63,7 @@ UPower::UPower(const std::string& id, const Json::Value& config) 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); + upower_tooltip = new UPowerTooltip(iconSize, tooltip_spacing, tooltip_padding); box_.set_tooltip_window(*upower_tooltip); box_.signal_query_tooltip().connect(sigc::mem_fun(*this, &UPower::show_tooltip_callback)); } @@ -72,13 +72,14 @@ UPower::UPower(const std::string& id, const Json::Value& config) G_BUS_NAME_WATCHER_FLAGS_AUTO_START, upowerAppear, upowerDisappear, this, NULL); - client = up_client_new_full(NULL, NULL); + GError* error = NULL; + client = up_client_new_full(NULL, &error); 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); + login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); if (!login1_connection) { throw std::runtime_error("Unable to connect to the SYSTEM Bus!..."); } else { @@ -98,7 +99,6 @@ UPower::UPower(const std::string& id, const Json::Value& config) } 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); diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp index 1a653f85..45544bbc 100644 --- a/src/modules/upower/upower_tooltip.cpp +++ b/src/modules/upower/upower_tooltip.cpp @@ -9,10 +9,11 @@ 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_) { + contentBox = new Gtk::Box(Gtk::ORIENTATION_VERTICAL); + // Sets the Tooltip Padding contentBox->set_margin_top(tooltipPadding); contentBox->set_margin_bottom(tooltipPadding); diff --git a/src/util/prepare_for_sleep.cpp b/src/util/prepare_for_sleep.cpp index 661285a2..218c1e29 100644 --- a/src/util/prepare_for_sleep.cpp +++ b/src/util/prepare_for_sleep.cpp @@ -7,7 +7,8 @@ namespace { class PrepareForSleep { private: PrepareForSleep() { - login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); + GError *error = NULL; + login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); if (!login1_connection) { spdlog::warn("Unable to connect to the SYSTEM Bus!..."); } else { From 4aee5977d6dd107f5063572533ab6b3d55f5a427 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 23 Oct 2023 21:07:25 -0300 Subject: [PATCH 154/364] fix: set workspace as persistent on create_workspace --- src/modules/hyprland/workspaces.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index aa84b454..684c2b92 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -487,6 +487,9 @@ void Workspaces::create_workspace(Json::Value const &workspace_data, }); if (workspace != workspaces_.end()) { + if (workspace_data["persistent"].asBool() and !(*workspace)->is_persistent()) { + (*workspace)->set_persistent(); + } return; } From ad7d4eb07dbc3a11deee5a64151dda935edc7c6f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 23 Oct 2023 00:54:01 +0200 Subject: [PATCH 155/364] sleeper_thread: Allow sleep_for with max duration The standard library has the implicit requirement that for std::condition_variable::sleep_for() the duration must not cause an overflow if added to the current time. This commit will reduce the duration accordingly to fit into the duration type. --- include/util/sleeper_thread.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/util/sleeper_thread.hpp b/include/util/sleeper_thread.hpp index 80acf169..4eee0aec 100644 --- a/include/util/sleeper_thread.hpp +++ b/include/util/sleeper_thread.hpp @@ -67,6 +67,12 @@ class SleeperThread { auto sleep_for(std::chrono::system_clock::duration dur) { std::unique_lock lk(mutex_); CancellationGuard cancel_lock; + using timepoint = std::chrono::system_clock::time_point; + static_assert(std::numeric_limits::max() >= + std::numeric_limits::max()); + if (std::chrono::system_clock::now() >= timepoint::max() - dur) { + dur = timepoint::max() - std::chrono::system_clock::now() - std::chrono::seconds(1); + } return condvar_.wait_for(lk, dur, [this] { return signal_ || !do_run_; }); } From 521dac80867a67e794ad6613d01e0dae04ea208a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 23 Oct 2023 01:11:38 +0200 Subject: [PATCH 156/364] sleeper_thread: Make sleep_for more robust In the previous fix for a passed max duration, the assumption was made that at maximum one second will pass between the duration assignment and the std::condition_variable::sleep_for() call. This implementation makes the behavior more predictable by using sleep_until() instead to emulate the sleep_for() behavior. --- include/util/sleeper_thread.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/util/sleeper_thread.hpp b/include/util/sleeper_thread.hpp index 4eee0aec..62d12931 100644 --- a/include/util/sleeper_thread.hpp +++ b/include/util/sleeper_thread.hpp @@ -67,13 +67,13 @@ class SleeperThread { auto sleep_for(std::chrono::system_clock::duration dur) { std::unique_lock lk(mutex_); CancellationGuard cancel_lock; - using timepoint = std::chrono::system_clock::time_point; - static_assert(std::numeric_limits::max() >= - std::numeric_limits::max()); - if (std::chrono::system_clock::now() >= timepoint::max() - dur) { - dur = timepoint::max() - std::chrono::system_clock::now() - std::chrono::seconds(1); + constexpr auto max_time_point = std::chrono::steady_clock::time_point::max(); + auto wait_end = max_time_point; + auto now = std::chrono::steady_clock::now(); + if (now < max_time_point - dur) { + wait_end = now + dur; } - return condvar_.wait_for(lk, dur, [this] { return signal_ || !do_run_; }); + return condvar_.wait_until(lk, wait_end, [this] { return signal_ || !do_run_; }); } auto sleep_until( From dd1de3efbff2badb3d3c5ae0813dcd7bf6c80036 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 23 Oct 2023 01:14:52 +0200 Subject: [PATCH 157/364] Revert "Revert "Fix potential memory leaks"" This reverts commit 2d33c20231a5baf3a427837ceead9046d5152f96 and reapplies various patches for memory leaks. The reason for the revert was a bug for a maximum duration interval which caused sleep_for() to cause unpredictable behavior. --- include/modules/upower/upower.hpp | 2 +- include/modules/upower/upower_tooltip.hpp | 3 ++- include/util/scope_guard.hpp | 21 ++++++++++++++++ src/ALabel.cpp | 2 +- src/modules/bluetooth.cpp | 8 +++++- src/modules/cpu_usage/bsd.cpp | 7 +++++- src/modules/custom.cpp | 8 +++++- src/modules/mpris/mpris.cpp | 30 +++++++++++++++-------- src/modules/sni/host.cpp | 16 +++++++++--- src/modules/sni/watcher.cpp | 8 +++++- src/modules/upower/upower.cpp | 8 +++--- src/modules/upower/upower_tooltip.cpp | 3 +-- src/util/prepare_for_sleep.cpp | 3 +-- 13 files changed, 90 insertions(+), 29 deletions(-) create mode 100644 include/util/scope_guard.hpp diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index 446d1f53..9c1a1830 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -69,7 +69,7 @@ class UPower : public AModule { UpDevice *displayDevice; guint login1_id; GDBusConnection *login1_connection; - UPowerTooltip *upower_tooltip; + std::unique_ptr upower_tooltip; std::string lastStatus; bool showAltText; bool upowerRunning; diff --git a/include/modules/upower/upower_tooltip.hpp b/include/modules/upower/upower_tooltip.hpp index 05e9dcb3..bc99abed 100644 --- a/include/modules/upower/upower_tooltip.hpp +++ b/include/modules/upower/upower_tooltip.hpp @@ -2,6 +2,7 @@ #include +#include #include #include "gtkmm/box.h" @@ -16,7 +17,7 @@ class UPowerTooltip : public Gtk::Window { const std::string getDeviceIcon(UpDeviceKind& kind); - Gtk::Box* contentBox; + std::unique_ptr contentBox; uint iconSize; uint tooltipSpacing; diff --git a/include/util/scope_guard.hpp b/include/util/scope_guard.hpp new file mode 100644 index 00000000..5c717704 --- /dev/null +++ b/include/util/scope_guard.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace waybar::util { + +template +class scope_guard { + public: + explicit scope_guard(Func&& exit_function) : f{std::forward(exit_function)} {} + scope_guard(const scope_guard&) = delete; + scope_guard(scope_guard&&) = default; + scope_guard& operator=(const scope_guard&) = delete; + scope_guard& operator=(scope_guard&&) = default; + ~scope_guard() { f(); } + + private: + Func f; +}; + +} // namespace waybar::util diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 4d8b2218..c87e3228 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -12,7 +12,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st : 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(100000000) + ? std::chrono::seconds::max() : std::chrono::seconds( config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)), default_format_(format_) { diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 9e207507..6b6cb7ef 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -6,12 +6,19 @@ #include #include +#include "util/scope_guard.hpp" + namespace { using GDBusManager = std::unique_ptr; auto generateManager() -> GDBusManager { GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error) { + g_error_free(error); + } + }); GDBusObjectManager* manager = g_dbus_object_manager_client_new_for_bus_sync( G_BUS_TYPE_SYSTEM, GDBusObjectManagerClientFlags::G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, @@ -19,7 +26,6 @@ auto generateManager() -> GDBusManager { if (error) { spdlog::error("g_dbus_object_manager_client_new_for_bus_sync() failed: {}", error->message); - g_error_free(error); } auto destructor = [](GDBusObjectManager* manager) { diff --git a/src/modules/cpu_usage/bsd.cpp b/src/modules/cpu_usage/bsd.cpp index c987a770..663bc0a7 100644 --- a/src/modules/cpu_usage/bsd.cpp +++ b/src/modules/cpu_usage/bsd.cpp @@ -9,6 +9,7 @@ #include // malloc #include "modules/cpu_usage.hpp" +#include "util/scope_guard.hpp" #if defined(__NetBSD__) || defined(__OpenBSD__) #include @@ -33,6 +34,11 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( int ncpu = sysconf(_SC_NPROCESSORS_CONF); size_t sz = CPUSTATES * (ncpu + 1) * sizeof(pcp_time_t); pcp_time_t *cp_time = static_cast(malloc(sz)), *pcp_time = cp_time; + waybar::util::scope_guard cp_time_deleter([cp_time]() { + if (cp_time) { + free(cp_time); + } + }); #if defined(__NetBSD__) int mib[] = { CTL_KERN, @@ -97,6 +103,5 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( } cpuinfo.emplace_back(single_cp_time[CP_IDLE], total); } - free(cp_time); return cpuinfo; } diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 4889b7a3..d0a39802 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -2,6 +2,8 @@ #include +#include "util/scope_guard.hpp" + waybar::modules::Custom::Custom(const std::string& name, const std::string& id, const Json::Value& config) : ALabel(config, "custom-" + name, id, "{}"), @@ -57,6 +59,11 @@ void waybar::modules::Custom::continuousWorker() { } thread_ = [this, cmd] { char* buff = nullptr; + waybar::util::scope_guard buff_deleter([buff]() { + if (buff) { + free(buff); + } + }); size_t len = 0; if (getline(&buff, &len, fp_) == -1) { int exit_code = 1; @@ -90,7 +97,6 @@ void waybar::modules::Custom::continuousWorker() { output_ = {0, output}; dp.emit(); } - free(buff); }; } diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 9c17f1e8..02138af7 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -6,6 +6,8 @@ #include #include +#include "util/scope_guard.hpp" + extern "C" { #include } @@ -117,6 +119,11 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error) { + g_error_free(error); + } + }); manager = playerctl_player_manager_new(&error); if (error) { throw std::runtime_error(fmt::format("unable to create MPRIS client: {}", error->message)); @@ -136,9 +143,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } else { GList* players = playerctl_list_players(&error); if (error) { - auto e = fmt::format("unable to list players: {}", error->message); - g_error_free(error); - throw std::runtime_error(e); + throw std::runtime_error(fmt::format("unable to list players: {}", error->message)); } for (auto p = players; p != NULL; p = p->next) { @@ -410,8 +415,7 @@ auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlaye return; } - GError* error = nullptr; - mpris->player = playerctl_player_new_from_name(player_name, &error); + mpris->player = playerctl_player_new_from_name(player_name, NULL); 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", @@ -478,6 +482,11 @@ auto Mpris::getPlayerInfo() -> std::optional { } GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error) { + g_error_free(error); + } + }); char* player_status = nullptr; auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED; @@ -487,9 +496,7 @@ auto Mpris::getPlayerInfo() -> std::optional { if (player_name == "playerctld") { GList* players = playerctl_list_players(&error); if (error) { - auto e = fmt::format("unable to list players: {}", error->message); - g_error_free(error); - throw std::runtime_error(e); + throw std::runtime_error(fmt::format("unable to list players: {}", error->message)); } // > get the list of players [..] in order of activity // https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249 @@ -568,12 +575,16 @@ auto Mpris::getPlayerInfo() -> std::optional { errorexit: spdlog::error("mpris[{}]: {}", info.name, error->message); - g_error_free(error); return std::nullopt; } bool Mpris::handleToggle(GdkEventButton* const& e) { GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error) { + g_error_free(error); + } + }); auto info = getPlayerInfo(); if (!info) return false; @@ -603,7 +614,6 @@ bool Mpris::handleToggle(GdkEventButton* const& e) { if (error) { spdlog::error("mpris[{}]: error running builtin on-click action: {}", (*info).name, error->message); - g_error_free(error); return false; } return true; diff --git a/src/modules/sni/host.cpp b/src/modules/sni/host.cpp index 0bbd4d2f..136c2941 100644 --- a/src/modules/sni/host.cpp +++ b/src/modules/sni/host.cpp @@ -2,6 +2,8 @@ #include +#include "util/scope_guard.hpp" + namespace waybar::modules::SNI { Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar, @@ -57,17 +59,20 @@ void Host::nameVanished(const Glib::RefPtr& conn, const G void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error != nullptr) { + g_error_free(error); + } + }); SnWatcher* watcher = sn_watcher_proxy_new_finish(res, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { spdlog::error("Host: {}", error->message); - g_error_free(error); return; } auto host = static_cast(data); host->watcher_ = watcher; if (error != nullptr) { spdlog::error("Host: {}", error->message); - g_error_free(error); return; } sn_watcher_call_register_host(host->watcher_, host->object_path_.c_str(), host->cancellable_, @@ -76,16 +81,19 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error != nullptr) { + g_error_free(error); + } + }); sn_watcher_call_register_host_finish(SN_WATCHER(src), res, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { spdlog::error("Host: {}", error->message); - g_error_free(error); return; } auto host = static_cast(data); if (error != nullptr) { spdlog::error("Host: {}", error->message); - g_error_free(error); return; } g_signal_connect(host->watcher_, "item-registered", G_CALLBACK(&Host::itemRegistered), data); diff --git a/src/modules/sni/watcher.cpp b/src/modules/sni/watcher.cpp index dfd076ef..22434709 100644 --- a/src/modules/sni/watcher.cpp +++ b/src/modules/sni/watcher.cpp @@ -2,6 +2,8 @@ #include +#include "util/scope_guard.hpp" + using namespace waybar::modules::SNI; Watcher::Watcher() @@ -29,6 +31,11 @@ Watcher::~Watcher() { void Watcher::busAcquired(const Glib::RefPtr& conn, Glib::ustring name) { GError* error = nullptr; + waybar::util::scope_guard error_deleter([error]() { + if (error) { + g_error_free(error); + } + }); g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(watcher_), conn->gobj(), "/StatusNotifierWatcher", &error); if (error != nullptr) { @@ -36,7 +43,6 @@ void Watcher::busAcquired(const Glib::RefPtr& conn, Glib: if (error->code != 2) { spdlog::error("Watcher: {}", error->message); } - g_error_free(error); return; } g_signal_connect_swapped(watcher_, "handle-register-item", diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index f2bc621d..e3b3981a 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -63,7 +63,7 @@ UPower::UPower(const std::string& id, const Json::Value& config) box_.set_has_tooltip(tooltip_enabled); if (tooltip_enabled) { // Sets the window to use when showing the tooltip - upower_tooltip = new UPowerTooltip(iconSize, tooltip_spacing, tooltip_padding); + 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)); } @@ -72,14 +72,13 @@ UPower::UPower(const std::string& id, const Json::Value& config) G_BUS_NAME_WATCHER_FLAGS_AUTO_START, upowerAppear, upowerDisappear, this, NULL); - GError* error = NULL; - client = up_client_new_full(NULL, &error); + 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, &error); + 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 { @@ -99,6 +98,7 @@ UPower::UPower(const std::string& id, const Json::Value& config) } 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); diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp index 45544bbc..1a653f85 100644 --- a/src/modules/upower/upower_tooltip.cpp +++ b/src/modules/upower/upower_tooltip.cpp @@ -9,11 +9,10 @@ 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_) { - contentBox = new Gtk::Box(Gtk::ORIENTATION_VERTICAL); - // Sets the Tooltip Padding contentBox->set_margin_top(tooltipPadding); contentBox->set_margin_bottom(tooltipPadding); diff --git a/src/util/prepare_for_sleep.cpp b/src/util/prepare_for_sleep.cpp index 218c1e29..661285a2 100644 --- a/src/util/prepare_for_sleep.cpp +++ b/src/util/prepare_for_sleep.cpp @@ -7,8 +7,7 @@ namespace { class PrepareForSleep { private: PrepareForSleep() { - GError *error = NULL; - login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); if (!login1_connection) { spdlog::warn("Unable to connect to the SYSTEM Bus!..."); } else { From 68dfd6aa3a72f8eca9f0306b730e90ccbf10fcc5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 23 Oct 2023 14:59:46 +0200 Subject: [PATCH 158/364] scope_guard/modules: Rename scope_guard to ScopeGuard Using pascal case for the class name keeps it more consistent with the majority of the other class names. --- include/util/scope_guard.hpp | 14 +++++++------- src/modules/bluetooth.cpp | 2 +- src/modules/custom.cpp | 2 +- src/modules/mpris/mpris.cpp | 6 +++--- src/modules/sni/host.cpp | 4 ++-- src/modules/sni/watcher.cpp | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/util/scope_guard.hpp b/include/util/scope_guard.hpp index 5c717704..0d78cad6 100644 --- a/include/util/scope_guard.hpp +++ b/include/util/scope_guard.hpp @@ -5,14 +5,14 @@ namespace waybar::util { template -class scope_guard { +class ScopeGuard { public: - explicit scope_guard(Func&& exit_function) : f{std::forward(exit_function)} {} - scope_guard(const scope_guard&) = delete; - scope_guard(scope_guard&&) = default; - scope_guard& operator=(const scope_guard&) = delete; - scope_guard& operator=(scope_guard&&) = default; - ~scope_guard() { f(); } + explicit ScopeGuard(Func&& exit_function) : f{std::forward(exit_function)} {} + ScopeGuard(const ScopeGuard&) = delete; + ScopeGuard(ScopeGuard&&) = default; + ScopeGuard& operator=(const ScopeGuard&) = delete; + ScopeGuard& operator=(ScopeGuard&&) = default; + ~ScopeGuard() { f(); } private: Func f; diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 6b6cb7ef..80e4731b 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -14,7 +14,7 @@ using GDBusManager = std::unique_ptr GDBusManager { GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { + waybar::util::ScopeGuard error_deleter([error]() { if (error) { g_error_free(error); } diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index d0a39802..287b5588 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -59,7 +59,7 @@ void waybar::modules::Custom::continuousWorker() { } thread_ = [this, cmd] { char* buff = nullptr; - waybar::util::scope_guard buff_deleter([buff]() { + waybar::util::ScopeGuard buff_deleter([buff]() { if (buff) { free(buff); } diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 02138af7..feb1f60f 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -119,7 +119,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { + waybar::util::ScopeGuard error_deleter([error]() { if (error) { g_error_free(error); } @@ -482,7 +482,7 @@ auto Mpris::getPlayerInfo() -> std::optional { } GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { + waybar::util::ScopeGuard error_deleter([error]() { if (error) { g_error_free(error); } @@ -580,7 +580,7 @@ errorexit: bool Mpris::handleToggle(GdkEventButton* const& e) { GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { + waybar::util::ScopeGuard error_deleter([error]() { if (error) { g_error_free(error); } diff --git a/src/modules/sni/host.cpp b/src/modules/sni/host.cpp index 136c2941..54faa16c 100644 --- a/src/modules/sni/host.cpp +++ b/src/modules/sni/host.cpp @@ -59,7 +59,7 @@ void Host::nameVanished(const Glib::RefPtr& conn, const G void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { + waybar::util::ScopeGuard error_deleter([error]() { if (error != nullptr) { g_error_free(error); } @@ -81,7 +81,7 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { + waybar::util::ScopeGuard error_deleter([error]() { if (error != nullptr) { g_error_free(error); } diff --git a/src/modules/sni/watcher.cpp b/src/modules/sni/watcher.cpp index 22434709..8c035ae1 100644 --- a/src/modules/sni/watcher.cpp +++ b/src/modules/sni/watcher.cpp @@ -31,7 +31,7 @@ Watcher::~Watcher() { void Watcher::busAcquired(const Glib::RefPtr& conn, Glib::ustring name) { GError* error = nullptr; - waybar::util::scope_guard error_deleter([error]() { + waybar::util::ScopeGuard error_deleter([error]() { if (error) { g_error_free(error); } From 799fce0dc6ec48df51633dd81bcfc24ad1c4b836 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 24 Oct 2023 18:56:45 +0200 Subject: [PATCH 159/364] ci: Set freebsd to timeout after 30min The job runs very unreliably and often times out after 6h of being stuck in a boot loop. This commit reduces the timeout to 30min. --- .github/workflows/freebsd.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 550f9453..30ef9421 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -12,6 +12,7 @@ jobs: - uses: actions/checkout@v3 - name: Test in FreeBSD VM uses: vmactions/freebsd-vm@v0 + timeout-minutes: 30 with: mem: 2048 usesh: true From 6ae354f564c02a89813543a0a91aa74c28b36b74 Mon Sep 17 00:00:00 2001 From: "Crom (Thibaut CHARLES)" Date: Sat, 14 Oct 2023 19:14:46 +0200 Subject: [PATCH 160/364] PoC --- cabi_example/main.c | 39 +++++++++++++++++++++++++++++++ cabi_example/meson.build | 21 +++++++++++++++++ include/factory.hpp | 1 + include/modules/cabi.hpp | 28 ++++++++++++++++++++++ meson.build | 2 ++ src/factory.cpp | 3 +++ src/modules/cabi.cpp | 50 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 144 insertions(+) create mode 100644 cabi_example/main.c create mode 100644 cabi_example/meson.build create mode 100644 include/modules/cabi.hpp create mode 100644 src/modules/cabi.cpp diff --git a/cabi_example/main.c b/cabi_example/main.c new file mode 100644 index 00000000..babb3aea --- /dev/null +++ b/cabi_example/main.c @@ -0,0 +1,39 @@ +#include +#include + +typedef struct { + GtkContainer* root; + GtkBox* container; + GtkLabel* label; + GtkButton* button; +} Instance; + +// +const size_t wbcabi_version = 1; + +void onclicked() { printf("You clicked the button\n"); } + +void* wbcabi_init(GtkContainer* root) { + // Allocate the instance object + Instance* inst = malloc(sizeof(Instance)); + inst->root = root; + + // Add a container for displaying the next widgets + inst->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5)); + gtk_container_add(GTK_CONTAINER(inst->root), GTK_WIDGET(inst->container)); + + // Add a label + inst->label = GTK_LABEL(gtk_label_new("This is a button:")); + gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(inst->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)); + + // Return instance object + return inst; +} +void wbcabi_deinit(void* instance) { free(instance); } + +char* wbcabi_last_error_str(void* instance) { return NULL; } \ No newline at end of file diff --git a/cabi_example/meson.build b/cabi_example/meson.build new file mode 100644 index 00000000..3f69d2b8 --- /dev/null +++ b/cabi_example/meson.build @@ -0,0 +1,21 @@ +project( + 'waybar_cabi_c_example', 'c', + version: '0.1.0', + license: 'MIT', +) + +compiler = meson.get_compiler('c') + + +shared_library('waybar_cabi_c_example', + [ + 'main.c', + ], + dependencies: [ + dependency('gtk+-3.0', version : ['>=3.22.0']) + ] + # include_directories: include_directories(['../../../include']), + # dependencies: [], + # install: true, + # install_dir: 'plugins', +) diff --git a/include/factory.hpp b/include/factory.hpp index cb25078d..068245e6 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -91,6 +91,7 @@ #include "modules/cava.hpp" #endif #include "bar.hpp" +#include "modules/cabi.hpp" #include "modules/custom.hpp" #include "modules/image.hpp" #include "modules/temperature.hpp" diff --git a/include/modules/cabi.hpp b/include/modules/cabi.hpp new file mode 100644 index 00000000..7eae48b7 --- /dev/null +++ b/include/modules/cabi.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include +#include + +#include "AModule.hpp" +#include "util/command.hpp" +#include "util/json.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class CABI : public AModule { + public: + CABI(const std::string&, const std::string&, const Json::Value&); + virtual ~CABI(); + + private: + void* cabi_instance_ = nullptr; + + std::function wbcabi_init_ = nullptr; + std::function wbcabi_deinit_ = [](void*) {}; + std::function wbcabi_last_error_str_ = [](void*) { return nullptr; }; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index bb9abdeb..99e8b562 100644 --- a/meson.build +++ b/meson.build @@ -190,6 +190,7 @@ if is_linux add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp') src_files += files( 'src/modules/battery.cpp', + 'src/modules/cabi.cpp', 'src/modules/cpu.cpp', 'src/modules/cpu_frequency/common.cpp', 'src/modules/cpu_frequency/linux.cpp', @@ -202,6 +203,7 @@ 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') src_files += files( + 'src/modules/cabi.cpp', 'src/modules/cpu.cpp', 'src/modules/cpu_frequency/bsd.cpp', 'src/modules/cpu_frequency/common.cpp', diff --git a/src/factory.cpp b/src/factory.cpp index aaf46036..73815842 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -201,6 +201,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { return new waybar::modules::Custom(ref.substr(7), id, config_[name]); } + if (ref.compare(0, 5, "cabi/") == 0 && ref.size() > 5) { + return new waybar::modules::CABI(ref.substr(5), 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/modules/cabi.cpp b/src/modules/cabi.cpp new file mode 100644 index 00000000..7fd0ad5c --- /dev/null +++ b/src/modules/cabi.cpp @@ -0,0 +1,50 @@ +#include "modules/cabi.hpp" + +#include + +namespace waybar::modules { + +CABI::CABI(const std::string& name, const std::string& id, const Json::Value& config) + : AModule(config, name, id, true, true) { + const auto dynlib_path = config_["path"].asString(); + + void* handle = dlopen(dynlib_path.c_str(), RTLD_LAZY); + if (handle == nullptr) { + throw std::runtime_error{dlerror()}; + } + + // Fetch ABI version + auto wbcabi_version = reinterpret_cast(dlsym(handle, "wbcabi_version")); + if (wbcabi_version == nullptr) { + throw std::runtime_error{"Missing wbcabi_version"}; + } + + // Fetch functions + if (*wbcabi_version == 1) { + wbcabi_init_ = reinterpret_cast(dlsym(handle, "wbcabi_init")); + if (wbcabi_init_ == nullptr) { + throw std::runtime_error{"Missing wbcabi_init function"}; + } + if (auto fn = reinterpret_cast(dlsym(handle, "wbcabi_deinit"))) + wbcabi_deinit_ = fn; + if (auto fn = reinterpret_cast(dlsym(handle, "wbcabi_last_error_str"))) + wbcabi_last_error_str_ = fn; + } else { + throw std::runtime_error{"Unknown wbcabi_version " + std::to_string(*wbcabi_version)}; + } + + cabi_instance_ = wbcabi_init_(dynamic_cast(&event_box_)->gobj()); + if (cabi_instance_ == nullptr) { + const auto err_str = wbcabi_last_error_str_(cabi_instance_); + throw std::runtime_error{std::string{"Failed to initialize C ABI plugin: "} + + (err_str != nullptr ? err_str : "unknown error")}; + } +} + +CABI::~CABI() { + if (cabi_instance_ != nullptr) { + wbcabi_deinit_(cabi_instance_); + } +} + +} // namespace waybar::modules From 088ca6b963a8ae1bedf9c0178404f3e3e11e5509 Mon Sep 17 00:00:00 2001 From: "Crom (Thibaut CHARLES)" Date: Mon, 16 Oct 2023 22:13:51 +0200 Subject: [PATCH 161/364] Added cffi/* module for third-party advanced modules --- cabi_example/main.c | 39 ------- cabi_example/meson.build | 21 ---- include/factory.hpp | 2 +- include/modules/cabi.hpp | 28 ----- include/modules/cffi.hpp | 50 ++++++++ meson.build | 4 +- .../custom_modules/cffi_example/.gitignore | 1 + .../custom_modules/cffi_example/README.md | 35 ++++++ resources/custom_modules/cffi_example/main.c | 63 ++++++++++ .../custom_modules/cffi_example/meson.build | 13 +++ .../cffi_example/waybar_cffi_module.h | 59 ++++++++++ src/factory.cpp | 4 +- src/modules/cabi.cpp | 50 -------- src/modules/cffi.cpp | 108 ++++++++++++++++++ 14 files changed, 334 insertions(+), 143 deletions(-) delete mode 100644 cabi_example/main.c delete mode 100644 cabi_example/meson.build delete mode 100644 include/modules/cabi.hpp create mode 100644 include/modules/cffi.hpp create mode 100644 resources/custom_modules/cffi_example/.gitignore create mode 100644 resources/custom_modules/cffi_example/README.md create mode 100644 resources/custom_modules/cffi_example/main.c create mode 100644 resources/custom_modules/cffi_example/meson.build create mode 100644 resources/custom_modules/cffi_example/waybar_cffi_module.h delete mode 100644 src/modules/cabi.cpp create mode 100644 src/modules/cffi.cpp diff --git a/cabi_example/main.c b/cabi_example/main.c deleted file mode 100644 index babb3aea..00000000 --- a/cabi_example/main.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include - -typedef struct { - GtkContainer* root; - GtkBox* container; - GtkLabel* label; - GtkButton* button; -} Instance; - -// -const size_t wbcabi_version = 1; - -void onclicked() { printf("You clicked the button\n"); } - -void* wbcabi_init(GtkContainer* root) { - // Allocate the instance object - Instance* inst = malloc(sizeof(Instance)); - inst->root = root; - - // Add a container for displaying the next widgets - inst->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5)); - gtk_container_add(GTK_CONTAINER(inst->root), GTK_WIDGET(inst->container)); - - // Add a label - inst->label = GTK_LABEL(gtk_label_new("This is a button:")); - gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(inst->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)); - - // Return instance object - return inst; -} -void wbcabi_deinit(void* instance) { free(instance); } - -char* wbcabi_last_error_str(void* instance) { return NULL; } \ No newline at end of file diff --git a/cabi_example/meson.build b/cabi_example/meson.build deleted file mode 100644 index 3f69d2b8..00000000 --- a/cabi_example/meson.build +++ /dev/null @@ -1,21 +0,0 @@ -project( - 'waybar_cabi_c_example', 'c', - version: '0.1.0', - license: 'MIT', -) - -compiler = meson.get_compiler('c') - - -shared_library('waybar_cabi_c_example', - [ - 'main.c', - ], - dependencies: [ - dependency('gtk+-3.0', version : ['>=3.22.0']) - ] - # include_directories: include_directories(['../../../include']), - # dependencies: [], - # install: true, - # install_dir: 'plugins', -) diff --git a/include/factory.hpp b/include/factory.hpp index 068245e6..bb00410e 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -91,7 +91,7 @@ #include "modules/cava.hpp" #endif #include "bar.hpp" -#include "modules/cabi.hpp" +#include "modules/cffi.hpp" #include "modules/custom.hpp" #include "modules/image.hpp" #include "modules/temperature.hpp" diff --git a/include/modules/cabi.hpp b/include/modules/cabi.hpp deleted file mode 100644 index 7eae48b7..00000000 --- a/include/modules/cabi.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "AModule.hpp" -#include "util/command.hpp" -#include "util/json.hpp" -#include "util/sleeper_thread.hpp" - -namespace waybar::modules { - -class CABI : public AModule { - public: - CABI(const std::string&, const std::string&, const Json::Value&); - virtual ~CABI(); - - private: - void* cabi_instance_ = nullptr; - - std::function wbcabi_init_ = nullptr; - std::function wbcabi_deinit_ = [](void*) {}; - std::function wbcabi_last_error_str_ = [](void*) { return nullptr; }; -}; - -} // namespace waybar::modules diff --git a/include/modules/cffi.hpp b/include/modules/cffi.hpp new file mode 100644 index 00000000..43545a4f --- /dev/null +++ b/include/modules/cffi.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include "AModule.hpp" +#include "util/command.hpp" +#include "util/json.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +extern "C" { +// C ABI representation of a config key/value pair +struct wbcffi_config_entry { + const char* key; + const char* value; +}; +} + +class CFFI : public AModule { + public: + CFFI(const std::string&, const std::string&, const Json::Value&); + virtual ~CFFI(); + + // virtual auto update() -> void override; + // virtual operator Gtk::Widget&() override; + + virtual auto refresh(int signal) -> void override; + virtual auto doAction(const std::string& name) -> void override; + + private: + /// + void* cffi_instance_ = nullptr; + + typedef void*(InitFn)(GtkContainer*, const struct wbcffi_config_entry* config_entries, + size_t config_entries_len); + typedef void(DenitFn)(void* instance); + typedef void(RefreshFn)(void* instance, int signal); + typedef void(DoActionFn)(void* instance, const char* name); + + // FFI hooks + struct { + std::function init = nullptr; + std::function deinit = nullptr; + std::function refresh = [](void*, int) {}; + std::function doAction = [](void*, const char*) {}; + } hooks_; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index 99e8b562..7c0f9965 100644 --- a/meson.build +++ b/meson.build @@ -190,7 +190,7 @@ if is_linux add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp') src_files += files( 'src/modules/battery.cpp', - 'src/modules/cabi.cpp', + 'src/modules/cffi.cpp', 'src/modules/cpu.cpp', 'src/modules/cpu_frequency/common.cpp', 'src/modules/cpu_frequency/linux.cpp', @@ -203,7 +203,7 @@ 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') src_files += files( - 'src/modules/cabi.cpp', + 'src/modules/cffi.cpp', 'src/modules/cpu.cpp', 'src/modules/cpu_frequency/bsd.cpp', 'src/modules/cpu_frequency/common.cpp', diff --git a/resources/custom_modules/cffi_example/.gitignore b/resources/custom_modules/cffi_example/.gitignore new file mode 100644 index 00000000..988107fe --- /dev/null +++ b/resources/custom_modules/cffi_example/.gitignore @@ -0,0 +1 @@ +.cache/ \ No newline at end of file diff --git a/resources/custom_modules/cffi_example/README.md b/resources/custom_modules/cffi_example/README.md new file mode 100644 index 00000000..71e6ce83 --- /dev/null +++ b/resources/custom_modules/cffi_example/README.md @@ -0,0 +1,35 @@ +# C FFI module + +A C FFI module is a dynamic library that exposes standard C functions and +constants, that Waybar can load and execute to create custom advanced widgets. + +Most language can implement the required functions and constants (C, C++, Rust, +Go, Python, ...), meaning you can develop custom modules using your language of +choice, as long as there's GTK bindings. + +# Usage + +## Building this module + +```bash +meson setup build +meson compile -C build +``` + +## Load the module + +Edit your waybar config: +```json +{ + // ... + "modules-center": [ + // ... + "cffi/c_example" + ], + // ... + "cffi/c_example": { + // Path to the compiled dynamic library file + "module_path": "resources/custom_modules/cffi_example/build/wb_cffi_example.so" + } +} +``` diff --git a/resources/custom_modules/cffi_example/main.c b/resources/custom_modules/cffi_example/main.c new file mode 100644 index 00000000..24644afe --- /dev/null +++ b/resources/custom_modules/cffi_example/main.c @@ -0,0 +1,63 @@ + +#include "waybar_cffi_module.h" + +typedef struct { + GtkContainer* root; + GtkBox* container; + GtkButton* button; +} Instance; + +// This static variable is shared between all instances of this module +static int instance_count = 0; + +void onclicked(GtkButton* button) { + char text[256]; + snprintf(text, 256, "Dice throw result: %d", rand() % 6 + 1); + gtk_button_set_label(button, text); +} + +// You must +const size_t wbcffi_version = 1; + +void* wbcffi_init(GtkContainer* root, const struct wbcffi_config_entry* config_entries, + size_t config_entries_len) { + printf("cffi_example: init config:\n"); + for (size_t i = 0; i < config_entries_len; i++) { + printf(" %s = %s\n", config_entries[i].key, config_entries[i].value); + } + + // Allocate the instance object + Instance* inst = malloc(sizeof(Instance)); + inst->root = root; + + // Add a container for displaying the next widgets + inst->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5)); + gtk_container_add(GTK_CONTAINER(inst->root), GTK_WIDGET(inst->container)); + + // Add a label + GtkLabel* label = GTK_LABEL(gtk_label_new("[Example C FFI Module:")); + gtk_container_add(GTK_CONTAINER(inst->container), 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)); + + // Add a label + label = GTK_LABEL(gtk_label_new("]")); + gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(label)); + + // Return instance object + printf("cffi_example inst=%p: init success ! (%d total instances)\n", inst, ++instance_count); + return inst; +} +void wbcffi_deinit(void* instance) { + printf("cffi_example inst=%p: free memory\n", instance); + free(instance); +} +void wbcffi_refresh(void* instance, int signal) { + printf("cffi_example inst=%p: Received refresh signal %d\n", instance, 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 new file mode 100644 index 00000000..dcde1048 --- /dev/null +++ b/resources/custom_modules/cffi_example/meson.build @@ -0,0 +1,13 @@ +project( + 'waybar_cffi_example', 'c', + version: '0.1.0', + license: 'MIT', +) + +shared_library('wb_cffi_example', + ['main.c'], + dependencies: [ + dependency('gtk+-3.0', version : ['>=3.22.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 new file mode 100644 index 00000000..af2fe581 --- /dev/null +++ b/resources/custom_modules/cffi_example/waybar_cffi_module.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Waybar ABI version. 1 is the latest version +extern const size_t wbcffi_version; + +/// Config key-value pair +struct wbcffi_config_entry { + /// Entry key + const char* key; + /// Entry value as string. JSON object and arrays are serialized. + const char* value; +}; + +/// Module init/new function, called on module instantiation +/// +/// MANDATORY CFFI function +/// +/// @param root_widget Root GTK widget instantiated by Waybar +/// @param config_entries Flat representation of the module JSON config. The data only available +/// during wbcffi_init call. +/// @param config_entries_len Number of entries in `config_entries` +/// +/// @return A untyped pointer to module data, NULL if the module failed to load. +void* wbcffi_init(GtkContainer* root_widget, const struct wbcffi_config_entry* config_entries, + size_t config_entries_len); + +/// Module deinit/delete function, called when Waybar is closed or when the module is removed +/// +/// MANDATORY CFFI function +/// +/// @param instance Module instance data (as returned by `wbcffi_init`) +void wbcffi_deinit(void* instance); + +/// When Waybar receives a POSIX signal, it forwards the signal to each module, calling this +/// function +/// +/// Optional CFFI function +/// +/// @param instance Module instance data (as returned by `wbcffi_init`) +/// @param signal Signal ID +void wbcffi_refresh(void* instance, int signal); + +/// +/// Optional CFFI function +/// +/// @param instance Module instance data (as returned by `wbcffi_init`) +/// @param name Action name +void wbcffi_doaction(void* instance, const char* name); + +#ifdef __cplusplus +} +#endif diff --git a/src/factory.cpp b/src/factory.cpp index 73815842..4df27cd4 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -201,8 +201,8 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { return new waybar::modules::Custom(ref.substr(7), id, config_[name]); } - if (ref.compare(0, 5, "cabi/") == 0 && ref.size() > 5) { - return new waybar::modules::CABI(ref.substr(5), id, config_[name]); + if (ref.compare(0, 5, "cffi/") == 0 && ref.size() > 5) { + return new waybar::modules::CFFI(ref.substr(5), id, config_[name]); } } catch (const std::exception& e) { auto err = fmt::format("Disabling module \"{}\", {}", name, e.what()); diff --git a/src/modules/cabi.cpp b/src/modules/cabi.cpp deleted file mode 100644 index 7fd0ad5c..00000000 --- a/src/modules/cabi.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "modules/cabi.hpp" - -#include - -namespace waybar::modules { - -CABI::CABI(const std::string& name, const std::string& id, const Json::Value& config) - : AModule(config, name, id, true, true) { - const auto dynlib_path = config_["path"].asString(); - - void* handle = dlopen(dynlib_path.c_str(), RTLD_LAZY); - if (handle == nullptr) { - throw std::runtime_error{dlerror()}; - } - - // Fetch ABI version - auto wbcabi_version = reinterpret_cast(dlsym(handle, "wbcabi_version")); - if (wbcabi_version == nullptr) { - throw std::runtime_error{"Missing wbcabi_version"}; - } - - // Fetch functions - if (*wbcabi_version == 1) { - wbcabi_init_ = reinterpret_cast(dlsym(handle, "wbcabi_init")); - if (wbcabi_init_ == nullptr) { - throw std::runtime_error{"Missing wbcabi_init function"}; - } - if (auto fn = reinterpret_cast(dlsym(handle, "wbcabi_deinit"))) - wbcabi_deinit_ = fn; - if (auto fn = reinterpret_cast(dlsym(handle, "wbcabi_last_error_str"))) - wbcabi_last_error_str_ = fn; - } else { - throw std::runtime_error{"Unknown wbcabi_version " + std::to_string(*wbcabi_version)}; - } - - cabi_instance_ = wbcabi_init_(dynamic_cast(&event_box_)->gobj()); - if (cabi_instance_ == nullptr) { - const auto err_str = wbcabi_last_error_str_(cabi_instance_); - throw std::runtime_error{std::string{"Failed to initialize C ABI plugin: "} + - (err_str != nullptr ? err_str : "unknown error")}; - } -} - -CABI::~CABI() { - if (cabi_instance_ != nullptr) { - wbcabi_deinit_(cabi_instance_); - } -} - -} // namespace waybar::modules diff --git a/src/modules/cffi.cpp b/src/modules/cffi.cpp new file mode 100644 index 00000000..d0cd765f --- /dev/null +++ b/src/modules/cffi.cpp @@ -0,0 +1,108 @@ +#include "modules/cffi.hpp" + +#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) { + const auto dynlib_path = config_["module_path"].asString(); + if (dynlib_path.empty()) { + throw std::runtime_error{"Missing or empty 'module_path' in module config"}; + } + + void* handle = dlopen(dynlib_path.c_str(), RTLD_LAZY); + if (handle == nullptr) { + throw std::runtime_error{std::string{"Failed to load CFFI module: "} + dlerror()}; + } + + // Fetch ABI version + auto wbcffi_version = reinterpret_cast(dlsym(handle, "wbcffi_version")); + if (wbcffi_version == nullptr) { + throw std::runtime_error{std::string{"Missing wbcffi_version function: "} + dlerror()}; + } + + // Fetch functions + if (*wbcffi_version == 1) { + // Mandatory functions + hooks_.init = reinterpret_cast(dlsym(handle, "wbcffi_init")); + if (!hooks_.init) { + throw std::runtime_error{std::string{"Missing wbcffi_init function: "} + dlerror()}; + } + hooks_.deinit = reinterpret_cast(dlsym(handle, "wbcffi_deinit")); + if (!hooks_.init) { + throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()}; + } + // Optional functions + if (auto fn = reinterpret_cast(dlsym(handle, "wbcffi_refresh"))) { + hooks_.refresh = fn; + } + if (auto fn = reinterpret_cast(dlsym(handle, "wbcffi_doaction"))) { + hooks_.doAction = fn; + } + } else { + throw std::runtime_error{"Unknown wbcffi_version " + std::to_string(*wbcffi_version)}; + } + + // Prepare init() arguments + // Convert JSON values to string + std::vector config_entries_stringstor; + const auto& keys = config.getMemberNames(); + for (size_t i = 0; i < keys.size(); i++) { + const auto& value = config[keys[i]]; + if (value.isConvertibleTo(Json::ValueType::stringValue)) { + config_entries_stringstor.push_back(config[keys[i]].asString()); + } else { + config_entries_stringstor.push_back(config[keys[i]].toStyledString()); + } + } + + // Prepare config_entries array + std::vector config_entries; + for (size_t i = 0; i < keys.size(); i++) { + config_entries.push_back( + wbcffi_config_entry{keys[i].c_str(), config_entries_stringstor[i].c_str()}); + } + + // Call init + cffi_instance_ = hooks_.init(dynamic_cast(&event_box_)->gobj(), + config_entries.data(), config_entries.size()); + + // Handle init failures + if (cffi_instance_ == nullptr) { + throw std::runtime_error{"Failed to initialize C ABI module"}; + } +} + +CFFI::~CFFI() { + if (cffi_instance_ != nullptr) { + hooks_.deinit(cffi_instance_); + } +} + +auto CFFI::refresh(int signal) -> void { + assert(cffi_instance_ != nullptr); + hooks_.refresh(cffi_instance_, signal); +} + +auto CFFI::doAction(const std::string& name) -> void { + assert(cffi_instance_ != nullptr); + if (!name.empty()) { + // TODO: Make a decision + // Calling AModule::doAction and hooks_.doAction will execute the action twice if it is + // configured in AModule::eventActionMap_ and implemented in the CFFI module. + // + // Should we block AModule::doAction() if the action is implemented in hooks_.doAction() ? + // (doAction could return true if the action has been processed) + // + hooks_.doAction(cffi_instance_, name.c_str()); + AModule::doAction(name); + } +} + +} // namespace waybar::modules From d86059016e6e6aaa29036faedbd0151413300902 Mon Sep 17 00:00:00 2001 From: "Crom (Thibaut CHARLES)" Date: Tue, 17 Oct 2023 18:11:48 +0200 Subject: [PATCH 162/364] Send update dispatcher though FFI --- include/AModule.hpp | 1 + include/modules/cffi.hpp | 9 ++++--- .../custom_modules/cffi_example/README.md | 3 +++ resources/custom_modules/cffi_example/main.c | 10 ++++++-- .../cffi_example/waybar_cffi_module.h | 23 +++++++++++++---- src/modules/cffi.cpp | 25 +++++++++++-------- 6 files changed, 50 insertions(+), 21 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 479755b7..0960b116 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -17,6 +17,7 @@ class AModule : public IModule { operator Gtk::Widget &() override; auto doAction(const std::string &name) -> void override; + /// Emitting on this dispatcher triggers a update() call Glib::Dispatcher dp; protected: diff --git a/include/modules/cffi.hpp b/include/modules/cffi.hpp index 43545a4f..ac3b3de7 100644 --- a/include/modules/cffi.hpp +++ b/include/modules/cffi.hpp @@ -22,21 +22,21 @@ class CFFI : public AModule { CFFI(const std::string&, const std::string&, const Json::Value&); virtual ~CFFI(); - // virtual auto update() -> void override; - // virtual operator Gtk::Widget&() override; - virtual auto refresh(int signal) -> void override; virtual auto doAction(const std::string& name) -> void override; + virtual auto update() -> void override; private: /// void* cffi_instance_ = nullptr; - typedef void*(InitFn)(GtkContainer*, const struct wbcffi_config_entry* config_entries, + typedef void*(InitFn)(GtkContainer* root_widget, const void (*trigger_update)(void*), + void* trigger_update_arg, const struct wbcffi_config_entry* config_entries, size_t config_entries_len); typedef void(DenitFn)(void* instance); typedef void(RefreshFn)(void* instance, int signal); typedef void(DoActionFn)(void* instance, const char* name); + typedef void(UpdateFn)(void* instance); // FFI hooks struct { @@ -44,6 +44,7 @@ class CFFI : public AModule { std::function deinit = nullptr; std::function refresh = [](void*, int) {}; std::function doAction = [](void*, const char*) {}; + std::function update = [](void*) {}; } hooks_; }; diff --git a/resources/custom_modules/cffi_example/README.md b/resources/custom_modules/cffi_example/README.md index 71e6ce83..88396c19 100644 --- a/resources/custom_modules/cffi_example/README.md +++ b/resources/custom_modules/cffi_example/README.md @@ -7,6 +7,9 @@ Most language can implement the required functions and constants (C, C++, Rust, Go, Python, ...), meaning you can develop custom modules using your language of choice, as long as there's GTK bindings. +Symbols to implement are documented in the +[waybar_cffi_module.h](waybar_cffi_module.h) file. + # Usage ## Building this module diff --git a/resources/custom_modules/cffi_example/main.c b/resources/custom_modules/cffi_example/main.c index 24644afe..c959c9a8 100644 --- a/resources/custom_modules/cffi_example/main.c +++ b/resources/custom_modules/cffi_example/main.c @@ -19,7 +19,8 @@ void onclicked(GtkButton* button) { // You must const size_t wbcffi_version = 1; -void* wbcffi_init(GtkContainer* root, const struct wbcffi_config_entry* config_entries, +void* wbcffi_init(GtkContainer* root_widget, void (*trigger_update)(void*), + void* trigger_update_arg, const struct wbcffi_config_entry* config_entries, size_t config_entries_len) { printf("cffi_example: init config:\n"); for (size_t i = 0; i < config_entries_len; i++) { @@ -28,7 +29,7 @@ void* wbcffi_init(GtkContainer* root, const struct wbcffi_config_entry* config_e // Allocate the instance object Instance* inst = malloc(sizeof(Instance)); - inst->root = root; + inst->root = root_widget; // Add a container for displaying the next widgets inst->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5)); @@ -51,13 +52,18 @@ void* wbcffi_init(GtkContainer* root, const struct wbcffi_config_entry* config_e printf("cffi_example inst=%p: init success ! (%d total instances)\n", inst, ++instance_count); return inst; } + void wbcffi_deinit(void* instance) { printf("cffi_example inst=%p: free memory\n", instance); free(instance); } + +void wbcffi_update(void* instance) { printf("cffi_example inst=%p: Update request\n", instance); } + void wbcffi_refresh(void* instance, int signal) { printf("cffi_example inst=%p: Received refresh signal %d\n", instance, 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/waybar_cffi_module.h b/resources/custom_modules/cffi_example/waybar_cffi_module.h index af2fe581..2696b670 100644 --- a/resources/custom_modules/cffi_example/waybar_cffi_module.h +++ b/resources/custom_modules/cffi_example/waybar_cffi_module.h @@ -23,12 +23,16 @@ struct wbcffi_config_entry { /// MANDATORY CFFI function /// /// @param root_widget Root GTK widget instantiated by Waybar +/// @param trigger_update Call this function with trigger_update_arg as argument to trigger +/// wbcffi_update() on the next GTK main event loop iteration +/// @param trigger_update_arg Argument for trigger_update call /// @param config_entries Flat representation of the module JSON config. The data only available /// during wbcffi_init call. /// @param config_entries_len Number of entries in `config_entries` /// /// @return A untyped pointer to module data, NULL if the module failed to load. -void* wbcffi_init(GtkContainer* root_widget, const struct wbcffi_config_entry* config_entries, +void* wbcffi_init(GtkContainer* root_widget, const void (*trigger_update)(void*), + void* trigger_update_arg, const struct wbcffi_config_entry* config_entries, size_t config_entries_len); /// Module deinit/delete function, called when Waybar is closed or when the module is removed @@ -38,8 +42,15 @@ void* wbcffi_init(GtkContainer* root_widget, const struct wbcffi_config_entry* c /// @param instance Module instance data (as returned by `wbcffi_init`) void wbcffi_deinit(void* instance); -/// When Waybar receives a POSIX signal, it forwards the signal to each module, calling this -/// function +/// Called from the GTK main event loop, to update the UI +/// +/// Optional CFFI function +/// +/// @param instance Module instance data (as returned by `wbcffi_init`) +/// @param action_name Action name +void wbcffi_update(void* instance); + +/// Called when Waybar receives a POSIX signal and forwards it to each module /// /// Optional CFFI function /// @@ -47,12 +58,14 @@ void wbcffi_deinit(void* instance); /// @param signal Signal ID void wbcffi_refresh(void* instance, int signal); +/// Called on module action (see +/// https://github.com/Alexays/Waybar/wiki/Configuration#module-actions-config) /// /// Optional CFFI function /// /// @param instance Module instance data (as returned by `wbcffi_init`) -/// @param name Action name -void wbcffi_doaction(void* instance, const char* name); +/// @param action_name Action name +void wbcffi_doaction(void* instance, const char* action_name); #ifdef __cplusplus } diff --git a/src/modules/cffi.cpp b/src/modules/cffi.cpp index d0cd765f..e7a28edd 100644 --- a/src/modules/cffi.cpp +++ b/src/modules/cffi.cpp @@ -39,6 +39,9 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()}; } // Optional functions + if (auto fn = reinterpret_cast(dlsym(handle, "wbcffi_update"))) { + hooks_.update = fn; + } if (auto fn = reinterpret_cast(dlsym(handle, "wbcffi_refresh"))) { hooks_.refresh = fn; } @@ -70,8 +73,10 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co } // Call init - cffi_instance_ = hooks_.init(dynamic_cast(&event_box_)->gobj(), - config_entries.data(), config_entries.size()); + cffi_instance_ = hooks_.init( + dynamic_cast(&event_box_)->gobj(), + [](void* dp) { ((Glib::Dispatcher*)dp)->emit(); }, &dp, config_entries.data(), + config_entries.size()); // Handle init failures if (cffi_instance_ == nullptr) { @@ -85,6 +90,14 @@ CFFI::~CFFI() { } } +auto CFFI::update() -> void { + assert(cffi_instance_ != nullptr); + hooks_.update(cffi_instance_); + + // Execute the on-update command set in config + AModule::update(); +} + auto CFFI::refresh(int signal) -> void { assert(cffi_instance_ != nullptr); hooks_.refresh(cffi_instance_, signal); @@ -93,15 +106,7 @@ auto CFFI::refresh(int signal) -> void { auto CFFI::doAction(const std::string& name) -> void { assert(cffi_instance_ != nullptr); if (!name.empty()) { - // TODO: Make a decision - // Calling AModule::doAction and hooks_.doAction will execute the action twice if it is - // configured in AModule::eventActionMap_ and implemented in the CFFI module. - // - // Should we block AModule::doAction() if the action is implemented in hooks_.doAction() ? - // (doAction could return true if the action has been processed) - // hooks_.doAction(cffi_instance_, name.c_str()); - AModule::doAction(name); } } From 02c64f3f1eb382827cda1280be8b1beed57aa761 Mon Sep 17 00:00:00 2001 From: "Crom (Thibaut CHARLES)" Date: Thu, 19 Oct 2023 22:10:32 +0200 Subject: [PATCH 163/364] Moved all waybar info into a single struct --- include/modules/cffi.hpp | 17 +++++++--- resources/custom_modules/cffi_example/main.c | 15 +++++---- .../cffi_example/waybar_cffi_module.h | 33 ++++++++++++++----- src/modules/cffi.cpp | 20 +++++++---- 4 files changed, 59 insertions(+), 26 deletions(-) diff --git a/include/modules/cffi.hpp b/include/modules/cffi.hpp index ac3b3de7..85f12989 100644 --- a/include/modules/cffi.hpp +++ b/include/modules/cffi.hpp @@ -9,13 +9,23 @@ namespace waybar::modules { +namespace ffi { extern "C" { -// C ABI representation of a config key/value pair +typedef struct wbcffi_module wbcffi_module; + +typedef struct { + wbcffi_module* obj; + const char* waybar_version; + GtkContainer* (*get_root_widget)(wbcffi_module*); + void (*queue_update)(wbcffi_module*); +} wbcffi_init_info; + struct wbcffi_config_entry { const char* key; const char* value; }; } +} // namespace ffi class CFFI : public AModule { public: @@ -30,9 +40,8 @@ class CFFI : public AModule { /// void* cffi_instance_ = nullptr; - typedef void*(InitFn)(GtkContainer* root_widget, const void (*trigger_update)(void*), - void* trigger_update_arg, const struct wbcffi_config_entry* config_entries, - size_t config_entries_len); + typedef void*(InitFn)(const ffi::wbcffi_init_info* init_info, + const ffi::wbcffi_config_entry* config_entries, size_t config_entries_len); typedef void(DenitFn)(void* instance); typedef void(RefreshFn)(void* instance, int signal); typedef void(DoActionFn)(void* instance, const char* name); diff --git a/resources/custom_modules/cffi_example/main.c b/resources/custom_modules/cffi_example/main.c index c959c9a8..ba2c8cf4 100644 --- a/resources/custom_modules/cffi_example/main.c +++ b/resources/custom_modules/cffi_example/main.c @@ -2,10 +2,10 @@ #include "waybar_cffi_module.h" typedef struct { - GtkContainer* root; + wbcffi_module* waybar_module; GtkBox* container; GtkButton* button; -} Instance; +} ExampleMod; // This static variable is shared between all instances of this module static int instance_count = 0; @@ -19,8 +19,7 @@ void onclicked(GtkButton* button) { // You must const size_t wbcffi_version = 1; -void* wbcffi_init(GtkContainer* root_widget, void (*trigger_update)(void*), - void* trigger_update_arg, const struct wbcffi_config_entry* config_entries, +void* wbcffi_init(const wbcffi_init_info* init_info, const wbcffi_config_entry* config_entries, size_t config_entries_len) { printf("cffi_example: init config:\n"); for (size_t i = 0; i < config_entries_len; i++) { @@ -28,12 +27,14 @@ void* wbcffi_init(GtkContainer* root_widget, void (*trigger_update)(void*), } // Allocate the instance object - Instance* inst = malloc(sizeof(Instance)); - inst->root = root_widget; + 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(inst->root), GTK_WIDGET(inst->container)); + gtk_container_add(GTK_CONTAINER(root), GTK_WIDGET(inst->container)); // Add a label GtkLabel* label = GTK_LABEL(gtk_label_new("[Example C FFI Module:")); diff --git a/resources/custom_modules/cffi_example/waybar_cffi_module.h b/resources/custom_modules/cffi_example/waybar_cffi_module.h index 2696b670..a7886bea 100644 --- a/resources/custom_modules/cffi_example/waybar_cffi_module.h +++ b/resources/custom_modules/cffi_example/waybar_cffi_module.h @@ -10,29 +10,46 @@ extern "C" { /// Waybar ABI version. 1 is the latest version extern const size_t wbcffi_version; +/// Private Waybar CFFI module +typedef struct wbcffi_module wbcffi_module; + +/// Waybar module information +typedef struct { + /// Waybar CFFI object pointer + wbcffi_module* obj; + + /// Waybar version string + const char* waybar_version; + + /// Returns the waybar widget allocated for this module + /// @param obj Waybar CFFI object pointer + GtkContainer* (*get_root_widget)(wbcffi_module* obj); + + /// Queues a request for calling wbcffi_update() on the next GTK main event + /// loop iteration + /// @param obj Waybar CFFI object pointer + void (*queue_update)(wbcffi_module*); +} wbcffi_init_info; + /// Config key-value pair -struct wbcffi_config_entry { +typedef struct { /// Entry key const char* key; /// Entry value as string. JSON object and arrays are serialized. const char* value; -}; +} wbcffi_config_entry; /// Module init/new function, called on module instantiation /// /// MANDATORY CFFI function /// -/// @param root_widget Root GTK widget instantiated by Waybar -/// @param trigger_update Call this function with trigger_update_arg as argument to trigger -/// wbcffi_update() on the next GTK main event loop iteration -/// @param trigger_update_arg Argument for trigger_update call +/// @param init_info Waybar module information /// @param config_entries Flat representation of the module JSON config. The data only available /// during wbcffi_init call. /// @param config_entries_len Number of entries in `config_entries` /// /// @return A untyped pointer to module data, NULL if the module failed to load. -void* wbcffi_init(GtkContainer* root_widget, const void (*trigger_update)(void*), - void* trigger_update_arg, const struct wbcffi_config_entry* config_entries, +void* wbcffi_init(const wbcffi_init_info* init_info, const wbcffi_config_entry* config_entries, size_t config_entries_len); /// Module deinit/delete function, called when Waybar is closed or when the module is removed diff --git a/src/modules/cffi.cpp b/src/modules/cffi.cpp index e7a28edd..e560659b 100644 --- a/src/modules/cffi.cpp +++ b/src/modules/cffi.cpp @@ -66,17 +66,23 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co } // Prepare config_entries array - std::vector config_entries; + std::vector config_entries; for (size_t i = 0; i < keys.size(); i++) { - config_entries.push_back( - wbcffi_config_entry{keys[i].c_str(), config_entries_stringstor[i].c_str()}); + config_entries.push_back({keys[i].c_str(), config_entries_stringstor[i].c_str()}); } + ffi::wbcffi_init_info init_info = { + .obj = (ffi::wbcffi_module*)this, + .waybar_version = VERSION, + .get_root_widget = + [](ffi::wbcffi_module* obj) { + return dynamic_cast(&((CFFI*)obj)->event_box_)->gobj(); + }, + .queue_update = [](ffi::wbcffi_module* obj) { ((CFFI*)obj)->dp.emit(); }, + }; + // Call init - cffi_instance_ = hooks_.init( - dynamic_cast(&event_box_)->gobj(), - [](void* dp) { ((Glib::Dispatcher*)dp)->emit(); }, &dp, config_entries.data(), - config_entries.size()); + cffi_instance_ = hooks_.init(&init_info, config_entries.data(), config_entries.size()); // Handle init failures if (cffi_instance_ == nullptr) { From efab1daa7edae14e9dc93f0cae5927e2a43c41f5 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Oct 2023 18:35:58 +0200 Subject: [PATCH 164/364] chore: switch freebsd action --- .github/workflows/freebsd.yml | 39 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 30ef9421..8da34569 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -1,6 +1,6 @@ name: freebsd -on: [ push, pull_request ] +on: [push, pull_request] jobs: clang: @@ -9,22 +9,21 @@ jobs: # https://github.com/actions/virtual-environments/issues/4060 - for lack of VirtualBox on MacOS 11 runners runs-on: macos-12 steps: - - uses: actions/checkout@v3 - - name: Test in FreeBSD VM - uses: vmactions/freebsd-vm@v0 - timeout-minutes: 30 - with: - mem: 2048 - usesh: true - prepare: | - export CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib # sndio - sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf - pkg install -y git # subprojects/date - pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \ - libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ - pkgconf pulseaudio scdoc sndio spdlog wayland-protocols upower \ - libinotify - run: | - meson build -Dman-pages=enabled - ninja -C build - meson test -C build --no-rebuild --print-errorlogs --suite waybar + - uses: actions/checkout@v3 + - name: Test in FreeBSD VM + uses: cross-platform-actions/action@v0.19.1 + timeout-minutes: 30 + with: + operating_system: freebsd + version: "13.2" + environment_variables: CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib + 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 \ + pkgconf pulseaudio scdoc sndio spdlog wayland-protocols upower \ + libinotify + meson build -Dman-pages=enabled + ninja -C build + meson test -C build --no-rebuild --print-errorlogs --suite waybar From 33f8a02fb5a49c5bfb3fa49084d2f51c05b64509 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 26 Oct 2023 17:19:18 +0200 Subject: [PATCH 165/364] ci: Increase freebsd timeout to 2h After switching to a new FreeBSD action, the job seems to take longer than 30 minutes. Therefore, an increase in the timeout is necessary. --- .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 8da34569..fb2511b5 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v3 - name: Test in FreeBSD VM uses: cross-platform-actions/action@v0.19.1 - timeout-minutes: 30 + timeout-minutes: 120 with: operating_system: freebsd version: "13.2" From 7d8c1494d757cc1180fc47135f5676d159918415 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 26 Oct 2023 17:30:48 +0200 Subject: [PATCH 166/364] cpu_usage: Fix ScopeGuard renaming in bsd-only file --- src/modules/cpu_usage/bsd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/cpu_usage/bsd.cpp b/src/modules/cpu_usage/bsd.cpp index 663bc0a7..d795a817 100644 --- a/src/modules/cpu_usage/bsd.cpp +++ b/src/modules/cpu_usage/bsd.cpp @@ -34,7 +34,7 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( int ncpu = sysconf(_SC_NPROCESSORS_CONF); size_t sz = CPUSTATES * (ncpu + 1) * sizeof(pcp_time_t); pcp_time_t *cp_time = static_cast(malloc(sz)), *pcp_time = cp_time; - waybar::util::scope_guard cp_time_deleter([cp_time]() { + waybar::util::ScopeGuard cp_time_deleter([cp_time]() { if (cp_time) { free(cp_time); } From 95b0647c911eb90cf641827d0bb3cf61324d285a Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Thu, 26 Oct 2023 22:17:20 +0200 Subject: [PATCH 167/364] chore: increase freebsd timeout --- .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 fb2511b5..0d7f29c4 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v3 - name: Test in FreeBSD VM uses: cross-platform-actions/action@v0.19.1 - timeout-minutes: 120 + timeout-minutes: 180 with: operating_system: freebsd version: "13.2" From f7224d84594627e66e3e900e4636e72ac9aac05c Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Thu, 26 Oct 2023 23:08:57 +0200 Subject: [PATCH 168/364] Initial implementation --- include/factory.hpp | 5 +- include/modules/privacy/privacy.hpp | 44 ++++++ include/modules/privacy/privacy_item.hpp | 46 ++++++ include/util/pipewire/pipewire_backend.hpp | 40 +++++ include/util/pipewire/privacy_node_info.hpp | 36 +++++ meson.build | 15 +- meson_options.txt | 1 + resources/icons/meson.build | 6 + .../waybar-privacy-audio-input-symbolic.svg | 4 + .../waybar-privacy-audio-output-symbolic.svg | 2 + .../waybar-privacy-screen-share-symbolic.svg | 7 + resources/icons/waybar_icons.gresource.xml | 8 + src/bar.cpp | 2 +- src/client.cpp | 6 + src/factory.cpp | 8 +- src/modules/privacy/privacy.cpp | 138 ++++++++++++++++ src/modules/privacy/privacy_item.cpp | 140 ++++++++++++++++ src/util/pipewire_backend.cpp | 149 ++++++++++++++++++ 18 files changed, 653 insertions(+), 4 deletions(-) create mode 100644 include/modules/privacy/privacy.hpp create mode 100644 include/modules/privacy/privacy_item.hpp create mode 100644 include/util/pipewire/pipewire_backend.hpp create mode 100644 include/util/pipewire/privacy_node_info.hpp create mode 100644 resources/icons/meson.build create mode 100644 resources/icons/waybar-privacy-audio-input-symbolic.svg create mode 100644 resources/icons/waybar-privacy-audio-output-symbolic.svg create mode 100644 resources/icons/waybar-privacy-screen-share-symbolic.svg create mode 100644 resources/icons/waybar_icons.gresource.xml create mode 100644 src/modules/privacy/privacy.cpp create mode 100644 src/modules/privacy/privacy_item.cpp create mode 100644 src/util/pipewire_backend.cpp diff --git a/include/factory.hpp b/include/factory.hpp index cb25078d..fea5ba99 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -68,6 +68,9 @@ #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 @@ -101,7 +104,7 @@ namespace waybar { class Factory { public: Factory(const Bar& bar, const Json::Value& config); - AModule* makeModule(const std::string& name) const; + AModule* makeModule(const std::string& name, const std::string& pos) const; private: const Bar& bar_; diff --git a/include/modules/privacy/privacy.hpp b/include/modules/privacy/privacy.hpp new file mode 100644 index 00000000..3ec767a6 --- /dev/null +++ b/include/modules/privacy/privacy.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +#include "ALabel.hpp" +#include "gtkmm/box.h" +#include "modules/privacy/privacy_item.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 { + public: + Privacy(const std::string &, const Json::Value &, const std::string &pos); + auto update() -> void override; + + void onPrivacyNodesChanged(); + + private: + std::list nodes_screenshare; // Screen is being shared + std::list nodes_audio_in; // Application is using the microphone + std::list nodes_audio_out; // Application is outputting audio + + PrivacyItem privacy_item_screenshare; + PrivacyItem privacy_item_audio_input; + PrivacyItem privacy_item_audio_output; + + sigc::connection visibility_conn; + + // Config + Gtk::Box box_; + uint iconSpacing = 4; + uint iconSize = 20; + uint transition_duration = 500; + + std::shared_ptr backend = nullptr; +}; + +} // namespace waybar::modules::privacy diff --git a/include/modules/privacy/privacy_item.hpp b/include/modules/privacy/privacy_item.hpp new file mode 100644 index 00000000..56d17acf --- /dev/null +++ b/include/modules/privacy/privacy_item.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include "gtkmm/box.h" +#include "gtkmm/image.h" +#include "gtkmm/revealer.h" +#include "util/pipewire/privacy_node_info.hpp" + +namespace waybar::modules::privacy { + +class PrivacyItem : public Gtk::Revealer { + public: + PrivacyItem(const Json::Value&, enum util::PipewireBackend::PrivacyNodeType privacy_type_, + const std::string& pos); + + bool is_enabled(); + + void set_in_use(bool in_use); + + void set_icon_size(uint size); + + private: + enum util::PipewireBackend::PrivacyNodeType privacy_type; + + std::mutex mutex_; + sigc::connection signal_conn; + + bool init = false; + bool in_use = false; + std::string lastStatus; + + // Config + bool enabled = true; + std::string iconName = "image-missing-symbolic"; + + Gtk::Box box_; + Gtk::Image icon_; +}; + +} // namespace waybar::modules::privacy diff --git a/include/util/pipewire/pipewire_backend.hpp b/include/util/pipewire/pipewire_backend.hpp new file mode 100644 index 00000000..fc622567 --- /dev/null +++ b/include/util/pipewire/pipewire_backend.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "util/backend_common.hpp" +#include "util/pipewire/privacy_node_info.hpp" + +namespace waybar::util::PipewireBackend { + +class PipewireBackend { + private: + pw_thread_loop* mainloop_; + pw_context* context_; + pw_core* core_; + + spa_hook registry_listener; + + /* Hack to keep constructor inaccessible but still public. + * This is required to be able to use std::make_shared. + * It is important to keep this class only accessible via a reference-counted + * 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 {}; + + public: + std::mutex mutex_; + + pw_registry* registry; + + sigc::signal privacy_nodes_changed_signal_event; + + std::unordered_map privacy_nodes; + + static std::shared_ptr getInstance(); + + PipewireBackend(private_constructor_tag tag); + ~PipewireBackend(); +}; +} // namespace waybar::util::pipewire::PipewireBackend diff --git a/include/util/pipewire/privacy_node_info.hpp b/include/util/pipewire/privacy_node_info.hpp new file mode 100644 index 00000000..c645ade5 --- /dev/null +++ b/include/util/pipewire/privacy_node_info.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include + +namespace waybar::util::PipewireBackend { + +enum PrivacyNodeType { + PRIVACY_NODE_TYPE_NONE, + PRIVACY_NODE_TYPE_VIDEO_INPUT, + PRIVACY_NODE_TYPE_AUDIO_INPUT, + PRIVACY_NODE_TYPE_AUDIO_OUTPUT +}; + +class PrivacyNodeInfo { + public: + PrivacyNodeType type = PRIVACY_NODE_TYPE_NONE; + uint32_t id; + uint32_t client_id; + enum pw_node_state state = PW_NODE_STATE_IDLE; + std::string media_class; + std::string media_name; + std::string node_name; + + struct spa_hook node_listener; + + bool changed = false; + + void* data; + + PrivacyNodeInfo(uint32_t id_, void* data_) : id(id_), data(data_) {} + + ~PrivacyNodeInfo() { spa_hook_remove(&node_listener); } +}; +} // namespace waybar::util::pipewire::PipewireBackend diff --git a/meson.build b/meson.build index bb9abdeb..8bea0ce3 100644 --- a/meson.build +++ b/meson.build @@ -98,6 +98,7 @@ libinput = dependency('libinput', required: get_option('libinput')) libnl = dependency('libnl-3.0', required: get_option('libnl')) libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl')) upower_glib = dependency('upower-glib', required: get_option('upower_glib')) +pipewire = dependency('libpipewire-0.3', required: get_option('pipewire')) playerctl = dependency('playerctl', version : ['>=2.0.0'], required: get_option('mpris')) libpulse = dependency('libpulse', required: get_option('pulseaudio')) libudev = dependency('libudev', required: get_option('libudev')) @@ -273,6 +274,14 @@ if (upower_glib.found() and giounix.found() and not get_option('logind').disable src_files += 'src/modules/upower/upower_tooltip.cpp' endif + +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' +endif + if (playerctl.found() and giounix.found() and not get_option('logind').disabled()) add_project_arguments('-DHAVE_MPRIS', language: 'cpp') src_files += 'src/modules/mpris/mpris.cpp' @@ -373,9 +382,12 @@ endif subdir('protocol') +app_resources = [] +subdir('resources/icons') + executable( 'waybar', - src_files, + [src_files, app_resources], dependencies: [ thread_dep, client_protos, @@ -392,6 +404,7 @@ executable( libnl, libnlgen, upower_glib, + pipewire, playerctl, libpulse, libjack, diff --git a/meson_options.txt b/meson_options.txt index 7dacf087..827f9ac1 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,6 +5,7 @@ option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev s option('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features') option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio') option('upower_glib', type: 'feature', value: 'auto', description: 'Enable support for upower') +option('pipewire', type: 'feature', value: 'auto', description: 'Enable support for pipewire') option('mpris', type: 'feature', value: 'auto', description: 'Enable support for mpris') option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit') option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray') diff --git a/resources/icons/meson.build b/resources/icons/meson.build new file mode 100644 index 00000000..05532d3d --- /dev/null +++ b/resources/icons/meson.build @@ -0,0 +1,6 @@ +gnome = import('gnome') + +app_resources += gnome.compile_resources('icon-resources', + 'waybar_icons.gresource.xml', + c_name: 'waybar_icons' +) diff --git a/resources/icons/waybar-privacy-audio-input-symbolic.svg b/resources/icons/waybar-privacy-audio-input-symbolic.svg new file mode 100644 index 00000000..61356891 --- /dev/null +++ b/resources/icons/waybar-privacy-audio-input-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/icons/waybar-privacy-audio-output-symbolic.svg b/resources/icons/waybar-privacy-audio-output-symbolic.svg new file mode 100644 index 00000000..10ad4f9d --- /dev/null +++ b/resources/icons/waybar-privacy-audio-output-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/resources/icons/waybar-privacy-screen-share-symbolic.svg b/resources/icons/waybar-privacy-screen-share-symbolic.svg new file mode 100644 index 00000000..9738c571 --- /dev/null +++ b/resources/icons/waybar-privacy-screen-share-symbolic.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/icons/waybar_icons.gresource.xml b/resources/icons/waybar_icons.gresource.xml new file mode 100644 index 00000000..077049bf --- /dev/null +++ b/resources/icons/waybar_icons.gresource.xml @@ -0,0 +1,8 @@ + + + + waybar-privacy-audio-input-symbolic.svg + waybar-privacy-audio-output-symbolic.svg + waybar-privacy-screen-share-symbolic.svg + + diff --git a/src/bar.cpp b/src/bar.cpp index d0a187c6..1ffe2ef6 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -760,7 +760,7 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, getModules(factory, ref, group_module); module = group_module; } else { - module = factory.makeModule(ref); + module = factory.makeModule(ref, pos); } std::shared_ptr module_sp(module); diff --git a/src/client.cpp b/src/client.cpp index cd0fa55b..066247e7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -4,6 +4,7 @@ #include +#include "gtkmm/icontheme.h" #include "idle-inhibit-unstable-v1-client-protocol.h" #include "util/clara.hpp" #include "util/format.hpp" @@ -244,6 +245,11 @@ int waybar::Client::main(int argc, char *argv[]) { } 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"); + gdk_display = Gdk::Display::get_default(); if (!gdk_display) { throw std::runtime_error("Can't find display"); diff --git a/src/factory.cpp b/src/factory.cpp index aaf46036..91a882b0 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -10,7 +10,8 @@ waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {} -waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { +waybar::AModule* waybar::Factory::makeModule(const std::string& name, + const std::string& pos) const { try { auto hash_pos = name.find('#'); auto ref = name.substr(0, hash_pos); @@ -30,6 +31,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { return new waybar::modules::upower::UPower(id, config_[name]); } #endif +#ifdef HAVE_PIPEWIRE + if (ref == "privacy") { + return new waybar::modules::privacy::Privacy(id, config_[name], pos); + } +#endif #ifdef HAVE_MPRIS if (ref == "mpris") { return new waybar::modules::mpris::Mpris(id, config_[name]); diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp new file mode 100644 index 00000000..ea97b352 --- /dev/null +++ b/src/modules/privacy/privacy.cpp @@ -0,0 +1,138 @@ +#include "modules/privacy/privacy.hpp" + +#include +#include +#include + +#include +#include +#include + +#include "AModule.hpp" +#include "gtkmm/image.h" + +namespace waybar::modules::privacy { + +using util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT; +using util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT; +using util::PipewireBackend::PRIVACY_NODE_TYPE_NONE; +using util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT; + +Privacy::Privacy(const std::string& id, const Json::Value& config, const std::string& pos) + : AModule(config, "privacy", id), + nodes_screenshare(), + nodes_audio_in(), + nodes_audio_out(), + privacy_item_screenshare(config["screenshare"], PRIVACY_NODE_TYPE_VIDEO_INPUT, pos), + privacy_item_audio_input(config["audio-in"], PRIVACY_NODE_TYPE_AUDIO_INPUT, pos), + privacy_item_audio_output(config["audio-out"], PRIVACY_NODE_TYPE_AUDIO_OUTPUT, pos), + visibility_conn(), + box_(Gtk::ORIENTATION_HORIZONTAL, 0) { + box_.set_name(name_); + box_.add(privacy_item_screenshare); + box_.add(privacy_item_audio_output); + box_.add(privacy_item_audio_input); + + event_box_.add(box_); + + // Icon Spacing + if (config_["icon-spacing"].isUInt()) { + iconSpacing = config_["icon-spacing"].asUInt(); + } + box_.set_spacing(iconSpacing); + + // Icon Size + if (config_["icon-size"].isUInt()) { + iconSize = config_["icon-size"].asUInt(); + } + privacy_item_screenshare.set_icon_size(iconSize); + privacy_item_audio_output.set_icon_size(iconSize); + privacy_item_audio_input.set_icon_size(iconSize); + + // Transition Duration + if (config_["transition-duration"].isUInt()) { + transition_duration = config_["transition-duration"].asUInt(); + } + privacy_item_screenshare.set_transition_duration(transition_duration); + privacy_item_audio_output.set_transition_duration(transition_duration); + privacy_item_audio_input.set_transition_duration(transition_duration); + + if (!privacy_item_screenshare.is_enabled() && !privacy_item_audio_input.is_enabled() && + !privacy_item_audio_output.is_enabled()) { + throw std::runtime_error("No privacy modules enabled"); + } + backend = util::PipewireBackend::PipewireBackend::getInstance(); + backend->privacy_nodes_changed_signal_event.connect( + sigc::mem_fun(*this, &Privacy::onPrivacyNodesChanged)); +} + +void Privacy::onPrivacyNodesChanged() { + nodes_audio_out.clear(); + nodes_audio_in.clear(); + nodes_screenshare.clear(); + + bool screenshare = false; + bool audio_in = false; + bool audio_out = false; + for (auto& node : backend->privacy_nodes) { + if (screenshare && audio_in && audio_out) break; + switch (node.second->state) { + case PW_NODE_STATE_RUNNING: + switch (node.second->type) { + case PRIVACY_NODE_TYPE_VIDEO_INPUT: + screenshare = true; + nodes_screenshare.push_back(node.second); + break; + case PRIVACY_NODE_TYPE_AUDIO_INPUT: + audio_in = true; + nodes_audio_in.push_back(node.second); + break; + case PRIVACY_NODE_TYPE_AUDIO_OUTPUT: + audio_out = true; + nodes_audio_out.push_back(node.second); + break; + case PRIVACY_NODE_TYPE_NONE: + continue; + } + break; + default: + break; + } + } + + dp.emit(); +} + +auto Privacy::update() -> void { + bool screenshare = !nodes_screenshare.empty(); + bool audio_in = !nodes_audio_in.empty(); + bool audio_out = !nodes_audio_out.empty(); + + privacy_item_screenshare.set_in_use(screenshare); + privacy_item_audio_input.set_in_use(audio_in); + privacy_item_audio_output.set_in_use(audio_out); + + // Hide the whole widget if none are in use + bool is_visible = screenshare || audio_in || audio_out; + if (is_visible != 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) { + event_box_.set_visible(true); + } else { + visibility_conn = Glib::signal_timeout().connect(sigc::track_obj( + [this] { + event_box_.set_visible(false); + return false; + }, + *this), + transition_duration); + } + } + + // Call parent update + AModule::update(); +} + +} // namespace waybar::modules::privacy diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp new file mode 100644 index 00000000..943dfdbf --- /dev/null +++ b/src/modules/privacy/privacy_item.cpp @@ -0,0 +1,140 @@ +#include "modules/privacy/privacy_item.hpp" + +#include +#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 { + +PrivacyItem::PrivacyItem(const Json::Value& config_, + enum util::PipewireBackend::PrivacyNodeType privacy_type_, + const std::string& pos) + : Gtk::Revealer(), + privacy_type(privacy_type_), + mutex_(), + signal_conn(), + box_(Gtk::ORIENTATION_HORIZONTAL, 0), + icon_() { + switch (privacy_type) { + case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT: + get_style_context()->add_class("audio-in"); + iconName = "waybar-privacy-audio-input-symbolic"; + break; + case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT: + get_style_context()->add_class("audio-out"); + iconName = "waybar-privacy-audio-output-symbolic"; + break; + case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: + get_style_context()->add_class("screenshare"); + iconName = "waybar-privacy-screen-share-symbolic"; + break; + default: + case util::PipewireBackend::PRIVACY_NODE_TYPE_NONE: + enabled = false; + return; + } + + // Set the reveal transition to not look weird when sliding in + if (pos == "modules-left") { + set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_SLIDE_RIGHT); + } else if (pos == "modules-center") { + set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_CROSSFADE); + } else if (pos == "modules-right") { + set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_SLIDE_LEFT); + } + + box_.set_name("privacy-item"); + box_.add(icon_); + add(box_); + + // Icon Name + if (config_["enabled"].isBool()) { + enabled = config_["enabled"].asBool(); + } + + // Icon Name + if (config_["icon-name"].isString()) { + iconName = config_["icon-name"].asString(); + } + icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID); + + // Don't show by default + set_reveal_child(true); + set_visible(false); +} + +bool PrivacyItem::is_enabled() { return enabled; } + +void PrivacyItem::set_in_use(bool in_use) { + mutex_.lock(); + if (this->in_use == in_use && init) { + mutex_.unlock(); + return; + } + + if (init) { + // Disconnect any previous connection so that it doesn't get activated in + // the future, hiding the module when it should be visible + signal_conn.disconnect(); + + this->in_use = in_use; + if (this->in_use) { + set_visible(true); + signal_conn = Glib::signal_timeout().connect(sigc::track_obj( + [this] { + set_reveal_child(true); + return false; + }, + *this), + 0); + } else { + set_reveal_child(false); + signal_conn = Glib::signal_timeout().connect(sigc::track_obj( + [this] { + set_visible(false); + return false; + }, + *this), + get_transition_duration()); + } + } else { + set_visible(false); + set_reveal_child(false); + } + this->init = true; + + // CSS status class + const std::string status = this->in_use ? "in-use" : ""; + // Remove last status if it exists + if (!lastStatus.empty() && get_style_context()->has_class(lastStatus)) { + get_style_context()->remove_class(lastStatus); + } + // Add the new status class to the Box + if (!status.empty() && !get_style_context()->has_class(status)) { + get_style_context()->add_class(status); + } + lastStatus = status; + + mutex_.unlock(); +} + +void PrivacyItem::set_icon_size(uint size) { icon_.set_pixel_size(size); } + +} // namespace waybar::modules::privacy diff --git a/src/util/pipewire_backend.cpp b/src/util/pipewire_backend.cpp new file mode 100644 index 00000000..a2ac64a1 --- /dev/null +++ b/src/util/pipewire_backend.cpp @@ -0,0 +1,149 @@ +#include "util/pipewire/pipewire_backend.hpp" + +namespace waybar::util::PipewireBackend { + +// TODO: Refresh on suspend wake +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_CLASS) == 0) { + p_node_info->media_class = item->value; + if (strcmp(p_node_info->media_class.c_str(), "Stream/Input/Video") == 0) { + p_node_info->type = PRIVACY_NODE_TYPE_VIDEO_INPUT; + } else if (strcmp(p_node_info->media_class.c_str(), "Stream/Input/Audio") == 0) { + p_node_info->type = PRIVACY_NODE_TYPE_AUDIO_INPUT; + } else if (strcmp(p_node_info->media_class.c_str(), "Stream/Output/Audio") == 0) { + p_node_info->type = PRIVACY_NODE_TYPE_AUDIO_OUTPUT; + } + } 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; + } + } + + if (p_node_info->type != PRIVACY_NODE_TYPE_NONE) { + backend->mutex_.lock(); + p_node_info->changed = true; + backend->privacy_nodes.insert_or_assign(info->id, p_node_info); + backend->mutex_.unlock(); + + backend->privacy_nodes_changed_signal_event.emit(); + } else { + if (p_node_info->changed) { + backend->mutex_.lock(); + PrivacyNodeInfo *node = backend->privacy_nodes.at(info->id); + delete node; + backend->privacy_nodes.erase(info->id); + backend->mutex_.unlock(); + + 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 registry_event_global(void *_data, uint32_t id, uint32_t permissions, const char *type, + uint32_t version, const struct spa_dict *props) { + if (strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return; + + PipewireBackend *backend = static_cast(_data); + struct pw_proxy *proxy = (pw_proxy *)pw_registry_bind(backend->registry, id, type, version, 0); + if (proxy) { + PrivacyNodeInfo *p_node_info; + backend->mutex_.lock(); + if (backend->privacy_nodes.contains(id)) { + p_node_info = backend->privacy_nodes.at(id); + } else { + p_node_info = new PrivacyNodeInfo(id, backend); + } + backend->mutex_.unlock(); + pw_proxy_add_object_listener(proxy, &p_node_info->node_listener, &node_events, p_node_info); + } +} + +static void registry_event_global_remove(void *_data, uint32_t id) { + auto backend = static_cast(_data); + + backend->mutex_.lock(); + if (backend->privacy_nodes.contains(id)) { + PrivacyNodeInfo *node_info = backend->privacy_nodes.at(id); + delete node_info; + 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() { + for (auto &node : privacy_nodes) { + delete node.second; + } + + 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 e73ea8d608e54dca4c99616444343a7c24340e61 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Thu, 26 Oct 2023 23:37:10 +0200 Subject: [PATCH 169/364] Fixed cases where the module would be hidden when it should be visible --- include/modules/privacy/privacy.hpp | 1 + src/modules/privacy/privacy.cpp | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/include/modules/privacy/privacy.hpp b/include/modules/privacy/privacy.hpp index 3ec767a6..b14cf452 100644 --- a/include/modules/privacy/privacy.hpp +++ b/include/modules/privacy/privacy.hpp @@ -30,6 +30,7 @@ class Privacy : public AModule { PrivacyItem privacy_item_audio_input; PrivacyItem privacy_item_audio_output; + std::mutex mutex_; sigc::connection visibility_conn; // Config diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index ea97b352..56fd6d88 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -67,6 +67,7 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st } void Privacy::onPrivacyNodesChanged() { + mutex_.lock(); nodes_audio_out.clear(); nodes_audio_in.clear(); nodes_screenshare.clear(); @@ -100,6 +101,7 @@ void Privacy::onPrivacyNodesChanged() { } } + mutex_.unlock(); dp.emit(); } @@ -121,13 +123,17 @@ auto Privacy::update() -> void { if (is_visible) { event_box_.set_visible(true); } else { - visibility_conn = Glib::signal_timeout().connect(sigc::track_obj( - [this] { - event_box_.set_visible(false); - return false; - }, - *this), - transition_duration); + visibility_conn = Glib::signal_timeout().connect( + sigc::track_obj( + [this] { + bool screenshare = !nodes_screenshare.empty(); + bool audio_in = !nodes_audio_in.empty(); + bool audio_out = !nodes_audio_out.empty(); + event_box_.set_visible(screenshare || audio_in || audio_out); + return false; + }, + *this), + transition_duration); } } From ace319b5158a8e07306b7fb4374357df28bb4411 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Thu, 26 Oct 2023 23:44:04 +0200 Subject: [PATCH 170/364] Updated default CSS to include the privacy module --- resources/style.css | 25 +++++++++++++++++++++++++ src/modules/privacy/privacy_item.cpp | 6 +++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/resources/style.css b/resources/style.css index cf5c5fb0..78cdf604 100644 --- a/resources/style.css +++ b/resources/style.css @@ -278,3 +278,28 @@ label:focus { #scratchpad.empty { background-color: transparent; } + +#privacy { + padding: 0; +} + +#privacy > box { + padding: 0; +} + +#privacy-item { + padding: 0 5px; + color: white; +} + +#privacy-item.screenshare { + background-color: #cf5700; +} + +#privacy-item.audio-in { + background-color: #1ca000; +} + +#privacy-item.audio-out { + background-color: #0069d4; +} diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index 943dfdbf..c859d7e1 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -34,15 +34,15 @@ PrivacyItem::PrivacyItem(const Json::Value& config_, icon_() { switch (privacy_type) { case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT: - get_style_context()->add_class("audio-in"); + box_.get_style_context()->add_class("audio-in"); iconName = "waybar-privacy-audio-input-symbolic"; break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT: - get_style_context()->add_class("audio-out"); + box_.get_style_context()->add_class("audio-out"); iconName = "waybar-privacy-audio-output-symbolic"; break; case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: - get_style_context()->add_class("screenshare"); + box_.get_style_context()->add_class("screenshare"); iconName = "waybar-privacy-screen-share-symbolic"; break; default: From 4a4c888d7d8ee4f86e2ba0fa3115e60fa161a7e4 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Fri, 27 Oct 2023 00:01:40 +0200 Subject: [PATCH 171/364] Fixed linter complaining --- include/util/pipewire/pipewire_backend.hpp | 2 +- include/util/pipewire/privacy_node_info.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/util/pipewire/pipewire_backend.hpp b/include/util/pipewire/pipewire_backend.hpp index fc622567..4e23b282 100644 --- a/include/util/pipewire/pipewire_backend.hpp +++ b/include/util/pipewire/pipewire_backend.hpp @@ -37,4 +37,4 @@ class PipewireBackend { PipewireBackend(private_constructor_tag tag); ~PipewireBackend(); }; -} // namespace waybar::util::pipewire::PipewireBackend +} // namespace waybar::util::PipewireBackend diff --git a/include/util/pipewire/privacy_node_info.hpp b/include/util/pipewire/privacy_node_info.hpp index c645ade5..b370cbb2 100644 --- a/include/util/pipewire/privacy_node_info.hpp +++ b/include/util/pipewire/privacy_node_info.hpp @@ -33,4 +33,4 @@ class PrivacyNodeInfo { ~PrivacyNodeInfo() { spa_hook_remove(&node_listener); } }; -} // namespace waybar::util::pipewire::PipewireBackend +} // namespace waybar::util::PipewireBackend From 1f0ce1a5d93f70aa1a17a5d553fbcb840c71aa64 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Fri, 27 Oct 2023 14:17:43 +0800 Subject: [PATCH 172/364] Fixed variable synchronization exception caused by the "hyprland/workspace" module receiving create or delete IPC requests too quickly --- src/modules/hyprland/workspaces.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 684c2b92..62383f46 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -153,17 +153,25 @@ auto Workspaces::register_ipc() -> void { } auto Workspaces::update() -> void { + //remove workspaces that wait to be removed + unsigned int current_remove_workspace_num = 0; for (const std::string &workspace_to_remove : workspaces_to_remove_) { remove_workspace(workspace_to_remove); + current_remove_workspace_num++; + } + for (unsigned int i = 0; i < current_remove_workspace_num; i++) { + workspaces_to_remove_.erase(workspaces_to_remove_.begin()); } - workspaces_to_remove_.clear(); - + //add workspaces that wait to be created + unsigned int current_create_workspace_num = 0; for (Json::Value const &workspace_to_create : workspaces_to_create_) { create_workspace(workspace_to_create); + current_create_workspace_num++; + } + for (unsigned int i = 0; i < current_create_workspace_num; i++) { + workspaces_to_create_.erase(workspaces_to_create_.begin()); } - - workspaces_to_create_.clear(); // get all active workspaces auto monitors = gIPC->getSocket1JsonReply("monitors"); From 86491e151247f3584624c2e64c9774de509cc114 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Sat, 28 Oct 2023 16:47:06 +0200 Subject: [PATCH 173/364] Call module emit in privacy module contructor --- src/modules/privacy/privacy.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 56fd6d88..f1f92838 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -64,6 +64,8 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st backend = util::PipewireBackend::PipewireBackend::getInstance(); backend->privacy_nodes_changed_signal_event.connect( sigc::mem_fun(*this, &Privacy::onPrivacyNodesChanged)); + + dp.emit(); } void Privacy::onPrivacyNodesChanged() { From 46e36c0e688071142935d7b8356f1dbce32e4985 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Sat, 28 Oct 2023 18:30:50 +0200 Subject: [PATCH 174/364] Simplified the privacy_item hiding/showing logic --- include/modules/privacy/privacy_item.hpp | 4 ++- src/modules/privacy/privacy.cpp | 2 ++ src/modules/privacy/privacy_item.cpp | 43 +++++++++++++----------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/include/modules/privacy/privacy_item.hpp b/include/modules/privacy/privacy_item.hpp index 56d17acf..321cae5c 100644 --- a/include/modules/privacy/privacy_item.hpp +++ b/include/modules/privacy/privacy_item.hpp @@ -29,7 +29,6 @@ class PrivacyItem : public Gtk::Revealer { enum util::PipewireBackend::PrivacyNodeType privacy_type; std::mutex mutex_; - sigc::connection signal_conn; bool init = false; bool in_use = false; @@ -41,6 +40,9 @@ class PrivacyItem : public Gtk::Revealer { Gtk::Box box_; Gtk::Image icon_; + + void on_child_revealed_changed(); + void on_map_changed(); }; } // namespace waybar::modules::privacy diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index f1f92838..06fb8259 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -125,6 +125,8 @@ auto Privacy::update() -> void { if (is_visible) { 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] { diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index c859d7e1..7ed8efe1 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -29,7 +29,6 @@ PrivacyItem::PrivacyItem(const Json::Value& config_, : Gtk::Revealer(), privacy_type(privacy_type_), mutex_(), - signal_conn(), box_(Gtk::ORIENTATION_HORIZONTAL, 0), icon_() { switch (privacy_type) { @@ -75,6 +74,10 @@ PrivacyItem::PrivacyItem(const Json::Value& config_, } icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID); + property_child_revealed().signal_changed().connect( + sigc::mem_fun(*this, &PrivacyItem::on_child_revealed_changed)); + signal_map().connect(sigc::mem_fun(*this, &PrivacyItem::on_map_changed)); + // Don't show by default set_reveal_child(true); set_visible(false); @@ -82,6 +85,18 @@ PrivacyItem::PrivacyItem(const Json::Value& config_, bool PrivacyItem::is_enabled() { return enabled; } +void PrivacyItem::on_child_revealed_changed() { + if (!this->get_child_revealed()) { + set_visible(false); + } +} + +void PrivacyItem::on_map_changed() { + if (this->get_visible()) { + set_reveal_child(true); + } +} + void PrivacyItem::set_in_use(bool in_use) { mutex_.lock(); if (this->in_use == in_use && init) { @@ -90,29 +105,19 @@ void PrivacyItem::set_in_use(bool in_use) { } if (init) { - // Disconnect any previous connection so that it doesn't get activated in - // the future, hiding the module when it should be visible - signal_conn.disconnect(); - this->in_use = in_use; if (this->in_use) { set_visible(true); - signal_conn = Glib::signal_timeout().connect(sigc::track_obj( - [this] { - set_reveal_child(true); - return false; - }, - *this), - 0); + // The `on_map_changed` callback will call `set_reveal_child(true)` + // when the widget is realized so we don't need to call that here. + // This fixes a bug where the revealer wouldn't start the animation + // due to us changing the visibility at the same time. } else { set_reveal_child(false); - signal_conn = Glib::signal_timeout().connect(sigc::track_obj( - [this] { - set_visible(false); - return false; - }, - *this), - get_transition_duration()); + // The `on_child_revealed_changed` callback will call `set_visible(false)` + // when the animation has finished so we don't need to call that here. + // We do this so that the widget gets hidden after the revealer hide animation + // has finished. } } else { set_visible(false); From d32da917e497546e63270637bb42cb9c211588d7 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Sun, 29 Oct 2023 00:17:53 +0200 Subject: [PATCH 175/364] Added tooltips --- include/modules/privacy/privacy_item.hpp | 15 +++-- include/util/pipewire/pipewire_backend.hpp | 2 +- include/util/pipewire/privacy_node_info.hpp | 35 +++++++++++- src/modules/privacy/privacy.cpp | 30 +++++----- src/modules/privacy/privacy_item.cpp | 63 +++++++++++++++++---- src/util/pipewire_backend.cpp | 18 +++--- 6 files changed, 121 insertions(+), 42 deletions(-) diff --git a/include/modules/privacy/privacy_item.hpp b/include/modules/privacy/privacy_item.hpp index 321cae5c..802ca08e 100644 --- a/include/modules/privacy/privacy_item.hpp +++ b/include/modules/privacy/privacy_item.hpp @@ -12,12 +12,15 @@ #include "gtkmm/revealer.h" #include "util/pipewire/privacy_node_info.hpp" +using waybar::util::PipewireBackend::PrivacyNodeInfo; +using waybar::util::PipewireBackend::PrivacyNodeType; + namespace waybar::modules::privacy { class PrivacyItem : public Gtk::Revealer { public: - PrivacyItem(const Json::Value&, enum util::PipewireBackend::PrivacyNodeType privacy_type_, - const std::string& pos); + PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_, + std::list *nodes, const std::string &pos); bool is_enabled(); @@ -26,9 +29,10 @@ class PrivacyItem : public Gtk::Revealer { void set_icon_size(uint size); private: - enum util::PipewireBackend::PrivacyNodeType privacy_type; + enum PrivacyNodeType privacy_type; + std::list *nodes; - std::mutex mutex_; + Gtk::Box tooltip_window; bool init = false; bool in_use = false; @@ -37,12 +41,15 @@ class PrivacyItem : public Gtk::Revealer { // Config bool enabled = true; std::string iconName = "image-missing-symbolic"; + bool tooltip = true; + uint tooltipIconSize = 24; Gtk::Box box_; Gtk::Image icon_; void on_child_revealed_changed(); void on_map_changed(); + void update_tooltip(); }; } // namespace waybar::modules::privacy diff --git a/include/util/pipewire/pipewire_backend.hpp b/include/util/pipewire/pipewire_backend.hpp index 4e23b282..8eb0184a 100644 --- a/include/util/pipewire/pipewire_backend.hpp +++ b/include/util/pipewire/pipewire_backend.hpp @@ -30,7 +30,7 @@ class PipewireBackend { sigc::signal privacy_nodes_changed_signal_event; - std::unordered_map privacy_nodes; + std::unordered_map privacy_nodes; static std::shared_ptr getInstance(); diff --git a/include/util/pipewire/privacy_node_info.hpp b/include/util/pipewire/privacy_node_info.hpp index b370cbb2..1c523f97 100644 --- a/include/util/pipewire/privacy_node_info.hpp +++ b/include/util/pipewire/privacy_node_info.hpp @@ -4,6 +4,8 @@ #include +#include "util/gtk_icon.hpp" + namespace waybar::util::PipewireBackend { enum PrivacyNodeType { @@ -22,15 +24,44 @@ class PrivacyNodeInfo { 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 node_listener; bool changed = false; - void* data; + void *data; - PrivacyNodeInfo(uint32_t id_, void* data_) : id(id_), data(data_) {} + PrivacyNodeInfo(uint32_t id_, void *data_) : id(id_), data(data_) {} ~PrivacyNodeInfo() { spa_hook_remove(&node_listener); } + + 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}; + 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; + } }; } // namespace waybar::util::PipewireBackend diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 06fb8259..2f7e4235 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -23,9 +23,12 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st nodes_screenshare(), nodes_audio_in(), nodes_audio_out(), - privacy_item_screenshare(config["screenshare"], PRIVACY_NODE_TYPE_VIDEO_INPUT, pos), - privacy_item_audio_input(config["audio-in"], PRIVACY_NODE_TYPE_AUDIO_INPUT, pos), - privacy_item_audio_output(config["audio-out"], PRIVACY_NODE_TYPE_AUDIO_OUTPUT, pos), + privacy_item_screenshare(config["screenshare"], PRIVACY_NODE_TYPE_VIDEO_INPUT, + &nodes_screenshare, pos), + privacy_item_audio_input(config["audio-in"], PRIVACY_NODE_TYPE_AUDIO_INPUT, &nodes_audio_in, + pos), + privacy_item_audio_output(config["audio-out"], PRIVACY_NODE_TYPE_AUDIO_OUTPUT, + &nodes_audio_out, pos), visibility_conn(), box_(Gtk::ORIENTATION_HORIZONTAL, 0) { box_.set_name(name_); @@ -74,25 +77,18 @@ void Privacy::onPrivacyNodesChanged() { nodes_audio_in.clear(); nodes_screenshare.clear(); - bool screenshare = false; - bool audio_in = false; - bool audio_out = false; for (auto& node : backend->privacy_nodes) { - if (screenshare && audio_in && audio_out) break; - switch (node.second->state) { + switch (node.second.state) { case PW_NODE_STATE_RUNNING: - switch (node.second->type) { + switch (node.second.type) { case PRIVACY_NODE_TYPE_VIDEO_INPUT: - screenshare = true; - nodes_screenshare.push_back(node.second); + nodes_screenshare.push_back(&node.second); break; case PRIVACY_NODE_TYPE_AUDIO_INPUT: - audio_in = true; - nodes_audio_in.push_back(node.second); + nodes_audio_in.push_back(&node.second); break; case PRIVACY_NODE_TYPE_AUDIO_OUTPUT: - audio_out = true; - nodes_audio_out.push_back(node.second); + nodes_audio_out.push_back(&node.second); break; case PRIVACY_NODE_TYPE_NONE: continue; @@ -108,6 +104,7 @@ void Privacy::onPrivacyNodesChanged() { } auto Privacy::update() -> void { + mutex_.lock(); bool screenshare = !nodes_screenshare.empty(); bool audio_in = !nodes_audio_in.empty(); bool audio_out = !nodes_audio_out.empty(); @@ -115,6 +112,7 @@ auto Privacy::update() -> void { privacy_item_screenshare.set_in_use(screenshare); privacy_item_audio_input.set_in_use(audio_in); privacy_item_audio_output.set_in_use(audio_out); + mutex_.unlock(); // Hide the whole widget if none are in use bool is_visible = screenshare || audio_in || audio_out; @@ -130,9 +128,11 @@ auto Privacy::update() -> void { visibility_conn = Glib::signal_timeout().connect( sigc::track_obj( [this] { + mutex_.lock(); bool screenshare = !nodes_screenshare.empty(); bool audio_in = !nodes_audio_in.empty(); bool audio_out = !nodes_audio_out.empty(); + mutex_.unlock(); event_box_.set_visible(screenshare || audio_in || audio_out); return false; }, diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index 7ed8efe1..f4c04ede 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -23,12 +23,12 @@ namespace waybar::modules::privacy { -PrivacyItem::PrivacyItem(const Json::Value& config_, - enum util::PipewireBackend::PrivacyNodeType privacy_type_, - const std::string& pos) +PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_, + std::list *nodes_, const std::string &pos) : Gtk::Revealer(), privacy_type(privacy_type_), - mutex_(), + nodes(nodes_), + tooltip_window(Gtk::ORIENTATION_VERTICAL, 0), box_(Gtk::ORIENTATION_HORIZONTAL, 0), icon_() { switch (privacy_type) { @@ -74,6 +74,26 @@ PrivacyItem::PrivacyItem(const Json::Value& config_, } icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID); + // Tooltip Icon Size + if (config_["tooltip-icon-size"].isUInt()) { + tooltipIconSize = config_["tooltip-icon-size"].asUInt(); + } + // Tooltip + if (config_["tooltip"].isString()) { + tooltip = config_["tooltip"].asBool(); + } + set_has_tooltip(tooltip); + 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)); + } + property_child_revealed().signal_changed().connect( sigc::mem_fun(*this, &PrivacyItem::on_child_revealed_changed)); signal_map().connect(sigc::mem_fun(*this, &PrivacyItem::on_map_changed)); @@ -83,6 +103,31 @@ PrivacyItem::PrivacyItem(const Json::Value& config_, set_visible(false); } +void PrivacyItem::update_tooltip() { + // Removes all old nodes + for (auto child : tooltip_window.get_children()) { + delete child; + } + + for (auto *node : *nodes) { + 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->get_icon_name(), Gtk::ICON_SIZE_INVALID); + box->add(*node_icon); + + // Set model + Gtk::Label *node_name = new Gtk::Label(node->get_name()); + box->add(*node_name); + + tooltip_window.add(*box); + } + + tooltip_window.show_all(); +} + bool PrivacyItem::is_enabled() { return enabled; } void PrivacyItem::on_child_revealed_changed() { @@ -98,12 +143,12 @@ void PrivacyItem::on_map_changed() { } void PrivacyItem::set_in_use(bool in_use) { - mutex_.lock(); - if (this->in_use == in_use && init) { - mutex_.unlock(); - return; + if (in_use) { + update_tooltip(); } + if (this->in_use == in_use && init) return; + if (init) { this->in_use = in_use; if (this->in_use) { @@ -136,8 +181,6 @@ void PrivacyItem::set_in_use(bool in_use) { get_style_context()->add_class(status); } lastStatus = status; - - mutex_.unlock(); } void PrivacyItem::set_icon_size(uint size) { icon_.set_pixel_size(size); } diff --git a/src/util/pipewire_backend.cpp b/src/util/pipewire_backend.cpp index a2ac64a1..47f4dc4f 100644 --- a/src/util/pipewire_backend.cpp +++ b/src/util/pipewire_backend.cpp @@ -26,21 +26,25 @@ static void get_node_info(void *data_, const struct pw_node_info *info) { 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; } } if (p_node_info->type != PRIVACY_NODE_TYPE_NONE) { backend->mutex_.lock(); p_node_info->changed = true; - backend->privacy_nodes.insert_or_assign(info->id, p_node_info); + backend->privacy_nodes.insert_or_assign(info->id, *p_node_info); backend->mutex_.unlock(); backend->privacy_nodes_changed_signal_event.emit(); } else { if (p_node_info->changed) { backend->mutex_.lock(); - PrivacyNodeInfo *node = backend->privacy_nodes.at(info->id); - delete node; backend->privacy_nodes.erase(info->id); backend->mutex_.unlock(); @@ -64,7 +68,7 @@ static void registry_event_global(void *_data, uint32_t id, uint32_t permissions PrivacyNodeInfo *p_node_info; backend->mutex_.lock(); if (backend->privacy_nodes.contains(id)) { - p_node_info = backend->privacy_nodes.at(id); + p_node_info = &backend->privacy_nodes.at(id); } else { p_node_info = new PrivacyNodeInfo(id, backend); } @@ -78,8 +82,6 @@ static void registry_event_global_remove(void *_data, uint32_t id) { backend->mutex_.lock(); if (backend->privacy_nodes.contains(id)) { - PrivacyNodeInfo *node_info = backend->privacy_nodes.at(id); - delete node_info; backend->privacy_nodes.erase(id); } backend->mutex_.unlock(); @@ -118,10 +120,6 @@ PipewireBackend::PipewireBackend(private_constructor_tag tag) } PipewireBackend::~PipewireBackend() { - for (auto &node : privacy_nodes) { - delete node.second; - } - if (registry != nullptr) { pw_proxy_destroy((struct pw_proxy *)registry); } From c4226f3745b26a2439017e307c2febde48e59ece Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:01:47 +0100 Subject: [PATCH 176/364] Readded signal_timeout instead of map to fix indicator being stuck --- include/modules/privacy/privacy_item.hpp | 4 +-- src/modules/privacy/privacy_item.cpp | 44 ++++++++++-------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/include/modules/privacy/privacy_item.hpp b/include/modules/privacy/privacy_item.hpp index 802ca08e..ac94d168 100644 --- a/include/modules/privacy/privacy_item.hpp +++ b/include/modules/privacy/privacy_item.hpp @@ -32,6 +32,8 @@ class PrivacyItem : public Gtk::Revealer { enum PrivacyNodeType privacy_type; std::list *nodes; + sigc::connection signal_conn; + Gtk::Box tooltip_window; bool init = false; @@ -47,8 +49,6 @@ class PrivacyItem : public Gtk::Revealer { Gtk::Box box_; Gtk::Image icon_; - void on_child_revealed_changed(); - void on_map_changed(); void update_tooltip(); }; diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index f4c04ede..9f1c0819 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -28,6 +27,7 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac : Gtk::Revealer(), privacy_type(privacy_type_), nodes(nodes_), + signal_conn(), tooltip_window(Gtk::ORIENTATION_VERTICAL, 0), box_(Gtk::ORIENTATION_HORIZONTAL, 0), icon_() { @@ -94,10 +94,6 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac *this)); } - property_child_revealed().signal_changed().connect( - sigc::mem_fun(*this, &PrivacyItem::on_child_revealed_changed)); - signal_map().connect(sigc::mem_fun(*this, &PrivacyItem::on_map_changed)); - // Don't show by default set_reveal_child(true); set_visible(false); @@ -130,18 +126,6 @@ void PrivacyItem::update_tooltip() { bool PrivacyItem::is_enabled() { return enabled; } -void PrivacyItem::on_child_revealed_changed() { - if (!this->get_child_revealed()) { - set_visible(false); - } -} - -void PrivacyItem::on_map_changed() { - if (this->get_visible()) { - set_reveal_child(true); - } -} - void PrivacyItem::set_in_use(bool in_use) { if (in_use) { update_tooltip(); @@ -150,20 +134,30 @@ void PrivacyItem::set_in_use(bool in_use) { if (this->in_use == in_use && init) return; if (init) { + // Disconnect any previous connection so that it doesn't get activated in + // the future, hiding the module when it should be visible + signal_conn.disconnect(); + this->in_use = in_use; + guint duration = 0; if (this->in_use) { set_visible(true); - // The `on_map_changed` callback will call `set_reveal_child(true)` - // when the widget is realized so we don't need to call that here. - // This fixes a bug where the revealer wouldn't start the animation - // due to us changing the visibility at the same time. } else { set_reveal_child(false); - // The `on_child_revealed_changed` callback will call `set_visible(false)` - // when the animation has finished so we don't need to call that here. - // We do this so that the widget gets hidden after the revealer hide animation - // has finished. + duration = get_transition_duration(); } + + signal_conn = Glib::signal_timeout().connect(sigc::track_obj( + [this] { + if (this->in_use) { + set_reveal_child(true); + } else { + set_visible(false); + } + return false; + }, + *this), + duration); } else { set_visible(false); set_reveal_child(false); From 67422eea3610f4578debcd475e13a8aabf1e240e Mon Sep 17 00:00:00 2001 From: cvhere <135428127+cvhere@users.noreply.github.com> Date: Tue, 31 Oct 2023 11:14:23 +0530 Subject: [PATCH 177/364] Skips logging NoActivePlayer error msgs --- src/modules/mpris/mpris.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index feb1f60f..cd69c336 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -574,7 +574,13 @@ auto Mpris::getPlayerInfo() -> std::optional { return info; errorexit: - spdlog::error("mpris[{}]: {}", info.name, error->message); + std::string errorMsg = error->message; + // When mpris checks for active player sessions periodically(5 secs), NoActivePlayer error message is + // thrown when there are no active sessions. This error message is spamming logs without having any value + // addition. Log the error only if the error we recceived is not NoActivePlayer. + if(errorMsg.rfind("GDBus.Error:com.github.altdesktop.playerctld.NoActivePlayer") == std::string::npos){ + spdlog::error("mpris[{}]: {}", info.name, error->message); + } return std::nullopt; } From c60a8e9836d72de69c9a02ddb4dd832912a8d706 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:46:21 +0100 Subject: [PATCH 178/364] free pipewire listeners on proxy destruction --- include/util/pipewire/privacy_node_info.hpp | 8 ++++++-- src/util/pipewire_backend.cpp | 19 +++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/include/util/pipewire/privacy_node_info.hpp b/include/util/pipewire/privacy_node_info.hpp index 1c523f97..cd50e3a0 100644 --- a/include/util/pipewire/privacy_node_info.hpp +++ b/include/util/pipewire/privacy_node_info.hpp @@ -29,7 +29,8 @@ class PrivacyNodeInfo { std::string pipewire_access_portal_app_id; std::string application_icon_name; - struct spa_hook node_listener; + struct spa_hook object_listener; + struct spa_hook proxy_listener; bool changed = false; @@ -37,7 +38,10 @@ class PrivacyNodeInfo { PrivacyNodeInfo(uint32_t id_, void *data_) : id(id_), data(data_) {} - ~PrivacyNodeInfo() { spa_hook_remove(&node_listener); } + ~PrivacyNodeInfo() { + spa_hook_remove(&object_listener); + spa_hook_remove(&proxy_listener); + } std::string get_name() { const std::vector names{&application_name, &node_name}; diff --git a/src/util/pipewire_backend.cpp b/src/util/pipewire_backend.cpp index 47f4dc4f..627d62f9 100644 --- a/src/util/pipewire_backend.cpp +++ b/src/util/pipewire_backend.cpp @@ -1,5 +1,7 @@ #include "util/pipewire/pipewire_backend.hpp" +#include "util/pipewire/privacy_node_info.hpp" + namespace waybar::util::PipewireBackend { // TODO: Refresh on suspend wake @@ -58,9 +60,21 @@ static const struct pw_node_events 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 (strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return; + if (!props || strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return; PipewireBackend *backend = static_cast(_data); struct pw_proxy *proxy = (pw_proxy *)pw_registry_bind(backend->registry, id, type, version, 0); @@ -73,7 +87,8 @@ static void registry_event_global(void *_data, uint32_t id, uint32_t permissions p_node_info = new PrivacyNodeInfo(id, backend); } backend->mutex_.unlock(); - pw_proxy_add_object_listener(proxy, &p_node_info->node_listener, &node_events, p_node_info); + 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); } } From 49caa4bf31658555e69e9b44f9b0a6dfc25aca12 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Tue, 31 Oct 2023 11:37:28 +0100 Subject: [PATCH 179/364] Add the PrivacyNodeInfo object as pw_proxy data --- include/util/pipewire/pipewire_backend.hpp | 2 +- include/util/pipewire/privacy_node_info.hpp | 11 +--- src/modules/privacy/privacy.cpp | 10 +-- src/util/pipewire_backend.cpp | 73 ++++++++++----------- 4 files changed, 40 insertions(+), 56 deletions(-) diff --git a/include/util/pipewire/pipewire_backend.hpp b/include/util/pipewire/pipewire_backend.hpp index 8eb0184a..4e23b282 100644 --- a/include/util/pipewire/pipewire_backend.hpp +++ b/include/util/pipewire/pipewire_backend.hpp @@ -30,7 +30,7 @@ class PipewireBackend { sigc::signal privacy_nodes_changed_signal_event; - std::unordered_map privacy_nodes; + std::unordered_map privacy_nodes; static std::shared_ptr getInstance(); diff --git a/include/util/pipewire/privacy_node_info.hpp b/include/util/pipewire/privacy_node_info.hpp index cd50e3a0..3b7f446d 100644 --- a/include/util/pipewire/privacy_node_info.hpp +++ b/include/util/pipewire/privacy_node_info.hpp @@ -32,17 +32,8 @@ class PrivacyNodeInfo { struct spa_hook object_listener; struct spa_hook proxy_listener; - bool changed = false; - void *data; - PrivacyNodeInfo(uint32_t id_, void *data_) : id(id_), data(data_) {} - - ~PrivacyNodeInfo() { - spa_hook_remove(&object_listener); - spa_hook_remove(&proxy_listener); - } - std::string get_name() { const std::vector names{&application_name, &node_name}; std::string name = "Unknown Application"; @@ -59,7 +50,7 @@ class PrivacyNodeInfo { std::string get_icon_name() { const std::vector names{&application_icon_name, &pipewire_access_portal_app_id, &application_name, &node_name}; - std::string name = "application-x-executable-symbolic"; + const std::string name = "application-x-executable-symbolic"; for (auto &name_ : names) { if (name_ != nullptr && name_->length() > 0 && DefaultGtkIconThemeWrapper::has_icon(*name_)) { return *name_; diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 2f7e4235..72b7928b 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -78,17 +78,17 @@ void Privacy::onPrivacyNodesChanged() { nodes_screenshare.clear(); for (auto& node : backend->privacy_nodes) { - switch (node.second.state) { + switch (node.second->state) { case PW_NODE_STATE_RUNNING: - switch (node.second.type) { + switch (node.second->type) { case PRIVACY_NODE_TYPE_VIDEO_INPUT: - nodes_screenshare.push_back(&node.second); + nodes_screenshare.push_back(node.second); break; case PRIVACY_NODE_TYPE_AUDIO_INPUT: - nodes_audio_in.push_back(&node.second); + nodes_audio_in.push_back(node.second); break; case PRIVACY_NODE_TYPE_AUDIO_OUTPUT: - nodes_audio_out.push_back(&node.second); + nodes_audio_out.push_back(node.second); break; case PRIVACY_NODE_TYPE_NONE: continue; diff --git a/src/util/pipewire_backend.cpp b/src/util/pipewire_backend.cpp index 627d62f9..5449cddc 100644 --- a/src/util/pipewire_backend.cpp +++ b/src/util/pipewire_backend.cpp @@ -4,7 +4,6 @@ namespace waybar::util::PipewireBackend { -// TODO: Refresh on suspend wake 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; @@ -15,15 +14,6 @@ static void get_node_info(void *data_, const struct pw_node_info *info) { 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_CLASS) == 0) { - p_node_info->media_class = item->value; - if (strcmp(p_node_info->media_class.c_str(), "Stream/Input/Video") == 0) { - p_node_info->type = PRIVACY_NODE_TYPE_VIDEO_INPUT; - } else if (strcmp(p_node_info->media_class.c_str(), "Stream/Input/Audio") == 0) { - p_node_info->type = PRIVACY_NODE_TYPE_AUDIO_INPUT; - } else if (strcmp(p_node_info->media_class.c_str(), "Stream/Output/Audio") == 0) { - p_node_info->type = PRIVACY_NODE_TYPE_AUDIO_OUTPUT; - } } 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) { @@ -37,22 +27,7 @@ static void get_node_info(void *data_, const struct pw_node_info *info) { } } - if (p_node_info->type != PRIVACY_NODE_TYPE_NONE) { - backend->mutex_.lock(); - p_node_info->changed = true; - backend->privacy_nodes.insert_or_assign(info->id, *p_node_info); - backend->mutex_.unlock(); - - backend->privacy_nodes_changed_signal_event.emit(); - } else { - if (p_node_info->changed) { - backend->mutex_.lock(); - backend->privacy_nodes.erase(info->id); - backend->mutex_.unlock(); - - backend->privacy_nodes_changed_signal_event.emit(); - } - } + backend->privacy_nodes_changed_signal_event.emit(); } static const struct pw_node_events node_events = { @@ -76,27 +51,45 @@ static void registry_event_global(void *_data, uint32_t id, uint32_t permissions uint32_t version, const struct spa_dict *props) { if (!props || strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return; - PipewireBackend *backend = static_cast(_data); - struct pw_proxy *proxy = (pw_proxy *)pw_registry_bind(backend->registry, id, type, version, 0); - if (proxy) { - PrivacyNodeInfo *p_node_info; - backend->mutex_.lock(); - if (backend->privacy_nodes.contains(id)) { - p_node_info = &backend->privacy_nodes.at(id); - } else { - p_node_info = new PrivacyNodeInfo(id, backend); - } - backend->mutex_.unlock(); - 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); + 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(); - if (backend->privacy_nodes.contains(id)) { + auto iter = backend->privacy_nodes.find(id); + if(iter != backend->privacy_nodes.end()) { backend->privacy_nodes.erase(id); } backend->mutex_.unlock(); From e9a66d68b7fdbe605ecc98eef2840c624d1ab242 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 31 Oct 2023 20:27:00 +0300 Subject: [PATCH 180/364] Fix debug mode. cava issue Signed-off-by: Viktar Lukashonak --- include/modules/cava.hpp | 14 ++++++++------ meson.build | 2 +- src/modules/cava.cpp | 16 ++++++++-------- subprojects/cava.wrap | 8 ++++---- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/include/modules/cava.hpp b/include/modules/cava.hpp index d4da2b77..00c23123 100644 --- a/include/modules/cava.hpp +++ b/include/modules/cava.hpp @@ -3,9 +3,11 @@ #include "ALabel.hpp" #include "util/sleeper_thread.hpp" +namespace cava { extern "C" { #include } +} namespace waybar::modules { using namespace std::literals::chrono_literals; @@ -21,13 +23,13 @@ class Cava final : public ALabel { util::SleeperThread thread_; util::SleeperThread thread_fetch_input_; - struct error_s error_ {}; // cava errors - struct config_params prm_ {}; // cava parameters - struct audio_raw audio_raw_ {}; // cava handled raw audio data(is based on audio_data) - struct audio_data audio_data_ {}; // cava audio data - struct cava_plan* plan_; //{new cava_plan{}}; + struct cava::error_s error_ {}; // cava errors + struct cava::config_params prm_ {}; // cava parameters + struct cava::audio_raw audio_raw_ {}; // cava handled raw audio data(is based on audio_data) + struct cava::audio_data audio_data_ {}; // cava audio data + struct cava::cava_plan* plan_; //{new cava_plan{}}; // Cava API to read audio source - ptr input_source_; + cava::ptr input_source_; // Delay to handle audio source std::chrono::milliseconds frame_time_milsec_{1s}; // Text to display diff --git a/meson.build b/meson.build index e71807ec..d32ab1cc 100644 --- a/meson.build +++ b/meson.build @@ -346,7 +346,7 @@ if get_option('experimental') endif cava = dependency('cava', - version : '>=0.8.5', + version : '>=0.9.1', 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 be9bef4e..6111258a 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -25,7 +25,7 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) // Override cava parameters by the user config prm_.inAtty = 0; - prm_.output = output_method::OUTPUT_RAW; + prm_.output = cava::output_method::OUTPUT_RAW; strcpy(prm_.data_format, "ascii"); strcpy(prm_.raw_target, "/dev/stdout"); prm_.ascii_range = config_["format-icons"].size() - 1; @@ -34,9 +34,9 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) prm_.bar_spacing = 0; prm_.bar_height = 32; prm_.bar_width = 1; - prm_.orientation = ORIENT_TOP; - prm_.xaxis = xaxis_scale::NONE; - prm_.mono_opt = AVERAGE; + prm_.orientation = cava::ORIENT_TOP; + prm_.xaxis = cava::xaxis_scale::NONE; + prm_.mono_opt = cava::AVERAGE; prm_.autobars = 0; prm_.gravity = 0; prm_.integral = 1; @@ -51,7 +51,7 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) prm_.upper_cut_off = config_["higher_cutoff_freq"].asLargestInt(); if (config_["sleep_timer"].isInt()) prm_.sleep_timer = config_["sleep_timer"].asInt(); if (config_["method"].isString()) - prm_.input = input_method_by_name(config_["method"].asString().c_str()); + 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(); @@ -65,7 +65,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()); // Make cava parameters configuration - plan_ = new cava_plan{}; + plan_ = new cava::cava_plan{}; audio_raw_.height = prm_.ascii_range; audio_data_.format = -1; @@ -155,12 +155,12 @@ auto waybar::modules::Cava::update() -> void { downThreadDelay(frame_time_milsec_, suspend_silence_delay_); // Process: execute cava pthread_mutex_lock(&audio_data_.lock); - cava_execute(audio_data_.cava_in, audio_data_.samples_counter, audio_raw_.cava_out, plan_); + cava::cava_execute(audio_data_.cava_in, audio_data_.samples_counter, audio_raw_.cava_out, plan_); if (audio_data_.samples_counter > 0) audio_data_.samples_counter = 0; pthread_mutex_unlock(&audio_data_.lock); // Do transformation under raw data - audio_raw_fetch(&audio_raw_, &prm_, &rePaint_); + audio_raw_fetch(&audio_raw_, &prm_, &rePaint_, plan_); if (rePaint_ == 1) { text_.clear(); diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap index f6973c83..73fc9512 100644 --- a/subprojects/cava.wrap +++ b/subprojects/cava.wrap @@ -1,7 +1,7 @@ [wrap-file] -directory = cava-0.8.5 -source_url = https://github.com/LukashonakV/cava/archive/0.8.5.tar.gz -source_filename = cava-0.8.5.tar.gz -source_hash = 9ce3df7d374dc83ed0704fe3caef5e00600ce061d85608aad4142d2c59aa4647 +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 [provide] cava = cava_dep From b9b89cce7efe18d4aa86b11d03e3d4f53a7f8618 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 31 Oct 2023 20:47:56 +0300 Subject: [PATCH 181/364] Happy linter Signed-off-by: Viktar Lukashonak --- src/modules/cava.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 6111258a..4b2ca785 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -155,7 +155,8 @@ auto waybar::modules::Cava::update() -> void { downThreadDelay(frame_time_milsec_, suspend_silence_delay_); // Process: execute cava pthread_mutex_lock(&audio_data_.lock); - cava::cava_execute(audio_data_.cava_in, audio_data_.samples_counter, audio_raw_.cava_out, plan_); + cava::cava_execute(audio_data_.cava_in, audio_data_.samples_counter, audio_raw_.cava_out, + plan_); if (audio_data_.samples_counter > 0) audio_data_.samples_counter = 0; pthread_mutex_unlock(&audio_data_.lock); From f511e6183253d40500b08c5a44a51ebf1ba505ba Mon Sep 17 00:00:00 2001 From: Chris Pahl Date: Tue, 31 Oct 2023 19:40:54 +0100 Subject: [PATCH 182/364] fix: custom: do not crash if input text is not valid utf-8 --- src/modules/custom.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 287b5588..3a3e0169 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -155,6 +155,7 @@ auto waybar::modules::Custom::update() -> void { } else { parseOutputRaw(); } + auto str = fmt::format(fmt::runtime(format_), text_, fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); @@ -195,18 +196,23 @@ void waybar::modules::Custom::parseOutputRaw() { std::string line; int i = 0; while (getline(output, line)) { + Glib::ustring validated_line = line; + if(!validated_line.validate()) { + validated_line = validated_line.make_valid(); + } + if (i == 0) { if (config_["escape"].isBool() && config_["escape"].asBool()) { - text_ = Glib::Markup::escape_text(line); + text_ = Glib::Markup::escape_text(validated_line); } else { - text_ = line; + text_ = validated_line; } - tooltip_ = line; + tooltip_ = validated_line; class_.clear(); } else if (i == 1) { - tooltip_ = line; + tooltip_ = validated_line; } else if (i == 2) { - class_.push_back(line); + class_.push_back(validated_line); } else { break; } From 9012cebbf2588e3962e2e7c3da8eb47342204d52 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 31 Oct 2023 23:31:58 +0300 Subject: [PATCH 183/364] Happy Linter Signed-off-by: Viktar Lukashonak --- include/modules/cava.hpp | 2 +- src/modules/custom.cpp | 2 +- src/modules/hyprland/workspaces.cpp | 4 ++-- src/modules/mpris/mpris.cpp | 10 ++++++---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/modules/cava.hpp b/include/modules/cava.hpp index 43924db0..430c71b7 100644 --- a/include/modules/cava.hpp +++ b/include/modules/cava.hpp @@ -7,7 +7,7 @@ namespace cava { extern "C" { #include } -} +} // namespace cava namespace waybar::modules { using namespace std::literals::chrono_literals; diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 3a3e0169..fa03c3be 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -197,7 +197,7 @@ void waybar::modules::Custom::parseOutputRaw() { int i = 0; while (getline(output, line)) { Glib::ustring validated_line = line; - if(!validated_line.validate()) { + if (!validated_line.validate()) { validated_line = validated_line.make_valid(); } diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 62383f46..e2b3da50 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -153,7 +153,7 @@ auto Workspaces::register_ipc() -> void { } auto Workspaces::update() -> void { - //remove workspaces that wait to be removed + // remove workspaces that wait to be removed unsigned int current_remove_workspace_num = 0; for (const std::string &workspace_to_remove : workspaces_to_remove_) { remove_workspace(workspace_to_remove); @@ -163,7 +163,7 @@ auto Workspaces::update() -> void { workspaces_to_remove_.erase(workspaces_to_remove_.begin()); } - //add workspaces that wait to be created + // add workspaces that wait to be created unsigned int current_create_workspace_num = 0; for (Json::Value const &workspace_to_create : workspaces_to_create_) { create_workspace(workspace_to_create); diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index cd69c336..eea9a82b 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -575,10 +575,12 @@ auto Mpris::getPlayerInfo() -> std::optional { errorexit: std::string errorMsg = error->message; - // When mpris checks for active player sessions periodically(5 secs), NoActivePlayer error message is - // thrown when there are no active sessions. This error message is spamming logs without having any value - // addition. Log the error only if the error we recceived is not NoActivePlayer. - if(errorMsg.rfind("GDBus.Error:com.github.altdesktop.playerctld.NoActivePlayer") == std::string::npos){ + // When mpris checks for active player sessions periodically(5 secs), NoActivePlayer error + // message is + // thrown when there are no active sessions. This error message is spamming logs without having + // any value addition. Log the error only if the error we recceived is not NoActivePlayer. + if (errorMsg.rfind("GDBus.Error:com.github.altdesktop.playerctld.NoActivePlayer") == + std::string::npos) { spdlog::error("mpris[{}]: {}", info.name, error->message); } return std::nullopt; From 335a736eeda07aa770e796ded3df44babcc505c8 Mon Sep 17 00:00:00 2001 From: akliuxingyuan Date: Wed, 1 Nov 2023 22:20:30 +0800 Subject: [PATCH 184/364] tray: load_icon use request_size directly --- src/modules/sni/item.cpp | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index dfaca665..c3de2357 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -356,32 +356,15 @@ Glib::RefPtr Item::getIconPixbuf() { } Glib::RefPtr Item::getIconByName(const std::string& name, int request_size) { - int tmp_size = 0; icon_theme->rescan_if_needed(); - auto sizes = icon_theme->get_icon_sizes(name.c_str()); - for (auto const& size : sizes) { - // -1 == scalable - if (size == request_size || size == -1) { - tmp_size = request_size; - break; - } else if (size < request_size) { - tmp_size = size; - } else if (size > tmp_size && tmp_size > 0) { - tmp_size = request_size; - break; - } - } - if (tmp_size == 0) { - tmp_size = request_size; - } if (!icon_theme_path.empty() && - icon_theme->lookup_icon(name.c_str(), tmp_size, + icon_theme->lookup_icon(name.c_str(), request_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE)) { - return icon_theme->load_icon(name.c_str(), tmp_size, + return icon_theme->load_icon(name.c_str(), request_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } - return DefaultGtkIconThemeWrapper::load_icon(name.c_str(), tmp_size, + return DefaultGtkIconThemeWrapper::load_icon(name.c_str(), request_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } From 8555456050d93a2e77d2315f0f120e155668f3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= Date: Wed, 1 Nov 2023 22:04:19 +0000 Subject: [PATCH 185/364] hyprland/workspaces: fix crash on monitor off/on --- src/modules/hyprland/workspaces.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index e2b3da50..91b6242e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -38,6 +38,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value : AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { + modulesReady = true; parse_config(config); box_.set_name("workspaces"); @@ -46,9 +47,12 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value } event_box_.add(box_); - register_ipc(); + if (!gIPC.get()) { + gIPC = std::make_unique(); + } init(); + register_ipc(); } auto Workspaces::parse_config(const Json::Value &config) -> void { @@ -127,12 +131,6 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { } auto Workspaces::register_ipc() -> void { - modulesReady = true; - - if (!gIPC) { - gIPC = std::make_unique(); - } - gIPC->registerForIPC("workspace", this); gIPC->registerForIPC("createworkspace", this); gIPC->registerForIPC("destroyworkspace", this); From 1c1a39f597051799b86449b673782daa0376e386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= Date: Thu, 2 Nov 2023 01:57:55 +0000 Subject: [PATCH 186/364] custom: reap zombie processes on termination --- include/util/command.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/util/command.hpp b/include/util/command.hpp index 0d729b77..faf32cf0 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -112,6 +112,10 @@ inline FILE* open(const std::string& cmd, int& pid) { execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); exit(0); } else { + reap_mtx.lock(); + reap.push_back(child_pid); + reap_mtx.unlock(); + ::close(fd[1]); } pid = child_pid; From c6a9b63189c1617606ba726f3fd7af5a80df97d6 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 2 Nov 2023 14:16:55 +0100 Subject: [PATCH 187/364] chore: 0.9.23 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index c44be3e3..ae6a1e77 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.22', + version: '0.9.23', license: 'MIT', meson_version: '>= 0.50.0', default_options : [ From 23bc4d66bfbf87d6eda819ad6a1771653fd1d5c1 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 21 Oct 2023 22:46:56 +0200 Subject: [PATCH 188/364] Added initial .clang-tidy file --- .clang-tidy | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .clang-tidy diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..aa2c544e --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,28 @@ +Checks: > + -*, + bugprone-* + misc-*, + modernize-*, + performance-*, + portability-*, + readability-*, + -fuchsia-trailing-return, + -readability-magic-numbers, + -modernize-use-nodiscard, + -modernize-use-trailing-return-type, + -readability-braces-around-statements, + -readability-redundant-access-specifiers, + -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.PrivateMemberPrefix, value: m_ } + - { key: readability-identifier-naming.EnumCase, value: CamelCase } + - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } + - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } From e7c2e9023625b25d73802b617fb52eb0a72fa623 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 3 Nov 2023 14:06:50 +0100 Subject: [PATCH 189/364] Revert "custom: reap zombie processes on termination" This reverts commit 1c1a39f597051799b86449b673782daa0376e386. --- include/util/command.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/util/command.hpp b/include/util/command.hpp index faf32cf0..0d729b77 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -112,10 +112,6 @@ inline FILE* open(const std::string& cmd, int& pid) { execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); exit(0); } else { - reap_mtx.lock(); - reap.push_back(child_pid); - reap_mtx.unlock(); - ::close(fd[1]); } pid = child_pid; From 48ec834ec9c7cdf146169c8b0f669ce1e9a8849e Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 3 Nov 2023 14:07:07 +0100 Subject: [PATCH 190/364] chore: 0.9.24 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ae6a1e77..de641b3b 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.23', + version: '0.9.24', license: 'MIT', meson_version: '>= 0.50.0', default_options : [ From ca7c9a68f12b05173249ee61a9162f82e18c8648 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Sat, 4 Nov 2023 13:18:52 +0100 Subject: [PATCH 191/364] Made creation of privacy modules more modular --- include/modules/privacy/privacy.hpp | 4 -- include/modules/privacy/privacy_item.hpp | 9 +-- src/modules/privacy/privacy.cpp | 72 +++++++++++++++++------- src/modules/privacy/privacy_item.cpp | 15 ++--- 4 files changed, 58 insertions(+), 42 deletions(-) diff --git a/include/modules/privacy/privacy.hpp b/include/modules/privacy/privacy.hpp index b14cf452..c6b09b2c 100644 --- a/include/modules/privacy/privacy.hpp +++ b/include/modules/privacy/privacy.hpp @@ -26,10 +26,6 @@ class Privacy : public AModule { std::list nodes_audio_in; // Application is using the microphone std::list nodes_audio_out; // Application is outputting audio - PrivacyItem privacy_item_screenshare; - PrivacyItem privacy_item_audio_input; - PrivacyItem privacy_item_audio_output; - std::mutex mutex_; sigc::connection visibility_conn; diff --git a/include/modules/privacy/privacy_item.hpp b/include/modules/privacy/privacy_item.hpp index ac94d168..40ed40c5 100644 --- a/include/modules/privacy/privacy_item.hpp +++ b/include/modules/privacy/privacy_item.hpp @@ -20,16 +20,14 @@ namespace waybar::modules::privacy { class PrivacyItem : public Gtk::Revealer { public: PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_, - std::list *nodes, const std::string &pos); + std::list *nodes, const std::string &pos, const uint icon_size, + const uint transition_duration); - bool is_enabled(); + enum PrivacyNodeType privacy_type; void set_in_use(bool in_use); - void set_icon_size(uint size); - private: - enum PrivacyNodeType privacy_type; std::list *nodes; sigc::connection signal_conn; @@ -41,7 +39,6 @@ class PrivacyItem : public Gtk::Revealer { std::string lastStatus; // Config - bool enabled = true; std::string iconName = "image-missing-symbolic"; bool tooltip = true; uint tooltipIconSize = 24; diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 72b7928b..e96f14fa 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -1,6 +1,7 @@ #include "modules/privacy/privacy.hpp" #include +#include #include #include @@ -10,6 +11,7 @@ #include "AModule.hpp" #include "gtkmm/image.h" +#include "modules/privacy/privacy_item.hpp" namespace waybar::modules::privacy { @@ -23,18 +25,9 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st nodes_screenshare(), nodes_audio_in(), nodes_audio_out(), - privacy_item_screenshare(config["screenshare"], PRIVACY_NODE_TYPE_VIDEO_INPUT, - &nodes_screenshare, pos), - privacy_item_audio_input(config["audio-in"], PRIVACY_NODE_TYPE_AUDIO_INPUT, &nodes_audio_in, - pos), - privacy_item_audio_output(config["audio-out"], PRIVACY_NODE_TYPE_AUDIO_OUTPUT, - &nodes_audio_out, pos), visibility_conn(), box_(Gtk::ORIENTATION_HORIZONTAL, 0) { box_.set_name(name_); - box_.add(privacy_item_screenshare); - box_.add(privacy_item_audio_output); - box_.add(privacy_item_audio_input); event_box_.add(box_); @@ -48,22 +41,45 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st if (config_["icon-size"].isUInt()) { iconSize = config_["icon-size"].asUInt(); } - privacy_item_screenshare.set_icon_size(iconSize); - privacy_item_audio_output.set_icon_size(iconSize); - privacy_item_audio_input.set_icon_size(iconSize); // Transition Duration if (config_["transition-duration"].isUInt()) { transition_duration = config_["transition-duration"].asUInt(); } - privacy_item_screenshare.set_transition_duration(transition_duration); - privacy_item_audio_output.set_transition_duration(transition_duration); - privacy_item_audio_input.set_transition_duration(transition_duration); - if (!privacy_item_screenshare.is_enabled() && !privacy_item_audio_input.is_enabled() && - !privacy_item_audio_output.is_enabled()) { - throw std::runtime_error("No privacy modules enabled"); + // 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) { + modules = Json::Value(Json::arrayValue); + for (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]; + 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); + box_.add(*item); + } + } + backend = util::PipewireBackend::PipewireBackend::getInstance(); backend->privacy_nodes_changed_signal_event.connect( sigc::mem_fun(*this, &Privacy::onPrivacyNodesChanged)); @@ -109,9 +125,23 @@ auto Privacy::update() -> void { bool audio_in = !nodes_audio_in.empty(); bool audio_out = !nodes_audio_out.empty(); - privacy_item_screenshare.set_in_use(screenshare); - privacy_item_audio_input.set_in_use(audio_in); - privacy_item_audio_output.set_in_use(audio_out); + for (Gtk::Widget* widget : box_.get_children()) { + PrivacyItem* module = dynamic_cast(widget); + if (!module) continue; + switch (module->privacy_type) { + case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: + module->set_in_use(screenshare); + break; + case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT: + module->set_in_use(audio_in); + break; + case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT: + module->set_in_use(audio_out); + break; + case util::PipewireBackend::PRIVACY_NODE_TYPE_NONE: + break; + } + } mutex_.unlock(); // Hide the whole widget if none are in use diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index 9f1c0819..1d4c8a0e 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -23,7 +23,8 @@ namespace waybar::modules::privacy { PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_, - std::list *nodes_, const std::string &pos) + std::list *nodes_, const std::string &pos, + const uint icon_size, const uint transition_duration) : Gtk::Revealer(), privacy_type(privacy_type_), nodes(nodes_), @@ -46,7 +47,6 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac break; default: case util::PipewireBackend::PRIVACY_NODE_TYPE_NONE: - enabled = false; return; } @@ -58,16 +58,13 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac } else if (pos == "modules-right") { set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_SLIDE_LEFT); } + set_transition_duration(transition_duration); box_.set_name("privacy-item"); box_.add(icon_); + icon_.set_pixel_size(icon_size); add(box_); - // Icon Name - if (config_["enabled"].isBool()) { - enabled = config_["enabled"].asBool(); - } - // Icon Name if (config_["icon-name"].isString()) { iconName = config_["icon-name"].asString(); @@ -124,8 +121,6 @@ void PrivacyItem::update_tooltip() { tooltip_window.show_all(); } -bool PrivacyItem::is_enabled() { return enabled; } - void PrivacyItem::set_in_use(bool in_use) { if (in_use) { update_tooltip(); @@ -177,6 +172,4 @@ void PrivacyItem::set_in_use(bool in_use) { lastStatus = status; } -void PrivacyItem::set_icon_size(uint size) { icon_.set_pixel_size(size); } - } // namespace waybar::modules::privacy From 6050fa3a43a0d34ee9addf7d499a80dc4f341ae9 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Sat, 4 Nov 2023 14:12:45 +0100 Subject: [PATCH 192/364] Added documentation --- README.md | 1 + include/modules/privacy/privacy.hpp | 2 +- include/modules/privacy/privacy_item.hpp | 1 - man/waybar-privacy.5.scd | 85 ++++++++++++++++++++++++ meson.build | 1 + resources/style.css | 4 -- src/modules/privacy/privacy_item.cpp | 12 ---- 7 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 man/waybar-privacy.5.scd diff --git a/README.md b/README.md index ac9718b5..6009d91a 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ - Network - Bluetooth - Pulseaudio +- Privacy Info - Wireplumber - Disk - Memory diff --git a/include/modules/privacy/privacy.hpp b/include/modules/privacy/privacy.hpp index c6b09b2c..b8e76768 100644 --- a/include/modules/privacy/privacy.hpp +++ b/include/modules/privacy/privacy.hpp @@ -33,7 +33,7 @@ class Privacy : public AModule { Gtk::Box box_; uint iconSpacing = 4; uint iconSize = 20; - uint transition_duration = 500; + uint transition_duration = 250; std::shared_ptr backend = nullptr; }; diff --git a/include/modules/privacy/privacy_item.hpp b/include/modules/privacy/privacy_item.hpp index 40ed40c5..a0e3038b 100644 --- a/include/modules/privacy/privacy_item.hpp +++ b/include/modules/privacy/privacy_item.hpp @@ -36,7 +36,6 @@ class PrivacyItem : public Gtk::Revealer { bool init = false; bool in_use = false; - std::string lastStatus; // Config std::string iconName = "image-missing-symbolic"; diff --git a/man/waybar-privacy.5.scd b/man/waybar-privacy.5.scd new file mode 100644 index 00000000..d13d8ed3 --- /dev/null +++ b/man/waybar-privacy.5.scd @@ -0,0 +1,85 @@ +waybar-privacy(5) + +# NAME + +waybar - privacy module + +# DESCRIPTION + +The *privacy* module displays if any application is capturing audio, sharing ++ +the screen or playing audio. + +# CONFIGURATION + +*icon-spacing*: ++ + typeof: integer ++ + default: 4 ++ + The spacing between each privacy icon. + +*icon-size*: ++ + typeof: integer ++ + default: 20 ++ + The size of each privacy icon. + +*transition-duration*: ++ + typeof: integer ++ + default: 250 ++ + Option to disable tooltip on hover. + +*modules* ++ + typeof: array of objects ++ + default: [{"type": "screenshare"}, {"type": "audio-in"}] ++ + Which privacy modules to monitor. See *MODULES CONFIGURATION* for++ + more information. + +# MODULES CONFIGURATION + +*type*: ++ + typeof: string ++ + values: "screenshare", "audio-in", "audio-out" ++ + Specifies which module to use and configure. + +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to disable tooltip on hover. + +*tooltip-icon-size*: ++ + typeof: integer ++ + default: 24 ++ + The size of each icon in the tooltip. + +# EXAMPLES + +``` +"privacy": { + "icon-spacing": 4, + "icon-size": 18, + "transition-duration": 250, + "modules": [ + { + "type": "screenshare", + "tooltip": true, + "tooltip-icon-size": 24 + }, + { + "type": "audio-out", + "tooltip": true, + "tooltip-icon-size": 24 + }, + { + "type": "audio-in", + "tooltip": true, + "tooltip-icon-size": 24 + } + ] +}, +``` + +# STYLE + +- *#privacy* +- *#privacy-item* +- *#privacy-item.screenshare* +- *#privacy-item.audio-in* +- *#privacy-item.audio-out* diff --git a/meson.build b/meson.build index 8bea0ce3..cac29747 100644 --- a/meson.build +++ b/meson.build @@ -467,6 +467,7 @@ if scdoc.found() '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', diff --git a/resources/style.css b/resources/style.css index 78cdf604..e6017fdb 100644 --- a/resources/style.css +++ b/resources/style.css @@ -283,10 +283,6 @@ label:focus { padding: 0; } -#privacy > box { - padding: 0; -} - #privacy-item { padding: 0 5px; color: white; diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index 1d4c8a0e..a0a2da57 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -158,18 +158,6 @@ void PrivacyItem::set_in_use(bool in_use) { set_reveal_child(false); } this->init = true; - - // CSS status class - const std::string status = this->in_use ? "in-use" : ""; - // Remove last status if it exists - if (!lastStatus.empty() && get_style_context()->has_class(lastStatus)) { - get_style_context()->remove_class(lastStatus); - } - // Add the new status class to the Box - if (!status.empty() && !get_style_context()->has_class(status)) { - get_style_context()->add_class(status); - } - lastStatus = status; } } // namespace waybar::modules::privacy From f21b1dfa4d052f37a0b9fa3c2cbf558a148a76b7 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Sat, 4 Nov 2023 14:14:37 +0100 Subject: [PATCH 193/364] fixed linter issues --- src/util/pipewire_backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/pipewire_backend.cpp b/src/util/pipewire_backend.cpp index 5449cddc..5fe3ba62 100644 --- a/src/util/pipewire_backend.cpp +++ b/src/util/pipewire_backend.cpp @@ -89,7 +89,7 @@ static void registry_event_global_remove(void *_data, uint32_t id) { backend->mutex_.lock(); auto iter = backend->privacy_nodes.find(id); - if(iter != backend->privacy_nodes.end()) { + if (iter != backend->privacy_nodes.end()) { backend->privacy_nodes.erase(id); } backend->mutex_.unlock(); From aa1f8a428b163538c68bdaa81afe6c54de3e09af Mon Sep 17 00:00:00 2001 From: Lukas Hannen Date: Mon, 6 Nov 2023 01:28:48 +0100 Subject: [PATCH 194/364] avoid converting to local_time when converting to zoned_time afterwards --- 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 3d6b8919..3d74f2fc 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -173,7 +173,7 @@ auto waybar::modules::Clock::update() -> void { // Define shift local time const auto shiftedNow{date::make_zoned( tz, date::local_days(shiftedDay) + - (now.get_local_time() - date::floor(now.get_local_time())))}; + (now.get_sys_time() - date::floor(now.get_sys_time())))}; label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now)); From 4d339f05afac327faa07b77ab5d2415018b66b15 Mon Sep 17 00:00:00 2001 From: Caleb Harper <28740091+Mrcarrot1@users.noreply.github.com> Date: Mon, 6 Nov 2023 09:50:13 -0600 Subject: [PATCH 195/364] Fix segmentation fault in WirePlumber module The WirePlumber module assumes that either the node's name or description will not be null. This leads to a segmentation fault when both are. The solution provided is to set self->node_name_ to a default value in this case. --- src/modules/wireplumber.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index b2d9b39d..a020e2a0 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -81,7 +81,7 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* auto nick = wp_properties_get(properties, "node.nick"); auto description = wp_properties_get(properties, "node.description"); - self->node_name_ = nick ? nick : description; + self->node_name_ = nick ? nick : description ? description : "Unknown node name"; spdlog::debug("[{}]: Updating node name to: {}", self->name_, self->node_name_); } From 86a38980e4a2eb242379b9221b06784d3e0d3eef Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 10 Nov 2023 17:57:26 +0300 Subject: [PATCH 196/364] c++20. clock chrono API. STL + format Signed-off-by: Viktar Lukashonak --- include/AModule.hpp | 2 +- include/modules/clock.hpp | 74 +++--- include/util/date.hpp | 65 +++-- man/waybar-clock.5.scd | 4 +- meson.build | 17 +- src/AModule.cpp | 5 +- src/modules/clock.cpp | 488 +++++++++++++++++--------------------- test/date.cpp | 98 +++++--- 8 files changed, 379 insertions(+), 374 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 479755b7..e037479b 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -40,7 +40,7 @@ class AModule : public IModule { private: bool handleUserEvent(GdkEventButton *const &ev); - + const bool isTooltip; std::vector pid_; gdouble distance_scrolled_y_; gdouble distance_scrolled_x_; diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 3aa5c9bc..d6aabaa0 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -6,38 +6,26 @@ namespace waybar::modules { -const std::string kCalendarPlaceholder = "calendar"; -const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list"; - -enum class WeeksSide { - LEFT, - RIGHT, - HIDDEN, -}; +const std::string kCldPlaceholder{"calendar"}; +const std::string kTZPlaceholder{"tz_list"}; enum class CldMode { MONTH, YEAR }; +enum class WS { LEFT, RIGHT, HIDDEN }; class Clock final : public ALabel { public: Clock(const std::string&, const Json::Value&); virtual ~Clock() = default; auto update() -> void override; - auto doAction(const std::string& name) -> void override; + auto doAction(const std::string&) -> void override; private: - util::SleeperThread thread_; - std::locale locale_; - std::vector time_zones_; - int current_time_zone_idx_; - bool is_calendar_in_tooltip_; - bool is_timezoned_list_in_tooltip_; - - auto first_day_of_week() -> date::weekday; - const date::time_zone* current_timezone(); - auto timezones_text(std::chrono::system_clock::time_point now) -> std::string; - - /*Calendar properties*/ - WeeksSide cldWPos_{WeeksSide::HIDDEN}; + const std::locale locale_; + // tooltip + const std::string tlpFmt_; + std::string tlpText_{""}; // tooltip text to print + // Calendar + const bool cldInTooltip_; // calendar in tooltip /* 0 - calendar.format.months 1 - calendar.format.weekdays @@ -47,28 +35,37 @@ class Clock final : public ALabel { 5 - tooltip-format */ std::map fmtMap_; + uint cldMonCols_{3}; // calendar count month columns + int cldWnLen_{3}; // calendar week number length + const int cldMonColLen_{20}; // calendar month column length + WS cldWPos_{WS::HIDDEN}; // calendar week side to print + months cldCurrShift_{0}; // calendar months shift + 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 + std::string cldMonCached_; // calendar Month mode. Cached calendar + day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight) + std::string cldText_{""}; // calendar text to print CldMode cldMode_{CldMode::MONTH}; - uint cldMonCols_{3}; // Count of the month in the row - int cldMonColLen_{20}; // Length of the month column - int cldWnLen_{3}; // Length of the week number - date::year_month_day cldYearShift_; - date::year_month cldMonShift_; - date::months cldCurrShift_{0}; - date::months cldShift_{0}; - std::string cldYearCached_{}; - std::string cldMonCached_{}; - date::day cldBaseDay_{0}; - /*Calendar functions*/ - auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd, - const date::time_zone* tz) -> const std::string; - /*Clock actions*/ + auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) + -> const std::string; + + // time zoned time in tooltip + const bool tzInTooltip_; // if need to print time zones text + std::vector tzList_; // time zones list + int tzCurrIdx_; // current time zone index for tzList_ + std::string tzText_{""}; // time zones text to print + util::SleeperThread thread_; + + auto getTZtext(sys_seconds now) -> std::string; + auto first_day_of_week() -> weekday; + // Module actions void cldModeSwitch(); void cldShift_up(); void cldShift_down(); void tz_up(); void tz_down(); - - // ModuleActionMap + // Module Action Map static inline std::map actionMap_{ {"mode", &waybar::modules::Clock::cldModeSwitch}, {"shift_up", &waybar::modules::Clock::cldShift_up}, @@ -76,4 +73,5 @@ class Clock final : public ALabel { {"tz_up", &waybar::modules::Clock::tz_up}, {"tz_down", &waybar::modules::Clock::tz_down}}; }; + } // namespace waybar::modules diff --git a/include/util/date.hpp b/include/util/date.hpp index 380bb6e7..962c810b 100644 --- a/include/util/date.hpp +++ b/include/util/date.hpp @@ -1,34 +1,52 @@ #pragma once -#include +#include #if HAVE_CHRONO_TIMEZONES -#include #include - -/* Compatibility layer for on top of C++20 */ -namespace date { - -using namespace std::chrono; - -namespace literals { -using std::chrono::last; -} - -inline auto format(const std::string& spec, const auto& ztime) { - return spec.empty() ? "" : std::vformat("{:L" + spec + "}", std::make_format_args(ztime)); -} - -inline auto format(const std::locale& loc, const std::string& spec, const auto& ztime) { - return spec.empty() ? "" : std::vformat(loc, "{:L" + spec + "}", std::make_format_args(ztime)); -} - -} // namespace date - #else #include +#include + +#include #endif +// Date +namespace date { +#if HAVE_CHRONO_TIMEZONES +using namespace std::chrono; +using namespace std; +#else + +using system_clock = std::chrono::system_clock; +using seconds = std::chrono::seconds; + +template +inline auto format(const char* spec, const T& arg) { + return date::format(std::regex_replace(spec, std::regex("\\{:L|\\}"), ""), arg); +} + +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 + +// Format +namespace waybar::util::date::format { +#if HAVE_CHRONO_TIMEZONES +using namespace std; +#else +using namespace fmt; +#endif +} // namespace waybar::util::date::format + +#if not HAVE_CHRONO_TIMEZONES template struct fmt::formatter> { std::string_view specs; @@ -58,3 +76,6 @@ struct fmt::formatter> { return fmt::format_to(ctx.out(), "{}", date::format(fmt::to_string(specs), ztime)); } }; +#endif + +using namespace date; diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index dc26c270..fc079338 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -85,7 +85,7 @@ $XDG_CONFIG_HOME/waybar/config ++ :[ same as format :[ Tooltip on hover -View all valid format options in *strftime(3)* or have a look +View all valid format options in *strftime(3)* or have a look https://en.cppreference.com/w/cpp/chrono/duration/formatter 2. Addressed by *clock: calendar* [- *Option* @@ -156,7 +156,7 @@ View all valid format options in *strftime(3)* or have a look ') -have_chrono_timezones = cpp_lib_chrono >= 201907 +have_chrono_timezones = cpp_lib_chrono >= 201611 + +if have_chrono_timezones + code = ''' +#include +using namespace std::chrono; +int main(int argc, char** argv) { + const time_zone* tz; + return 0; +} +''' + if not compiler.links(code) + have_chrono_timezones = false + endif +endif + if have_chrono_timezones tz_dep = declare_dependency() else diff --git a/src/AModule.cpp b/src/AModule.cpp index 398fa518..9a9f1386 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -10,6 +10,7 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: bool enable_click, bool enable_scroll) : name_(std::move(name)), config_(std::move(config)), + isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true}, distance_scrolled_y_(0.0), distance_scrolled_x_(0.0) { // Configure module action Map @@ -189,9 +190,7 @@ bool AModule::handleScroll(GdkEventScroll* e) { return true; } -bool AModule::tooltipEnabled() { - return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true; -} +bool AModule::tooltipEnabled() { return isTooltip; } AModule::operator Gtk::Widget&() { return event_box_; } diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 3d74f2fc..e003107a 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,325 +1,286 @@ #include "modules/clock.hpp" -#include #include -#include #include #include -#include -#include #include "util/ustring_clen.hpp" + #ifdef HAVE_LANGINFO_1STDAY #include #include #endif +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), - current_time_zone_idx_{0}, - is_calendar_in_tooltip_{false}, - is_timezoned_list_in_tooltip_{false} { + 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}, + tzCurrIdx_{0} { + tlpText_ = tlpFmt_; + if (config_["timezones"].isArray() && !config_["timezones"].empty()) { for (const auto& zone_name : config_["timezones"]) { if (!zone_name.isString()) continue; if (zone_name.asString().empty()) // local time should be shown - time_zones_.push_back(date::current_zone()); + tzList_.push_back(current_zone()); else try { - time_zones_.push_back(date::locate_zone(zone_name.asString())); + tzList_.push_back(locate_zone(zone_name.asString())); } catch (const std::exception& e) { spdlog::warn("Timezone: {0}. {1}", zone_name.asString(), e.what()); } } } else if (config_["timezone"].isString()) { if (config_["timezone"].asString().empty()) - time_zones_.push_back(date::current_zone()); + // local time should be shown + tzList_.push_back(current_zone()); else try { - time_zones_.push_back(date::locate_zone(config_["timezone"].asString())); + tzList_.push_back(locate_zone(config_["timezone"].asString())); } catch (const std::exception& e) { spdlog::warn("Timezone: {0}. {1}", config_["timezone"].asString(), e.what()); } } + if (!tzList_.size()) tzList_.push_back(current_zone()); - // If all timezones are parsed and no one is good - if (!time_zones_.size()) { - time_zones_.push_back(date::current_zone()); - } - - // Check if a particular placeholder is present in the tooltip format, to know what to calculate - // on update. - if (config_["tooltip-format"].isString()) { - std::string trimmed_format{config_["tooltip-format"].asString()}; - fmtMap_.insert({5, trimmed_format}); - trimmed_format.erase(std::remove_if(trimmed_format.begin(), trimmed_format.end(), - [](unsigned char x) { return std::isspace(x); }), - trimmed_format.end()); - - if (trimmed_format.find("{" + kCalendarPlaceholder + "}") != std::string::npos) { - is_calendar_in_tooltip_ = true; - } - if (trimmed_format.find("{" + KTimezonedTimeListPlaceholder + "}") != std::string::npos) { - is_timezoned_list_in_tooltip_ = true; - } - } - - // Calendar configuration - if (is_calendar_in_tooltip_) { - if (config_[kCalendarPlaceholder]["weeks-pos"].isString()) { - if (config_[kCalendarPlaceholder]["weeks-pos"].asString() == "left") { - cldWPos_ = WeeksSide::LEFT; - } else if (config_[kCalendarPlaceholder]["weeks-pos"].asString() == "right") { - cldWPos_ = WeeksSide::RIGHT; - } - } - if (config_[kCalendarPlaceholder]["format"]["months"].isString()) - fmtMap_.insert({0, config_[kCalendarPlaceholder]["format"]["months"].asString()}); - else - fmtMap_.insert({0, "{}"}); - if (config_[kCalendarPlaceholder]["format"]["days"].isString()) - fmtMap_.insert({2, config_[kCalendarPlaceholder]["format"]["days"].asString()}); - else - fmtMap_.insert({2, "{}"}); - if (config_[kCalendarPlaceholder]["format"]["weeks"].isString() && - cldWPos_ != WeeksSide::HIDDEN) { - fmtMap_.insert( - {4, std::regex_replace(config_[kCalendarPlaceholder]["format"]["weeks"].asString(), - std::regex("\\{\\}"), - (first_day_of_week() == date::Monday) ? "{:%W}" : "{:%U}")}); - Glib::ustring tmp{std::regex_replace(fmtMap_[4], std::regex("]+>|\\{.*\\}"), "")}; - cldWnLen_ += tmp.size(); - } else { - if (cldWPos_ != WeeksSide::HIDDEN) - fmtMap_.insert({4, (first_day_of_week() == date::Monday) ? "{:%W}" : "{:%U}"}); - else - cldWnLen_ = 0; - } - if (config_[kCalendarPlaceholder]["format"]["weekdays"].isString()) - fmtMap_.insert({1, config_[kCalendarPlaceholder]["format"]["weekdays"].asString()}); - else - fmtMap_.insert({1, "{}"}); - if (config_[kCalendarPlaceholder]["format"]["today"].isString()) { - fmtMap_.insert({3, config_[kCalendarPlaceholder]["format"]["today"].asString()}); - cldBaseDay_ = - date::year_month_day{date::floor(std::chrono::system_clock::now())}.day(); - } else - fmtMap_.insert({3, "{}"}); - if (config_[kCalendarPlaceholder]["mode"].isString()) { - const std::string cfgMode{(config_[kCalendarPlaceholder]["mode"].isString()) - ? config_[kCalendarPlaceholder]["mode"].asString() - : "month"}; - const std::map monthModes{{"month", CldMode::MONTH}, - {"year", CldMode::YEAR}}; + // Calendar properties + if (cldInTooltip_) { + if (config_[kCldPlaceholder]["mode"].isString()) { + const std::string cfgMode{config_[kCldPlaceholder]["mode"].asString()}; + const std::map monthModes{{"month", CldMode::MONTH}, + {"year", CldMode::YEAR}}; if (monthModes.find(cfgMode) != monthModes.end()) cldMode_ = monthModes.at(cfgMode); else spdlog::warn( - "Clock calendar configuration \"mode\"\"\" \"{0}\" is not recognized. Mode = \"month\" " - "is using instead", + "Clock calendar configuration mode \"{0}\" is not recognized. Mode = \"month\" is " + "using instead", cfgMode); } - if (config_[kCalendarPlaceholder]["mode-mon-col"].isInt()) { - cldMonCols_ = config_[kCalendarPlaceholder]["mode-mon-col"].asInt(); - if (cldMonCols_ == 0u || 12 % cldMonCols_ != 0u) { - cldMonCols_ = 3u; + if (config_[kCldPlaceholder]["weeks-pos"].isString()) { + if (config_[kCldPlaceholder]["weeks-pos"].asString() == "left") cldWPos_ = WS::LEFT; + if (config_[kCldPlaceholder]["weeks-pos"].asString() == "right") cldWPos_ = WS::RIGHT; + } + if (config_[kCldPlaceholder]["format"]["months"].isString()) + fmtMap_.insert({0, config_[kCldPlaceholder]["format"]["months"].asString()}); + else + fmtMap_.insert({0, "{}"}); + if (config_[kCldPlaceholder]["format"]["weekdays"].isString()) + fmtMap_.insert({1, config_[kCldPlaceholder]["format"]["weekdays"].asString()}); + else + fmtMap_.insert({1, "{}"}); + + if (config_[kCldPlaceholder]["format"]["days"].isString()) + fmtMap_.insert({2, config_[kCldPlaceholder]["format"]["days"].asString()}); + else + fmtMap_.insert({2, "{}"}); + if (config_[kCldPlaceholder]["format"]["today"].isString()) { + fmtMap_.insert({3, config_[kCldPlaceholder]["format"]["today"].asString()}); + cldBaseDay_ = + year_month_day{ + floor(zoned_time{current_zone(), system_clock::now()}.get_local_time())} + .day(); + } else + fmtMap_.insert({3, "{}"}); + if (config_[kCldPlaceholder]["format"]["weeks"].isString() && cldWPos_ != WS::HIDDEN) { + fmtMap_.insert({4, std::regex_replace(config_[kCldPlaceholder]["format"]["weeks"].asString(), + std::regex("\\{\\}"), + (first_day_of_week() == Monday) ? "{:%W}" : "{:%U}")}); + Glib::ustring tmp{std::regex_replace(fmtMap_[4], std::regex("]+>|\\{.*\\}"), "")}; + cldWnLen_ += tmp.size(); + } else { + if (cldWPos_ != WS::HIDDEN) + fmtMap_.insert({4, (first_day_of_week() == Monday) ? "{:%W}" : "{:%U}"}); + else + cldWnLen_ = 0; + } + if (config_[kCldPlaceholder]["mode-mon-col"].isInt()) { + cldMonCols_ = config_[kCldPlaceholder]["mode-mon-col"].asInt(); + if (cldMonCols_ == 0u || (12 % cldMonCols_) != 0u) { spdlog::warn( - "Clock calendar configuration \"mode-mon-col\" = {0} must be one of [1, 2, 3, 4, 6, " - "12]. Value 3 is using instead", + "Clock calendar configuration mode-mon-col = {0} must be one of [1, 2, 3, 4, 6, 12]. " + "Value 3 is using instead", cldMonCols_); + cldMonCols_ = 3u; } } else cldMonCols_ = 1; - if (config_[kCalendarPlaceholder]["on-scroll"].isInt()) { - cldShift_ = date::months{config_[kCalendarPlaceholder]["on-scroll"].asInt()}; + if (config_[kCldPlaceholder]["on-scroll"].isInt()) { event_box_.add_events(Gdk::LEAVE_NOTIFY_MASK); event_box_.signal_leave_notify_event().connect([this](GdkEventCrossing*) { - cldCurrShift_ = date::months{0}; + cldCurrShift_ = months{0}; return false; }); } } - if (config_["locale"].isString()) - locale_ = std::locale(config_["locale"].asString()); - else - locale_ = std::locale(""); - thread_ = [this] { dp.emit(); - auto now = std::chrono::system_clock::now(); - /* difference with projected wakeup time */ - auto diff = now.time_since_epoch() % interval_; - /* sleep until the next projected time */ - thread_.sleep_for(interval_ - diff); + thread_.sleep_for(interval_ - system_clock::now().time_since_epoch() % interval_); }; } -const date::time_zone* waybar::modules::Clock::current_timezone() { - return time_zones_[current_time_zone_idx_]; -} - auto waybar::modules::Clock::update() -> void { - const auto* tz{current_timezone()}; - const date::zoned_time now{ - tz, - date::floor( - std::chrono::system_clock::now())}; // Define local time is based on provided time zone - const date::year_month_day today{ - date::floor(now.get_local_time())}; // Convert now to year_month_day - const date::year_month_day shiftedDay{today + cldCurrShift_}; // Shift today - // Define shift local time - const auto shiftedNow{date::make_zoned( - tz, date::local_days(shiftedDay) + - (now.get_sys_time() - date::floor(now.get_sys_time())))}; + auto tz{tzList_[tzCurrIdx_]}; + const zoned_time now{tz, floor(system_clock::now())}; - label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now)); + label_.set_markup(fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(now))); if (tooltipEnabled()) { - const std::string tz_text{(is_timezoned_list_in_tooltip_) ? timezones_text(now.get_sys_time()) - : ""}; - const std::string cld_text{(is_calendar_in_tooltip_) ? get_calendar(today, shiftedDay, tz) - : ""}; + const year_month_day today{floor(now.get_local_time())}; + const auto shiftedDay{today + cldCurrShift_}; + const zoned_time shiftedNow{ + tz, local_days(shiftedDay) + (now.get_local_time() - floor(now.get_local_time()))}; - const std::string text{fmt::format(locale_, fmt::runtime(fmtMap_[5]), shiftedNow, - fmt::arg(KTimezonedTimeListPlaceholder.c_str(), tz_text), - fmt::arg(kCalendarPlaceholder.c_str(), cld_text))}; - label_.set_tooltip_markup(text); + if (tzInTooltip_) tzText_ = getTZtext(now.get_sys_time()); + if (cldInTooltip_) cldText_ = get_calendar(today, shiftedDay, tz); + if (tzInTooltip_ || cldInTooltip_) { + // 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_ = fmt_lib::vformat(locale_, tlpText_, fmt_lib::make_format_args(shiftedNow)); + + label_.set_tooltip_markup(tlpText_); } - // Call parent update ALabel::update(); } -auto waybar::modules::Clock::doAction(const std::string& name) -> void { - if ((actionMap_[name])) { - (this->*actionMap_[name])(); - update(); - } else - spdlog::error("Clock. Unsupported action \"{0}\"", name); +auto waybar::modules::Clock::getTZtext(sys_seconds now) -> std::string { + if (tzList_.size() == 1) return ""; + + 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}}; + os << fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(zt)) << '\n'; + } + + return os.str(); } -// The number of weeks in calendar month layout plus 1 more for calendar titles -const unsigned cldRowsInMonth(const date::year_month& ym, const date::weekday& firstdow) { - using namespace date; - return static_cast( - ceil((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count()) + - 2; +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 date::year_month& ym, const date::weekday& firstdow, - unsigned const line) -> const date::year_month_weekday { - unsigned index = line - 2; - auto sd = date::sys_days{ym / 1}; - if (date::weekday{sd} == firstdow) ++index; - auto ymdw = ym / firstdow[index]; - return ymdw; +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]; } -auto getCalendarLine(const date::year_month_day& currDate, const date::year_month ym, - const unsigned line, const date::weekday& firstdow, - const std::locale* const locale_) -> std::string { - using namespace date::literals; - std::ostringstream res; +auto getCalendarLine(const year_month_day& currDate, const year_month ym, const unsigned line, + const weekday& firstdow, const std::locale* const locale_) -> std::string { + std::ostringstream os; switch (line) { + // Print month and year title case 0: { - // Output month and year title - res << date::format(*locale_, "%B %Y", ym); + os << date::format(*locale_, "{:L%B %Y}", ym); break; } + // Print weekday names title case 1: { - // Output weekday names title auto wd{firstdow}; + Glib::ustring wdStr; + Glib::ustring::size_type wdLen{0}; + int clen{0}; do { - Glib::ustring wd_ustring{date::format(*locale_, "%a", wd)}; - auto clen{ustring_clen(wd_ustring)}; - auto wd_len{wd_ustring.length()}; + wdStr = date::format(*locale_, "{:L%a}", wd); + clen = ustring_clen(wdStr); + wdLen = wdStr.length(); while (clen > 2) { - wd_ustring = wd_ustring.substr(0, wd_len - 1); - --wd_len; - clen = ustring_clen(wd_ustring); + wdStr = wdStr.substr(0, wdLen - 1); + --wdLen; + clen = ustring_clen(wdStr); } const std::string pad(2 - clen, ' '); - if (wd != firstdow) res << ' '; + if (wd != firstdow) os << ' '; - res << pad << wd_ustring; + os << pad << wdStr; } while (++wd != firstdow); break; } + // Print first week prefixed with spaces if necessary case 2: { - // Output first week prefixed with spaces if necessary - auto wd = date::weekday{ym / 1}; - res << std::string(static_cast((wd - firstdow).count()) * 3, ' '); + auto wd{weekday{ym / 1}}; + os << std::string((wd - firstdow).count() * 3, ' '); - if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / 1_d) - res << date::format("%e", 1_d); + if (currDate != ym / 1d) + os << date::format(*locale_, "{:L%e}", 1d); else - res << "{today}"; - - auto d = 2_d; + os << "{today}"; + auto d{2d}; while (++wd != firstdow) { - if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / d) - res << date::format(" %e", d); + if (currDate != ym / d) + os << date::format(*locale_, " {:L%e}", d); else - res << " {today}"; + os << " {today}"; ++d; } break; } + // Print non-first week default: { - // Output a non-first week: - auto ymdw{cldGetWeekForLine(ym, firstdow, line)}; - if (ymdw.ok()) { - auto d = date::year_month_day{ymdw}.day(); - auto const e = (ym / last).day(); - auto wd = firstdow; + auto ymdTmp{cldGetWeekForLine(ym, firstdow, line)}; + if (ymdTmp.ok()) { + auto d{year_month_day{ymdTmp}.day()}; + const auto dlast{(ym / last).day()}; + auto wd{firstdow}; - if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / d) - res << date::format("%e", d); + if (currDate != ym / d) + os << date::format(*locale_, "{:L%e}", d); else - res << "{today}"; + os << "{today}"; - while (++wd != firstdow && ++d <= e) { - if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / d) - res << date::format(" %e", d); + while (++wd != firstdow && ++d <= dlast) { + if (currDate != ym / d) + os << date::format(*locale_, " {:L%e}", d); else - res << " {today}"; + os << " {today}"; } - // Append row with spaces if the week did not complete - res << std::string(static_cast((firstdow - wd).count()) * 3, ' '); + // Append row with spaces if the week was not completed + os << std::string((firstdow - wd).count() * 3, ' '); } break; } } - return res.str(); + return os.str(); } -auto waybar::modules::Clock::get_calendar(const date::year_month_day& today, - const date::year_month_day& ymd, - const date::time_zone* tz) -> const std::string { +auto waybar::modules::Clock::get_calendar(const year_month_day& today, const year_month_day& ymd, + const time_zone* tz) -> const std::string { + const auto firstdow{first_day_of_week()}; + const auto maxRows{12 / cldMonCols_}; const auto ym{ymd.year() / ymd.month()}; const auto y{ymd.year()}; const auto d{ymd.day()}; - const auto firstdow = first_day_of_week(); - const auto maxRows{12 / cldMonCols_}; + std::ostringstream os; std::ostringstream tmp; if (cldMode_ == CldMode::YEAR) { - if (y / date::month{1} / 1 == cldYearShift_) + if (y / month{1} / 1 == cldYearShift_) if (d == cldBaseDay_ || (uint)cldBaseDay_ == 0u) return cldYearCached_; else cldBaseDay_ = d; else - cldYearShift_ = y / date::month{1} / 1; + cldYearShift_ = y / month{1} / 1; } if (cldMode_ == CldMode::MONTH) { if (ym == cldMonShift_) @@ -330,67 +291,69 @@ auto waybar::modules::Clock::get_calendar(const date::year_month_day& today, else cldMonShift_ = ym; } - + // Pad object + const std::string pads(cldWnLen_, ' '); // Compute number of lines needed for each calendar month unsigned ml[12]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; for (auto& m : ml) { if (cldMode_ == CldMode::YEAR || m == static_cast(ymd.month())) - m = cldRowsInMonth(y / date::month{m}, firstdow); + m = cldRowsInMonth(y / month{m}, firstdow); else m = 0u; } for (auto row{0u}; row < maxRows; ++row) { - const auto lines = *std::max_element(std::begin(ml) + (row * cldMonCols_), - std::begin(ml) + ((row + 1) * cldMonCols_)); + const auto lines{*std::max_element(std::begin(ml) + (row * cldMonCols_), + std::begin(ml) + ((row + 1) * cldMonCols_))}; for (auto line{0u}; line < lines; ++line) { for (auto col{0u}; col < cldMonCols_; ++col) { - const auto mon{date::month{row * cldMonCols_ + col + 1}}; + const auto mon{month{row * cldMonCols_ + col + 1}}; if (cldMode_ == CldMode::YEAR || y / mon == ym) { - date::year_month ymTmp{y / mon}; - if (col != 0 && cldMode_ == CldMode::YEAR) os << " "; + const year_month ymTmp{y / mon}; + if (col != 0 && cldMode_ == CldMode::YEAR) os << std::string(3, ' '); // Week numbers on the left - if (cldWPos_ == WeeksSide::LEFT && line > 0) { + if (cldWPos_ == WS::LEFT && line > 0) { if (line > 1) { - if (line < ml[static_cast(ymTmp.month()) - 1u]) - os << fmt::format(fmt::runtime(fmtMap_[4]), - (line == 2) - ? date::zoned_seconds{tz, date::local_days{ymTmp / 1}} - : date::zoned_seconds{tz, date::local_days{cldGetWeekForLine( - ymTmp, firstdow, line)}}) + if (line < ml[(unsigned)ymTmp.month() - 1u]) + os << fmt_lib::vformat( + locale_, fmtMap_[4], + fmt_lib::make_format_args( + (line == 2) ? zoned_seconds{tz, local_days{ymTmp / 1}} + : zoned_seconds{tz, local_days{cldGetWeekForLine( + ymTmp, firstdow, line)}})) << ' '; else - os << std::string(cldWnLen_, ' '); + os << pads; } } - os << fmt::format( - fmt::runtime((cldWPos_ != WeeksSide::LEFT || line == 0) ? "{:<{}}" : "{:>{}}"), - getCalendarLine(today, ymTmp, line, firstdow, &locale_), - (cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0))); + 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_)); // Week numbers on the right - if (cldWPos_ == WeeksSide ::RIGHT && line > 0) { + if (cldWPos_ == WS::RIGHT && line > 0) { if (line > 1) { - if (line < ml[static_cast(ymTmp.month()) - 1u]) + if (line < ml[(unsigned)ymTmp.month() - 1u]) os << ' ' - << fmt::format(fmt::runtime(fmtMap_[4]), - (line == 2) - ? date::zoned_seconds{tz, date::local_days{ymTmp / 1}} - : date::zoned_seconds{tz, date::local_days{cldGetWeekForLine( - ymTmp, firstdow, line)}}); + << fmt_lib::vformat( + locale_, fmtMap_[4], + fmt_lib::make_format_args( + (line == 2) ? zoned_seconds{tz, local_days{ymTmp / 1}} + : zoned_seconds{tz, local_days{cldGetWeekForLine( + ymTmp, firstdow, line)}})); else - os << std::string(cldWnLen_, ' '); + os << pads; } } } } - - // Apply user formats to calendar + // Apply user's formats if (line < 2) - tmp << fmt::format(fmt::runtime(fmtMap_[line]), os.str()); + tmp << fmt_lib::vformat(locale_, fmtMap_[line], fmt_lib::make_format_args(os.str())); else tmp << os.str(); // Clear ostringstream @@ -400,10 +363,10 @@ auto waybar::modules::Clock::get_calendar(const date::year_month_day& today, if (row + 1u != maxRows && cldMode_ == CldMode::YEAR) tmp << '\n'; } - os << fmt::format( // Apply days format - fmt::runtime(fmt::format(fmt::runtime(fmtMap_[2]), tmp.str())), - // Apply today format - fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", d)))); + os << std::regex_replace( + fmt_lib::vformat(locale_, fmtMap_[2], fmt_lib::make_format_args(tmp.str())), + std::regex("\\{today\\}"), + fmt_lib::vformat(locale_, fmtMap_[3], fmt_lib::make_format_args(date::format("{:L%e}", d)))); if (cldMode_ == CldMode::YEAR) cldYearCached_ = os.str(); @@ -413,50 +376,34 @@ auto waybar::modules::Clock::get_calendar(const date::year_month_day& today, return os.str(); } -/*Clock actions*/ +// Actions handler +auto waybar::modules::Clock::doAction(const std::string& name) -> void { + if (actionMap_[name]) { + (this->*actionMap_[name])(); + } else + spdlog::error("Clock. Unsupported action \"{0}\"", name); +} + +// Module actions void waybar::modules::Clock::cldModeSwitch() { cldMode_ = (cldMode_ == CldMode::YEAR) ? CldMode::MONTH : CldMode::YEAR; } void waybar::modules::Clock::cldShift_up() { - cldCurrShift_ += ((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; + cldCurrShift_ += (months)((cldMode_ == CldMode::YEAR) ? 12 : 1); } void waybar::modules::Clock::cldShift_down() { - cldCurrShift_ -= ((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; + cldCurrShift_ -= (months)((cldMode_ == CldMode::YEAR) ? 12 : 1); } void waybar::modules::Clock::tz_up() { - auto nr_zones = time_zones_.size(); - - if (nr_zones == 1) return; - - size_t new_idx = current_time_zone_idx_ + 1; - current_time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx; + const auto tzSize{tzList_.size()}; + if (tzSize == 1) return; + size_t newIdx{tzCurrIdx_ + 1lu}; + tzCurrIdx_ = (newIdx == tzSize) ? 0 : newIdx; } void waybar::modules::Clock::tz_down() { - auto nr_zones = time_zones_.size(); - - if (nr_zones == 1) return; - - current_time_zone_idx_ = current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1; -} - -auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point now) - -> std::string { - if (time_zones_.size() == 1) { - return ""; - } - std::stringstream os; - for (size_t time_zone_idx = 0; time_zone_idx < time_zones_.size(); ++time_zone_idx) { - if (static_cast(time_zone_idx) == current_time_zone_idx_) { - continue; - } - const date::time_zone* timezone = time_zones_[time_zone_idx]; - if (!timezone) { - timezone = date::current_zone(); - } - auto ztime = date::zoned_time{timezone, date::floor(now)}; - os << fmt::format(locale_, fmt::runtime(format_), ztime) << '\n'; - } - return os.str(); + const auto tzSize{tzList_.size()}; + if (tzSize == 1) return; + tzCurrIdx_ = (tzCurrIdx_ == 0) ? tzSize - 1 : tzCurrIdx_ - 1; } #ifdef HAVE_LANGINFO_1STDAY @@ -468,17 +415,16 @@ using deleting_unique_ptr = std::unique_ptr>; #endif // Computations done similarly to Linux cal utility. -auto waybar::modules::Clock::first_day_of_week() -> date::weekday { +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)}; if (posix_locale) { - const int i = (std::intptr_t)nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get()); - auto ymd = date::year(i / 10000) / (i / 100 % 100) / (i % 100); - auto wd = date::weekday(ymd); - uint8_t j = *nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, posix_locale.get()); - return wd + date::days(j - 1); + 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)}}; + const auto j{(uint8_t)*nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, posix_locale.get())}; + return wd + days{j - 1}; } #endif - return date::Sunday; + return Sunday; } diff --git a/test/date.cpp b/test/date.cpp index aa6d79b0..004d6aa8 100644 --- a/test/date.cpp +++ b/test/date.cpp @@ -1,6 +1,5 @@ #include "util/date.hpp" -#include #include #include #include @@ -20,13 +19,13 @@ #endif using namespace std::literals::chrono_literals; - +namespace fmt_lib = waybar::util::date::format; /* * Check that the date/time formatter with locale and timezone support is working as expected. */ -const date::zoned_time TEST_TIME = date::zoned_time{ - "UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s}; +const zoned_time TEST_TIME{ + "UTC", local_days{Monday[1] / January / 2022} + 13h + 4min + 5s}; /* * Check if the date formatted with LC_TIME=en_US is within expectations. @@ -52,10 +51,11 @@ static const bool LC_TIME_is_sane = []() { TEST_CASE("Format UTC time", "[clock][util]") { const auto loc = std::locale("C"); const auto tm = TEST_TIME; - - CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified - CHECK(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC"); - CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); +#if not HAVE_CHRONO_TIMEZONES + CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified +#endif + CHECK(fmt_lib::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC"); + CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103130405"); if (!LC_TIME_is_sane) { SKIP("Locale support check failed, skip tests"); @@ -66,11 +66,15 @@ TEST_CASE("Format UTC time", "[clock][util]") { try { const auto loc = std::locale("en_US.UTF-8"); - CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified - CHECK_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 +#if not HAVE_CHRONO_TIMEZONES + CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified + CHECK_THAT(fmt_lib::format(loc, "{:%c}", tm), // HowardHinnant/date#704 Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM")); - CHECK(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 01:04:05 PM"); - CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + CHECK(fmt_lib::format(loc, "{:%x %X}", tm) == "01/03/2022 01:04:05 PM"); +#else + CHECK(fmt_lib::format(loc, "{:%F %r}", tm) == "2022-01-03 01:04:05 PM"); +#endif + CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103130405"); } catch (const std::runtime_error &) { WARN("Locale en_US not found, skip tests"); } @@ -79,11 +83,15 @@ TEST_CASE("Format UTC time", "[clock][util]") { try { const auto loc = std::locale("en_GB.UTF-8"); - CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified - CHECK_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 +#if not HAVE_CHRONO_TIMEZONES + CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified + CHECK_THAT(fmt_lib::format(loc, "{:%c}", tm), // HowardHinnant/date#704 Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05")); - CHECK(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 13:04:05"); - CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + CHECK(fmt_lib::format(loc, "{:%x %X}", tm) == "03/01/22 13:04:05"); +#else + CHECK(fmt_lib::format(loc, "{:%F %T}", tm) == "2022-01-03 13:04:05"); +#endif + CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103130405"); } catch (const std::runtime_error &) { WARN("Locale en_GB not found, skip tests"); } @@ -92,11 +100,15 @@ TEST_CASE("Format UTC time", "[clock][util]") { try { const auto loc = std::locale::global(std::locale("en_US.UTF-8")); - CHECK(fmt::format("{}", tm).empty()); // no format specified - CHECK_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 +#if not HAVE_CHRONO_TIMEZONES + CHECK(fmt_lib::format("{}", tm).empty()); // no format specified + CHECK_THAT(fmt_lib::format("{:%c}", tm), // HowardHinnant/date#704 Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM")); - CHECK(fmt::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM"); - CHECK(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + CHECK(fmt_lib::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM"); +#else + CHECK(fmt_lib::format("{:%F %r}", tm) == "2022-01-03 01:04:05 PM"); +#endif + CHECK(fmt_lib::format("{:%Y%m%d%H%M%S}", tm) == "20220103130405"); std::locale::global(loc); } catch (const std::runtime_error &) { @@ -107,11 +119,13 @@ TEST_CASE("Format UTC time", "[clock][util]") { TEST_CASE("Format zoned time", "[clock][util]") { const auto loc = std::locale("C"); - const auto tm = date::zoned_time{"America/New_York", TEST_TIME}; + const auto tm = zoned_time{"America/New_York", TEST_TIME}; - CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified - CHECK(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST"); - CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); +#if not HAVE_CHRONO_TIMEZONES + CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified +#endif + CHECK(fmt_lib::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST"); + CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103080405"); if (!LC_TIME_is_sane) { SKIP("Locale support check failed, skip tests"); @@ -122,11 +136,15 @@ TEST_CASE("Format zoned time", "[clock][util]") { try { const auto loc = std::locale("en_US.UTF-8"); - CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified - CHECK_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 +#if not HAVE_CHRONO_TIMEZONES + CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified + CHECK_THAT(fmt_lib::format(loc, "{:%c}", tm), // HowardHinnant/date#704 Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM")); - CHECK(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 08:04:05 AM"); - CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + CHECK(fmt_lib::format(loc, "{:%x %X}", tm) == "01/03/2022 08:04:05 AM"); +#else + CHECK(fmt_lib::format(loc, "{:%F %r}", tm) == "2022-01-03 08:04:05 AM"); +#endif + CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103080405"); } catch (const std::runtime_error &) { WARN("Locale en_US not found, skip tests"); } @@ -135,11 +153,15 @@ TEST_CASE("Format zoned time", "[clock][util]") { try { const auto loc = std::locale("en_GB.UTF-8"); - CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified - CHECK_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 +#if not HAVE_CHRONO_TIMEZONES + CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified + CHECK_THAT(fmt_lib::format(loc, "{:%c}", tm), // HowardHinnant/date#704 Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05")); - CHECK(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 08:04:05"); - CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + CHECK(fmt_lib::format(loc, "{:%x %X}", tm) == "03/01/22 08:04:05"); +#else + CHECK(fmt_lib::format(loc, "{:%F %T}", tm) == "2022-01-03 08:04:05"); +#endif + CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103080405"); } catch (const std::runtime_error &) { WARN("Locale en_GB not found, skip tests"); } @@ -148,11 +170,15 @@ TEST_CASE("Format zoned time", "[clock][util]") { try { const auto loc = std::locale::global(std::locale("en_US.UTF-8")); - CHECK(fmt::format("{}", tm).empty()); // no format specified - CHECK_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 +#if not HAVE_CHRONO_TIMEZONES + CHECK(fmt_lib::format("{}", tm).empty()); // no format specified + CHECK_THAT(fmt_lib::format("{:%c}", tm), // HowardHinnant/date#704 Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM")); - CHECK(fmt::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM"); - CHECK(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + CHECK(fmt_lib::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM"); +#else + CHECK(fmt_lib::format("{:%F %r}", tm) == "2022-01-03 08:04:05 AM"); +#endif + CHECK(fmt_lib::format("{:%Y%m%d%H%M%S}", tm) == "20220103080405"); std::locale::global(loc); } catch (const std::runtime_error &) { From 7308893adbbdd6ad770812bec788f5177894f4dc Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Mon, 13 Nov 2023 09:59:50 +0300 Subject: [PATCH 197/364] Happy clang. fmt9-10 Signed-off-by: Viktar Lukashonak --- src/modules/clock.cpp | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index e003107a..d78d4c26 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -316,15 +316,17 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea // Week numbers on the left if (cldWPos_ == WS::LEFT && line > 0) { if (line > 1) { - if (line < ml[(unsigned)ymTmp.month() - 1u]) + if (line < ml[(unsigned)ymTmp.month() - 1u]) { os << fmt_lib::vformat( locale_, fmtMap_[4], fmt_lib::make_format_args( - (line == 2) ? zoned_seconds{tz, local_days{ymTmp / 1}} - : zoned_seconds{tz, local_days{cldGetWeekForLine( - ymTmp, firstdow, line)}})) + (line == 2) + ? static_cast( + zoned_seconds{tz, local_days{ymTmp / 1}}) + : static_cast(zoned_seconds{ + tz, local_days{cldGetWeekForLine(ymTmp, firstdow, line)}}))) << ' '; - else + } else os << pads; } } @@ -342,9 +344,11 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea << fmt_lib::vformat( locale_, fmtMap_[4], fmt_lib::make_format_args( - (line == 2) ? zoned_seconds{tz, local_days{ymTmp / 1}} - : zoned_seconds{tz, local_days{cldGetWeekForLine( - ymTmp, firstdow, line)}})); + (line == 2) ? static_cast( + zoned_seconds{tz, local_days{ymTmp / 1}}) + : static_cast( + zoned_seconds{tz, local_days{cldGetWeekForLine( + ymTmp, firstdow, line)}}))); else os << pads; } @@ -353,7 +357,9 @@ 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], fmt_lib::make_format_args(os.str())); + tmp << fmt_lib::vformat( + locale_, fmtMap_[line], + fmt_lib::make_format_args(static_cast(os.str()))); else tmp << os.str(); // Clear ostringstream @@ -364,9 +370,12 @@ 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::make_format_args(tmp.str())), + fmt_lib::vformat(locale_, fmtMap_[2], + fmt_lib::make_format_args(static_cast(tmp.str()))), std::regex("\\{today\\}"), - fmt_lib::vformat(locale_, fmtMap_[3], fmt_lib::make_format_args(date::format("{:L%e}", d)))); + fmt_lib::vformat(locale_, fmtMap_[3], + fmt_lib::make_format_args( + static_cast(date::format("{:L%e}", d))))); if (cldMode_ == CldMode::YEAR) cldYearCached_ = os.str(); From 375a38a489adcbc376f85ef8745d583c9491d54c Mon Sep 17 00:00:00 2001 From: fdev31 Date: Tue, 14 Nov 2023 21:26:17 +0100 Subject: [PATCH 198/364] Fix for #2646 --- src/modules/hyprland/workspaces.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 91b6242e..1f817262 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -311,7 +311,8 @@ void Workspaces::on_workspace_created(std::string const &payload) { void Workspaces::on_workspace_moved(std::string const &payload) { std::string workspace = payload.substr(0, payload.find(',')); std::string new_output = payload.substr(payload.find(',') + 1); - if (bar_.output->name == new_output) { // TODO: implement this better + bool should_show = show_special() || ! workspace.starts_with("special"); + if (should_show && bar_.output->name == new_output) { // TODO: implement this better const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (Json::Value workspace_json : workspaces_json) { std::string name = workspace_json["name"].asString(); From d4559c7f006b565abe9ca7a3d90d928217b60822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6blom?= Date: Thu, 16 Nov 2023 16:59:05 +0100 Subject: [PATCH 199/364] Using 'image-missing' fallback if no taskbar icon is found --- src/modules/wlr/taskbar.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index f4b137c0..0d558582 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -22,6 +22,7 @@ #include "util/format.hpp" #include "util/rewrite_string.hpp" #include "util/string.hpp" +#include "util/gtk_icon.hpp" namespace waybar::modules::wlr { @@ -182,11 +183,21 @@ bool Task::image_load_icon(Gtk::Image &image, const Glib::RefPtr 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)) + if (Glib::file_test(ret_icon_name, Glib::FILE_TEST_EXISTS)) { pixbuf = load_icon_from_file(ret_icon_name, scaled_icon_size); - else - pixbuf = {}; + 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 (pixbuf) { @@ -304,6 +315,10 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, with_icon_ = true; } + if (app_id_.empty()) { + handle_app_id("unknown"); + } + /* Strip spaces at the beginning and end of the format strings */ format_tooltip_.clear(); if (!config_["tooltip"].isBool() || config_["tooltip"].asBool()) { @@ -392,6 +407,11 @@ void Task::hide_if_ignored() { } void Task::handle_app_id(const char *app_id) { + if (app_id_.empty()) { + spdlog::debug(fmt::format("Task ({}) setting app_id to {}", id_, app_id)); + } else { + spdlog::debug(fmt::format("Task ({}) overwriting app_id '{}' with '{}'", id_, app_id_, app_id)); + } app_id_ = app_id; hide_if_ignored(); From b6b495e1bc70ed0f29f15f997286718c22c1c535 Mon Sep 17 00:00:00 2001 From: aserowy Date: Sat, 18 Nov 2023 11:36:47 +0100 Subject: [PATCH 200/364] hyprland module: added sort_workspaces after rename occured Signed-off-by: aserowy --- 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 1f817262..332d7617 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -339,6 +339,7 @@ void Workspaces::on_workspace_renamed(std::string const &payload) { break; } } + sort_workspaces(); } void Workspaces::on_monitor_focused(std::string const &payload) { From d2dd2d553f8dca281aa9113102ac325a3b28b5d7 Mon Sep 17 00:00:00 2001 From: Kauan Decarli Date: Sat, 18 Nov 2023 13:59:21 -0300 Subject: [PATCH 201/364] cpu_usage/linux: count iowait toward idle time --- src/modules/cpu_usage/linux.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/cpu_usage/linux.cpp b/src/modules/cpu_usage/linux.cpp index 28930c1c..bcd9594e 100644 --- a/src/modules/cpu_usage/linux.cpp +++ b/src/modules/cpu_usage/linux.cpp @@ -21,8 +21,9 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( size_t idle_time = 0; size_t total_time = 0; - if (times.size() >= 4) { - idle_time = times[3]; + if (times.size() >= 5) { + // idle + iowait + idle_time = times[3] + times[4]; total_time = std::accumulate(times.begin(), times.end(), 0); } cpuinfo.emplace_back(idle_time, total_time); From 50a4fe96237d870095b45e6ff1ac298f00d8ee2b Mon Sep 17 00:00:00 2001 From: Anthony Ruhier Date: Thu, 23 Nov 2023 00:34:23 +0100 Subject: [PATCH 202/364] privacy: fix visibility when a module is disabled Only consider a privacy module as visible if it is enabled in the configuration. Otherwise, when screensharing or audio-in or audio-out is in use but the associated module is not enabled, the privacy widget is empty but still considered as visible. --- src/modules/privacy/privacy.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index e96f14fa..64a1572b 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -121,21 +121,22 @@ void Privacy::onPrivacyNodesChanged() { auto Privacy::update() -> void { mutex_.lock(); - bool screenshare = !nodes_screenshare.empty(); - bool audio_in = !nodes_audio_in.empty(); - bool audio_out = !nodes_audio_out.empty(); + bool screenshare, audio_in, audio_out; for (Gtk::Widget* widget : box_.get_children()) { PrivacyItem* module = dynamic_cast(widget); if (!module) continue; switch (module->privacy_type) { case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: + screenshare = !nodes_screenshare.empty(); module->set_in_use(screenshare); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT: + audio_in = !nodes_audio_in.empty(); module->set_in_use(audio_in); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT: + audio_out = !nodes_audio_out.empty(); module->set_in_use(audio_out); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_NONE: From 0162dbd4856d4d95181e57aed3e92a9e6be3ffcc Mon Sep 17 00:00:00 2001 From: Jeremy Huang Date: Mon, 27 Nov 2023 16:20:05 -0800 Subject: [PATCH 203/364] add button single click check to hyprland workspaces --- src/modules/hyprland/workspaces.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 332d7617..092f8bd6 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -311,7 +311,7 @@ void Workspaces::on_workspace_created(std::string const &payload) { void Workspaces::on_workspace_moved(std::string const &payload) { std::string workspace = payload.substr(0, payload.find(',')); std::string new_output = payload.substr(payload.find(',') + 1); - bool should_show = show_special() || ! workspace.starts_with("special"); + bool should_show = show_special() || !workspace.starts_with("special"); if (should_show && bar_.output->name == new_output) { // TODO: implement this better const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (Json::Value workspace_json : workspaces_json) { @@ -849,19 +849,21 @@ std::string &Workspace::select_icon(std::map &icons_ma } bool Workspace::handle_clicked(GdkEventButton *bt) const { - try { - if (id() > 0) { // normal or numbered persistent - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); - } else if (!is_special()) { // named - 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 or numbered persistent + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } else if (!is_special()) { // 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()); } - return true; - } catch (const std::exception &e) { - spdlog::error("Failed to dispatch workspace: {}", e.what()); } return false; } From 2334faa7fd4593b0f15c3b83d9f50dc523b04be8 Mon Sep 17 00:00:00 2001 From: czadowanie Date: Tue, 28 Nov 2023 19:39:08 +0100 Subject: [PATCH 204/364] upower: show-icon config option --- include/modules/upower/upower.hpp | 1 + src/modules/upower/upower.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index 9c1a1830..eda8ab05 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -72,6 +72,7 @@ class UPower : public AModule { std::unique_ptr upower_tooltip; std::string lastStatus; bool showAltText; + bool showIcon = true; bool upowerRunning; guint upowerWatcher_id; std::string nativePath_; diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index e3b3981a..3554d43b 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -18,7 +18,15 @@ UPower::UPower(const std::string& id, const Json::Value& config) m_Mutex(), client(), showAltText(false) { - box_.pack_start(icon_); + // 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_); From 6be5f7cb2923eec480cb0bc8a4fd999569967eaa Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Wed, 29 Nov 2023 22:46:58 +0100 Subject: [PATCH 205/364] Disconnect from PipeWire when destroying the WirePlumber module This fixes a crash where PipeWire tries to send events to a destroyed WirePlumber module. --- src/modules/wireplumber.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index a020e2a0..b2fcb492 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -46,6 +46,7 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val } waybar::modules::Wireplumber::~Wireplumber() { + wp_core_disconnect(wp_core_); g_clear_pointer(&apis_, g_ptr_array_unref); g_clear_object(&om_); g_clear_object(&wp_core_); From 2e2cb67cf14e709966fd3a6001e2af97614d8877 Mon Sep 17 00:00:00 2001 From: chayleaf Date: Sat, 2 Dec 2023 01:10:19 +0700 Subject: [PATCH 206/364] flake: update & cleanup --- .gitignore | 4 ++ flake.lock | 99 ++++--------------------------------------------- flake.nix | 79 +++++++++++++-------------------------- nix/default.nix | 9 +---- 4 files changed, 37 insertions(+), 154 deletions(-) diff --git a/.gitignore b/.gitignore index 4d7babf3..68bc0dc4 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,7 @@ packagecache *.out *.app /.direnv/ + +# Nix +result +result-* diff --git a/flake.lock b/flake.lock index 3b29a610..4bf02e17 100644 --- a/flake.lock +++ b/flake.lock @@ -1,32 +1,13 @@ { "nodes": { - "devshell": { - "inputs": { - "nixpkgs": "nixpkgs", - "systems": "systems" - }, - "locked": { - "lastModified": 1692523566, - "narHash": "sha256-VDJDihK6jNebVw9y3qKCVD6+6QaC/x8kxZzL4MaIPPY=", - "owner": "numtide", - "repo": "devshell", - "rev": "d208c58e2f7afef838add5f18a9936b12a71d695", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "devshell", - "type": "github" - } - }, "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -35,47 +16,13 @@ "type": "github" } }, - "flake-utils": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1689068808, - "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "nixpkgs": { "locked": { - "lastModified": 1677383253, - "narHash": "sha256-UfpzWfSxkfXHnb4boXZNaKsAcUrZT9Hw+tao1oZxd08=", + "lastModified": 1701253981, + "narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9952d6bc395f5841262b006fbace8dd7e143b634", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1692638711, - "narHash": "sha256-J0LgSFgJVGCC1+j5R2QndadWI1oumusg6hCtYAzLID4=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "91a22f76cd1716f9d0149e8a5c68424bb691de15", + "rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58", "type": "github" }, "original": { @@ -87,40 +34,8 @@ }, "root": { "inputs": { - "devshell": "devshell", "flake-compat": "flake-compat", - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs_2" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" + "nixpkgs": "nixpkgs" } } }, diff --git a/flake.nix b/flake.nix index 15e70b63..cc830c7e 100644 --- a/flake.nix +++ b/flake.nix @@ -3,26 +3,19 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - devshell.url = "github:numtide/devshell"; - flake-utils.url = "github:numtide/flake-utils"; flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; }; - outputs = { self, flake-utils, devshell, nixpkgs, flake-compat }: + outputs = { self, nixpkgs, ... }: let inherit (nixpkgs) lib; - genSystems = lib.genAttrs [ + genSystems = func: lib.genAttrs [ "x86_64-linux" "aarch64-linux" - ]; - - pkgsFor = genSystems (system: - import nixpkgs { - inherit system; - }); + ] (system: func (import nixpkgs { inherit system; })); mkDate = longDate: (lib.concatStringsSep "-" [ (builtins.substring 0 4 longDate) @@ -33,58 +26,36 @@ { overlays.default = final: prev: { waybar = final.callPackage ./nix/default.nix { - version = prev.waybar.version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); + # 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 - (system: - (self.overlays.default pkgsFor.${system} pkgsFor.${system}) - // { - default = self.packages.${system}.waybar; - }); + packages = genSystems (pkgs: + let packages = self.overlays.default pkgs pkgs; + in packages // { + default = packages.waybar; + }); } // - flake-utils.lib.eachDefaultSystem (system: { - devShell = - let pkgs = import nixpkgs { - inherit system; + genSystems (pkgs: { + devShells.default = + pkgs.mkShell { + name = "waybar-shell"; - overlays = [ devshell.overlays.default ]; - }; - in - pkgs.devshell.mkShell { - imports = [ "${pkgs.devshell.extraModulesDir}/language/c.nix" ]; + # most of these aren't actually used in the waybar derivation, this is just in case + # they will ever start being used + inherit (pkgs.waybar) buildInputs depsBuildBuild depsBuildBuildPropagated depsBuildTarget + depsBuildTargetPropagated depsHostHost depsHostHostPropagated depsTargetTarget + depsTargetTargetPropagated propagatedBuildInputs propagatedNativeBuildInputs strictDeps; - devshell.packages = with pkgs; [ + nativeBuildInputs = pkgs.waybar.nativeBuildInputs ++ (with pkgs; [ clang-tools gdb - # from nativeBuildInputs - gnumake - meson - ninja - pkg-config - scdoc - ] ++ (map lib.getDev [ - # from buildInputs - wayland wlroots gtkmm3 libsigcxx jsoncpp spdlog gtk-layer-shell howard-hinnant-date libxkbcommon - # optional dependencies - gobject-introspection glib playerctl python3.pkgs.pygobject3 - libevdev libinput libjack2 libmpdclient playerctl libnl - libpulseaudio sndio sway libdbusmenu-gtk3 udev upower wireplumber - - # from propagated build inputs? - at-spi2-atk atkmm cairo cairomm catch2 fmt_8 fontconfig - gdk-pixbuf glibmm gtk3 harfbuzz pango pangomm wayland-protocols ]); - - env = with pkgs; [ - { name = "CPLUS_INCLUDE_PATH"; prefix = "$DEVSHELL_DIR/include"; } - { name = "PKG_CONFIG_PATH"; prefix = "$DEVSHELL_DIR/lib/pkgconfig"; } - { name = "PKG_CONFIG_PATH"; prefix = "$DEVSHELL_DIR/share/pkgconfig"; } - { name = "PATH"; prefix = "${wayland.bin}/bin"; } - { name = "LIBRARY_PATH"; prefix = "${lib.getLib sndio}/lib"; } - { name = "LIBRARY_PATH"; prefix = "${lib.getLib zlib}/lib"; } - { name = "LIBRARY_PATH"; prefix = "${lib.getLib howard-hinnant-date}/lib"; } - ]; }; }); } diff --git a/nix/default.nix b/nix/default.nix index fc77225d..5efa5da4 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -5,16 +5,9 @@ waybar.overrideAttrs (prev: { inherit version; - # version = "0.9.17"; src = lib.cleanSourceWith { - filter = name: type: - let - baseName = baseNameOf (toString name); - in - ! ( - lib.hasSuffix ".nix" baseName - ); + filter = name: type: type != "regular" || !lib.hasSuffix ".nix" name; src = lib.cleanSource ../.; }; }) From b1744278d15132a6199e30dbcc87b4e7d2d41eab Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 1 Dec 2023 14:02:25 -0600 Subject: [PATCH 207/364] chore: lint fix --- 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 0d558582..9a8b89e7 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -20,9 +20,9 @@ #include "glibmm/fileutils.h" #include "glibmm/refptr.h" #include "util/format.hpp" +#include "util/gtk_icon.hpp" #include "util/rewrite_string.hpp" #include "util/string.hpp" -#include "util/gtk_icon.hpp" namespace waybar::modules::wlr { @@ -190,8 +190,8 @@ bool Task::image_load_icon(Gtk::Image &image, const Glib::RefPtr 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); + pixbuf = DefaultGtkIconThemeWrapper::load_icon( + "image-missing", scaled_icon_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); spdlog::debug("{} Loaded icon from resource", repr()); } catch (...) { pixbuf = {}; From 44f309678bc36ca4cccda25e1daf35c96dba7fcc Mon Sep 17 00:00:00 2001 From: czadowanie Date: Mon, 4 Dec 2023 21:28:37 +0100 Subject: [PATCH 208/364] man: document upower/show-icon --- man/waybar-upower.5.scd | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/man/waybar-upower.5.scd b/man/waybar-upower.5.scd index 5ccda07c..df28d743 100644 --- a/man/waybar-upower.5.scd +++ b/man/waybar-upower.5.scd @@ -57,6 +57,11 @@ compatible devices in the tooltip. typeof: string ++ Command to execute when clicked on the module. +*show-icon*: ++ + typeof: bool ++ + default: true ++ + Option to disable battery icon + # FORMAT REPLACEMENTS *{percentage}*: The battery capacity in percentage @@ -93,6 +98,15 @@ depending on the charging state. "tooltip": true, "tooltip-spacing": 20 } +``` +``` +"upower": { + "show-icon": false, + "hide-if-empty": true, + "tooltip": true, + "tooltip-spacing": 20 +} + ``` # STYLE From 4283195803e7ce83014f95e26659935784e0659c Mon Sep 17 00:00:00 2001 From: czadowanie Date: Mon, 4 Dec 2023 21:32:08 +0100 Subject: [PATCH 209/364] man: add dot in upower/show-icon --- man/waybar-upower.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-upower.5.scd b/man/waybar-upower.5.scd index df28d743..5e2a8eb8 100644 --- a/man/waybar-upower.5.scd +++ b/man/waybar-upower.5.scd @@ -60,7 +60,7 @@ compatible devices in the tooltip. *show-icon*: ++ typeof: bool ++ default: true ++ - Option to disable battery icon + Option to disable battery icon. # FORMAT REPLACEMENTS From 8a84cb609ec51ed1e57ad609ce6192c7fb046741 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Mon, 4 Dec 2023 15:05:00 +0100 Subject: [PATCH 210/364] clang-tidy: added StaticConstantCase CheckOption --- .clang-tidy | 1 + 1 file changed, 1 insertion(+) diff --git a/.clang-tidy b/.clang-tidy index aa2c544e..b7a33451 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -26,3 +26,4 @@ CheckOptions: - { 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 4d5b715dc4d24b9f7d74e0907c991b3be5d68ce5 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Mon, 4 Dec 2023 12:10:16 +0100 Subject: [PATCH 211/364] applied clang-tidy suggestions for hyprland workspaces --- include/modules/hyprland/workspaces.hpp | 214 +++--- src/modules/hyprland/workspaces.cpp | 873 ++++++++++++------------ src/util/enum.cpp | 4 +- 3 files changed, 544 insertions(+), 547 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 53bc55fc..a17c2db4 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -34,82 +34,82 @@ class WindowCreationPayload { std::string window_class, std::string window_title); WindowCreationPayload(Json::Value const& client_data); - int increment_time_spent_uncreated(); - bool is_empty(Workspaces& workspace_manager); - bool repr_is_ready() const { return std::holds_alternative(window_); } + int incrementTimeSpentUncreated(); + bool isEmpty(Workspaces& workspace_manager); + bool reprIsReady() const { return std::holds_alternative(m_window); } std::string repr(Workspaces& workspace_manager); - std::string workspace_name() const { return workspace_name_; } - WindowAddress addr() const { return window_address_; } + std::string getWorkspaceName() const { return m_workspaceName; } + WindowAddress getAddress() const { return m_windowAddress; } - void move_to_worksace(std::string& new_workspace_name); + void moveToWorksace(std::string& new_workspace_name); private: - void clear_addr(); - void clear_workspace_name(); + void clearAddr(); + void clearWorkspaceName(); using Repr = std::string; using ClassAndTitle = std::pair; - std::variant window_; + std::variant m_window; - WindowAddress window_address_; - std::string workspace_name_; + WindowAddress m_windowAddress; + std::string m_workspaceName; - int time_spent_uncreated_ = 0; + 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& select_icon(std::map& icons_map); - Gtk::Button& button() { return button_; }; + std::string& selectIcon(std::map& icons_map); + Gtk::Button& button() { return m_button; }; - int id() const { return id_; }; - std::string name() const { return name_; }; - std::string output() const { return output_; }; - bool active() const { return active_; }; - bool is_special() const { return is_special_; }; - bool is_persistent() const { return is_persistent_; }; - bool is_visible() const { return is_visible_; }; - bool is_empty() const { return windows_ == 0; }; - bool is_urgent() const { return is_urgent_; }; + 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 isSpecial() const { return m_isSpecial; }; + bool isPersistent() const { return m_isPersistent; }; + bool isVisible() const { return m_isVisible; }; + bool isEmpty() const { return m_windows == 0; }; + bool isUrgent() const { return m_isUrgent; }; - bool handle_clicked(GdkEventButton* bt) const; - void set_active(bool value = true) { active_ = value; }; - void set_persistent(bool value = true) { is_persistent_ = value; }; - void set_urgent(bool value = true) { is_urgent_ = value; }; - void set_visible(bool value = true) { is_visible_ = value; }; - void set_windows(uint value) { windows_ = value; }; - void set_name(std::string const& value) { name_ = value; }; - bool contains_window(WindowAddress const& addr) const { return window_map_.contains(addr); } - void insert_window(WindowCreationPayload create_window_paylod); - std::string remove_window(WindowAddress const& addr); - void initialize_window_map(const Json::Value& clients_data); + bool handleClicked(GdkEventButton* bt) const; + void setActive(bool value = true) { m_active = 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; }; + void setWindows(uint value) { m_windows = value; }; + void setName(std::string const& value) { m_name = 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 on_window_opened(WindowCreationPayload const& create_window_paylod); - std::optional close_window(WindowAddress const& addr); + 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& workspace_manager_; + Workspaces& m_workspaceManager; - int id_; - std::string name_; - std::string output_; - uint windows_; - bool active_ = false; - bool is_special_ = false; - bool is_persistent_ = false; - bool is_urgent_ = false; - bool is_visible_ = false; + int m_id; + std::string m_name; + std::string m_output; + uint m_windows; + bool m_active = false; + bool m_isSpecial = false; + bool m_isPersistent = false; + bool m_isUrgent = false; + bool m_isVisible = false; - std::map window_map_; + std::map m_windowMap; - Gtk::Button button_; - Gtk::Box content_; - Gtk::Label label_; + Gtk::Button m_button; + Gtk::Box m_content; + Gtk::Label m_label; }; class Workspaces : public AModule, public EventHandler { @@ -119,85 +119,85 @@ class Workspaces : public AModule, public EventHandler { void update() override; void init(); - auto all_outputs() const -> bool { return all_outputs_; } - auto show_special() const -> bool { return show_special_; } - auto active_only() const -> bool { return active_only_; } + auto allOutputs() const -> bool { return m_allOutputs; } + auto showSpecial() const -> bool { return m_showSpecial; } + auto activeOnly() const -> bool { return m_activeOnly; } - auto get_bar_output() const -> std::string { return bar_.output->name; } + auto getBarOutput() const -> std::string { return m_bar.output->name; } - std::string get_rewrite(std::string window_class, std::string window_title); - std::string& get_window_separator() { return format_window_separator_; } - bool is_workspace_ignored(std::string const& workspace_name); + std::string getRewrite(std::string window_class, std::string window_title); + std::string& getWindowSeparator() { return m_formatWindowSeparator; } + bool isWorkspaceIgnored(std::string const& workspace_name); - bool window_rewrite_config_uses_title() const { return any_window_rewrite_rule_uses_title_; } + bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; } private: void onEvent(const std::string& e) override; - void update_window_count(); - void sort_workspaces(); - void create_workspace(Json::Value const& workspace_data, - Json::Value const& clients_data = Json::Value::nullRef); - void remove_workspace(std::string const& name); - void set_urgent_workspace(std::string const& windowaddress); - void parse_config(const Json::Value& config); - void register_ipc(); + void updateWindowCount(); + void sortWorkspaces(); + void createWorkspace(Json::Value const& workspace_data, + Json::Value const& clients_data = Json::Value::nullRef); + void removeWorkspace(std::string const& name); + void setUrgentWorkspace(std::string const& windowaddress); + void parseConfig(const Json::Value& config); + void registerIpc(); // workspace events - void on_workspace_activated(std::string const& payload); - void on_workspace_destroyed(std::string const& payload); - void on_workspace_created(std::string const& payload); - void on_workspace_moved(std::string const& payload); - void on_workspace_renamed(std::string const& payload); + void onWorkspaceActivated(std::string const& payload); + void onWorkspaceDestroyed(std::string const& payload); + void onWorkspaceCreated(std::string const& payload); + void onWorkspaceMoved(std::string const& payload); + void onWorkspaceRenamed(std::string const& payload); // monitor events - void on_monitor_focused(std::string const& payload); + void onMonitorFocused(std::string const& payload); // window events - void on_window_opened(std::string const& payload); - void on_window_closed(std::string const& payload); - void on_window_moved(std::string const& payload); + void onWindowOpened(std::string const& payload); + void onWindowClosed(std::string const& addr); + void onWindowMoved(std::string const& payload); - void on_window_title_event(std::string const& payload); + void onWindowTitleEvent(std::string const& payload); - int window_rewrite_priority_function(std::string const& window_rule); + int windowRewritePriorityFunction(std::string const& window_rule); - bool all_outputs_ = false; - bool show_special_ = false; - bool active_only_ = false; + bool m_allOutputs = false; + bool m_showSpecial = false; + bool m_activeOnly = false; - enum class SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; - util::EnumParser enum_parser_; - SORT_METHOD sort_by_ = SORT_METHOD::DEFAULT; - std::map sort_map_ = {{"ID", SORT_METHOD::ID}, - {"NAME", SORT_METHOD::NAME}, - {"NUMBER", SORT_METHOD::NUMBER}, - {"DEFAULT", SORT_METHOD::DEFAULT}}; + enum class SortMethod { ID, NAME, NUMBER, DEFAULT }; + util::EnumParser m_enumParser; + SortMethod m_sortBy = SortMethod::DEFAULT; + std::map m_sortMap = {{"ID", SortMethod::ID}, + {"NAME", SortMethod::NAME}, + {"NUMBER", SortMethod::NUMBER}, + {"DEFAULT", SortMethod::DEFAULT}}; - void fill_persistent_workspaces(); - void create_persistent_workspaces(); - std::vector persistent_workspaces_to_create_; - bool persistent_created_ = false; + void fillPersistentWorkspaces(); + void createPersistentWorkspaces(); + std::vector m_persistentWorkspacesToCreate; + bool m_persistentCreated = false; - std::string format_; + std::string m_format; - std::map icons_map_; - util::RegexCollection window_rewrite_rules_; - bool any_window_rewrite_rule_uses_title_ = false; - std::string format_window_separator_; + std::map m_iconsMap; + util::RegexCollection m_windowRewriteRules; + bool m_anyWindowRewriteRuleUsesTitle = false; + std::string m_formatWindowSeparator; - bool with_icon_; - uint64_t monitor_id_; - std::string active_workspace_name_; - std::vector> workspaces_; - std::vector workspaces_to_create_; - std::vector workspaces_to_remove_; - std::vector windows_to_create_; + bool m_withIcon; + uint64_t m_monitorId; + std::string m_activeWorkspaceName; + std::vector> m_workspaces; + std::vector m_workspacesToCreate; + std::vector m_workspacesToRemove; + std::vector m_windowsToCreate; - std::vector ignore_workspaces_; + std::vector m_ignoreWorkspaces; - std::mutex mutex_; - const Bar& bar_; - Gtk::Box box_; + std::mutex m_mutex; + const Bar& m_bar; + Gtk::Box m_box; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 092f8bd6..7ad87bab 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -14,21 +14,21 @@ namespace waybar::modules::hyprland { -int Workspaces::window_rewrite_priority_function(std::string const &window_rule) { +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 has_title = window_rule.find("title") != std::string::npos; - bool const has_class = window_rule.find("class") != std::string::npos; + bool const hasTitle = window_rule.find("title") != std::string::npos; + bool const hasClass = window_rule.find("class") != std::string::npos; - if (has_title && has_class) { - any_window_rewrite_rule_uses_title_ = true; + if (hasTitle && hasClass) { + m_anyWindowRewriteRuleUsesTitle = true; return 3; } - if (has_title) { - any_window_rewrite_rule_uses_title_ = true; + if (hasTitle) { + m_anyWindowRewriteRuleUsesTitle = true; return 2; } - if (has_class) { + if (hasClass) { return 1; } return 0; @@ -36,101 +36,100 @@ int Workspaces::window_rewrite_priority_function(std::string const &window_rule) Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, false), - bar_(bar), - box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { + m_bar(bar), + m_box(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { modulesReady = true; - parse_config(config); + parseConfig(config); - box_.set_name("workspaces"); + m_box.set_name("workspaces"); if (!id.empty()) { - box_.get_style_context()->add_class(id); + m_box.get_style_context()->add_class(id); } - event_box_.add(box_); + event_box_.add(m_box); - if (!gIPC.get()) { + if (!gIPC) { gIPC = std::make_unique(); } init(); - register_ipc(); + registerIpc(); } -auto Workspaces::parse_config(const Json::Value &config) -> void { - const Json::Value &config_format = config["format"]; +auto Workspaces::parseConfig(const Json::Value &config) -> void { + const Json::Value &configFormat = config["format"]; - format_ = config_format.isString() ? config_format.asString() : "{name}"; - with_icon_ = format_.find("{icon}") != std::string::npos; + m_format = configFormat.isString() ? configFormat.asString() : "{name}"; + m_withIcon = m_format.find("{icon}") != std::string::npos; - if (with_icon_ && icons_map_.empty()) { - Json::Value format_icons = config["format-icons"]; - for (std::string &name : format_icons.getMemberNames()) { - icons_map_.emplace(name, format_icons[name].asString()); + 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()); } - icons_map_.emplace("", ""); + m_iconsMap.emplace("", ""); } - auto config_all_outputs = config_["all-outputs"]; - if (config_all_outputs.isBool()) { - all_outputs_ = config_all_outputs.asBool(); + auto configAllOutputs = config_["all-outputs"]; + if (configAllOutputs.isBool()) { + m_allOutputs = configAllOutputs.asBool(); } - auto config_show_special = config_["show-special"]; - if (config_show_special.isBool()) { - show_special_ = config_show_special.asBool(); + auto configShowSpecial = config_["show-special"]; + if (configShowSpecial.isBool()) { + m_showSpecial = configShowSpecial.asBool(); } - auto config_active_only = config_["active-only"]; - if (config_active_only.isBool()) { - active_only_ = config_active_only.asBool(); + auto configActiveOnly = config_["active-only"]; + if (configActiveOnly.isBool()) { + m_activeOnly = configActiveOnly.asBool(); } - auto config_sort_by = config_["sort-by"]; - if (config_sort_by.isString()) { - auto sort_by_str = config_sort_by.asString(); + auto configSortBy = config_["sort-by"]; + if (configSortBy.isString()) { + auto sortByStr = configSortBy.asString(); try { - sort_by_ = enum_parser_.parseStringToEnum(sort_by_str, sort_map_); + 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. - sort_by_ = SORT_METHOD::DEFAULT; + m_sortBy = SortMethod::DEFAULT; g_warning("Invalid string representation for sort-by. Falling back to default sort method."); } } - Json::Value ignore_workspaces = config["ignore-workspaces"]; - if (ignore_workspaces.isArray()) { - for (Json::Value &workspace_regex : ignore_workspaces) { - if (workspace_regex.isString()) { - std::string rule_string = workspace_regex.asString(); + Json::Value ignoreWorkspaces = config["ignore-workspaces"]; + if (ignoreWorkspaces.isArray()) { + for (Json::Value &workspaceRegex : ignoreWorkspaces) { + if (workspaceRegex.isString()) { + std::string ruleString = workspaceRegex.asString(); try { - const std::regex rule{rule_string, std::regex_constants::icase}; - ignore_workspaces_.emplace_back(rule); + const std::regex rule{ruleString, std::regex_constants::icase}; + m_ignoreWorkspaces.emplace_back(rule); } catch (const std::regex_error &e) { - spdlog::error("Invalid rule {}: {}", rule_string, e.what()); + spdlog::error("Invalid rule {}: {}", ruleString, e.what()); } } else { - spdlog::error("Not a string: '{}'", workspace_regex); + spdlog::error("Not a string: '{}'", workspaceRegex); } } } - const Json::Value &format_window_separator = config["format-window-separator"]; - format_window_separator_ = - format_window_separator.isString() ? format_window_separator.asString() : " "; + const Json::Value &formatWindowSeparator = config["format-window-separator"]; + m_formatWindowSeparator = + formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " "; - const Json::Value &window_rewrite = config["window-rewrite"]; + const Json::Value &windowRewrite = config["window-rewrite"]; - const Json::Value &window_rewrite_default_config = config["window-rewrite-default"]; - std::string window_rewrite_default = - window_rewrite_default_config.isString() ? window_rewrite_default_config.asString() : "?"; + const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"]; + std::string windowRewriteDefault = + windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; - window_rewrite_rules_ = util::RegexCollection( - window_rewrite, window_rewrite_default, [this](std::string &window_rule) { - return this->window_rewrite_priority_function(window_rule); - }); + m_windowRewriteRules = util::RegexCollection( + windowRewrite, windowRewriteDefault, + [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); } -auto Workspaces::register_ipc() -> void { +auto Workspaces::registerIpc() -> void { gIPC->registerForIPC("workspace", this); gIPC->registerForIPC("createworkspace", this); gIPC->registerForIPC("destroyworkspace", this); @@ -142,7 +141,7 @@ auto Workspaces::register_ipc() -> void { gIPC->registerForIPC("movewindow", this); gIPC->registerForIPC("urgent", this); - if (window_rewrite_config_uses_title()) { + if (windowRewriteConfigUsesTitle()) { spdlog::info( "Registering for Hyprland's 'windowtitle' events because a user-defined window " "rewrite rule uses the 'title' field."); @@ -152,81 +151,81 @@ auto Workspaces::register_ipc() -> void { auto Workspaces::update() -> void { // remove workspaces that wait to be removed - unsigned int current_remove_workspace_num = 0; - for (const std::string &workspace_to_remove : workspaces_to_remove_) { - remove_workspace(workspace_to_remove); - current_remove_workspace_num++; + unsigned int currentRemoveWorkspaceNum = 0; + for (const std::string &workspaceToRemove : m_workspacesToRemove) { + removeWorkspace(workspaceToRemove); + currentRemoveWorkspaceNum++; } - for (unsigned int i = 0; i < current_remove_workspace_num; i++) { - workspaces_to_remove_.erase(workspaces_to_remove_.begin()); + for (unsigned int i = 0; i < currentRemoveWorkspaceNum; i++) { + m_workspacesToRemove.erase(m_workspacesToRemove.begin()); } // add workspaces that wait to be created - unsigned int current_create_workspace_num = 0; - for (Json::Value const &workspace_to_create : workspaces_to_create_) { - create_workspace(workspace_to_create); - current_create_workspace_num++; + unsigned int currentCreateWorkspaceNum = 0; + for (Json::Value const &workspaceToCreate : m_workspacesToCreate) { + createWorkspace(workspaceToCreate); + currentCreateWorkspaceNum++; } - for (unsigned int i = 0; i < current_create_workspace_num; i++) { - workspaces_to_create_.erase(workspaces_to_create_.begin()); + for (unsigned int i = 0; i < currentCreateWorkspaceNum; i++) { + m_workspacesToCreate.erase(m_workspacesToCreate.begin()); } // get all active workspaces auto monitors = gIPC->getSocket1JsonReply("monitors"); - std::vector visible_workspaces; + std::vector visibleWorkspaces; for (Json::Value &monitor : monitors) { auto ws = monitor["activeWorkspace"]; if (ws.isObject() && (ws["name"].isString())) { - visible_workspaces.push_back(ws["name"].asString()); + visibleWorkspaces.push_back(ws["name"].asString()); } } - for (auto &workspace : workspaces_) { + for (auto &workspace : m_workspaces) { // active - workspace->set_active(workspace->name() == active_workspace_name_); + workspace->setActive(workspace->name() == m_activeWorkspaceName); // disable urgency if workspace is active - if (workspace->name() == active_workspace_name_ && workspace->is_urgent()) { - workspace->set_urgent(false); + if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { + workspace->setUrgent(false); } // visible - workspace->set_visible(std::find(visible_workspaces.begin(), visible_workspaces.end(), - workspace->name()) != visible_workspaces.end()); + workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), + workspace->name()) != visibleWorkspaces.end()); // set workspace icon - std::string &workspace_icon = icons_map_[""]; - if (with_icon_) { - workspace_icon = workspace->select_icon(icons_map_); + std::string &workspaceIcon = m_iconsMap[""]; + if (m_withIcon) { + workspaceIcon = workspace->selectIcon(m_iconsMap); } - workspace->update(format_, workspace_icon); + workspace->update(m_format, workspaceIcon); } - bool any_window_created = false; - std::vector not_created; + bool anyWindowCreated = false; + std::vector notCreated; - for (auto &window_payload : windows_to_create_) { + for (auto &windowPayload : m_windowsToCreate) { bool created = false; - for (auto &workspace : workspaces_) { - if (workspace->on_window_opened(window_payload)) { + for (auto &workspace : m_workspaces) { + if (workspace->onWindowOpened(windowPayload)) { created = true; - any_window_created = true; + anyWindowCreated = true; break; } } if (!created) { static auto const WINDOW_CREATION_TIMEOUT = 2; - if (window_payload.increment_time_spent_uncreated() < WINDOW_CREATION_TIMEOUT) { - not_created.push_back(window_payload); + if (windowPayload.incrementTimeSpentUncreated() < WINDOW_CREATION_TIMEOUT) { + notCreated.push_back(windowPayload); } } } - if (any_window_created) { + if (anyWindowCreated) { dp.emit(); } - windows_to_create_.clear(); - windows_to_create_ = not_created; + m_windowsToCreate.clear(); + m_windowsToCreate = notCreated; AModule::update(); } @@ -239,8 +238,8 @@ bool isDoubleSpecial(std::string const &workspace_name) { return workspace_name.find("special:special:") != std::string::npos; } -bool Workspaces::is_workspace_ignored(std::string const &name) { - for (auto &rule : ignore_workspaces_) { +bool Workspaces::isWorkspaceIgnored(std::string const &name) { + for (auto &rule : m_ignoreWorkspaces) { if (std::regex_match(name, rule)) { return true; break; @@ -251,394 +250,392 @@ bool Workspaces::is_workspace_ignored(std::string const &name) { } void Workspaces::onEvent(const std::string &ev) { - std::lock_guard lock(mutex_); + std::lock_guard lock(m_mutex); std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>')); std::string payload = ev.substr(eventName.size() + 2); if (eventName == "workspace") { - on_workspace_activated(payload); + onWorkspaceActivated(payload); } else if (eventName == "destroyworkspace") { - on_workspace_destroyed(payload); + onWorkspaceDestroyed(payload); } else if (eventName == "createworkspace") { - on_workspace_created(payload); + onWorkspaceCreated(payload); } else if (eventName == "focusedmon") { - on_monitor_focused(payload); - } else if (eventName == "moveworkspace" && !all_outputs()) { - on_workspace_moved(payload); + onMonitorFocused(payload); + } else if (eventName == "moveworkspace" && !allOutputs()) { + onWorkspaceMoved(payload); } else if (eventName == "openwindow") { - on_window_opened(payload); + onWindowOpened(payload); } else if (eventName == "closewindow") { - on_window_closed(payload); + onWindowClosed(payload); } else if (eventName == "movewindow") { - on_window_moved(payload); + onWindowMoved(payload); } else if (eventName == "urgent") { - set_urgent_workspace(payload); + setUrgentWorkspace(payload); } else if (eventName == "renameworkspace") { - on_workspace_renamed(payload); + onWorkspaceRenamed(payload); } else if (eventName == "windowtitle") { - on_window_title_event(payload); + onWindowTitleEvent(payload); } dp.emit(); } -void Workspaces::on_workspace_activated(std::string const &payload) { - active_workspace_name_ = payload; +void Workspaces::onWorkspaceActivated(std::string const &payload) { + m_activeWorkspaceName = payload; } -void Workspaces::on_workspace_destroyed(std::string const &payload) { +void Workspaces::onWorkspaceDestroyed(std::string const &payload) { if (!isDoubleSpecial(payload)) { - workspaces_to_remove_.push_back(payload); + m_workspacesToRemove.push_back(payload); } } -void Workspaces::on_workspace_created(std::string const &payload) { - const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); +void Workspaces::onWorkspaceCreated(std::string const &payload) { + const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - if (!is_workspace_ignored(payload)) { - for (Json::Value workspace_json : workspaces_json) { - std::string name = workspace_json["name"].asString(); + if (!isWorkspaceIgnored(payload)) { + for (Json::Value workspaceJson : workspacesJson) { + std::string name = workspaceJson["name"].asString(); if (name == payload && - (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (show_special() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { - workspaces_to_create_.push_back(workspace_json); + (allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && + (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { + m_workspacesToCreate.push_back(workspaceJson); break; } } } } -void Workspaces::on_workspace_moved(std::string const &payload) { +void Workspaces::onWorkspaceMoved(std::string const &payload) { std::string workspace = payload.substr(0, payload.find(',')); - std::string new_output = payload.substr(payload.find(',') + 1); - bool should_show = show_special() || !workspace.starts_with("special"); - if (should_show && bar_.output->name == new_output) { // TODO: implement this better - const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); - for (Json::Value workspace_json : workspaces_json) { - std::string name = workspace_json["name"].asString(); - if (name == workspace && bar_.output->name == workspace_json["monitor"].asString()) { - workspaces_to_create_.push_back(workspace_json); + std::string newOutput = payload.substr(payload.find(',') + 1); + bool shouldShow = showSpecial() || !workspace.starts_with("special"); + if (shouldShow && m_bar.output->name == newOutput) { // TODO: implement this better + const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + for (Json::Value workspaceJson : workspacesJson) { + std::string name = workspaceJson["name"].asString(); + if (name == workspace && m_bar.output->name == workspaceJson["monitor"].asString()) { + m_workspacesToCreate.push_back(workspaceJson); break; } } } else { - workspaces_to_remove_.push_back(workspace); + m_workspacesToRemove.push_back(workspace); } } -void Workspaces::on_workspace_renamed(std::string const &payload) { - std::string workspace_id_str = payload.substr(0, payload.find(',')); - int workspace_id = workspace_id_str == "special" ? -99 : std::stoi(workspace_id_str); - std::string new_name = payload.substr(payload.find(',') + 1); - for (auto &workspace : workspaces_) { - if (workspace->id() == workspace_id) { - if (workspace->name() == active_workspace_name_) { - active_workspace_name_ = new_name; +void Workspaces::onWorkspaceRenamed(std::string const &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); + for (auto &workspace : m_workspaces) { + if (workspace->id() == workspaceId) { + if (workspace->name() == m_activeWorkspaceName) { + m_activeWorkspaceName = newName; } - workspace->set_name(new_name); + workspace->setName(newName); break; } } - sort_workspaces(); + sortWorkspaces(); } -void Workspaces::on_monitor_focused(std::string const &payload) { - active_workspace_name_ = payload.substr(payload.find(',') + 1); +void Workspaces::onMonitorFocused(std::string const &payload) { + m_activeWorkspaceName = payload.substr(payload.find(',') + 1); } -void Workspaces::on_window_opened(std::string const &payload) { - update_window_count(); - size_t last_comma_idx = 0; - size_t next_comma_idx = payload.find(','); - std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx); +void Workspaces::onWindowOpened(std::string const &payload) { + updateWindowCount(); + size_t lastCommaIdx = 0; + size_t nextCommaIdx = payload.find(','); + std::string windowAddress = payload.substr(lastCommaIdx, nextCommaIdx - lastCommaIdx); - last_comma_idx = next_comma_idx; - next_comma_idx = payload.find(',', next_comma_idx + 1); - std::string workspace_name = - payload.substr(last_comma_idx + 1, next_comma_idx - last_comma_idx - 1); + lastCommaIdx = nextCommaIdx; + nextCommaIdx = payload.find(',', nextCommaIdx + 1); + std::string workspaceName = payload.substr(lastCommaIdx + 1, nextCommaIdx - lastCommaIdx - 1); - last_comma_idx = next_comma_idx; - next_comma_idx = payload.find(',', next_comma_idx + 1); - std::string window_class = - payload.substr(last_comma_idx + 1, next_comma_idx - last_comma_idx - 1); + lastCommaIdx = nextCommaIdx; + nextCommaIdx = payload.find(',', nextCommaIdx + 1); + std::string windowClass = payload.substr(lastCommaIdx + 1, nextCommaIdx - lastCommaIdx - 1); - std::string window_title = payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx); + std::string windowTitle = payload.substr(nextCommaIdx + 1, payload.length() - nextCommaIdx); - windows_to_create_.emplace_back(workspace_name, window_address, window_class, window_title); + m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowClass, windowTitle); } -void Workspaces::on_window_closed(std::string const &addr) { - update_window_count(); - for (auto &workspace : workspaces_) { - if (workspace->close_window(addr)) { +void Workspaces::onWindowClosed(std::string const &addr) { + updateWindowCount(); + for (auto &workspace : m_workspaces) { + if (workspace->closeWindow(addr)) { break; } } } -void Workspaces::on_window_moved(std::string const &payload) { - update_window_count(); - size_t last_comma_idx = 0; - size_t next_comma_idx = payload.find(','); - std::string window_address = payload.substr(last_comma_idx, next_comma_idx - last_comma_idx); +void Workspaces::onWindowMoved(std::string const &payload) { + updateWindowCount(); + size_t lastCommaIdx = 0; + size_t nextCommaIdx = payload.find(','); + std::string windowAddress = payload.substr(lastCommaIdx, nextCommaIdx - lastCommaIdx); - std::string workspace_name = - payload.substr(next_comma_idx + 1, payload.length() - next_comma_idx); + std::string workspaceName = payload.substr(nextCommaIdx + 1, payload.length() - nextCommaIdx); - std::string window_repr; + std::string windowRepr; // If the window was still queued to be created, just change its destination // and exit - for (auto &window : windows_to_create_) { - if (window.addr() == window_address) { - window.move_to_worksace(workspace_name); + for (auto &window : m_windowsToCreate) { + if (window.getAddress() == windowAddress) { + window.moveToWorksace(workspaceName); return; } } // Take the window's representation from the old workspace... - for (auto &workspace : workspaces_) { - if (auto window_addr = workspace->close_window(window_address); window_addr != std::nullopt) { - window_repr = window_addr.value(); + for (auto &workspace : m_workspaces) { + if (auto windowAddr = workspace->closeWindow(windowAddress); windowAddr != std::nullopt) { + windowRepr = windowAddr.value(); break; } } // ...and add it to the new workspace - if (!window_repr.empty()) { - windows_to_create_.emplace_back(workspace_name, window_address, window_repr); + if (!windowRepr.empty()) { + m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowRepr); } } -void Workspaces::on_window_title_event(std::string const &payload) { - auto window_workspace = - std::find_if(workspaces_.begin(), workspaces_.end(), - [payload](auto &workspace) { return workspace->contains_window(payload); }); +void Workspaces::onWindowTitleEvent(std::string const &payload) { + auto windowWorkspace = + std::find_if(m_workspaces.begin(), m_workspaces.end(), + [payload](auto &workspace) { return workspace->containsWindow(payload); }); - if (window_workspace != workspaces_.end()) { - Json::Value clients_data = gIPC->getSocket1JsonReply("clients"); - std::string json_window_address = fmt::format("0x{}", payload); + if (windowWorkspace != m_workspaces.end()) { + Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); + std::string jsonWindowAddress = fmt::format("0x{}", payload); auto client = - std::find_if(clients_data.begin(), clients_data.end(), [json_window_address](auto &client) { - return client["address"].asString() == json_window_address; + std::find_if(clientsData.begin(), clientsData.end(), [jsonWindowAddress](auto &client) { + return client["address"].asString() == jsonWindowAddress; }); if (!client->empty()) { - (*window_workspace)->insert_window({*client}); + (*windowWorkspace)->insertWindow({*client}); } } } -void Workspaces::update_window_count() { - const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); - for (auto &workspace : workspaces_) { - auto workspace_json = std::find_if( - workspaces_json.begin(), workspaces_json.end(), +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(); }); uint32_t count = 0; - if (workspace_json != workspaces_json.end()) { + if (workspaceJson != workspacesJson.end()) { try { - count = (*workspace_json)["windows"].asUInt(); + count = (*workspaceJson)["windows"].asUInt(); } catch (const std::exception &e) { spdlog::error("Failed to update window count: {}", e.what()); } } - workspace->set_windows(count); + workspace->setWindows(count); } } -void Workspace::initialize_window_map(const Json::Value &clients_data) { - window_map_.clear(); +void Workspace::initializeWindowMap(const Json::Value &clients_data) { + m_windowMap.clear(); for (auto client : clients_data) { if (client["workspace"]["id"].asInt() == id()) { - insert_window({client}); + insertWindow({client}); } } } -void Workspace::insert_window(WindowCreationPayload create_window_paylod) { - if (!create_window_paylod.is_empty(workspace_manager_)) { - window_map_[create_window_paylod.addr()] = create_window_paylod.repr(workspace_manager_); +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::remove_window(WindowAddress const &addr) { - std::string window_repr = window_map_[addr]; - window_map_.erase(addr); - return window_repr; +std::string Workspace::removeWindow(WindowAddress const &addr) { + std::string windowRepr = m_windowMap[addr]; + m_windowMap.erase(addr); + return windowRepr; } -bool Workspace::on_window_opened(WindowCreationPayload const &create_window_paylod) { - if (create_window_paylod.workspace_name() == name()) { - insert_window(create_window_paylod); +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::close_window(WindowAddress const &addr) { - if (window_map_.contains(addr)) { - return remove_window(addr); +std::optional Workspace::closeWindow(WindowAddress const &addr) { + if (m_windowMap.contains(addr)) { + return removeWindow(addr); } return std::nullopt; } -void Workspaces::create_workspace(Json::Value const &workspace_data, - Json::Value const &clients_data) { +void Workspaces::createWorkspace(Json::Value const &workspace_data, + Json::Value const &clients_data) { // avoid recreating existing workspaces - auto workspace_name = workspace_data["name"].asString(); + auto workspaceName = workspace_data["name"].asString(); auto workspace = std::find_if( - workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr const &w) { - return (workspace_name.starts_with("special:") && workspace_name.substr(8) == w->name()) || - workspace_name == w->name(); + 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(); }); - if (workspace != workspaces_.end()) { - if (workspace_data["persistent"].asBool() and !(*workspace)->is_persistent()) { - (*workspace)->set_persistent(); + if (workspace != m_workspaces.end()) { + if (workspace_data["persistent"].asBool() and !(*workspace)->isPersistent()) { + (*workspace)->setPersistent(); } return; } // create new workspace - workspaces_.emplace_back(std::make_unique(workspace_data, *this, clients_data)); - Gtk::Button &new_workspace_button = workspaces_.back()->button(); - box_.pack_start(new_workspace_button, false, false); - sort_workspaces(); - new_workspace_button.show_all(); + 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::remove_workspace(std::string const &name) { +void Workspaces::removeWorkspace(std::string const &name) { auto workspace = - std::find_if(workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr &x) { + 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(); }); - if (workspace == workspaces_.end()) { + if (workspace == m_workspaces.end()) { // happens when a workspace on another monitor is destroyed return; } - if ((*workspace)->is_persistent()) { - // don't remove persistent workspaces, create_workspace will take care of replacement + if ((*workspace)->isPersistent()) { + // don't remove persistent workspaces, createWorkspace will take care of replacement return; } - box_.remove(workspace->get()->button()); - workspaces_.erase(workspace); + m_box.remove(workspace->get()->button()); + m_workspaces.erase(workspace); } -void Workspaces::fill_persistent_workspaces() { +void Workspaces::fillPersistentWorkspaces() { 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()) { - const Json::Value persistent_workspaces = config_["persistent-workspaces"].isObject() - ? config_["persistent-workspaces"] - : config_["persistent_workspaces"]; - const std::vector keys = persistent_workspaces.getMemberNames(); + 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 can_create = - (key == "*" && std::find(keys.begin(), keys.end(), bar_.output->name) == keys.end()) || - key == bar_.output->name; - const Json::Value &value = persistent_workspaces[key]; + 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 (can_create) { + if (canCreate) { int amount = value.asInt(); spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, - bar_.output->name); + m_bar.output->name); for (int i = 0; i < amount; i++) { - persistent_workspaces_to_create_.emplace_back( - std::to_string(monitor_id_ * amount + i + 1)); + m_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 (can_create) { + if (canCreate) { for (const Json::Value &workspace : value) { if (workspace.isInt()) { - spdlog::debug("Creating workspace {} on monitor {}", workspace, bar_.output->name); - persistent_workspaces_to_create_.emplace_back(std::to_string(workspace.asInt())); + 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() == bar_.output->name) { - persistent_workspaces_to_create_.emplace_back(key); + if (monitor.isString() && monitor.asString() == m_bar.output->name) { + m_persistentWorkspacesToCreate.emplace_back(key); break; } } } } else { // this workspace should be displayed on all monitors - persistent_workspaces_to_create_.emplace_back(key); + m_persistentWorkspacesToCreate.emplace_back(key); } } } } -void Workspaces::create_persistent_workspaces() { - for (const std::string &workspace_name : persistent_workspaces_to_create_) { - Json::Value new_workspace; +void Workspaces::createPersistentWorkspaces() { + for (const std::string &workspaceName : m_persistentWorkspacesToCreate) { + Json::Value newWorkspace; try { // numbered persistent workspaces get the name as ID - new_workspace["id"] = workspace_name == "special" ? -99 : std::stoi(workspace_name); + newWorkspace["id"] = workspaceName == "special" ? -99 : std::stoi(workspaceName); } catch (const std::exception &e) { // named persistent workspaces start with ID=0 - new_workspace["id"] = 0; + newWorkspace["id"] = 0; } - new_workspace["name"] = workspace_name; - new_workspace["monitor"] = bar_.output->name; - new_workspace["windows"] = 0; - new_workspace["persistent"] = true; + newWorkspace["name"] = workspaceName; + newWorkspace["monitor"] = m_bar.output->name; + newWorkspace["windows"] = 0; + newWorkspace["persistent"] = true; - create_workspace(new_workspace); + createWorkspace(newWorkspace); } } void Workspaces::init() { - active_workspace_name_ = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); // get monitor ID from name (used by persistent workspaces) - monitor_id_ = 0; + m_monitorId = 0; auto monitors = gIPC->getSocket1JsonReply("monitors"); - auto current_monitor = std::find_if( + auto currentMonitor = std::find_if( monitors.begin(), monitors.end(), - [this](const Json::Value &m) { return m["name"].asString() == bar_.output->name; }); - if (current_monitor == monitors.end()) { - spdlog::error("Monitor '{}' does not have an ID? Using 0", bar_.output->name); + [this](const Json::Value &m) { return m["name"].asString() == m_bar.output->name; }); + if (currentMonitor == monitors.end()) { + spdlog::error("Monitor '{}' does not have an ID? Using 0", m_bar.output->name); } else { - monitor_id_ = (*current_monitor)["id"].asInt(); + m_monitorId = (*currentMonitor)["id"].asInt(); } - const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); - const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); + const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + const Json::Value clientsJson = gIPC->getSocket1JsonReply("clients"); - for (Json::Value workspace_json : workspaces_json) { - std::string workspace_name = workspace_json["name"].asString(); - if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (!workspace_name.starts_with("special") || show_special()) && - !is_workspace_ignored(workspace_name)) { - create_workspace(workspace_json, clients_json); + 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); } } - fill_persistent_workspaces(); - create_persistent_workspaces(); + fillPersistentWorkspaces(); + createPersistentWorkspaces(); - update_window_count(); + updateWindowCount(); - sort_workspaces(); + sortWorkspaces(); dp.emit(); } @@ -646,41 +643,41 @@ void Workspaces::init() { Workspaces::~Workspaces() { gIPC->unregisterForIPC(this); // wait for possible event handler to finish - std::lock_guard lg(mutex_); + std::lock_guard lg(m_mutex); } Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager, const Json::Value &clients_data) - : workspace_manager_(workspace_manager), - id_(workspace_data["id"].asInt()), - name_(workspace_data["name"].asString()), - output_(workspace_data["monitor"].asString()), // TODO:allow using monitor desc - windows_(workspace_data["windows"].asInt()), - active_(true) { - if (name_.starts_with("name:")) { - name_ = name_.substr(5); - } else if (name_.starts_with("special")) { - name_ = id_ == -99 ? name_ : name_.substr(8); - is_special_ = true; + : 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_active(true) { + 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; } if (workspace_data.isMember("persistent")) { - is_persistent_ = workspace_data["persistent"].asBool(); + m_isPersistent = workspace_data["persistent"].asBool(); } - button_.add_events(Gdk::BUTTON_PRESS_MASK); - button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked), - false); + m_button.add_events(Gdk::BUTTON_PRESS_MASK); + m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked), + false); - button_.set_relief(Gtk::RELIEF_NONE); - content_.set_center_widget(label_); - button_.add(content_); + m_button.set_relief(Gtk::RELIEF_NONE); + m_content.set_center_widget(m_label); + m_button.add(m_content); - initialize_window_map(clients_data); + initializeWindowMap(clients_data); } -void add_or_remove_class(const Glib::RefPtr &context, bool condition, - const std::string &class_name) { +void addOrRemoveClass(const Glib::RefPtr &context, bool condition, + const std::string &class_name) { if (condition) { context->add_class(class_name); } else { @@ -690,76 +687,76 @@ void add_or_remove_class(const Glib::RefPtr &context, bool co void Workspace::update(const std::string &format, const std::string &icon) { // clang-format off - if (this->workspace_manager_.active_only() && \ - !this->active() && \ - !this->is_persistent() && \ - !this->is_visible() && \ - !this->is_special()) { + if (this->m_workspaceManager.activeOnly() && \ + !this->isActive() && \ + !this->isPersistent() && \ + !this->isVisible() && \ + !this->isSpecial()) { // clang-format on - // if active_only is true, hide if not active, persistent, visible or special - button_.hide(); + // if activeOnly is true, hide if not active, persistent, visible or special + m_button.hide(); return; } - button_.show(); + m_button.show(); - auto style_context = button_.get_style_context(); - add_or_remove_class(style_context, active(), "active"); - add_or_remove_class(style_context, is_special(), "special"); - add_or_remove_class(style_context, is_empty(), "empty"); - add_or_remove_class(style_context, is_persistent(), "persistent"); - add_or_remove_class(style_context, is_urgent(), "urgent"); - add_or_remove_class(style_context, is_visible(), "visible"); + 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"); std::string windows; - auto window_separator = workspace_manager_.get_window_separator(); + auto windowSeparator = m_workspaceManager.getWindowSeparator(); - bool is_not_first = false; + bool isNotFirst = false; - for (auto &[_pid, window_repr] : window_map_) { - if (is_not_first) { - windows.append(window_separator); + for (auto &[_pid, window_repr] : m_windowMap) { + if (isNotFirst) { + windows.append(windowSeparator); } - is_not_first = true; + isNotFirst = true; windows.append(window_repr); } - label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), - fmt::arg("name", name()), fmt::arg("icon", icon), - fmt::arg("windows", windows))); + 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::sort_workspaces() { - std::sort(workspaces_.begin(), workspaces_.end(), +void Workspaces::sortWorkspaces() { + std::sort(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr &a, std::unique_ptr &b) { // Helper comparisons - auto is_id_less = a->id() < b->id(); - auto is_name_less = a->name() < b->name(); + auto isIdLess = a->id() < b->id(); + auto isNameLess = a->name() < b->name(); - switch (sort_by_) { - case SORT_METHOD::ID: - return is_id_less; - case SORT_METHOD::NAME: - return is_name_less; - case SORT_METHOD::NUMBER: + switch (m_sortBy) { + case SortMethod::ID: + return isIdLess; + case SortMethod::NAME: + return isNameLess; + case SortMethod::NUMBER: try { return std::stoi(a->name()) < std::stoi(b->name()); } catch (const std::invalid_argument &) { // Handle the exception if necessary. break; } - case SORT_METHOD::DEFAULT: + case SortMethod::DEFAULT: default: // Handle the default case here. // normal -> named persistent -> named -> special -> named special // both normal (includes numbered persistent) => sort by ID if (a->id() > 0 && b->id() > 0) { - return is_id_less; + return isIdLess; } // one normal, one special => normal first - if ((a->is_special()) ^ (b->is_special())) { - return b->is_special(); + if ((a->isSpecial()) ^ (b->isSpecial())) { + return b->isSpecial(); } // only one normal, one named @@ -768,92 +765,92 @@ void Workspaces::sort_workspaces() { } // both special - if (a->is_special() && b->is_special()) { + if (a->isSpecial() && b->isSpecial()) { // if one is -99 => put it last 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) - return is_name_less; + return isNameLess; } // sort non-special named workspaces by name (ID <= -1377) - return is_name_less; + return isNameLess; break; } // Return a default value if none of the cases match. - return is_name_less; // You can adjust this to your specific needs. + return isNameLess; // You can adjust this to your specific needs. }); - for (size_t i = 0; i < workspaces_.size(); ++i) { - box_.reorder_child(workspaces_[i]->button(), i); + for (size_t i = 0; i < m_workspaces.size(); ++i) { + m_box.reorder_child(m_workspaces[i]->button(), i); } } -std::string &Workspace::select_icon(std::map &icons_map) { - if (is_urgent()) { - auto urgent_icon_it = icons_map.find("urgent"); - if (urgent_icon_it != icons_map.end()) { - return urgent_icon_it->second; +std::string &Workspace::selectIcon(std::map &icons_map) { + if (isUrgent()) { + auto urgentIconIt = icons_map.find("urgent"); + if (urgentIconIt != icons_map.end()) { + return urgentIconIt->second; } } - if (active()) { - auto active_icon_it = icons_map.find("active"); - if (active_icon_it != icons_map.end()) { - return active_icon_it->second; + if (isActive()) { + auto activeIconIt = icons_map.find("active"); + if (activeIconIt != icons_map.end()) { + return activeIconIt->second; } } - if (is_special()) { - auto special_icon_it = icons_map.find("special"); - if (special_icon_it != icons_map.end()) { - return special_icon_it->second; + if (isSpecial()) { + auto specialIconIt = icons_map.find("special"); + if (specialIconIt != icons_map.end()) { + return specialIconIt->second; } } - auto named_icon_it = icons_map.find(name()); - if (named_icon_it != icons_map.end()) { - return named_icon_it->second; + auto namedIconIt = icons_map.find(name()); + if (namedIconIt != icons_map.end()) { + return namedIconIt->second; } - if (is_visible()) { - auto visible_icon_it = icons_map.find("visible"); - if (visible_icon_it != icons_map.end()) { - return visible_icon_it->second; + if (isVisible()) { + auto visibleIconIt = icons_map.find("visible"); + if (visibleIconIt != icons_map.end()) { + return visibleIconIt->second; } } - if (is_empty()) { - auto empty_icon_it = icons_map.find("empty"); - if (empty_icon_it != icons_map.end()) { - return empty_icon_it->second; + if (isEmpty()) { + auto emptyIconIt = icons_map.find("empty"); + if (emptyIconIt != icons_map.end()) { + return emptyIconIt->second; } } - if (is_persistent()) { - auto persistent_icon_it = icons_map.find("persistent"); - if (persistent_icon_it != icons_map.end()) { - return persistent_icon_it->second; + if (isPersistent()) { + auto persistentIconIt = icons_map.find("persistent"); + if (persistentIconIt != icons_map.end()) { + return persistentIconIt->second; } } - auto default_icon_it = icons_map.find("default"); - if (default_icon_it != icons_map.end()) { - return default_icon_it->second; + auto defaultIconIt = icons_map.find("default"); + if (defaultIconIt != icons_map.end()) { + return defaultIconIt->second; } - return name_; + return m_name; } -bool Workspace::handle_clicked(GdkEventButton *bt) const { +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 (!is_special()) { // named + } else if (!isSpecial()) { // named gIPC->getSocket1Reply("dispatch workspace name:" + name()); } else if (id() != -99) { // named special gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); @@ -868,92 +865,92 @@ bool Workspace::handle_clicked(GdkEventButton *bt) const { return false; } -void Workspaces::set_urgent_workspace(std::string const &windowaddress) { - const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); - int workspace_id = -1; +void Workspaces::setUrgentWorkspace(std::string const &windowaddress) { + const Json::Value clientsJson = gIPC->getSocket1JsonReply("clients"); + int workspaceId = -1; - for (Json::Value client_json : clients_json) { - if (client_json["address"].asString().ends_with(windowaddress)) { - workspace_id = client_json["workspace"]["id"].asInt(); + for (Json::Value clientJson : clientsJson) { + if (clientJson["address"].asString().ends_with(windowaddress)) { + workspaceId = clientJson["workspace"]["id"].asInt(); break; } } auto workspace = - std::find_if(workspaces_.begin(), workspaces_.end(), - [&](std::unique_ptr &x) { return x->id() == workspace_id; }); - if (workspace != workspaces_.end()) { - workspace->get()->set_urgent(); + std::find_if(m_workspaces.begin(), m_workspaces.end(), + [workspaceId](std::unique_ptr &x) { return x->id() == workspaceId; }); + if (workspace != m_workspaces.end()) { + workspace->get()->setUrgent(); } } -std::string Workspaces::get_rewrite(std::string window_class, std::string window_title) { - std::string window_repr_key; - if (window_rewrite_config_uses_title()) { - window_repr_key = fmt::format("class<{}> title<{}>", window_class, window_title); +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 { - window_repr_key = fmt::format("class<{}>", window_class); + windowReprKey = fmt::format("class<{}>", window_class); } - return window_rewrite_rules_.get(window_repr_key); + return m_windowRewriteRules.get(windowReprKey); } WindowCreationPayload::WindowCreationPayload(std::string workspace_name, WindowAddress window_address, std::string window_repr) - : window_(std::move(window_repr)), - window_address_(std::move(window_address)), - workspace_name_(std::move(workspace_name)) { - clear_addr(); - clear_workspace_name(); + : 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) - : window_(std::make_pair(std::move(window_class), std::move(window_title))), - window_address_(std::move(window_address)), - workspace_name_(std::move(workspace_name)) { - clear_addr(); - clear_workspace_name(); + : 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) - : window_(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), - window_address_(client_data["address"].asString()), - workspace_name_(client_data["workspace"]["name"].asString()) { - clear_addr(); - clear_workspace_name(); + : 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(window_)) { - return std::get(window_); + if (std::holds_alternative(m_window)) { + return std::get(m_window); } - if (std::holds_alternative(window_)) { - auto [window_class, window_title] = std::get(window_); - return workspace_manager.get_rewrite(window_class, window_title); + 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::is_empty(Workspaces &workspace_manager) { - if (std::holds_alternative(window_)) { - return std::get(window_).empty(); +bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) { + if (std::holds_alternative(m_window)) { + return std::get(m_window).empty(); } - if (std::holds_alternative(window_)) { - auto [window_class, window_title] = std::get(window_); + if (std::holds_alternative(m_window)) { + auto [window_class, window_title] = std::get(m_window); return (window_class.empty() && - (!workspace_manager.window_rewrite_config_uses_title() || window_title.empty())); + (!workspace_manager.windowRewriteConfigUsesTitle() || window_title.empty())); } // Unreachable - spdlog::error("WorkspaceWindow::is_empty: Unreachable"); - throw std::runtime_error("WorkspaceWindow::is_empty: Unreachable"); + spdlog::error("WorkspaceWindow::isEmpty: Unreachable"); + throw std::runtime_error("WorkspaceWindow::isEmpty: Unreachable"); } -int WindowCreationPayload::increment_time_spent_uncreated() { return time_spent_uncreated_++; } +int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; } -void WindowCreationPayload::clear_addr() { +void WindowCreationPayload::clearAddr() { // substr(2, ...) is necessary because Hyprland's JSON follows this format: // 0x{ADDR} // While Hyprland's IPC follows this format: @@ -961,13 +958,13 @@ void WindowCreationPayload::clear_addr() { static const std::string ADDR_PREFIX = "0x"; static const int ADDR_PREFIX_LEN = ADDR_PREFIX.length(); - if (window_address_.starts_with(ADDR_PREFIX)) { - window_address_ = - window_address_.substr(ADDR_PREFIX_LEN, window_address_.length() - ADDR_PREFIX_LEN); + if (m_windowAddress.starts_with(ADDR_PREFIX)) { + m_windowAddress = + m_windowAddress.substr(ADDR_PREFIX_LEN, m_windowAddress.length() - ADDR_PREFIX_LEN); } } -void WindowCreationPayload::clear_workspace_name() { +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 @@ -976,14 +973,14 @@ void WindowCreationPayload::clear_workspace_name() { static const std::string SPECIAL_QUALIFIER_PREFIX = "special:"; static const int SPECIAL_QUALIFIER_PREFIX_LEN = SPECIAL_QUALIFIER_PREFIX.length(); - if (workspace_name_.starts_with(SPECIAL_QUALIFIER_PREFIX)) { - workspace_name_ = workspace_name_.substr( - SPECIAL_QUALIFIER_PREFIX_LEN, workspace_name_.length() - SPECIAL_QUALIFIER_PREFIX_LEN); + if (m_workspaceName.starts_with(SPECIAL_QUALIFIER_PREFIX)) { + m_workspaceName = m_workspaceName.substr( + SPECIAL_QUALIFIER_PREFIX_LEN, m_workspaceName.length() - SPECIAL_QUALIFIER_PREFIX_LEN); } } -void WindowCreationPayload::move_to_worksace(std::string &new_workspace_name) { - workspace_name_ = new_workspace_name; +void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) { + m_workspaceName = new_workspace_name; } } // namespace waybar::modules::hyprland diff --git a/src/util/enum.cpp b/src/util/enum.cpp index a29304c5..dc3eae0c 100644 --- a/src/util/enum.cpp +++ b/src/util/enum.cpp @@ -28,7 +28,7 @@ EnumType EnumParser::parseStringToEnum(const std::string& str, std::map capitalizedEnumMap; std::transform( enumMap.begin(), enumMap.end(), std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()), - [this](const auto& pair) { return std::make_pair(capitalize(pair.first), pair.second); }); + [](const auto& pair) { return std::make_pair(capitalize(pair.first), pair.second); }); // Return enum match of string auto it = capitalizedEnumMap.find(uppercaseStr); @@ -40,6 +40,6 @@ EnumType EnumParser::parseStringToEnum(const std::string& str, // Explicit instantiations for specific EnumType types you intend to use // Add explicit instantiations for all relevant EnumType types -template struct EnumParser; +template struct EnumParser; } // namespace waybar::util From d94519a93c56c36c0d1f4dfa15cf12f172d9d2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= Date: Thu, 7 Dec 2023 07:53:45 +0100 Subject: [PATCH 212/364] Clear README.md from duplicated list of distributions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn’t make sense to keep the list in README.md when we maintain it in Wiki as well. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 6009d91a..3441ff8c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # Waybar [![Licence](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Paypal Donate](https://img.shields.io/badge/Donate-Paypal-2244dd.svg)](https://paypal.me/ARouillard)
![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png) > Highly customizable Wayland bar for Sway and Wlroots based compositors.
-> Available in Arch [extra](https://www.archlinux.org/packages/extra/x86_64/waybar/) or -[AUR](https://aur.archlinux.org/packages/waybar-git/), [Gentoo](https://packages.gentoo.org/packages/gui-apps/waybar), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar).
+> Available in [all major distributions](https://github.com/Alexays/Waybar/wiki/Installation)
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)* #### Current features From 4822f967b23a70662c2f56179266c46f1e76aafd Mon Sep 17 00:00:00 2001 From: Rice8Dyb6 Date: Mon, 11 Dec 2023 00:17:35 +0700 Subject: [PATCH 213/364] Sway/workspaces: fix persistent icon --- 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 6674731f..1a47e478 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -314,7 +314,7 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node if (config_["format-icons"][key].isString() && node[key].asBool()) { return config_["format-icons"][key].asString(); } - } else if (config_["format_icons"]["persistent"].isString() && + } else if (config_["format-icons"]["persistent"].isString() && node["target_output"].isString()) { return config_["format-icons"]["persistent"].asString(); } else if (config_["format-icons"][key].isString()) { From 75f9141cac9af2f33bac7311ba72793750cc141a Mon Sep 17 00:00:00 2001 From: "Crom (Thibaut CHARLES)" Date: Tue, 12 Dec 2023 16:16:21 +0100 Subject: [PATCH 214/364] Added cffi man page --- man/waybar-cffi.5.scd | 37 +++++++++++++++++++++++++++++++++++++ meson.build | 1 + 2 files changed, 38 insertions(+) create mode 100644 man/waybar-cffi.5.scd diff --git a/man/waybar-cffi.5.scd b/man/waybar-cffi.5.scd new file mode 100644 index 00000000..926511d8 --- /dev/null +++ b/man/waybar-cffi.5.scd @@ -0,0 +1,37 @@ +waybar-cffi(5) +# NAME + +waybar - cffi module + +# DESCRIPTION + +The *cffi* module gives full control of a GTK widget to a third-party dynamic library, to create more complex modules using different programming languages. + +# CONFIGURATION + +Addressed by *cffi/* + +*module_path*: ++ + typeof: string ++ + The path to the dynamic library to load to control the widget. + +Some additional configuration may be required depending on the cffi dynamic library being used. + + +# EXAMPLES + +## C example: + +An example module written in C can be found at https://github.com/Alexays/Waybar/resources/custom_modules/cffi_example/ + +Waybar config to enable the module: +``` +"cffi/c_example": { + "module_path": ".config/waybar/cffi/wb_cffi_example.so" +} +``` + + +# STYLE + +The classes and IDs are managed by the cffi dynamic library. diff --git a/meson.build b/meson.build index 7c0f9965..a7d9e5b4 100644 --- a/meson.build +++ b/meson.build @@ -442,6 +442,7 @@ if scdoc.found() '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', From da9cc77db8e394387c7a9be91278e2b2dffff8be Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Wed, 13 Dec 2023 11:54:04 +0100 Subject: [PATCH 215/364] Added sudo, python3-pip & python3-venv to debian image, required for clang-tidy GH action --- Dockerfiles/debian | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 578588c7..7536fdfd 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -2,6 +2,12 @@ FROM debian:sid -RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 libplayerctl-dev && \ - apt-get clean +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 From 2600f53bdd436b3598e2e4e951f57f22827b07fd Mon Sep 17 00:00:00 2001 From: Ryan Delaney Date: Wed, 13 Dec 2023 10:39:59 -0500 Subject: [PATCH 216/364] Clarify that the configuration file is JSON*C* JSON is JSONC --- man/waybar.5.scd.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index e85033d4..26b9ecfc 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -6,7 +6,7 @@ waybar - configuration file # DESCRIPTION -The configuration uses the JSON file format and is named *config*. +The configuration uses the JSONC file format and is named *config*. Valid locations for this file are: From 2cd67fdd0a599be6fff813257e3ac460feb814f3 Mon Sep 17 00:00:00 2001 From: Ryan Delaney Date: Wed, 13 Dec 2023 10:40:13 -0500 Subject: [PATCH 217/364] The file can also be named config.jsonc --- man/waybar.5.scd.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 26b9ecfc..e20eff4c 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -6,7 +6,7 @@ waybar - configuration file # DESCRIPTION -The configuration uses the JSONC file format and is named *config*. +The configuration uses the JSONC file format and is named *config* or *config.jsonc*. Valid locations for this file are: From 68e525df588beb686e44d26271d0addc699afb44 Mon Sep 17 00:00:00 2001 From: Ryan Delaney Date: Wed, 13 Dec 2023 11:22:57 -0500 Subject: [PATCH 218/364] Remove 'config' from list of valid locations Since the file can also be named config.jsonc, that will remove ambiguity --- man/waybar.5.scd.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index e20eff4c..e076b000 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -10,11 +10,11 @@ The configuration uses the JSONC file format and is named *config* or *config.js Valid locations for this file are: -- *$XDG_CONFIG_HOME/waybar/config* -- *~/.config/waybar/config* -- *~/waybar/config* -- */etc/xdg/waybar/config* -- *@sysconfdir@/xdg/waybar/config* +- *$XDG_CONFIG_HOME/waybar/* +- *~/.config/waybar/* +- *~/waybar/* +- */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 Also, a minimal example configuration can be found at the bottom of this man page. From 0ea5143493bff3257740bee349e7b7f02b633ef0 Mon Sep 17 00:00:00 2001 From: Jo De Boeck Date: Tue, 19 Dec 2023 22:54:12 +0200 Subject: [PATCH 219/364] Pass WAYBAR_OUTPUT_NAME environment variable to custom exec scripts Signed-off-by: Jo De Boeck --- include/modules/custom.hpp | 3 ++- include/util/command.hpp | 11 +++++++---- src/factory.cpp | 2 +- src/modules/custom.cpp | 11 ++++++----- src/modules/image.cpp | 2 +- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/include/modules/custom.hpp b/include/modules/custom.hpp index c9992695..2c7ba8a8 100644 --- a/include/modules/custom.hpp +++ b/include/modules/custom.hpp @@ -14,7 +14,7 @@ namespace waybar::modules { class Custom : public ALabel { public: - Custom(const std::string&, const std::string&, const Json::Value&); + Custom(const std::string&, const std::string&, const Json::Value&, const std::string&); virtual ~Custom(); auto update() -> void override; void refresh(int /*signal*/) override; @@ -30,6 +30,7 @@ class Custom : public ALabel { bool handleToggle(GdkEventButton* const& e) override; const std::string name_; + const std::string output_name_; std::string text_; std::string id_; std::string alt_; diff --git a/include/util/command.hpp b/include/util/command.hpp index 0d729b77..4b9decaa 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -66,7 +66,7 @@ inline int close(FILE* fp, pid_t pid) { return stat; } -inline FILE* open(const std::string& cmd, int& pid) { +inline FILE* open(const std::string& cmd, int& pid, const std::string& output_name) { if (cmd == "") return nullptr; int fd[2]; // Open the pipe with the close-on-exec flag set, so it will not be inherited @@ -109,6 +109,9 @@ inline FILE* open(const std::string& cmd, int& pid) { ::close(fd[0]); dup2(fd[1], 1); setpgid(child_pid, child_pid); + if (output_name != "") { + setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1); + } execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); exit(0); } else { @@ -118,9 +121,9 @@ inline FILE* open(const std::string& cmd, int& pid) { return fdopen(fd[0], "r"); } -inline struct res exec(const std::string& cmd) { +inline struct res exec(const std::string& cmd, const std::string& output_name) { int pid; - auto fp = command::open(cmd, pid); + auto fp = command::open(cmd, pid, output_name); if (!fp) return {-1, ""}; auto output = command::read(fp); auto stat = command::close(fp, pid); @@ -129,7 +132,7 @@ inline struct res exec(const std::string& cmd) { inline struct res execNoRead(const std::string& cmd) { int pid; - auto fp = command::open(cmd, pid); + auto fp = command::open(cmd, pid, ""); if (!fp) return {-1, ""}; auto stat = command::close(fp, pid); return {WEXITSTATUS(stat), ""}; diff --git a/src/factory.cpp b/src/factory.cpp index ce5b925e..f3d7ebe6 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -205,7 +205,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, return new waybar::modules::Temperature(id, config_[name]); } if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { - return new waybar::modules::Custom(ref.substr(7), id, config_[name]); + return new waybar::modules::Custom(ref.substr(7), id, config_[name], bar_.output->name); } if (ref.compare(0, 5, "cffi/") == 0 && ref.size() > 5) { return new waybar::modules::CFFI(ref.substr(5), id, config_[name]); diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index fa03c3be..6101f874 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -5,9 +5,10 @@ #include "util/scope_guard.hpp" waybar::modules::Custom::Custom(const std::string& name, const std::string& id, - const Json::Value& config) + const Json::Value& config, const std::string& output_name) : ALabel(config, "custom-" + name, id, "{}"), name_(name), + output_name_(output_name), id_(id), percentage_(0), fp_(nullptr), @@ -42,7 +43,7 @@ void waybar::modules::Custom::delayWorker() { } if (can_update) { if (config_["exec"].isString()) { - output_ = util::command::exec(config_["exec"].asString()); + output_ = util::command::exec(config_["exec"].asString(), output_name_); } dp.emit(); } @@ -53,7 +54,7 @@ void waybar::modules::Custom::delayWorker() { void waybar::modules::Custom::continuousWorker() { auto cmd = config_["exec"].asString(); pid_ = -1; - fp_ = util::command::open(cmd, pid_); + fp_ = util::command::open(cmd, pid_, output_name_); if (!fp_) { throw std::runtime_error("Unable to open " + cmd); } @@ -79,7 +80,7 @@ void waybar::modules::Custom::continuousWorker() { if (config_["restart-interval"].isUInt()) { pid_ = -1; thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt())); - fp_ = util::command::open(cmd, pid_); + fp_ = util::command::open(cmd, pid_, output_name_); if (!fp_) { throw std::runtime_error("Unable to open " + cmd); } @@ -112,7 +113,7 @@ void waybar::modules::Custom::waitingWorker() { } if (can_update) { if (config_["exec"].isString()) { - output_ = util::command::exec(config_["exec"].asString()); + output_ = util::command::exec(config_["exec"].asString(), output_name_); } dp.emit(); } diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 843cd954..08b03b92 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -45,7 +45,7 @@ auto waybar::modules::Image::update() -> void { if (config_["path"].isString()) { path_ = config_["path"].asString(); } else if (config_["exec"].isString()) { - output_ = util::command::exec(config_["exec"].asString()); + output_ = util::command::exec(config_["exec"].asString(), ""); parseOutputRaw(); } else { path_ = ""; From dbe02ebe4118523689844a74a73b857895b3c730 Mon Sep 17 00:00:00 2001 From: Tuur Vanhoutte <4633209+zjeffer@users.noreply.github.com> Date: Tue, 19 Dec 2023 16:28:38 +0100 Subject: [PATCH 220/364] Added clang-tidy GitHub action Created clang-tidy.yml Renamed lint.yml to clang-format.yml --- .../workflows/{lint.yml => clang-format.yml} | 5 +-- .github/workflows/clang-tidy.yml | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) rename .github/workflows/{lint.yml => clang-format.yml} (74%) create mode 100644 .github/workflows/clang-tidy.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/clang-format.yml similarity index 74% rename from .github/workflows/lint.yml rename to .github/workflows/clang-format.yml index 5504dc32..46e338ef 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/clang-format.yml @@ -1,4 +1,4 @@ -name: Linter +name: clang-format on: [push, pull_request] @@ -8,7 +8,8 @@ jobs: 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 + clangFormatVersion: 16 diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml new file mode 100644 index 00000000..5993adb0 --- /dev/null +++ b/.github/workflows/clang-tidy.yml @@ -0,0 +1,31 @@ +name: clang-tidy + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + container: + image: alexays/waybar:debian + steps: + - uses: actions/checkout@v3 + - name: configure + 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: zjeffer/cpp-linter-action@test/process-compilation-database + name: clang-tidy + id: clang-tidy-check + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PIP_NO_CACHE_DIR: false + with: + style: "" # empty string => don't do clang-format checks here, we do them in clang-format.yml + files-changed-only: true # only check files that have changed + lines-changed-only: true # only check lines that have changed + tidy-checks: "" # empty string => use the .clang-tidy file + version: "17" # clang-tools version + database: "build" # path to the compile_commands.json file + - name: Check if clang-tidy failed on any files + if: steps.clang-tidy-check.outputs.checks-failed > 0 + run: echo "Some files failed the linting checks!" && exit 1 From 49caab47a6b0c8fb9805c00556230f5d85ccb139 Mon Sep 17 00:00:00 2001 From: Alan Vannereau Date: Fri, 22 Dec 2023 11:50:41 +0100 Subject: [PATCH 221/364] Fix wireplumber reverse-scroll option --- src/modules/wireplumber.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index b2fcb492..51bb708d 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -317,13 +317,6 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) { if (dir == SCROLL_DIR::NONE) { return true; } - if (config_["reverse-scrolling"].asInt() == 1) { - if (dir == SCROLL_DIR::UP) { - dir = SCROLL_DIR::DOWN; - } else if (dir == SCROLL_DIR::DOWN) { - dir = SCROLL_DIR::UP; - } - } double max_volume = 1; double step = 1.0 / 100.0; if (config_["scroll-step"].isDouble()) { From 77f0584d0d7e5e3a965bf37259ab4ebf320aa7f9 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 24 Dec 2023 23:08:22 +0100 Subject: [PATCH 222/364] Bump clang-tidy cpp-linter-action version to v2.7.5 --- .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 5993adb0..86dd1f73 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -13,7 +13,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: zjeffer/cpp-linter-action@test/process-compilation-database + - uses: cpp-linter/cpp-linter-action@v2.7.5 name: clang-tidy id: clang-tidy-check env: From 13e904c20d6fbab09779600d0c6965a7c9cfc252 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Mon, 25 Dec 2023 00:26:39 +0100 Subject: [PATCH 223/364] Bump cross-platform-action to attempt fixing freebsd runner --- .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 0d7f29c4..46419c1d 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Test in FreeBSD VM - uses: cross-platform-actions/action@v0.19.1 + uses: cross-platform-actions/action@v0.21.1 timeout-minutes: 180 with: operating_system: freebsd From e64d66ab243fc7c5b9614d0310a8b858f67fed5b Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 23 Dec 2023 21:38:19 +0800 Subject: [PATCH 224/364] fix:the workspace data is null in a small probability update fix clang-format warn no manual unlock --- 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 7ad87bab..e0e00eec 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include #include @@ -14,6 +16,8 @@ namespace waybar::modules::hyprland { +std::shared_mutex workspaceCreateSmtx; + 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 @@ -161,6 +165,7 @@ auto Workspaces::update() -> void { } // add workspaces that wait to be created + std::shared_lock workspaceCreateShareLock(workspaceCreateSmtx); unsigned int currentCreateWorkspaceNum = 0; for (Json::Value const &workspaceToCreate : m_workspacesToCreate) { createWorkspace(workspaceToCreate); @@ -300,6 +305,7 @@ void Workspaces::onWorkspaceCreated(std::string const &payload) { if (name == payload && (allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { + std::unique_lock workspaceCreateUniqueLock(workspaceCreateSmtx); m_workspacesToCreate.push_back(workspaceJson); break; } From 182272b8b517031fa73191bce4af884ddb072b37 Mon Sep 17 00:00:00 2001 From: Evan Overman Date: Thu, 28 Dec 2023 19:30:26 -0800 Subject: [PATCH 225/364] fix #2650 --- 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 6101f874..60ab340b 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -14,7 +14,7 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id, fp_(nullptr), pid_(-1) { dp.emit(); - if (!config_["signal"].empty() && config_["interval"].empty()) { + if (!config_["signal"].empty() && config_["interval"].empty() && config_["restart-interval"].empty()) { waitingWorker(); } else if (interval_.count() > 0) { delayWorker(); From 6b860f86903a1383f7e96a8b8851a904cfd5f6f4 Mon Sep 17 00:00:00 2001 From: Evan Overman Date: Thu, 28 Dec 2023 19:36:21 -0800 Subject: [PATCH 226/364] clang formating --- src/modules/custom.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 60ab340b..d2fe27cf 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -14,7 +14,8 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id, fp_(nullptr), pid_(-1) { dp.emit(); - if (!config_["signal"].empty() && config_["interval"].empty() && config_["restart-interval"].empty()) { + if (!config_["signal"].empty() && config_["interval"].empty() && + config_["restart-interval"].empty()) { waitingWorker(); } else if (interval_.count() > 0) { delayWorker(); From 19bf4d0544d32336f30378e64780719679cd4a44 Mon Sep 17 00:00:00 2001 From: Evan Overman Date: Thu, 28 Dec 2023 19:38:01 -0800 Subject: [PATCH 227/364] clang format trailing whitespace --- 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 d2fe27cf..d628b85d 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -14,7 +14,7 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id, fp_(nullptr), pid_(-1) { dp.emit(); - if (!config_["signal"].empty() && config_["interval"].empty() && + if (!config_["signal"].empty() && config_["interval"].empty() && config_["restart-interval"].empty()) { waitingWorker(); } else if (interval_.count() > 0) { From ff09ef6d71ca52a5fc2a96a595a4c022937b4ebb Mon Sep 17 00:00:00 2001 From: Emily Ellis Date: Sat, 30 Dec 2023 21:07:40 -0500 Subject: [PATCH 228/364] hyprland/workspaces: allow using the original window class/title This turns the values of window rewrite rules in hyprland/workspaces from static strings to format strings with the values {class} and {title} available. --- man/waybar-hyprland-workspaces.5.scd | 2 +- src/modules/hyprland/workspaces.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 278b2e12..fb7b6e4d 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -24,7 +24,7 @@ Addressed by *hyprland/workspaces* *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. + 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. *window-rewrite-default*: diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 7ad87bab..9b2dd3bc 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -891,7 +891,9 @@ std::string Workspaces::getRewrite(std::string window_class, std::string window_ } else { windowReprKey = fmt::format("class<{}>", window_class); } - return m_windowRewriteRules.get(windowReprKey); + auto const rewriteRule = m_windowRewriteRules.get(windowReprKey); + return fmt::format(fmt::runtime(rewriteRule), fmt::arg("class", window_class), + fmt::arg("title", window_title)); } WindowCreationPayload::WindowCreationPayload(std::string workspace_name, From 7783c81861da9d15ba3d232cf95c2e7393d7b85e Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Thu, 4 Jan 2024 17:22:27 +0300 Subject: [PATCH 229/364] Catch2 bump Signed-off-by: Viktar Lukashonak --- meson.build | 2 +- subprojects/catch2.wrap | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index df245a28..c4f40205 100644 --- a/meson.build +++ b/meson.build @@ -535,7 +535,7 @@ endif catch2 = dependency( 'catch2', - version: '>=2.0.0', + version: '>=3.5.1', fallback: ['catch2', 'catch2_dep'], required: get_option('tests'), ) diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap index 691d39c8..f2dfd57c 100644 --- a/subprojects/catch2.wrap +++ b/subprojects/catch2.wrap @@ -1,10 +1,10 @@ [wrap-file] -directory = Catch2-3.4.0 -source_url = https://github.com/catchorg/Catch2/archive/v3.4.0.tar.gz -source_filename = Catch2-3.4.0.tar.gz -source_hash = 122928b814b75717316c71af69bd2b43387643ba076a6ec16e7882bfb2dfacbb -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.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 [provide] catch2 = catch2_dep 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 230/364] 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 017b0e48..bbdd0eed 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 f3d7ebe6..1828fb46 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 70268c8a..6c39eb0f 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 a34e3ccc86e180df182f4aa48a9b2af599cfb27e Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Fri, 5 Jan 2024 13:49:11 +0800 Subject: [PATCH 231/364] Improvements for Hyprland workspace 1. Utilize `m_mutex` to safeguard member fields of `hyprland::Workspaces` as they are modified by multiple threads, including the event listener thread and UI thread. This applies to all member fields, not just `m_workspacesToCreate`. 2. Tidy up the create/remove workspace code. --- include/modules/hyprland/workspaces.hpp | 2 ++ src/modules/hyprland/workspaces.cpp | 40 +++++++++++-------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index a17c2db4..0109149e 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -161,6 +161,8 @@ class Workspaces : public AModule, public EventHandler { int windowRewritePriorityFunction(std::string const& window_rule); + void doUpdate(); + bool m_allOutputs = false; bool m_showSpecial = false; bool m_activeOnly = false; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 74a09f80..3d8a5932 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -4,11 +4,8 @@ #include #include -#include #include -#include #include -#include #include #include @@ -16,8 +13,6 @@ namespace waybar::modules::hyprland { -std::shared_mutex workspaceCreateSmtx; - 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 @@ -153,27 +148,26 @@ auto Workspaces::registerIpc() -> void { } } -auto Workspaces::update() -> void { +/** + * Workspaces::doUpdate - update workspaces in UI thread. + * + * Note: some memberfields are modified by both UI thread and event listener thread, use m_mutex to + * protect these member fields, and lock should released before calling AModule::update(). + */ +void Workspaces::doUpdate() { + std::unique_lock lock(m_mutex); + // remove workspaces that wait to be removed - unsigned int currentRemoveWorkspaceNum = 0; - for (const std::string &workspaceToRemove : m_workspacesToRemove) { - removeWorkspace(workspaceToRemove); - currentRemoveWorkspaceNum++; - } - for (unsigned int i = 0; i < currentRemoveWorkspaceNum; i++) { - m_workspacesToRemove.erase(m_workspacesToRemove.begin()); + for (auto &elem : m_workspacesToRemove) { + removeWorkspace(elem); } + m_workspacesToRemove.clear(); // add workspaces that wait to be created - std::shared_lock workspaceCreateShareLock(workspaceCreateSmtx); - unsigned int currentCreateWorkspaceNum = 0; - for (Json::Value const &workspaceToCreate : m_workspacesToCreate) { - createWorkspace(workspaceToCreate); - currentCreateWorkspaceNum++; - } - for (unsigned int i = 0; i < currentCreateWorkspaceNum; i++) { - m_workspacesToCreate.erase(m_workspacesToCreate.begin()); + for (auto &elem : m_workspacesToCreate) { + createWorkspace(elem); } + m_workspacesToCreate.clear(); // get all active workspaces auto monitors = gIPC->getSocket1JsonReply("monitors"); @@ -231,7 +225,10 @@ auto Workspaces::update() -> void { m_windowsToCreate.clear(); m_windowsToCreate = notCreated; +} +auto Workspaces::update() -> void { + doUpdate(); AModule::update(); } @@ -305,7 +302,6 @@ void Workspaces::onWorkspaceCreated(std::string const &payload) { if (name == payload && (allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { - std::unique_lock workspaceCreateUniqueLock(workspaceCreateSmtx); m_workspacesToCreate.push_back(workspaceJson); break; } From bdd7271da9aa30aecde9ef703a38a9195ec284c5 Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Sat, 6 Jan 2024 12:57:27 +0800 Subject: [PATCH 232/364] Improvements for Hyprland backend 1. Fix warnings reported by clang tidy 2. Use unique lock instead of manully lock/unlock on mutex. The RAII style locking makes sure mutex is unlocked when exceptions are thrown --- include/modules/hyprland/backend.hpp | 10 ++-- src/modules/hyprland/backend.cpp | 79 +++++++++++----------------- 2 files changed, 35 insertions(+), 54 deletions(-) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index 7d97b553..d197df3a 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -1,11 +1,9 @@ #pragma once -#include #include #include #include #include -#include #include #include "util/json.hpp" @@ -25,16 +23,16 @@ class IPC { void registerForIPC(const std::string&, EventHandler*); void unregisterForIPC(EventHandler*); - std::string getSocket1Reply(const std::string& rq); + static std::string getSocket1Reply(const std::string& rq); Json::Value getSocket1JsonReply(const std::string& rq); private: void startIPC(); void parseIPC(const std::string&); - std::mutex callbackMutex; - util::JsonParser parser_; - std::list> callbacks; + std::mutex m_callbackMutex; + util::JsonParser m_parser; + std::list> m_callbacks; }; inline std::unique_ptr gIPC; diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 5a48d369..3c8313fc 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -1,21 +1,16 @@ #include "modules/hyprland/backend.hpp" -#include #include #include #include -#include -#include -#include #include #include #include #include #include -#include -#include #include +#include namespace waybar::modules::hyprland { @@ -24,9 +19,9 @@ void IPC::startIPC() { std::thread([&]() { // check for hyprland - const char* HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE"); + const char* his = getenv("HYPRLAND_INSTANCE_SIGNATURE"); - if (!HIS) { + if (his == nullptr) { spdlog::warn("Hyprland is not running, Hyprland IPC will not be available."); return; } @@ -45,8 +40,8 @@ void IPC::startIPC() { addr.sun_family = AF_UNIX; - // socket path - std::string socketPath = "/tmp/hypr/" + std::string(HIS) + "/.socket2.sock"; + // socket path, specified by EventManager of Hyprland + std::string socketPath = "/tmp/hypr/" + std::string(his) + "/.socket2.sock"; strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); @@ -61,10 +56,9 @@ void IPC::startIPC() { auto file = fdopen(socketfd, "r"); - while (1) { - // read - + while (true) { char buffer[1024]; // Hyprland socket2 events are max 1024 bytes + auto recievedCharPtr = fgets(buffer, 1024, file); if (!recievedCharPtr) { @@ -72,28 +66,21 @@ void IPC::startIPC() { continue; } - callbackMutex.lock(); - std::string messageRecieved(buffer); - messageRecieved = messageRecieved.substr(0, messageRecieved.find_first_of('\n')); - spdlog::debug("hyprland IPC received {}", messageRecieved); - parseIPC(messageRecieved); - callbackMutex.unlock(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); } }).detach(); } void IPC::parseIPC(const std::string& ev) { - // todo std::string request = ev.substr(0, ev.find_first_of('>')); + std::unique_lock lock(m_callbackMutex); - for (auto& [eventname, handler] : callbacks) { + for (auto& [eventname, handler] : m_callbacks) { if (eventname == request) { handler->onEvent(ev); } @@ -104,11 +91,9 @@ void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) { if (!ev_handler) { return; } - callbackMutex.lock(); - callbacks.emplace_back(std::make_pair(ev, ev_handler)); - - callbackMutex.unlock(); + std::unique_lock lock(m_callbackMutex); + m_callbacks.emplace_back(ev, ev_handler); } void IPC::unregisterForIPC(EventHandler* ev_handler) { @@ -116,37 +101,35 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { return; } - callbackMutex.lock(); + std::unique_lock lock(m_callbackMutex); - for (auto it = callbacks.begin(); it != callbacks.end();) { - auto it_current = it; - it++; - auto& [eventname, handler] = *it_current; + for (auto it = m_callbacks.begin(); it != m_callbacks.end();) { + auto& [eventname, handler] = *it; if (handler == ev_handler) { - callbacks.erase(it_current); + m_callbacks.erase(it++); + } else { + ++it; } } - - callbackMutex.unlock(); } std::string IPC::getSocket1Reply(const std::string& rq) { // basically hyprctl - struct addrinfo ai_hints; - struct addrinfo* ai_res = NULL; - const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0); + struct addrinfo aiHints; + struct addrinfo* aiRes = nullptr; + const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0); - if (SERVERSOCKET < 0) { + if (serverSocket < 0) { spdlog::error("Hyprland IPC: Couldn't open a socket (1)"); return ""; } - memset(&ai_hints, 0, sizeof(struct addrinfo)); - ai_hints.ai_family = AF_UNSPEC; - ai_hints.ai_socktype = SOCK_STREAM; + memset(&aiHints, 0, sizeof(struct addrinfo)); + aiHints.ai_family = AF_UNSPEC; + aiHints.ai_socktype = SOCK_STREAM; - if (getaddrinfo("localhost", NULL, &ai_hints, &ai_res) != 0) { + if (getaddrinfo("localhost", nullptr, &aiHints, &aiRes) != 0) { spdlog::error("Hyprland IPC: Couldn't get host (2)"); return ""; } @@ -173,13 +156,13 @@ std::string IPC::getSocket1Reply(const std::string& rq) { return ""; } - if (connect(SERVERSOCKET, reinterpret_cast(&serverAddress), sizeof(serverAddress)) < + if (connect(serverSocket, reinterpret_cast(&serverAddress), sizeof(serverAddress)) < 0) { spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); return ""; } - auto sizeWritten = write(SERVERSOCKET, rq.c_str(), rq.length()); + auto sizeWritten = write(serverSocket, rq.c_str(), rq.length()); if (sizeWritten < 0) { spdlog::error("Hyprland IPC: Couldn't write (4)"); @@ -190,22 +173,22 @@ std::string IPC::getSocket1Reply(const std::string& rq) { std::string response; do { - sizeWritten = read(SERVERSOCKET, buffer, 8192); + sizeWritten = read(serverSocket, buffer, 8192); if (sizeWritten < 0) { spdlog::error("Hyprland IPC: Couldn't read (5)"); - close(SERVERSOCKET); + close(serverSocket); return ""; } response.append(buffer, sizeWritten); } while (sizeWritten > 0); - close(SERVERSOCKET); + close(serverSocket); return response; } Json::Value IPC::getSocket1JsonReply(const std::string& rq) { - return parser_.parse(getSocket1Reply("j/" + rq)); + return m_parser.parse(getSocket1Reply("j/" + rq)); } } // namespace waybar::modules::hyprland From d4d5a54e63742aa96320f1468bc76e47aef7d6be Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 8 Jan 2024 09:37:48 -0600 Subject: [PATCH 233/364] chore: flake lock update --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 4bf02e17..25f12644 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1701253981, - "narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=", + "lastModified": 1704538339, + "narHash": "sha256-1734d3mQuux9ySvwf6axRWZRBhtcZA9Q8eftD6EZg6U=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58", + "rev": "46ae0210ce163b3cba6c7da08840c1d63de9c701", "type": "github" }, "original": { From b90af74d15d913c62de01b04bf7469e4bb3cf544 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 8 Jan 2024 09:38:05 -0600 Subject: [PATCH 234/364] feat: enable direnv and fix flake devshell output --- .envrc.sample => .envrc | 0 flake.nix | 46 +++++++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 20 deletions(-) rename .envrc.sample => .envrc (100%) diff --git a/.envrc.sample b/.envrc similarity index 100% rename from .envrc.sample rename to .envrc diff --git a/flake.nix b/flake.nix index cc830c7e..ebaeb81f 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "Highly customizable Wayland bar for Sway and Wlroots based compositors."; + description = "Highly customizable Wayland bar for Sway and Wlroots based compositors"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; @@ -15,7 +15,8 @@ genSystems = func: lib.genAttrs [ "x86_64-linux" "aarch64-linux" - ] (system: func (import nixpkgs { inherit system; })); + ] + (system: func (import nixpkgs { inherit system; })); mkDate = longDate: (lib.concatStringsSep "-" [ (builtins.substring 0 4 longDate) @@ -24,6 +25,27 @@ ]); in { + devShells = genSystems + (pkgs: + { + default = + pkgs.mkShell + { + name = "waybar-shell"; + + # inherit attributes from upstream nixpkgs derivation + inherit (pkgs.waybar) buildInputs depsBuildBuild depsBuildBuildPropagated depsBuildTarget + depsBuildTargetPropagated depsHostHost depsHostHostPropagated depsTargetTarget + depsTargetTargetPropagated propagatedBuildInputs propagatedNativeBuildInputs strictDeps; + + # overrides for local development + nativeBuildInputs = pkgs.waybar.nativeBuildInputs ++ (with pkgs; [ + clang-tools + gdb + ]); + }; + }); + overlays.default = final: prev: { waybar = final.callPackage ./nix/default.nix { # take the first "version: '...'" from meson.build @@ -35,27 +57,11 @@ + "+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; }); - } // - genSystems (pkgs: { - devShells.default = - pkgs.mkShell { - name = "waybar-shell"; - - # most of these aren't actually used in the waybar derivation, this is just in case - # they will ever start being used - inherit (pkgs.waybar) buildInputs depsBuildBuild depsBuildBuildPropagated depsBuildTarget - depsBuildTargetPropagated depsHostHost depsHostHostPropagated depsTargetTarget - depsTargetTargetPropagated propagatedBuildInputs propagatedNativeBuildInputs strictDeps; - - nativeBuildInputs = pkgs.waybar.nativeBuildInputs ++ (with pkgs; [ - clang-tools - gdb - ]); - }; - }); + }; } From 512c6fb12717050e8cf58e6ea375595c849ec543 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 8 Jan 2024 17:10:13 -0300 Subject: [PATCH 235/364] feat: add orphan windows attribute to workspaces this attribute will keep every window that doesn't have an associated workspace in the current bar --- include/modules/hyprland/workspaces.hpp | 8 +++++ src/modules/hyprland/workspaces.cpp | 48 +++++++++++++++++++++---- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 0109149e..d05a52fa 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -163,10 +163,18 @@ class Workspaces : public AModule, public EventHandler { void doUpdate(); + void extendOrphans(int workspaceId, Json::Value const& clientsJson); + void registerOrphanWindow(WindowCreationPayload create_window_paylod); + bool m_allOutputs = false; bool m_showSpecial = false; bool m_activeOnly = false; + // Map for windows stored in workspaces not present in the current bar. + // This happens when the user has multiple monitors (hence, multiple bars) + // and doesn't share windows accross bars (a.k.a `all-outputs` = false) + std::map m_orphanWindowMap; + enum class SortMethod { ID, NAME, NUMBER, DEFAULT }; util::EnumParser m_enumParser; SortMethod m_sortBy = SortMethod::DEFAULT; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3d8a5932..eb624a10 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -128,6 +128,12 @@ 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); + } +} + auto Workspaces::registerIpc() -> void { gIPC->registerForIPC("workspace", this); gIPC->registerForIPC("createworkspace", this); @@ -215,6 +221,8 @@ void Workspaces::doUpdate() { static auto const WINDOW_CREATION_TIMEOUT = 2; if (windowPayload.incrementTimeSpentUncreated() < WINDOW_CREATION_TIMEOUT) { notCreated.push_back(windowPayload); + } else { + registerOrphanWindow(windowPayload); } } } @@ -402,18 +410,35 @@ void Workspaces::onWindowMoved(std::string const &payload) { } } - // ...and add it to the new workspace + // ...if it was empty, check if the window is an orphan... + if (windowRepr.empty() && m_orphanWindowMap.contains(windowAddress)) { + windowRepr = m_orphanWindowMap[windowAddress]; + } + + // ...and then add it to the new workspace if (!windowRepr.empty()) { m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowRepr); } } void Workspaces::onWindowTitleEvent(std::string const &payload) { - auto windowWorkspace = - std::find_if(m_workspaces.begin(), m_workspaces.end(), - [payload](auto &workspace) { return workspace->containsWindow(payload); }); + std::optional> inserter; - if (windowWorkspace != m_workspaces.end()) { + if (m_orphanWindowMap.contains(payload)) { + inserter = [this](WindowCreationPayload wcp) { this->registerOrphanWindow(std::move(wcp)); }; + } else { + auto windowWorkspace = + std::find_if(m_workspaces.begin(), m_workspaces.end(), + [payload](auto &workspace) { return workspace->containsWindow(payload); }); + + if (windowWorkspace != m_workspaces.end()) { + inserter = [windowWorkspace](WindowCreationPayload wcp) { + (*windowWorkspace)->insertWindow(std::move(wcp)); + }; + } + } + + if (inserter.has_value()) { Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); std::string jsonWindowAddress = fmt::format("0x{}", payload); @@ -423,7 +448,7 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { }); if (!client->empty()) { - (*windowWorkspace)->insertWindow({*client}); + (*inserter)({*client}); } } } @@ -605,6 +630,14 @@ void Workspaces::createPersistentWorkspaces() { } } +void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) { + for (const auto &client : clientsJson) { + if (client["workspace"]["id"].asInt() == workspaceId) { + registerOrphanWindow({client}); + } + } +} + void Workspaces::init() { m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); @@ -625,10 +658,13 @@ void Workspaces::init() { for (Json::Value workspaceJson : workspacesJson) { std::string workspaceName = workspaceJson["name"].asString(); + spdlog::info("initing workpsace {}:{}", workspaceJson["id"], workspaceJson["name"]); if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && (!workspaceName.starts_with("special") || showSpecial()) && !isWorkspaceIgnored(workspaceName)) { createWorkspace(workspaceJson, clientsJson); + } else { + extendOrphans(workspaceJson["id"].asInt(), clientsJson); } } From bc7acbde5c5f85382a7055eaf6b39a3d9d8af7a7 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 8 Jan 2024 18:18:11 -0300 Subject: [PATCH 236/364] fix: rename windows while queued for creation this avoids the window arriving with the wrong icon when its eventually able to be created --- src/modules/hyprland/workspaces.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index eb624a10..e7ed064b 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -424,6 +424,7 @@ void Workspaces::onWindowMoved(std::string const &payload) { void Workspaces::onWindowTitleEvent(std::string const &payload) { std::optional> inserter; + // If the window was an orphan, rename it at the orphan's vector if (m_orphanWindowMap.contains(payload)) { inserter = [this](WindowCreationPayload wcp) { this->registerOrphanWindow(std::move(wcp)); }; } else { @@ -431,10 +432,21 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { std::find_if(m_workspaces.begin(), m_workspaces.end(), [payload](auto &workspace) { return workspace->containsWindow(payload); }); + // If the window exists on a workspace, rename it at the workspace's window + // map if (windowWorkspace != m_workspaces.end()) { inserter = [windowWorkspace](WindowCreationPayload wcp) { (*windowWorkspace)->insertWindow(std::move(wcp)); }; + } else { + auto queuedWindow = std::find_if( + m_windowsToCreate.begin(), m_windowsToCreate.end(), + [payload](auto &windowPayload) { return windowPayload.getAddress() == payload; }); + + // If the window was queued, rename it in the queue + if (queuedWindow != m_windowsToCreate.end()) { + inserter = [queuedWindow](WindowCreationPayload wcp) { *queuedWindow = std::move(wcp); }; + } } } From 4339030c9d31f78fe5f14eee832c8e80c9f4f967 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 8 Jan 2024 20:11:39 -0300 Subject: [PATCH 237/364] feat: fetch clients data when moving workspaces accross monitors --- include/modules/hyprland/workspaces.hpp | 5 ++-- src/modules/hyprland/workspaces.cpp | 36 +++++++++++-------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index d05a52fa..d2006fcc 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -145,7 +145,8 @@ class Workspaces : public AModule, public EventHandler { // workspace events void onWorkspaceActivated(std::string const& payload); void onWorkspaceDestroyed(std::string const& payload); - void onWorkspaceCreated(std::string const& payload); + void onWorkspaceCreated(std::string const& workspaceName, + Json::Value const& clientsData = Json::Value::nullRef); void onWorkspaceMoved(std::string const& payload); void onWorkspaceRenamed(std::string const& payload); @@ -199,7 +200,7 @@ class Workspaces : public AModule, public EventHandler { uint64_t m_monitorId; std::string m_activeWorkspaceName; std::vector> m_workspaces; - std::vector m_workspacesToCreate; + std::vector> m_workspacesToCreate; std::vector m_workspacesToRemove; std::vector m_windowsToCreate; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index e7ed064b..3f85b975 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -170,8 +170,8 @@ void Workspaces::doUpdate() { m_workspacesToRemove.clear(); // add workspaces that wait to be created - for (auto &elem : m_workspacesToCreate) { - createWorkspace(elem); + for (auto &[workspaceData, clientsData] : m_workspacesToCreate) { + createWorkspace(workspaceData, clientsData); } m_workspacesToCreate.clear(); @@ -301,16 +301,17 @@ void Workspaces::onWorkspaceDestroyed(std::string const &payload) { } } -void Workspaces::onWorkspaceCreated(std::string const &payload) { +void Workspaces::onWorkspaceCreated(std::string const &workspaceName, + Json::Value const &clientsData) { const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - if (!isWorkspaceIgnored(payload)) { + if (!isWorkspaceIgnored(workspaceName)) { for (Json::Value workspaceJson : workspacesJson) { std::string name = workspaceJson["name"].asString(); - if (name == payload && + if (name == workspaceName && (allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && - (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { - m_workspacesToCreate.push_back(workspaceJson); + (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(workspaceName)) { + m_workspacesToCreate.emplace_back(workspaceJson, clientsData); break; } } @@ -318,20 +319,14 @@ void Workspaces::onWorkspaceCreated(std::string const &payload) { } void Workspaces::onWorkspaceMoved(std::string const &payload) { - std::string workspace = payload.substr(0, payload.find(',')); - std::string newOutput = payload.substr(payload.find(',') + 1); - bool shouldShow = showSpecial() || !workspace.starts_with("special"); - if (shouldShow && m_bar.output->name == newOutput) { // TODO: implement this better - const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - for (Json::Value workspaceJson : workspacesJson) { - std::string name = workspaceJson["name"].asString(); - if (name == workspace && m_bar.output->name == workspaceJson["monitor"].asString()) { - m_workspacesToCreate.push_back(workspaceJson); - break; - } - } + std::string workspaceName = payload.substr(0, payload.find(',')); + std::string monitorName = payload.substr(payload.find(',') + 1); + + if (m_bar.output->name == monitorName) { + Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); + onWorkspaceCreated(workspaceName, clientsData); } else { - m_workspacesToRemove.push_back(workspace); + onWorkspaceDestroyed(workspaceName); } } @@ -670,7 +665,6 @@ void Workspaces::init() { for (Json::Value workspaceJson : workspacesJson) { std::string workspaceName = workspaceJson["name"].asString(); - spdlog::info("initing workpsace {}:{}", workspaceJson["id"], workspaceJson["name"]); if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && (!workspaceName.starts_with("special") || showSpecial()) && !isWorkspaceIgnored(workspaceName)) { From c69a6dde67758441ff0c25ff0efca57f22d2247a Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 8 Jan 2024 20:31:15 -0300 Subject: [PATCH 238/364] chore: update Hyprland's featureset in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3441ff8c..07b11152 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ #### Current features - Sway (Workspaces, Binding mode, Focused window name) - River (Mapping mode, Tags, Focused window name) -- Hyprland (Focused window name) +- Hyprland (Window Icons, Workspaces, Focused window name) - DWL (Tags) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc) - Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Local time From eedd1f8e6a3dfd305af9359209e02edd5b68d40a Mon Sep 17 00:00:00 2001 From: oxalica Date: Tue, 9 Jan 2024 18:05:17 +0800 Subject: [PATCH 239/364] Add module systemd-failed-units --- include/factory.hpp | 3 + include/modules/systemd_failed_units.hpp | 30 +++++ man/waybar-systemd-failed-units.5.scd | 63 +++++++++++ meson.build | 3 + src/factory.cpp | 5 + src/modules/systemd_failed_units.cpp | 133 +++++++++++++++++++++++ 6 files changed, 237 insertions(+) create mode 100644 include/modules/systemd_failed_units.hpp create mode 100644 man/waybar-systemd-failed-units.5.scd create mode 100644 src/modules/systemd_failed_units.cpp diff --git a/include/factory.hpp b/include/factory.hpp index b54fcb2e..9ce680d7 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -93,6 +93,9 @@ #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" diff --git a/include/modules/systemd_failed_units.hpp b/include/modules/systemd_failed_units.hpp new file mode 100644 index 00000000..7e0b1a91 --- /dev/null +++ b/include/modules/systemd_failed_units.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include "ALabel.hpp" + +namespace waybar::modules { + +class SystemdFailedUnits : public ALabel { + public: + SystemdFailedUnits(const std::string&, const Json::Value&); + virtual ~SystemdFailedUnits(); + auto update() -> void override; + + private: + bool hide_on_ok; + std::string format_ok; + + bool update_pending; + std::string last_status; + uint32_t nr_failed_system, nr_failed_user; + Glib::RefPtr system_proxy, user_proxy; + + void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name, + const Glib::VariantContainerBase &arguments); + void updateData(); +}; + +} // namespace waybar::modules diff --git a/man/waybar-systemd-failed-units.5.scd b/man/waybar-systemd-failed-units.5.scd new file mode 100644 index 00000000..ac92c533 --- /dev/null +++ b/man/waybar-systemd-failed-units.5.scd @@ -0,0 +1,63 @@ +waybar-systemd-failed-units(5) + +# NAME + +waybar - systemd failed units monitor module + +# DESCRIPTION + +The *systemd-failed-units* module displays the number of failed systemd units. + +# CONFIGURATION + +Addressed by *systemd-failed-units* + +*format*: ++ + typeof: string ++ + default: *{nr_failed} failed* ++ + The format, how information should be displayed. This format is used when other formats aren't specified. + +*format-ok*: ++ + typeof: string ++ + This format is used when there is no failing units. + +*user*: ++ + typeof: bool ++ + default: *true* ++ + Option to count user systemd units. + +*system*: ++ + typeof: bool ++ + default: *true* ++ + Option to count systemwide (PID=1) systemd units. + +*hide-on-ok*: ++ + typeof: bool ++ + default: *true* ++ + Option to hide this module when there is no failing units. + +# FORMAT REPLACEMENTS + +*{nr_failed_system}*: Number of failed units from systemwide (PID=1) systemd. + +*{nr_failed_user}*: Number of failed units from user systemd. + +*{nr_failed}*: Number of total failed units. + +# EXAMPLES + +``` +"systemd-failed-units": { + "hide-on-ok": false, + "format": "✗ {nr_failed}", + "format-ok": "✓", + "system": true, + "user": false, +} +``` + +# STYLE + +- *#systemd-failed-units* +- *#systemd-failed-units.ok* +- *#systemd-failed-units.degraded* diff --git a/meson.build b/meson.build index c4f40205..c1ae48b5 100644 --- a/meson.build +++ b/meson.build @@ -204,6 +204,7 @@ inc_dirs = ['include'] if is_linux add_project_arguments('-DHAVE_CPU_LINUX', language: 'cpp') add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp') + add_project_arguments('-DHAVE_SYSTEMD_MONITOR', language: 'cpp') src_files += files( 'src/modules/battery.cpp', 'src/modules/cffi.cpp', @@ -214,6 +215,7 @@ if is_linux 'src/modules/cpu_usage/linux.cpp', 'src/modules/memory/common.cpp', 'src/modules/memory/linux.cpp', + 'src/modules/systemd_failed_units.cpp', ) elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp') @@ -495,6 +497,7 @@ if scdoc.found() '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', diff --git a/src/factory.cpp b/src/factory.cpp index f3d7ebe6..00fc42ed 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -200,6 +200,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "cava") { return new waybar::modules::Cava(id, config_[name]); } +#endif +#ifdef HAVE_SYSTEMD_MONITOR + if (ref == "systemd-failed-units") { + return new waybar::modules::SystemdFailedUnits(id, config_[name]); + } #endif if (ref == "temperature") { return new waybar::modules::Temperature(id, config_[name]); diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp new file mode 100644 index 00000000..382eea4a --- /dev/null +++ b/src/modules/systemd_failed_units.cpp @@ -0,0 +1,133 @@ +#include "modules/systemd_failed_units.hpp" + +#include +#include +#include +#include + +static const unsigned UPDATE_DEBOUNCE_TIME_MS = 1000; + +namespace waybar::modules { + +SystemdFailedUnits::SystemdFailedUnits(const std::string& id, const Json::Value& config) + : ALabel(config, "systemd-failed-units", id, "{nr_failed} failed", 1), + hide_on_ok(true), + update_pending(false), + nr_failed_system(0), + nr_failed_user(0), + last_status() { + if (config["hide-on-ok"].isBool()) { + hide_on_ok = config["hide-on-ok"].asBool(); + } + if (config["format-ok"].isString()) { + format_ok = config["format-ok"].asString(); + } else { + format_ok = format_; + } + + /* 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"); + if (!system_proxy) { + throw std::runtime_error("Unable to connect to systemwide systemd DBus!"); + } + system_proxy->signal_signal().connect(sigc::mem_fun(*this, &SystemdFailedUnits::notify_cb)); + } + 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"); + if (!user_proxy) { + throw std::runtime_error("Unable to connect to user systemd DBus!"); + } + user_proxy->signal_signal().connect(sigc::mem_fun(*this, &SystemdFailedUnits::notify_cb)); + } + + updateData(); + /* Always update for the first time. */ + dp.emit(); +} + +SystemdFailedUnits::~SystemdFailedUnits() { + if (system_proxy) system_proxy.reset(); + 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 { + 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); + } +} + +void SystemdFailedUnits::updateData() { + update_pending = false; + + auto load = [](const char* kind, Glib::RefPtr &proxy) -> uint32_t { + try { + auto parameters = Glib::VariantContainerBase( + g_variant_new("(ss)", "org.freedesktop.systemd1.Manager", "NFailedUnits")); + Glib::VariantContainerBase data = proxy->call_sync("Get", parameters); + if (data && data.is_of_type(Glib::VariantType("(v)"))) { + Glib::VariantBase variant; + g_variant_get(data.gobj_copy(), "(v)", &variant); + if (variant && variant.is_of_type(Glib::VARIANT_TYPE_UINT32)) { + uint32_t value = 0; + g_variant_get(variant.gobj_copy(), "u", &value); + return value; + } + } + } catch (Glib::Error& e) { + spdlog::error("Failed to get {} failed units: {}", kind, e.what().c_str()); + } + return 0; + }; + + if (system_proxy) { + nr_failed_system = load("systemwide", system_proxy); + } + if (user_proxy) { + nr_failed_user = load("user", user_proxy); + } + dp.emit(); +} + +auto SystemdFailedUnits::update() -> void { + uint32_t nr_failed = nr_failed_system + nr_failed_user; + + // Hide if needed. + if (nr_failed == 0 && hide_on_ok) { + event_box_.set_visible(false); + return; + } + if (!event_box_.get_visible()) { + event_box_.set_visible(true); + } + + // Set state class. + const std::string status = nr_failed == 0 ? "ok" : "degraded"; + if (!last_status.empty() && label_.get_style_context()->has_class(last_status)) { + label_.get_style_context()->remove_class(last_status); + } + if (!label_.get_style_context()->has_class(status)) { + label_.get_style_context()->add_class(status); + } + 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))); + ALabel::update(); +} + +} // namespace waybar::modules::systemd_failed_units From 9e08512927d2de34a55281ee7fc3f13c36c6c9c5 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Wed, 10 Jan 2024 02:24:51 -0300 Subject: [PATCH 240/364] feat: strip workspace qualifiers when creating windows --- src/modules/hyprland/workspaces.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3f85b975..b05ce134 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -1023,6 +1023,11 @@ void WindowCreationPayload::clearWorkspaceName() { 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) { From b239c77d7418fa9c6488a095f7e722bb5d13586a Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 10 Jan 2024 12:17:46 -0600 Subject: [PATCH 241/364] fix: temporary catch2_3 override until upstreamed to nixpkgs --- nix/default.nix | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/nix/default.nix b/nix/default.nix index 5efa5da4..e2643084 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,9 +1,20 @@ { lib +, pkgs , waybar , version }: - -waybar.overrideAttrs (prev: { +let + catch2_3 = { + src = pkgs.fetchFromGitHub + { + owner = "catchorg"; + repo = "Catch2"; + rev = "v3.5.1"; + hash = "sha256-OyYNUfnu6h1+MfCF8O+awQ4Usad0qrdCtdZhYgOY+Vw="; + }; + }; +in +(waybar.overrideAttrs (oldAttrs: rec { inherit version; src = lib.cleanSourceWith { @@ -11,3 +22,9 @@ waybar.overrideAttrs (prev: { src = lib.cleanSource ../.; }; }) +).override { + catch2_3 = pkgs.catch2_3.overrideAttrs (oldAttrs: { + version = "3.5.1"; + src = catch2_3.src; + }); +} 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 242/364] 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 7cd43552..f0736f9b 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; - const Json::Value parse(const std::string& data) const { - Json::Value root(Json::objectValue); - if (data.empty()) { - return root; + Json::Value parse(const std::string& jsonStr) { + Json::Value 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 00000000..99a8649e --- /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 02cbb2a4..7a55a473 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 243/364] 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 09d53e7f..09a7ff30 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 244/364] 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 d78d4c26..5bfd0c49 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 245/364] 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 5bfd0c49..495dfab3 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 246/364] 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 7827f4a8..52a6a2d1 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 247/364] 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 6c39eb0f..f9f9cea4 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 248/364] 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 f9f9cea4..2495b33a 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 249/364] 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 9ec29ef3..641ee6a7 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 00000000..62507e42 --- /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 c1ae48b5..d7549731 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 066247e7..ff1be5d8 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 00000000..4038f1f3 --- /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 00000000..3ee1fb6e --- /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 02cbb2a4..358d6c96 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 250/364] 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 9a8b89e7..0eaf264a 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 251/364] 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 62507e42..4da64ec6 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 4038f1f3..9bbd0f93 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(""); } - return 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 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 3ee1fb6e..ac7b3eb7 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 252/364] 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 ff1be5d8..bd0ee41a 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 253/364] 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 eda8ab05..d763259b 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 254/364] 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 1671e711..e3a69e38 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 08b03b92..9d59b4a3 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 255/364] 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 9d59b4a3..3c90b557 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 256/364] 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 e6017fdb..3d44829f 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 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 258/364] 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 7eef1448..b7319d58 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 259/364] 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 8d9b6f2f..2f53103e 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 b7319d58..f4dd72c4 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 260/364] 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 e076b000..17324d69 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 261/364] * 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 4da64ec6..4826fc31 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 9bbd0f93..45fd801a 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 ac7b3eb7..01850bc3 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 262/364] 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 7e0b1a91..d305264d 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 2495b33a..9003db6e 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 382eea4a..56e624cf 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 263/364] 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 bd0ee41a..e32f8140 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 264/364] fix: tpyo --- src/client.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index e32f8140..73c06fb8 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 265/364] 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 cad36e51..f9061043 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 266/364] 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 0efffe64..4258252a 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 cdb653f9..3343b8d5 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 1a47e478..327ba909 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 267/364] 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 d6aabaa0..8b597c4e 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 495dfab3..6b1975ba 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 268/364] 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 fc079338..e8ef7bed 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 269/364] 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 6b1975ba..e83cbef0 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 270/364] 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 f9061043..9deb4f3c 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 271/364] 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 c87e3228..4163385f 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 272/364] 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 5ef2f4c9..054c9bd2 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 273/364] 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 d2006fcc..9f49dd76 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 b05ce134..a12366e4 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 274/364] 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 a12366e4..44c840fa 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 275/364] 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 44c840fa..531ac304 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 276/364] 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 531ac304..e4c02de1 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 277/364] 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 daad8ab1..adf03a1f 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 278/364] 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 e4c02de1..78d59cf7 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 279/364] 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 78d59cf7..d824b941 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 280/364] 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 d824b941..0f0afb10 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 281/364] 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 0f0afb10..8d4c416f 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 282/364] 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 d305264d..9c3fbcee 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 283/364] 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 7f78c165..449eb1b3 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 48479568..64b2bde1 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 0703eaf7..4fdb6590 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 284/364] 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 3f8d6280..0dc0b11a 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 d1d9a116..ce27fc9a 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 2f555a693617868669c4370b192fa12652bc3776 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 14 Feb 2024 19:14:39 -0800 Subject: [PATCH 285/364] 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 d2cbffa0..0cacc3d7 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 1ffe2ef6..0857724e 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) + 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 if (position == "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; - } 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 7faa5c52..afe658e1 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 b05ce134..4200f85e 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 5e5d4acd..edab0827 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 baa6b7ec..cad8c762 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 09a7ff30..8a6a5b82 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 1a47e478..37cc7485 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 0eaf264a..57f72978 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 ce14b3b5..17e5bc9c 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 286/364] 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 df0165cf..c15efb00 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 c87e3228..7840819d 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 a5e3889c..b434be30 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 d628b85d..7f66213d 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 afe658e1..f36ece1d 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 4200f85e..88b5e135 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 3c90b557..8274d323 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 edab0827..18ce0a7c 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 cad8c762..9e7cd5aa 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 8a6a5b82..a2c56808 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 37cc7485..c8ec4387 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 57f72978..2709584b 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 17e5bc9c..f556a161 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 287/364] 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 d628b85d..6c493a58 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 288/364] 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 67e4c89c..4c219031 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 289/364] 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 d7549731..b6a402ef 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 9deb4f3c..262cae65 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 c0ce0076..07227546 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 ce27fc9a..9f2a9829 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 73fc9512..19383d11 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 290/364] 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 9ce680d7..2eba6c84 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 bbdd0eed..7955e598 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 d7549731..f2c74947 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 2ad5b6fa..c39f8ca0 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 291/364] 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 2eba6c84..ddf545da 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 f2c74947..cb2b4a33 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 c39f8ca0..2692bd85 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 292/364] 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 cb2b4a33..2b4f94a2 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 e1e745a9..0935b6c6 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 4c71d326..7c922671 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 293/364] 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 2b4f94a2..3f1b5a91 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 294/364] 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 3f1b5a91..08de3df4 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 295/364] 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 fb7b6e4d..12c1fe39 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 296/364] 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 d7549731..69eddb55 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 555fbcb6..cb730345 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 297/364] 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 641ee6a7..0e68f002 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 17324d69..628bbf61 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 69eddb55..648de3fd 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 827f9ac1..fef50839 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 e1e745a9..6a86fa67 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 f9a4fe05..00000000 --- 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 0857724e..cfe723a3 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 73c06fb8..7c59dd5e 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 298/364] 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 648de3fd..ddb757f6 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 cfe723a3..e919ded2 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 299/364] 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 ddf545da..339f92ed 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 08de3df4..481fded2 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 2692bd85..a3b66136 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 300/364] 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 481fded2..d2dbd1f9 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 301/364] 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 4826fc31..032b2382 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 302/364] fix build warning --- src/bar.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bar.cpp b/src/bar.cpp index 0857724e..cc8318c2 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 303/364] hyprland/workspaces: Use hyprland's persistent workspaces configuration --- include/modules/hyprland/workspaces.hpp | 25 +- src/modules/hyprland/workspaces.cpp | 323 +++++++++++++++--------- src/util/regex_collection.cpp | 2 +- 3 files changed, 220 insertions(+), 130 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index d2006fcc..827888b8 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 88b5e135..96893179 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(); +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; - 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]; + 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, - m_bar.output->name); - for (int i = 0; i < amount; i++) { - m_persistentWorkspacesToCreate.emplace_back( - std::to_string(m_monitorId * amount + i + 1)); - } + 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); + } + } + + for (auto const &workspace : persistentWorkspacesToCreate) { + auto const workspaceData = createPersistentWorkspaceData(workspace, m_bar.output->name); + m_workspacesToCreate.emplace_back(workspaceData, clientsJson); + } +} + +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::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); - } -} - -void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) { - for (const auto &client : clientsJson) { - if (client["workspace"]["id"].asInt() == workspaceId) { - registerOrphanWindow({client}); - } - } -} - -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); + } +} + +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()); } - const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - const Json::Value clientsJson = gIPC->getSocket1JsonReply("clients"); + // 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); + } +} +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(); 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 df0879c1..704d6545 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 304/364] 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 0cacc3d7..e218496c 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 e919ded2..f7847ae1 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 305/364] 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 e218496c..6dc3c03d 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 f7847ae1..33a8b141 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 306/364] 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 77723bc0..ea65b923 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 307/364] 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 e83cbef0..c889a13f 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 054c9bd2..accab969 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 308/364] 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 339f92ed..61002b0b 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 d2dbd1f9..475fcac9 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 a3b66136..e11d33ac 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 309/364] 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 46e338ef..40fd3126 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 86dd1f73..191cabc7 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 46419c1d..f0b8f69c 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 d97612d5..821f2c45 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 310/364] 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 449eb1b3..7f78c165 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 64b2bde1..48479568 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 4fdb6590..0703eaf7 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 311/364] 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 00000000..b412f4aa --- /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 00000000..321f6916 --- /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 312/364] 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 4258252a..0efffe64 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 3343b8d5..cdb653f9 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 f96cccfd..c8ec4387 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 313/364] 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 61002b0b..f805aab5 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 e11d33ac..6b709f33 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 314/364] 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 edea9634..3e393121 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 315/364] 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 7536fdfd..0745935e 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 316/364] 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 821f2c45..dc6b7ede 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 317/364] 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 2678cb13..46ff9926 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 01850bc3..f3888a83 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 004d6aa8..d317f98a 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 daeee69e..15e17b0f 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 318/364] 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 c889a13f..97e8a4a7 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 319/364] 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 962c810b..2431b766 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 97e8a4a7..b54a360f 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 320/364] 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 321f6916..b899465f 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 321/364] chore: more labels --- .github/labeler.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index b412f4aa..a89e734f 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 322/364] 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 ea65b923..79456fdb 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 323/364] 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 e2643084..bf8f2f21 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 324/364] 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 79456fdb..1af02b55 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 325/364] 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 0efffe64..4258252a 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 cdb653f9..3343b8d5 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 c8ec4387..1bc9f382 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 326/364] 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 191cabc7..83d1ffc0 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 327/364] 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 83d1ffc0..a39bd23d 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 328/364] 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 b899465f..94dc42d2 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 329/364] 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 c837c1fd..31165fa5 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 330/364] 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 51a48373..4aea4171 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 @@ class PlayerManager: 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 331/364] 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 31165fa5..743fb288 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 332/364] 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 3e9519f5..ea58c4f0 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 c257ed75..1c62fd95 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 a5860bd0..a005df17 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 2540c07f1d2080876d9d58a4f12dd2718267be73 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 24 Feb 2024 18:24:39 -0800 Subject: [PATCH 333/364] 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 adf03a1f..420262db 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 334/364] 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 3d44829f..6e4fcebc 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 335/364] 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 b7a33451..f74eae65 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 336/364] 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 d197df3a..9ce0ec33 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 eb220609..47a4d69c 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 4ff232ff..98b52efb 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 ea4d83b2..593e3422 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 3c8313fc..05db94ec 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 5339ee9e..549faf73 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 9f2a9829..3575b4ac 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 1af02b55..c7d287e5 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 21089596448d7c1e6a75ad97b33916272f6019b3 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 25 Feb 2024 11:21:58 -0800 Subject: [PATCH 337/364] 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 420262db..f225e4fc 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 338/364] 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 628bbf61..2d4de0c9 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 46ff9926..b995d569 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 339/364] 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 a238143b..1ceed73b 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 340/364] 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 a7e2380a..ef379ff3 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 341/364] 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 8d46b1a1..41870077 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 3e393121..882e3806 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 342/364] 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 41870077..91ea1653 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 343/364] 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 ef379ff3..051654df 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(); + 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); + } + 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); - - int margin_top = config_["margin-top"].isInt() ? config_["margin-top"].asInt() : 6; - box_.set_margin_top(margin_top); + 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 344/364] 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 1ceed73b..0dd87425 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 345/364] 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 07b11152..65be764c 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 00000000..71654e36 --- /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 b995d569..10c82620 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 f225e4fc..00612136 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 6e4fcebc..7f708ff4 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 6b709f33..7dc6709e 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 00000000..f4dfd1c8 --- /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 346/364] 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 71654e36..40a512f1 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 f4dfd1c8..e5e379db 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 347/364] 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 051654df..ee68a22e 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 348/364] 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 593e3422..f2c266bd 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 c7d287e5..ec151a7b 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 349/364] 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 0dd87425..e64e6daa 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 350/364] 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 1bc9f382..eda53dde 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 351/364] 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 ee68a22e..d7ee666e 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 352/364] 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 f0b8f69c..7b27fdb6 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 353/364] 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 40a512f1..92ead748 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 e5e379db..3dd43b87 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 354/364] 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 73062c76..188b4a16 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 355/364] 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 1276e3c3..2c9071b4 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 188b4a16..192e6c1a 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 aa1a18f8..3d7c8561 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 356/364] 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 f225e4fc..10ccfe52 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 357/364] 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 92ead748..72ffb314 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 00612136..4e300a33 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 3dd43b87..6b684662 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 358/364] 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 72ffb314..bfb5c99d 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 6b684662..bd6a52a7 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(); + } + } +} + +// 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"); } } 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)); - } + 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; + // 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 359/364] 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 00000000..435fdbdf --- /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 10c82620..4ce7363d 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 360/364] 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 bfb5c99d..edd9fe00 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 bd6a52a7..ae3d7443 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 361/364] 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 7f708ff4..b5859390 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 362/364] 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 435fdbdf..82fad13b 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 4e300a33..58813925 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 7dc6709e..94076201 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 363/364] 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 eda53dde..6464bf9a 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 25b85800a55a218f01e030e497441cc0304dd013 Mon Sep 17 00:00:00 2001 From: Merlin Sievers Date: Mon, 4 Mar 2024 20:00:57 +0100 Subject: [PATCH 364/364] 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 7db18a20..b92abd12 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 52a6a2d1..e359ea2e 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 1fdd984b..3808e855 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 48479568..fcbd1265 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 67e4c89c..b968e781 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 d466bddf..a279718b 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 3f8d6280..9f5429b4 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 287def1a..71b3b30c 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 1233eb7d..47b6ffce 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 3af71b61..87a38354 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 55c74b0b..e0252caf 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 ffef0fef..fe6ee5a1 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 117b816c..186d73c6 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 08c86d3d..b5580c52 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 e04245ee..4bc75258 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 f6f682d0..1c09d6f6 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 aea6e205..2d63b5e1 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 9c202b2a..dbd9f130 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 1bb0484a..197aaba0 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 87e70adf..44c8b81a 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 9b793f32..037e6b55 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 1d6e7d2e..ff2168ea 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 5424deb6..b08fd90f 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 ++