From bad6cfae6f638b166d55ad543050c69286038840 Mon Sep 17 00:00:00 2001 From: Bruce Blore Date: Sat, 22 Apr 2023 23:43:04 -0700 Subject: [PATCH 001/842] 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 5bfdbf116d8d184a168ebaed6003a2326d495947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Tue, 13 Jun 2023 01:37:45 +0200 Subject: [PATCH 002/842] sway/window: app_id on unfocused workspaces If the unfocused workspace has a single window, use window-specific styling when offscreen-css is enabled. --- src/modules/sway/window.cpp | 55 +++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 030f93bf..be6bce0d 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -251,6 +251,39 @@ std::pair leafNodesInWorkspace(const Json::Value& node) { return {sum, floating_sum}; } +std::optional> getSingleChildNode(const Json::Value& node) { + auto const& nodes = node["nodes"]; + if (nodes.empty()) { + if (node["type"].asString() == "workspace") + return {}; + else if (node["type"].asString() == "floating_con") { + return {}; + } else { + return {std::cref(node)}; + } + } + auto it = std::cbegin(nodes); + if (it == std::cend(nodes)) { + return {}; + } + auto const& child = *it; + ++it; + if (it != std::cend(nodes)) { + return {}; + } + return {getSingleChildNode(child)}; +} + +std::tuple getWindowInfo(const Json::Value& node) { + const auto app_id = node["app_id"].isString() ? node["app_id"].asString() + : node["window_properties"]["instance"].asString(); + const auto app_class = node["window_properties"]["class"].isString() + ? node["window_properties"]["class"].asString() + : ""; + const auto shell = node["shell"].isString() ? node["shell"].asString() : ""; + return {app_id, app_class, shell}; +} + std::tuple gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Value& config_, const Bar& bar_, Json::Value& parentWorkspace, @@ -287,12 +320,7 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu // found node spdlog::trace("actual output {}, output found {}, node (focused) found {}", bar_.output->name, output, node["name"].asString()); - auto app_id = node["app_id"].isString() ? node["app_id"].asString() - : node["window_properties"]["instance"].asString(); - const auto app_class = node["window_properties"]["class"].isString() - ? node["window_properties"]["class"].asString() - : ""; - const auto shell = node["shell"].isString() ? node["shell"].asString() : ""; + const auto [app_id, app_class, shell] = getWindowInfo(node); int nb = node.size(); int floating_count = 0; std::string workspace_layout = ""; @@ -332,15 +360,24 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu std::pair all_leaf_nodes = leafNodesInWorkspace(immediateParent); // using an empty string as default ensures that no window depending styles are set due to the // checks above for !name.empty() + std::string app_id = ""; + std::string app_class = ""; + std::string workspace_layout = ""; + if (all_leaf_nodes.first == 1) { + const auto single_child = getSingleChildNode(immediateParent); + if (single_child.has_value()) { + std::tie(app_id, app_class, workspace_layout) = getWindowInfo(single_child.value()); + } + } return {all_leaf_nodes.first, all_leaf_nodes.second, 0, (all_leaf_nodes.first > 0 || all_leaf_nodes.second > 0) ? config_["offscreen-css-text"].asString() : "", - "", - "", - "", + app_id, + app_class, + workspace_layout, immediateParent["layout"].asString()}; } From cd49eef22956fb11e5f1ddd2a4d8e629c274f903 Mon Sep 17 00:00:00 2001 From: Yifei Teng Date: Fri, 30 Jun 2023 23:25:24 -0700 Subject: [PATCH 003/842] Partially revert 3af1853260 to fix use-after-free After upgrading to the latest release of Waybar the bar will crash whenever I close the laptop lid. After some debugging I believe it is because the watching added by watch_name is not being correctly canceled using unwatch_name. After the Tray object and Host object are destroyed, additional callbacks will become use-after-free. Looks like commit 3af1853260dafc43c992fc2357a3f3bace3bccaa removed the unwatch_name. I'm not sure why it did that, but it seemed dangerous. Additionally, bus_name_id_ is created by own_name. According to that function's documentation, the correct inverse operation is unown_name. --- src/modules/sni/host.cpp | 6 +++++- src/modules/sni/watcher.cpp | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/sni/host.cpp b/src/modules/sni/host.cpp index fff8e019..0bbd4d2f 100644 --- a/src/modules/sni/host.cpp +++ b/src/modules/sni/host.cpp @@ -19,9 +19,13 @@ Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar, Host::~Host() { if (bus_name_id_ > 0) { - Gio::DBus::unwatch_name(bus_name_id_); + Gio::DBus::unown_name(bus_name_id_); bus_name_id_ = 0; } + if (watcher_id_ > 0) { + Gio::DBus::unwatch_name(watcher_id_); + watcher_id_ = 0; + } g_cancellable_cancel(cancellable_); g_clear_object(&cancellable_); g_clear_object(&watcher_); diff --git a/src/modules/sni/watcher.cpp b/src/modules/sni/watcher.cpp index 663fdcdc..dfd076ef 100644 --- a/src/modules/sni/watcher.cpp +++ b/src/modules/sni/watcher.cpp @@ -14,6 +14,10 @@ Watcher::Watcher() watcher_(sn_watcher_skeleton_new()) {} Watcher::~Watcher() { + if (hosts_ != nullptr) { + g_slist_free_full(hosts_, gfWatchFree); + hosts_ = nullptr; + } if (items_ != nullptr) { g_slist_free_full(items_, gfWatchFree); items_ = nullptr; From cdece498c1e3849ea6e8ff7795ddce1ca8265803 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Sun, 2 Jul 2023 20:58:42 +0300 Subject: [PATCH 004/842] hyprland/window: .solo class accounts for hidden & fullscreen windows --- include/modules/hyprland/window.hpp | 1 + src/modules/hyprland/window.cpp | 40 +++++++++++++++++------------ 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index 950be05f..54e7e2b7 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -40,6 +40,7 @@ class Window : public waybar::ALabel, public EventHandler { std::string last_solo_class_; bool solo_; bool all_floating_; + bool hidden_; bool fullscreen_; }; diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index cb820bcd..a5bc55ee 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -64,8 +64,9 @@ auto Window::update() -> void { setClass("empty", workspace_.windows == 0); setClass("solo", solo_); - setClass("fullscreen", fullscreen_); setClass("floating", all_floating_); + setClass("hidden", hidden_); + setClass("fullscreen", fullscreen_); if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) { if (bar_.window.get_style_context()->has_class(last_solo_class_)) { @@ -126,36 +127,43 @@ void Window::queryActiveWorkspace() { } if (workspace_.windows > 0) { - const auto clients = gIPC->getSocket1Reply("j/clients"); - Json::Value json = parser_.parse(clients); - assert(json.isArray()); - auto active_window = std::find_if(json.begin(), json.end(), [&](Json::Value window) { + const auto clients = gIPC->getSocket1JsonReply("clients"); + assert(clients.isArray()); + auto active_window = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { return window["address"] == workspace_.last_window; }); - if (active_window == std::end(json)) { + if (active_window == std::end(clients)) { return; } - if (workspace_.windows == 1 && !(*active_window)["floating"].asBool()) { - solo_class_ = (*active_window)["class"].asString(); - } else { - solo_class_ = ""; - } std::vector workspace_windows; - std::copy_if(json.begin(), json.end(), std::back_inserter(workspace_windows), + std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows), [&](Json::Value window) { return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); }); solo_ = 1 == std::count_if(workspace_windows.begin(), workspace_windows.end(), - [&](Json::Value window) { return !window["floating"].asBool(); }); + [&](Json::Value window) { return !window["floating"].asBool() && !window["hidden"].asBool(); }); all_floating_ = std::all_of(workspace_windows.begin(), workspace_windows.end(), - [&](Json::Value window) { return window["floating"].asBool(); }); + [&](Json::Value window) { return window["floating"].asBool() && !window["hidden"].asBool(); }); + hidden_ = std::any_of(workspace_windows.begin(), workspace_windows.end(), + [&](Json::Value window) { return window["hidden"].asBool(); }); fullscreen_ = (*active_window)["fullscreen"].asBool(); + + if (fullscreen_) { + solo_ = true; + } + + if (solo_) { + solo_class_ = (*active_window)["class"].asString(); + } else { + solo_class_ = ""; + } } else { - solo_class_ = ""; - solo_ = false; all_floating_ = false; + hidden_ = false; fullscreen_ = false; + solo_ = false; + solo_class_ = ""; } } From fc632f50eca8145030f89c1d97c053d2db4021ce Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 4 Jul 2023 22:52:24 +0200 Subject: [PATCH 005/842] fix: lint --- src/modules/mpris/mpris.cpp | 17 ++++++++--------- src/modules/sway/workspaces.cpp | 4 ++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index a5621758..aa425489 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -83,7 +83,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) // "dynamic-priority" has been kept for backward compatibility if (config_["dynamic-importance-order"].isArray() || config_["dynamic-priority"].isArray()) { dynamic_prio_.clear(); - const auto& dynamic_priority = config_["dynamic-importance-order"].isArray() ? config_["dynamic-importance-order"] : config_["dynamic-priority"]; + const auto& dynamic_priority = config_["dynamic-importance-order"].isArray() + ? config_["dynamic-importance-order"] + : config_["dynamic-priority"]; for (const auto& value : dynamic_priority) { if (value.isString()) { dynamic_prio_.push_back(value.asString()); @@ -299,9 +301,9 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> "position") != dynamic_order_.end()); if (truncated && dynamic_len_ >= 0) { - //Since the first element doesn't present a separator and we don't know a priori which one - //it will be, we add a "virtual separatorLen" to the dynamicLen, since we are adding the - //separatorLen to all the other lengths. + // Since the first element doesn't present a separator and we don't know a priori which one + // it will be, we add a "virtual separatorLen" to the dynamicLen, since we are adding the + // separatorLen to all the other lengths. size_t separatorLen = utf8_width(dynamic_separator_); size_t dynamicLen = dynamic_len_ + separatorLen; if (showArtist) artistLen += separatorLen; @@ -361,12 +363,9 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> std::string previousOrder = ""; for (const std::string& order : dynamic_order_) { - if ((order == "artist" && showArtist) || - (order == "album" && showAlbum) || + if ((order == "artist" && showArtist) || (order == "album" && showAlbum) || (order == "title" && showTitle)) { - if (previousShown && - previousOrder != "length" && - previousOrder != "position") { + if (previousShown && previousOrder != "length" && previousOrder != "position") { dynamic << dynamic_separator_; } diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index c1cfd5a4..638ed9d9 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -328,7 +328,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { } } if (!config_["warp-on-scroll"].asBool()) { - ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping none")); + ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping none")); } try { ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, "--no-auto-back-and-forth", name)); @@ -336,7 +336,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { spdlog::error("Workspaces: {}", e.what()); } if (!config_["warp-on-scroll"].asBool()) { - ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping container")); + ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping container")); } return true; } From d8a808f76c51ded496b8be244513dad9345adb68 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 4 Jul 2023 23:03:45 +0200 Subject: [PATCH 006/842] chore: 0.9.19 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index aa250b7f..4137d225 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.18', + version: '0.9.19', license: 'MIT', meson_version: '>= 0.50.0', default_options : [ From d21f29cb14b54e60e895dbeb5e8a9cfe97d62dc7 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Tue, 4 Jul 2023 23:05:26 +0200 Subject: [PATCH 007/842] Fixed build warnings --- include/modules/mpris/mpris.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index a0aee3b2..ad4dac1e 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -66,9 +66,9 @@ class Mpris : public ALabel { int album_len_; int title_len_; int dynamic_len_; - std::string dynamic_separator_; - std::vector dynamic_order_; std::vector dynamic_prio_; + std::vector dynamic_order_; + std::string dynamic_separator_; bool truncate_hours_; bool tooltip_len_limits_; std::string ellipsis_; From 55c59253d666e801899c49469550a3a7c3f14014 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Wed, 5 Jul 2023 03:15:59 +0300 Subject: [PATCH 008/842] Update man pages --- man/waybar-hyprland-window.5.scd | 19 +++++++++++++++++++ man/waybar-sway-window.5.scd | 6 +++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd index 0135d7c9..58227092 100644 --- a/man/waybar-hyprland-window.5.scd +++ b/man/waybar-hyprland-window.5.scd @@ -21,6 +21,10 @@ Addressed by *hyprland/window* typeof: object ++ Rules to rewrite window title. See *rewrite rules*. +*separate-outputs*: ++ + typeof: bool ++ + Show the active window of the monitor the bar belongs to, instead of the focused window. + # REWRITE RULES *rewrite* is an object where keys are regular expressions and values are @@ -48,3 +52,18 @@ Invalid expressions (e.g., mismatched parentheses) are skipped. # STYLE - *#window* +- *#window.empty* When no windows are in the workspace + +The following classes are applied to the entire Waybar rather than just the +window widget: + +- *window#waybar.empty* When no windows are in the workspace +- *window#waybar.solo* When one tiled window is visible in the workspace + (floating windows may be present) +- *window#waybar.* Where ** is the *class* (e.g. *chromium*) of + the solo tiled window in the workspace (use *hyprctl clients* to see classes) +- *window#waybar.floating* When there are only floating windows in the workspace +- *window#waybar.fullscreen* When there is a fullscreen window in the workspace; + useful with Hyprland's *fullscreen, 1* mode +- *window#waybar.hidden* When there are hidden windows in the workspace; usually + occurs due to window swallowing diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 0dd16295..8091e249 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -84,7 +84,7 @@ Addressed by *sway/window* typeof: bool ++ default: false ++ If the workspace itself is focused and the workspace contains nodes or floating_nodes, show the workspace name. If not set, text remains empty but styles according to nodes in the workspace are still applied. - + *rewrite*: ++ typeof: object ++ Rules to rewrite the module format output. See *rewrite rules*. @@ -136,6 +136,10 @@ Invalid expressions (e.g., mismatched parentheses) are skipped. # STYLE - *#window* + +The following classes are applied to the entire Waybar rather than just the +window widget: + - *window#waybar.empty* When no windows are in the workspace, or screen is not focused and offscreen-css option is not set - *window#waybar.solo* When one tiled window is in the workspace - *window#waybar.floating* When there are only floating windows in the workspace From e125bbeb4dc06c3bd7d4536df168b06804d996b5 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Wed, 5 Jul 2023 03:38:38 +0300 Subject: [PATCH 009/842] hyprland/window: properly check visibility for .floating class --- src/modules/hyprland/window.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index a5bc55ee..f429b8db 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -141,12 +141,16 @@ void Window::queryActiveWorkspace() { [&](Json::Value window) { return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); }); - solo_ = 1 == std::count_if(workspace_windows.begin(), workspace_windows.end(), - [&](Json::Value window) { return !window["floating"].asBool() && !window["hidden"].asBool(); }); - all_floating_ = std::all_of(workspace_windows.begin(), workspace_windows.end(), - [&](Json::Value window) { return window["floating"].asBool() && !window["hidden"].asBool(); }); hidden_ = std::any_of(workspace_windows.begin(), workspace_windows.end(), - [&](Json::Value window) { return window["hidden"].asBool(); }); + [&](Json::Value window) { return window["hidden"].asBool(); }); + std::vector visible_windows; + std::copy_if(workspace_windows.begin(), workspace_windows.end(), + std::back_inserter(visible_windows), + [&](Json::Value window) { return !window["hidden"].asBool(); }); + solo_ = 1 == std::count_if(visible_windows.begin(), visible_windows.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_ = (*active_window)["fullscreen"].asBool(); if (fullscreen_) { From 1fb2b8efd5a1e4ab861f1b0f8e0bde7ad578b8ef Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Wed, 5 Jul 2023 17:38:53 +0000 Subject: [PATCH 010/842] fix(util): don't abort modules from SleeperThread after 3c9cbc99d736 [warning] module sway/workspaces: Disabling module "sway/workspaces", Unable to connect to the SYSTEM Bus!... [warning] module sway/mode: Disabling module "sway/mode", Unable to connect to the SYSTEM Bus!... [warning] module sway/scratchpad: Disabling module "sway/scratchpad", Unable to connect to the SYSTEM Bus!... [warning] module custom/media: Disabling module "custom/media", Unable to connect to the SYSTEM Bus!... [warning] module sway/window: Disabling module "sway/window", Unable to connect to the SYSTEM Bus!... [warning] module cpu: Disabling module "cpu", Unable to connect to the SYSTEM Bus!... [warning] module memory: Disabling module "memory", Unable to connect to the SYSTEM Bus!... [warning] module temperature: Disabling module "temperature", Unable to connect to the SYSTEM Bus!... [warning] module sway/language: Disabling module "sway/language", Unable to connect to the SYSTEM Bus!... [warning] module battery: Disabling module "battery", Unable to connect to the SYSTEM Bus!... [warning] module battery#bat2: Disabling module "battery#bat2", Unable to connect to the SYSTEM Bus!... --- src/util/prepare_for_sleep.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/prepare_for_sleep.cpp b/src/util/prepare_for_sleep.cpp index 221497e8..218c1e29 100644 --- a/src/util/prepare_for_sleep.cpp +++ b/src/util/prepare_for_sleep.cpp @@ -1,6 +1,7 @@ #include "util/prepare_for_sleep.h" #include +#include namespace { class PrepareForSleep { @@ -9,7 +10,7 @@ class PrepareForSleep { GError *error = 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!..."); + spdlog::warn("Unable to connect to the SYSTEM Bus!..."); } else { login1_id = g_dbus_connection_signal_subscribe( login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager", From e2bfa5e019f62677aae786e5f202a87e06f7b3be Mon Sep 17 00:00:00 2001 From: Calvin Chu Date: Thu, 6 Jul 2023 11:01:24 +1000 Subject: [PATCH 011/842] hyprland/window: fix no info with separate-outputs=true --- src/modules/hyprland/window.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index f429b8db..74a2a432 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -103,9 +103,9 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { const auto workspaces = gIPC->getSocket1JsonReply("workspaces"); assert(workspaces.isArray()); - auto workspace = std::find_if(monitors.begin(), monitors.end(), + auto workspace = std::find_if(workspaces.begin(), workspaces.end(), [&](Json::Value workspace) { return workspace["id"] == id; }); - if (workspace == std::end(monitors)) { + if (workspace == std::end(workspaces)) { spdlog::warn("No workspace with id {}", id); return Workspace{-1, 0, "", ""}; } From b20041d85d35b6977c5a5e9cd2d8774762e337ff Mon Sep 17 00:00:00 2001 From: ViktarL <23121044+LukashonakV@users.noreply.github.com> Date: Sat, 8 Jul 2023 08:41:37 +0000 Subject: [PATCH 012/842] cava dependencies --- Dockerfiles/archlinux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index cab4146b..73f19067 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,5 +3,5 @@ FROM archlinux:base-devel RUN pacman -Syu --noconfirm && \ - pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl && \ + pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw && \ sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen From d774de6c46221530ba8060b92f544a3f6c2d88a4 Mon Sep 17 00:00:00 2001 From: sigboe Date: Sat, 8 Jul 2023 21:32:19 +0200 Subject: [PATCH 013/842] fix, default to true, sway/workspaces: warp-on-scroll --- src/modules/sway/workspaces.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 638ed9d9..90efe7a1 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -327,7 +327,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { return true; } } - if (!config_["warp-on-scroll"].asBool()) { + if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) { ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping none")); } try { @@ -335,7 +335,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } - if (!config_["warp-on-scroll"].asBool()) { + if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) { ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping container")); } return true; From c4bace504ce058ab784c44c9357fd5bd27aedffb Mon Sep 17 00:00:00 2001 From: MisterPine Date: Sat, 8 Jul 2023 21:42:28 +0200 Subject: [PATCH 014/842] Separate query and struct --- include/modules/hyprland/window.hpp | 14 ++++++++++++ src/modules/hyprland/window.cpp | 35 ++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index 54e7e2b7..bd438044 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -24,8 +24,20 @@ class Window : public waybar::ALabel, public EventHandler { static auto parse(const Json::Value&) -> Workspace; }; + struct WindowData { + bool floating; + int monitor; + std::string class_name; + std::string initial_class_name; + std::string title; + std::string initial_title; + + static auto parse(const Json::Value&) -> WindowData; + }; + auto getActiveWorkspace(const std::string&) -> Workspace; auto getActiveWorkspace() -> Workspace; + auto getWindowData(const std::string& window_id) -> WindowData; void onEvent(const std::string&) override; void queryActiveWorkspace(); void setClass(const std::string&, bool enable); @@ -34,7 +46,9 @@ class Window : public waybar::ALabel, public EventHandler { std::mutex mutex_; const Bar& bar_; util::JsonParser parser_; + std::string last_window_address_; std::string last_title_; + WindowData window_data_; Workspace workspace_; std::string solo_class_; std::string last_solo_class_; diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 74a2a432..d6a226cc 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -14,7 +14,7 @@ namespace waybar::modules::hyprland { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : ALabel(config, "window", id, "{}", 0, true), bar_(bar) { + : ALabel(config, "window", id, "{title}", 0, true), bar_(bar) { modulesReady = true; separate_outputs = config["separate-outputs"].asBool(); @@ -44,6 +44,7 @@ auto Window::update() -> void { std::lock_guard lg(mutex_); std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title); + std::string window_address = workspace_.last_window; if (window_name != last_title_) { if (window_name.empty()) { @@ -54,10 +55,19 @@ auto Window::update() -> void { last_title_ = window_name; } + if (window_address != last_window_address_) { + last_window_address_ = window_address; + window_data_ = getWindowData(window_address); + } + if (!format_.empty()) { label_.show(); - label_.set_markup(fmt::format(fmt::runtime(format_), - waybar::util::rewriteString(window_name, config_["rewrite"]))); + 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)), + config_["rewrite"])); } else { label_.hide(); } @@ -117,6 +127,25 @@ auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { value["lastwindowtitle"].asString()}; } +auto Window::getWindowData(const std::string& window_address) -> WindowData { + const auto clients = gIPC->getSocket1JsonReply("clients"); + assert(clients.isArray()); + auto window = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { + return window["address"] == window_address; + }); + if (window == std::end(clients)) { + spdlog::warn("No client with address {}", window_address); + return WindowData{false, -1, "", "", "", ""}; + } + return WindowData::parse(*window); +} + +auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData { + return WindowData{value["floating"].asBool(), value["monitor"].asInt(), + value["class"].asString(), value["initialClass"].asString(), + value["title"].asString(), value["initialTitle"].asString()}; +} + void Window::queryActiveWorkspace() { std::lock_guard lg(mutex_); From c5f1771375fbc3fcdeb8b6434d9acd731eb9fe64 Mon Sep 17 00:00:00 2001 From: MisterPine Date: Sat, 8 Jul 2023 22:05:15 +0200 Subject: [PATCH 015/842] Use already existing `queryActiveWorkspace()` --- include/modules/hyprland/window.hpp | 4 +--- src/modules/hyprland/window.cpp | 13 +++++-------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index bd438044..3f9b89bf 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -26,7 +26,7 @@ class Window : public waybar::ALabel, public EventHandler { struct WindowData { bool floating; - int monitor; + int monitor = -1; std::string class_name; std::string initial_class_name; std::string title; @@ -46,8 +46,6 @@ class Window : public waybar::ALabel, public EventHandler { std::mutex mutex_; const Bar& bar_; util::JsonParser parser_; - std::string last_window_address_; - std::string last_title_; WindowData window_data_; Workspace workspace_; std::string solo_class_; diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index d6a226cc..01f672a4 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -46,18 +46,13 @@ auto Window::update() -> void { std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title); std::string window_address = workspace_.last_window; - if (window_name != last_title_) { + if (window_name != window_data_.title) { if (window_name.empty()) { label_.get_style_context()->add_class("empty"); } else { label_.get_style_context()->remove_class("empty"); } - last_title_ = window_name; - } - - if (window_address != last_window_address_) { - last_window_address_ = window_address; - window_data_ = getWindowData(window_address); + window_data_.title = window_name; } if (!format_.empty()) { @@ -165,6 +160,7 @@ void Window::queryActiveWorkspace() { return; } + window_data_ = WindowData::parse(*active_window); std::vector workspace_windows; std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows), [&](Json::Value window) { @@ -187,11 +183,12 @@ void Window::queryActiveWorkspace() { } if (solo_) { - solo_class_ = (*active_window)["class"].asString(); + solo_class_ = window_data_.class_name; } else { solo_class_ = ""; } } else { + window_data_ = WindowData{}; all_floating_ = false; hidden_ = false; fullscreen_ = false; From 2ae13c40921d19b9a78d8f8adfd404a80e659bd5 Mon Sep 17 00:00:00 2001 From: MisterPine Date: Sat, 8 Jul 2023 22:27:25 +0200 Subject: [PATCH 016/842] consitent naming --- src/modules/hyprland/window.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 01f672a4..fea0d310 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -59,9 +59,9 @@ auto Window::update() -> void { 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("initial-title", window_data_.initial_title), fmt::arg("class", window_data_.class_name), - fmt::arg("initialClass", window_data_.initial_class_name)), + fmt::arg("initial-class", window_data_.initial_class_name)), config_["rewrite"])); } else { label_.hide(); From 1887512ba1c388855c58ea9ffc99e4933dd0e3a7 Mon Sep 17 00:00:00 2001 From: MisterPine Date: Sat, 8 Jul 2023 22:29:30 +0200 Subject: [PATCH 017/842] Update scd --- man/waybar-hyprland-window.5.scd | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd index 58227092..53ee22a1 100644 --- a/man/waybar-hyprland-window.5.scd +++ b/man/waybar-hyprland-window.5.scd @@ -14,7 +14,7 @@ Addressed by *hyprland/window* *format*: ++ typeof: string ++ - default: {} ++ + default: {title} ++ The format, how information should be displayed. On {} the current window title is displayed. *rewrite*: ++ @@ -25,6 +25,17 @@ Addressed by *hyprland/window* typeof: bool ++ Show the active window of the monitor the bar belongs to, instead of the focused window. +# FORMAT REPLACEMENTS +See the output of "hyprctl clients" for examples + +*{title}*: The current title of the focused window. + +*{initial-title}*: The initial title of the focused window. + +*{class}*: The current class of the focused window. + +*{initial-class}*: The initial class of the focused window. + # REWRITE RULES *rewrite* is an object where keys are regular expressions and values are From 9ee883ee1b61aeb978bedff31451501286127e70 Mon Sep 17 00:00:00 2001 From: MisterPine Date: Sat, 8 Jul 2023 23:11:11 +0200 Subject: [PATCH 018/842] No dashes is format arg name --- man/waybar-hyprland-window.5.scd | 4 ++-- src/modules/hyprland/window.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd index 53ee22a1..2a3f62f1 100644 --- a/man/waybar-hyprland-window.5.scd +++ b/man/waybar-hyprland-window.5.scd @@ -30,11 +30,11 @@ See the output of "hyprctl clients" for examples *{title}*: The current title of the focused window. -*{initial-title}*: The initial title of the focused window. +*{initialTitle}*: The initial title of the focused window. *{class}*: The current class of the focused window. -*{initial-class}*: The initial class of the focused window. +*{initialClass}*: The initial class of the focused window. # REWRITE RULES diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index fea0d310..01f672a4 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -59,9 +59,9 @@ auto Window::update() -> void { label_.show(); label_.set_markup(waybar::util::rewriteString( fmt::format(fmt::runtime(format_), fmt::arg("title", window_name), - fmt::arg("initial-title", window_data_.initial_title), + fmt::arg("initialTitle", window_data_.initial_title), fmt::arg("class", window_data_.class_name), - fmt::arg("initial-class", window_data_.initial_class_name)), + fmt::arg("initialClass", window_data_.initial_class_name)), config_["rewrite"])); } else { label_.hide(); From f97c1c7136528798f17f5f1b621e41ae46d66aaf Mon Sep 17 00:00:00 2001 From: MisterPine Date: Sat, 8 Jul 2023 23:22:29 +0200 Subject: [PATCH 019/842] remove getWindowData --- include/modules/hyprland/window.hpp | 1 - src/modules/hyprland/window.cpp | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index 3f9b89bf..44253e1f 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -37,7 +37,6 @@ class Window : public waybar::ALabel, public EventHandler { auto getActiveWorkspace(const std::string&) -> Workspace; auto getActiveWorkspace() -> Workspace; - auto getWindowData(const std::string& window_id) -> WindowData; void onEvent(const std::string&) override; void queryActiveWorkspace(); void setClass(const std::string&, bool enable); diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 01f672a4..7fc7fe28 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -122,19 +122,6 @@ auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { value["lastwindowtitle"].asString()}; } -auto Window::getWindowData(const std::string& window_address) -> WindowData { - const auto clients = gIPC->getSocket1JsonReply("clients"); - assert(clients.isArray()); - auto window = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { - return window["address"] == window_address; - }); - if (window == std::end(clients)) { - spdlog::warn("No client with address {}", window_address); - return WindowData{false, -1, "", "", "", ""}; - } - return WindowData::parse(*window); -} - auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData { return WindowData{value["floating"].asBool(), value["monitor"].asInt(), value["class"].asString(), value["initialClass"].asString(), From 56f956ff9016d22cc83125ce2554173c43f02525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABlys=20Bras=20de=20fer?= Date: Sun, 9 Jul 2023 01:44:39 +0200 Subject: [PATCH 020/842] clock: handle timezone changes (again) --- src/modules/clock.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 17cfd8da..59963efd 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -24,7 +24,8 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) for (const auto& zone_name : config_["timezones"]) { if (!zone_name.isString()) continue; if (zone_name.asString().empty()) - time_zones_.push_back(date::current_zone()); + // nullptr means that local time should be shown + time_zones_.push_back(nullptr); else try { time_zones_.push_back(date::locate_zone(zone_name.asString())); @@ -34,7 +35,8 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } } else if (config_["timezone"].isString()) { if (config_["timezone"].asString().empty()) - time_zones_.push_back(date::current_zone()); + // nullptr means that local time should be shown + time_zones_.push_back(nullptr); else try { time_zones_.push_back(date::locate_zone(config_["timezone"].asString())); @@ -43,10 +45,10 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } } - // If all timezones are parsed and no one is good, add current time zone. nullptr in timezones - // vector means that local time should be shown + // If all timezones are parsed and no one is good if (!time_zones_.size()) { - time_zones_.push_back(date::current_zone()); + // nullptr means that local time should be shown + time_zones_.push_back(nullptr); } // Check if a particular placeholder is present in the tooltip format, to know what to calculate @@ -156,8 +158,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } const date::time_zone* waybar::modules::Clock::current_timezone() { - return time_zones_[current_time_zone_idx_] ? time_zones_[current_time_zone_idx_] - : date::current_zone(); + return is_timezone_fixed() ? time_zones_[current_time_zone_idx_] : date::current_zone(); } bool waybar::modules::Clock::is_timezone_fixed() { From a373f6b65442d199069ba28aee2d97d572fffd89 Mon Sep 17 00:00:00 2001 From: MisterPine Date: Mon, 10 Jul 2023 22:02:03 +0200 Subject: [PATCH 021/842] Icon working --- include/modules/hyprland/window.hpp | 9 ++- src/modules/hyprland/window.cpp | 109 +++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index 44253e1f..a916610c 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -1,13 +1,13 @@ #include -#include "ALabel.hpp" +#include "AIconLabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" #include "util/json.hpp" namespace waybar::modules::hyprland { -class Window : public waybar::ALabel, public EventHandler { +class Window : public waybar::AIconLabel, public EventHandler { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); virtual ~Window(); @@ -40,6 +40,8 @@ class Window : public waybar::ALabel, public EventHandler { void onEvent(const std::string&) override; void queryActiveWorkspace(); void setClass(const std::string&, bool enable); + void updateAppIconName(); + void updateAppIcon(); bool separate_outputs; std::mutex mutex_; @@ -53,6 +55,9 @@ class Window : public waybar::ALabel, public EventHandler { bool all_floating_; bool hidden_; bool fullscreen_; + unsigned app_icon_size_{24}; + bool update_app_icon_{true}; + std::string app_icon_name_; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 7fc7fe28..59303afa 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -1,23 +1,35 @@ #include "modules/hyprland/window.hpp" +#include +#include +#include #include #include +#include +#include #include #include #include #include "modules/hyprland/backend.hpp" +#include "util/gtk_icon.hpp" #include "util/json.hpp" #include "util/rewrite_string.hpp" namespace waybar::modules::hyprland { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : ALabel(config, "window", id, "{title}", 0, true), bar_(bar) { + : AIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { modulesReady = true; separate_outputs = config["separate-outputs"].asBool(); + // Icon size + if (config["icon-size"].isUInt()) { + app_icon_size_ = config["icon-size"].asUInt(); + } + image_.set_pixel_size(app_icon_size_); + if (!gIPC.get()) { gIPC = std::make_unique(); } @@ -39,6 +51,98 @@ Window::~Window() { std::lock_guard lg(mutex_); } +std::optional getDesktopFilePath(const std::string& app_class) { + 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_class + ".desktop"; + if (std::filesystem::exists(desktop_file_path)) { + return desktop_file_path; + } + } + return {}; +} + +std::optional getIconName(const std::string& app_class) { + const auto desktop_file_path = getDesktopFilePath(app_class); + if (!desktop_file_path.has_value()) { + // Try some heuristics to find a matching icon + + if (DefaultGtkIconThemeWrapper::has_icon(app_class)) { + return app_class; + } + + const auto app_identifier_desktop = app_class + "-desktop"; + if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) { + 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_class.find_first_of(' '); + if (first_space != std::string::npos) { + const auto first_word = to_lower(app_class.substr(0, first_space)); + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { + return first_word; + } + } + + const auto first_dash = app_class.find_first_of('-'); + if (first_dash != std::string::npos) { + const auto first_word = to_lower(app_class.substr(0, first_dash)); + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { + return first_word; + } + } + + return {}; + } + + try { + Glib::KeyFile desktop_file; + desktop_file.load_from_file(desktop_file_path.value()); + return desktop_file.get_string("Desktop Entry", "Icon"); + } catch (Glib::FileError& error) { + spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), + error.what().c_str()); + } catch (Glib::KeyFileError& error) { + spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), + error.what().c_str()); + } + return {}; +} + +void Window::updateAppIconName() { + if (!iconEnabled()) { + return; + } + + const auto icon_name = getIconName(window_data_.class_name); + if (icon_name.has_value()) { + app_icon_name_ = icon_name.value(); + } else { + app_icon_name_ = ""; + } + update_app_icon_ = true; +} + +void Window::updateAppIcon() { + if (update_app_icon_) { + update_app_icon_ = false; + if (app_icon_name_.empty()) { + image_.set_visible(false); + } else { + image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); + image_.set_visible(true); + } + } +} + auto Window::update() -> void { // fix ampersands std::lock_guard lg(mutex_); @@ -86,6 +190,8 @@ auto Window::update() -> void { } last_solo_class_ = solo_class_; + updateAppIcon(); + ALabel::update(); } @@ -148,6 +254,7 @@ void Window::queryActiveWorkspace() { } window_data_ = WindowData::parse(*active_window); + updateAppIconName(); std::vector workspace_windows; std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows), [&](Json::Value window) { From 6e9ba3fc01859b782d89a81d0fd21445ed3d8ce1 Mon Sep 17 00:00:00 2001 From: MisterPine Date: Mon, 10 Jul 2023 22:26:02 +0200 Subject: [PATCH 022/842] Fix spacing if icon is false --- 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 59303afa..68c379bf 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -192,7 +192,7 @@ auto Window::update() -> void { updateAppIcon(); - ALabel::update(); + AIconLabel::update(); } auto Window::getActiveWorkspace() -> Workspace { From 00e143d47e983d093135f4c2be53cb77902dfbd2 Mon Sep 17 00:00:00 2001 From: MisterPine Date: Mon, 10 Jul 2023 22:50:58 +0200 Subject: [PATCH 023/842] Introducce AAppIconLabel class Implemented for hyprland --- include/AAppIconLabel.hpp | 26 ++++++ include/modules/hyprland/window.hpp | 9 +- meson.build | 1 + src/AAppIconLabel.cpp | 124 ++++++++++++++++++++++++++++ src/modules/hyprland/window.cpp | 108 +----------------------- 5 files changed, 156 insertions(+), 112 deletions(-) create mode 100644 include/AAppIconLabel.hpp create mode 100644 src/AAppIconLabel.cpp diff --git a/include/AAppIconLabel.hpp b/include/AAppIconLabel.hpp new file mode 100644 index 00000000..a2ae4f59 --- /dev/null +++ b/include/AAppIconLabel.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "AIconLabel.hpp" + +namespace waybar { + +class AAppIconLabel : public AIconLabel { + public: + AAppIconLabel(const Json::Value &config, const std::string &name, const std::string &id, + const std::string &format, uint16_t interval = 0, bool ellipsize = false, + bool enable_click = false, bool enable_scroll = false); + virtual ~AAppIconLabel() = default; + auto update() -> void override; + + protected: + void updateAppIconName(const std::string &app_identifier); + void updateAppIcon(); + unsigned app_icon_size_{24}; + bool update_app_icon_{true}; + std::string app_icon_name_; +}; + +} // namespace waybar diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index a916610c..5316c0e4 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -1,13 +1,13 @@ #include -#include "AIconLabel.hpp" +#include "AAppIconLabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" #include "util/json.hpp" namespace waybar::modules::hyprland { -class Window : public waybar::AIconLabel, public EventHandler { +class Window : public waybar::AAppIconLabel, public EventHandler { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); virtual ~Window(); @@ -40,8 +40,6 @@ class Window : public waybar::AIconLabel, public EventHandler { void onEvent(const std::string&) override; void queryActiveWorkspace(); void setClass(const std::string&, bool enable); - void updateAppIconName(); - void updateAppIcon(); bool separate_outputs; std::mutex mutex_; @@ -55,9 +53,6 @@ class Window : public waybar::AIconLabel, public EventHandler { bool all_floating_; bool hidden_; bool fullscreen_; - unsigned app_icon_size_{24}; - bool update_app_icon_{true}; - std::string app_icon_name_; }; } // namespace waybar::modules::hyprland diff --git a/meson.build b/meson.build index 4137d225..f8b4a2d6 100644 --- a/meson.build +++ b/meson.build @@ -159,6 +159,7 @@ src_files = files( 'src/AModule.cpp', 'src/ALabel.cpp', 'src/AIconLabel.cpp', + 'src/AAppIconLabel.cpp', 'src/modules/custom.cpp', 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp new file mode 100644 index 00000000..6e3d1641 --- /dev/null +++ b/src/AAppIconLabel.cpp @@ -0,0 +1,124 @@ +#include "AAppIconLabel.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +#include "util/gtk_icon.hpp" + +namespace waybar { + +AAppIconLabel::AAppIconLabel(const Json::Value& config, const std::string& name, + const std::string& id, const std::string& format, uint16_t interval, + bool ellipsize, bool enable_click, bool enable_scroll) + : AIconLabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) { + // Icon size + if (config["icon-size"].isUInt()) { + app_icon_size_ = config["icon-size"].asUInt(); + } + image_.set_pixel_size(app_icon_size_); +} + +std::optional getDesktopFilePath(const std::string& 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)) { + return desktop_file_path; + } + } + return {}; +} + +std::optional getIconName(const std::string& app_identifier) { + const auto desktop_file_path = getDesktopFilePath(app_identifier); + if (!desktop_file_path.has_value()) { + // Try some heuristics to find a matching icon + + if (DefaultGtkIconThemeWrapper::has_icon(app_identifier)) { + return app_identifier; + } + + const auto app_identifier_desktop = app_identifier + "-desktop"; + if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) { + 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)); + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { + return first_word; + } + } + + 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)); + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { + return first_word; + } + } + + return {}; + } + + try { + Glib::KeyFile desktop_file; + desktop_file.load_from_file(desktop_file_path.value()); + return desktop_file.get_string("Desktop Entry", "Icon"); + } catch (Glib::FileError& error) { + spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), + error.what().c_str()); + } catch (Glib::KeyFileError& error) { + spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), + error.what().c_str()); + } + return {}; +} + +void AAppIconLabel::updateAppIconName(const std::string& app_identifier) { + if (!iconEnabled()) { + return; + } + + const auto icon_name = getIconName(app_identifier); + if (icon_name.has_value()) { + app_icon_name_ = icon_name.value(); + } else { + app_icon_name_ = ""; + } + update_app_icon_ = true; +} + +void AAppIconLabel::updateAppIcon() { + if (update_app_icon_) { + update_app_icon_ = false; + if (app_icon_name_.empty()) { + image_.set_visible(false); + } else { + image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); + image_.set_visible(true); + } + } +} + +auto AAppIconLabel::update() -> void { + updateAppIcon(); + AIconLabel::update(); +} + +} // namespace waybar diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 68c379bf..cb7bf756 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -6,8 +6,6 @@ #include #include -#include -#include #include #include #include @@ -20,16 +18,10 @@ namespace waybar::modules::hyprland { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : AIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { + : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { modulesReady = true; separate_outputs = config["separate-outputs"].asBool(); - // Icon size - if (config["icon-size"].isUInt()) { - app_icon_size_ = config["icon-size"].asUInt(); - } - image_.set_pixel_size(app_icon_size_); - if (!gIPC.get()) { gIPC = std::make_unique(); } @@ -51,98 +43,6 @@ Window::~Window() { std::lock_guard lg(mutex_); } -std::optional getDesktopFilePath(const std::string& app_class) { - 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_class + ".desktop"; - if (std::filesystem::exists(desktop_file_path)) { - return desktop_file_path; - } - } - return {}; -} - -std::optional getIconName(const std::string& app_class) { - const auto desktop_file_path = getDesktopFilePath(app_class); - if (!desktop_file_path.has_value()) { - // Try some heuristics to find a matching icon - - if (DefaultGtkIconThemeWrapper::has_icon(app_class)) { - return app_class; - } - - const auto app_identifier_desktop = app_class + "-desktop"; - if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) { - 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_class.find_first_of(' '); - if (first_space != std::string::npos) { - const auto first_word = to_lower(app_class.substr(0, first_space)); - if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { - return first_word; - } - } - - const auto first_dash = app_class.find_first_of('-'); - if (first_dash != std::string::npos) { - const auto first_word = to_lower(app_class.substr(0, first_dash)); - if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { - return first_word; - } - } - - return {}; - } - - try { - Glib::KeyFile desktop_file; - desktop_file.load_from_file(desktop_file_path.value()); - return desktop_file.get_string("Desktop Entry", "Icon"); - } catch (Glib::FileError& error) { - spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), - error.what().c_str()); - } catch (Glib::KeyFileError& error) { - spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), - error.what().c_str()); - } - return {}; -} - -void Window::updateAppIconName() { - if (!iconEnabled()) { - return; - } - - const auto icon_name = getIconName(window_data_.class_name); - if (icon_name.has_value()) { - app_icon_name_ = icon_name.value(); - } else { - app_icon_name_ = ""; - } - update_app_icon_ = true; -} - -void Window::updateAppIcon() { - if (update_app_icon_) { - update_app_icon_ = false; - if (app_icon_name_.empty()) { - image_.set_visible(false); - } else { - image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); - image_.set_visible(true); - } - } -} - auto Window::update() -> void { // fix ampersands std::lock_guard lg(mutex_); @@ -190,9 +90,7 @@ auto Window::update() -> void { } last_solo_class_ = solo_class_; - updateAppIcon(); - - AIconLabel::update(); + AAppIconLabel::update(); } auto Window::getActiveWorkspace() -> Workspace { @@ -254,7 +152,7 @@ void Window::queryActiveWorkspace() { } window_data_ = WindowData::parse(*active_window); - updateAppIconName(); + updateAppIconName(window_data_.class_name); std::vector workspace_windows; std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows), [&](Json::Value window) { From 31683d9e2a96bba4bfe96af522a162df574c2646 Mon Sep 17 00:00:00 2001 From: MisterPine Date: Mon, 10 Jul 2023 22:55:46 +0200 Subject: [PATCH 024/842] Implemented AAppIconLabel for sway/window --- include/modules/sway/window.hpp | 9 +-- src/modules/sway/window.cpp | 111 +------------------------------- 2 files changed, 5 insertions(+), 115 deletions(-) diff --git a/include/modules/sway/window.hpp b/include/modules/sway/window.hpp index 22f5a59c..427c2e81 100644 --- a/include/modules/sway/window.hpp +++ b/include/modules/sway/window.hpp @@ -4,7 +4,7 @@ #include -#include "AIconLabel.hpp" +#include "AAppIconLabel.hpp" #include "bar.hpp" #include "client.hpp" #include "modules/sway/ipc/client.hpp" @@ -12,7 +12,7 @@ namespace waybar::modules::sway { -class Window : public AIconLabel, public sigc::trackable { +class Window : public AAppIconLabel, public sigc::trackable { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); virtual ~Window() = default; @@ -25,8 +25,6 @@ class Window : public AIconLabel, public sigc::trackable { std::tuple getFocusedNode(const Json::Value& nodes, std::string& output); void getTree(); - void updateAppIconName(); - void updateAppIcon(); const Bar& bar_; std::string window_; @@ -37,9 +35,6 @@ class Window : public AIconLabel, public sigc::trackable { std::string old_app_id_; std::size_t app_nb_; std::string shell_; - unsigned app_icon_size_{24}; - bool update_app_icon_{true}; - std::string app_icon_name_; int floating_count_; util::JsonParser parser_; std::mutex mutex_; diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 50aea602..ef076285 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -17,13 +17,7 @@ namespace waybar::modules::sway { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) { - // Icon size - if (config_["icon-size"].isUInt()) { - app_icon_size_ = config["icon-size"].asUInt(); - } - image_.set_pixel_size(app_icon_size_); - + : AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) { ipc_.subscribe(R"(["window","workspace"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd)); @@ -49,7 +43,7 @@ void Window::onCmd(const struct Ipc::ipc_response& res) { auto output = payload["output"].isString() ? payload["output"].asString() : ""; std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) = getFocusedNode(payload["nodes"], output); - updateAppIconName(); + updateAppIconName(app_id_); dp.emit(); } catch (const std::exception& e) { spdlog::error("Window: {}", e.what()); @@ -57,105 +51,6 @@ void Window::onCmd(const struct Ipc::ipc_response& res) { } } -std::optional getDesktopFilePath(const std::string& app_id, - const std::string& app_class) { - 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_id + ".desktop"; - if (std::filesystem::exists(desktop_file_path)) { - return desktop_file_path; - } - if (!app_class.empty()) { - desktop_file_path = data_app_dir + app_class + ".desktop"; - if (std::filesystem::exists(desktop_file_path)) { - return desktop_file_path; - } - } - } - return {}; -} - -std::optional getIconName(const std::string& app_id, const std::string& app_class) { - const auto desktop_file_path = getDesktopFilePath(app_id, app_class); - if (!desktop_file_path.has_value()) { - // Try some heuristics to find a matching icon - - if (DefaultGtkIconThemeWrapper::has_icon(app_id)) { - return app_id; - } - - const auto app_id_desktop = app_id + "-desktop"; - if (DefaultGtkIconThemeWrapper::has_icon(app_id_desktop)) { - return app_id_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_id.find_first_of(' '); - if (first_space != std::string::npos) { - const auto first_word = to_lower(app_id.substr(0, first_space)); - if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { - return first_word; - } - } - - const auto first_dash = app_id.find_first_of('-'); - if (first_dash != std::string::npos) { - const auto first_word = to_lower(app_id.substr(0, first_dash)); - if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { - return first_word; - } - } - - return {}; - } - - try { - Glib::KeyFile desktop_file; - desktop_file.load_from_file(desktop_file_path.value()); - return desktop_file.get_string("Desktop Entry", "Icon"); - } catch (Glib::FileError& error) { - spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), - error.what().c_str()); - } catch (Glib::KeyFileError& error) { - spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), - error.what().c_str()); - } - return {}; -} - -void Window::updateAppIconName() { - if (!iconEnabled()) { - return; - } - - const auto icon_name = getIconName(app_id_, app_class_); - if (icon_name.has_value()) { - app_icon_name_ = icon_name.value(); - } else { - app_icon_name_ = ""; - } - update_app_icon_ = true; -} - -void Window::updateAppIcon() { - if (update_app_icon_) { - update_app_icon_ = false; - if (app_icon_name_.empty()) { - image_.set_visible(false); - } else { - image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); - image_.set_visible(true); - } - } -} - auto Window::update() -> void { spdlog::trace("workspace layout {}, tiled count {}, floating count {}", layout_, app_nb_, floating_count_); @@ -210,7 +105,7 @@ auto Window::update() -> void { updateAppIcon(); // Call parent update - AIconLabel::update(); + AAppIconLabel::update(); } void Window::setClass(std::string classname, bool enable) { From a8a1a4985f2a36e66f62fb52d2b807b091cc94dc Mon Sep 17 00:00:00 2001 From: MisterPine Date: Mon, 10 Jul 2023 23:48:18 +0200 Subject: [PATCH 025/842] Add removed secondary identifier (class for xwayland under sway) --- include/AAppIconLabel.hpp | 3 ++- src/AAppIconLabel.cpp | 19 ++++++++++++++----- src/modules/hyprland/window.cpp | 2 +- src/modules/sway/window.cpp | 2 +- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/include/AAppIconLabel.hpp b/include/AAppIconLabel.hpp index a2ae4f59..d09ab14a 100644 --- a/include/AAppIconLabel.hpp +++ b/include/AAppIconLabel.hpp @@ -16,7 +16,8 @@ class AAppIconLabel : public AIconLabel { auto update() -> void override; protected: - void updateAppIconName(const std::string &app_identifier); + void updateAppIconName(const std::string &app_identifier, + const std::string &alternative_app_identifier); void updateAppIcon(); unsigned app_icon_size_{24}; bool update_app_icon_{true}; diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index 6e3d1641..a238143b 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -24,7 +24,8 @@ AAppIconLabel::AAppIconLabel(const Json::Value& config, const std::string& name, image_.set_pixel_size(app_icon_size_); } -std::optional getDesktopFilePath(const std::string& app_identifier) { +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/"; @@ -32,12 +33,19 @@ std::optional getDesktopFilePath(const std::string& app_identifier) if (std::filesystem::exists(desktop_file_path)) { 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)) { + return desktop_file_path; + } + } } return {}; } -std::optional getIconName(const std::string& app_identifier) { - const auto desktop_file_path = getDesktopFilePath(app_identifier); +std::optional getIconName(const std::string& app_identifier, + const std::string& alternative_app_identifier) { + const auto desktop_file_path = getDesktopFilePath(app_identifier, alternative_app_identifier); if (!desktop_file_path.has_value()) { // Try some heuristics to find a matching icon @@ -90,12 +98,13 @@ std::optional getIconName(const std::string& app_identifier) { return {}; } -void AAppIconLabel::updateAppIconName(const std::string& app_identifier) { +void AAppIconLabel::updateAppIconName(const std::string& app_identifier, + const std::string& alternative_app_identifier) { if (!iconEnabled()) { return; } - const auto icon_name = getIconName(app_identifier); + const auto icon_name = getIconName(app_identifier, alternative_app_identifier); if (icon_name.has_value()) { app_icon_name_ = icon_name.value(); } else { diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index cb7bf756..f3b2a767 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -152,7 +152,7 @@ void Window::queryActiveWorkspace() { } window_data_ = WindowData::parse(*active_window); - updateAppIconName(window_data_.class_name); + 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), [&](Json::Value window) { diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index ef076285..06acdcaa 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -43,7 +43,7 @@ void Window::onCmd(const struct Ipc::ipc_response& res) { auto output = payload["output"].isString() ? payload["output"].asString() : ""; std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) = getFocusedNode(payload["nodes"], output); - updateAppIconName(app_id_); + updateAppIconName(app_id_, app_class_); dp.emit(); } catch (const std::exception& e) { spdlog::error("Window: {}", e.what()); From 7aae93e7ed41bf20565a1421dbfa4f6a9def53b1 Mon Sep 17 00:00:00 2001 From: Standreas Date: Wed, 12 Jul 2023 16:31:58 +0200 Subject: [PATCH 026/842] Fix wrong name for {name} --- man/waybar-wlr-taskbar.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 5626eaec..2fc86938 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -89,7 +89,7 @@ Addressed by *wlr/taskbar* *{icon}*: The icon of the application. -*{title}*: 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 fils found, otherwise same as {app_id} *{title}*: The title of the application. From 14c65505933d251c1da28b0e0ac87a8586b143c2 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Wed, 12 Jul 2023 17:56:12 +0300 Subject: [PATCH 027/842] hyprland/window: Fix overlap with .hidden class from default style --- man/waybar-hyprland-window.5.scd | 4 ++-- src/modules/hyprland/window.cpp | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd index 2a3f62f1..b8b25ced 100644 --- a/man/waybar-hyprland-window.5.scd +++ b/man/waybar-hyprland-window.5.scd @@ -76,5 +76,5 @@ window widget: - *window#waybar.floating* When there are only floating windows in the workspace - *window#waybar.fullscreen* When there is a fullscreen window in the workspace; useful with Hyprland's *fullscreen, 1* mode -- *window#waybar.hidden* When there are hidden windows in the workspace; usually - occurs due to window swallowing +- *window#waybar.hidden-window* When there are hidden windows in the workspace; + can occur due to window swallowing, *fullscreen, 1* mode, or grouped windows diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index f3b2a767..7519e80e 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -11,8 +11,6 @@ #include #include "modules/hyprland/backend.hpp" -#include "util/gtk_icon.hpp" -#include "util/json.hpp" #include "util/rewrite_string.hpp" namespace waybar::modules::hyprland { @@ -74,7 +72,7 @@ auto Window::update() -> void { setClass("empty", workspace_.windows == 0); setClass("solo", solo_); setClass("floating", all_floating_); - setClass("hidden", hidden_); + setClass("hidden-window", hidden_); setClass("fullscreen", fullscreen_); if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) { From daca57129fdc6587b769646d86779914f91ea012 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Wed, 12 Jul 2023 19:01:45 +0300 Subject: [PATCH 028/842] hyprland/window: rename .hidden to .swallowing (and fix grouped windows) --- include/modules/hyprland/window.hpp | 4 +++- man/waybar-hyprland-window.5.scd | 3 +-- src/modules/hyprland/window.cpp | 24 ++++++++++++++++-------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index 5316c0e4..fd68b049 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -31,6 +31,8 @@ class Window : public waybar::AAppIconLabel, public EventHandler { std::string initial_class_name; std::string title; std::string initial_title; + bool fullscreen; + bool grouped; static auto parse(const Json::Value&) -> WindowData; }; @@ -51,7 +53,7 @@ class Window : public waybar::AAppIconLabel, public EventHandler { std::string last_solo_class_; bool solo_; bool all_floating_; - bool hidden_; + bool swallowing_; bool fullscreen_; }; diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd index b8b25ced..64e48e4e 100644 --- a/man/waybar-hyprland-window.5.scd +++ b/man/waybar-hyprland-window.5.scd @@ -76,5 +76,4 @@ window widget: - *window#waybar.floating* When there are only floating windows in the workspace - *window#waybar.fullscreen* When there is a fullscreen window in the workspace; useful with Hyprland's *fullscreen, 1* mode -- *window#waybar.hidden-window* When there are hidden windows in the workspace; - can occur due to window swallowing, *fullscreen, 1* mode, or grouped windows +- *window#waybar.swallowing* When there is a swallowed window in the workspace diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 7519e80e..d01ac9dd 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -72,7 +72,7 @@ auto Window::update() -> void { setClass("empty", workspace_.windows == 0); setClass("solo", solo_); setClass("floating", all_floating_); - setClass("hidden-window", hidden_); + setClass("swallowing", swallowing_); setClass("fullscreen", fullscreen_); if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) { @@ -125,9 +125,10 @@ auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { } auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData { - return WindowData{value["floating"].asBool(), value["monitor"].asInt(), - value["class"].asString(), value["initialClass"].asString(), - value["title"].asString(), value["initialTitle"].asString()}; + return WindowData{value["floating"].asBool(), value["monitor"].asInt(), + value["class"].asString(), value["initialClass"].asString(), + value["title"].asString(), value["initialTitle"].asString(), + value["fullscreen"].asBool(), !value["grouped"].empty()}; } void Window::queryActiveWorkspace() { @@ -156,8 +157,8 @@ void Window::queryActiveWorkspace() { [&](Json::Value window) { return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); }); - hidden_ = std::any_of(workspace_windows.begin(), workspace_windows.end(), - [&](Json::Value window) { return window["hidden"].asBool(); }); + swallowing_ = std::any_of(workspace_windows.begin(), workspace_windows.end(), + [&](Json::Value window) { return !window["swallowing"].isNull(); }); std::vector visible_windows; std::copy_if(workspace_windows.begin(), workspace_windows.end(), std::back_inserter(visible_windows), @@ -166,12 +167,19 @@ void Window::queryActiveWorkspace() { [&](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_ = (*active_window)["fullscreen"].asBool(); + fullscreen_ = window_data_.fullscreen; + // Fullscreen windows look like they are solo if (fullscreen_) { solo_ = true; } + // Grouped windows have a tab bar and therefore don't look fullscreen or solo + if (window_data_.grouped) { + fullscreen_ = false; + solo_ = false; + } + if (solo_) { solo_class_ = window_data_.class_name; } else { @@ -180,7 +188,7 @@ void Window::queryActiveWorkspace() { } else { window_data_ = WindowData{}; all_floating_ = false; - hidden_ = false; + swallowing_ = false; fullscreen_ = false; solo_ = false; solo_class_ = ""; From 52983c7188830931826c18f5df05b4b0aeb2f59e Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sat, 15 Jul 2023 17:43:22 +0000 Subject: [PATCH 029/842] workspaces.cpp --- src/modules/hyprland/workspaces.cpp | 145 ++++++++++++++++++++-------- 1 file changed, 107 insertions(+), 38 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index e169f916..20881fe9 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -28,6 +28,16 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value icons_map_.emplace("", ""); } + auto config_all_outputs = config_["all-outputs"]; + if (config_all_outputs.isBool()) { + all_outputs_ = config_all_outputs.asBool(); + } + + auto config_show_special = config_["show-special"]; + if (config_show_special.isBool()) { + show_special_ = config_show_special.asBool(); + } + box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -43,10 +53,12 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value gIPC->registerForIPC("workspace", this); gIPC->registerForIPC("createworkspace", this); gIPC->registerForIPC("destroyworkspace", this); + gIPC->registerForIPC("focusedmon", this); + gIPC->registerForIPC("moveworkspace", this); } auto Workspaces::update() -> void { - for (int &workspace_to_remove : workspaces_to_remove_) { + for (std::string workspace_to_remove : workspaces_to_remove_) { remove_workspace(workspace_to_remove); } @@ -56,19 +68,14 @@ auto Workspaces::update() -> void { create_workspace(workspace_to_create); } - workspaces_to_create_.clear(); - - for (std::unique_ptr &workspace : workspaces_) { - workspace->set_active(workspace->id() == active_workspace_id); - + for (auto &workspace : workspaces_) { + workspace->set_active(workspace->name() == active_workspace_name); std::string &workspace_icon = icons_map_[""]; if (with_icon_) { workspace_icon = workspace->select_icon(icons_map_); } - workspace->update(format_, workspace_icon); } - AModule::update(); } @@ -76,35 +83,58 @@ void Workspaces::onEvent(const std::string &ev) { std::lock_guard lock(mutex_); std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>')); std::string payload = ev.substr(eventName.size() + 2); + if (eventName == "workspace") { - std::from_chars(payload.data(), payload.data() + payload.size(), active_workspace_id); + active_workspace_name = payload; + } else if (eventName == "destroyworkspace") { - int deleted_workspace_id; - std::from_chars(payload.data(), payload.data() + payload.size(), deleted_workspace_id); - workspaces_to_remove_.push_back(deleted_workspace_id); + workspaces_to_remove_.push_back(payload); + } else if (eventName == "createworkspace") { - int new_workspace_id; - std::from_chars(payload.data(), payload.data() + payload.size(), new_workspace_id); - workspaces_to_create_.push_back(new_workspace_id); + const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); + for (auto &workspace_json : workspaces_json) { + if (workspace_json["name"].asString() == payload && + (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && + (workspace_json["name"].asString().find("special:") != 0 || show_special())) + create_workspace(workspace_json); + } + + } else if (eventName == "focusedmon") { + active_workspace_name = payload.substr(payload.find(",") + 1); + + } else if (eventName == "moveworkspace") { + std::string workspace = payload.substr(0, payload.find(",")); + std::string new_monitor = payload.substr(payload.find(",") + 1); + if (all_outputs()) { + if (bar_.output->name == new_monitor) { // TODO: implement this better + const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); + for (auto &workspace_json : workspaces_json) { + if (workspace_json["name"].asString() == workspace && + bar_.output->name == workspace_json["monitor"].asString()) + create_workspace(workspace_json); + } + } else { + workspaces_to_remove_.push_back(workspace); + } + } } dp.emit(); } -void Workspaces::create_workspace(int id) { - workspaces_.push_back(std::make_unique(id)); +void Workspaces::create_workspace(const Json::Value &value) { + workspaces_.push_back(std::make_unique(value)); Gtk::Button &new_workspace_button = workspaces_.back()->button(); box_.pack_start(new_workspace_button, false, false); sort_workspaces(); new_workspace_button.show_all(); } -void Workspaces::remove_workspace(int id) { +void Workspaces::remove_workspace(std::string name) { auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(), - [&](std::unique_ptr &x) { return x->id() == id; }); + [&](std::unique_ptr &x) { return x->name() == name; }); if (workspace == workspaces_.end()) { - spdlog::warn("Can't find workspace with id {}", id); return; } @@ -113,16 +143,13 @@ void Workspaces::remove_workspace(int id) { } void Workspaces::init() { - const auto activeWorkspace = WorkspaceDto::parse(gIPC->getSocket1JsonReply("activeworkspace")); - active_workspace_id = activeWorkspace.id; + active_workspace_name = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (const Json::Value &workspace_json : workspaces_json) { - workspaces_.push_back( - std::make_unique(Workspace(WorkspaceDto::parse(workspace_json)))); - } - - for (auto &workspace : workspaces_) { - box_.pack_start(workspace->button(), false, false); + if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && + (workspace_json["name"].asString().find("special") != 0 || show_special())) + create_workspace(workspace_json); } sort_workspaces(); @@ -136,13 +163,27 @@ Workspaces::~Workspaces() { std::lock_guard lg(mutex_); } -WorkspaceDto WorkspaceDto::parse(const Json::Value &value) { - return WorkspaceDto{value["id"].asInt()}; -} +Workspace::Workspace(const Json::Value &value) { + id_ = value["id"].asInt(); + name_ = value["name"].asString(); + monitor_ = value["monitor"].asString(); // TODO:allow using monitor desc + windows_ = value["id"].asInt(); + active_ = 1; + is_special_ = 0; -Workspace::Workspace(WorkspaceDto dto) : Workspace(dto.id){}; + if (name_.find("name:") == 0) { + name_ = name_.substr(5); + } else if (name_.find("special") == 0) { + name_ = id_ == -99 ? name_ : name_.substr(13); + is_special_ = 1; + } + // spdlog::info("\nNew workspace:\n\tid:({})\n\tname:({})\n\tmonitor:({})\n\tspecial:({})\n", id_, + // name_, monitor_, is_special_ ? "yes" : "no"); //make less noisy + + button_.add_events(Gdk::BUTTON_PRESS_MASK); + button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked), + false); -Workspace::Workspace(int id) : id_(id) { button_.set_relief(Gtk::RELIEF_NONE); content_.set_center_widget(label_); button_.add(content_); @@ -161,14 +202,29 @@ void Workspace::update(const std::string &format, const std::string &icon) { Glib::RefPtr style_context = button_.get_style_context(); add_or_remove_class(style_context, active(), "active"); - label_.set_markup( - fmt::format(fmt::runtime(format), fmt::arg("id", id()), 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() { std::sort(workspaces_.begin(), workspaces_.end(), - [](std::unique_ptr &lhs, std::unique_ptr &rhs) { - return lhs->id() < rhs->id(); + [](std::unique_ptr &a, std::unique_ptr &b) { + // normal -> named -> special -> named special + if (a->id() > 0 && b->id() > 0) { + return a->id() < b->id(); + } + if (a->id() < 0 && b->id() < 0) { + if ((a->is_special()) ^ (a->is_special())) { + return a->id() > b->id(); + } else { + return a->id() < b->id(); + } + } + if ((a->id() > 0) ^ (b->id() > 0)) { + return a->id() > b->id(); + } + spdlog::error("huh!!?"); + return false; }); for (size_t i = 0; i < workspaces_.size(); ++i) { @@ -193,7 +249,20 @@ std::string &Workspace::select_icon(std::map &icons_ma if (default_icon_it != icons_map.end()) { return default_icon_it->second; } - return icons_map[""]; } + +auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { + if (id() > 0) { // normal + system(("hyprctl dispatch workspace " + std::to_string(id()) + " &> /dev/null").c_str()); + } else if (!is_special()) { // named normal + system(("hyprctl dispatch workspace name:" + name() + " &> /dev/null").c_str()); + } else if (id() != -99) { // named special + system(("hyprctl dispatch togglespecialworkspace name:" + name() + " &> /dev/null").c_str()); + } else { // special + system("hyprctl dispatch togglespecialworkspace special &> /dev/null"); + } + return 1; +} + } // namespace waybar::modules::hyprland From 495b63d7dcfd2c67b66b67ed24d77bc54305de6c Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sat, 15 Jul 2023 17:44:57 +0000 Subject: [PATCH 030/842] workspaces.hpp --- include/modules/hyprland/workspaces.hpp | 41 ++++++++++++++++--------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 500bbe36..12590fcd 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -9,27 +9,30 @@ namespace waybar::modules::hyprland { -struct WorkspaceDto { - int id; - - static WorkspaceDto parse(const Json::Value& value); -}; - class Workspace { public: - Workspace(int id); - Workspace(WorkspaceDto dto); - int id() const { return id_; }; - int active() const { return active_; }; + Workspace(const Json::Value& value); std::string& select_icon(std::map& icons_map); - void set_active(bool value = true) { active_ = value; }; Gtk::Button& button() { return button_; }; + int id() const { return id_; }; + std::string name() const { return name_; }; + std::string monitor() const { return monitor_; }; + int active() const { return active_; }; + bool is_special() const { return is_special_; }; + + auto handle_clicked(GdkEventButton* bt) -> bool; + void set_active(bool value) { active_ = value; }; + void update(const std::string& format, const std::string& icon); private: int id_; + std::string name_; + std::string monitor_; + int windows_; bool active_; + bool is_special_; Gtk::Button button_; Gtk::Box content_; @@ -43,19 +46,27 @@ 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 get_bar_output() const -> std::string { return bar_.output->name; } + private: void onEvent(const std::string&) override; void sort_workspaces(); - void create_workspace(int id); - void remove_workspace(int id); + void create_workspace(const Json::Value& value); + void remove_workspace(std::string name); + + bool all_outputs_ = false; + bool show_special_ = false; std::string format_; std::map icons_map_; bool with_icon_; - int active_workspace_id; + std::string active_workspace_name; std::vector> workspaces_; std::vector workspaces_to_create_; - std::vector workspaces_to_remove_; + std::vector workspaces_to_remove_; std::mutex mutex_; const Bar& bar_; Gtk::Box box_; From 75e21c485340ecd5f396b26aed06c7efe82ed30e Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sat, 15 Jul 2023 18:36:55 +0000 Subject: [PATCH 031/842] Update waybar-hyprland-workspaces.5.scd --- man/waybar-hyprland-workspaces.5.scd | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 0678fb22..88ca775b 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -25,6 +25,8 @@ Addressed by *hyprland/workspaces* *{id}*: id of workspace assigned by compositor +*{name}*: workspace name assigned by compositor + *{icon}*: Icon, as defined in *format-icons*. # ICONS @@ -48,7 +50,9 @@ Additional to workspace name matching, the following *format-icons* can be set. "active": "", "default": "" }, - "sort-by-number": true + "sort-by-number": true, + "all-outputs": false, + "show-special": false } ``` From f8a9a970b2f7adc66625547816b6d5695c036f05 Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sat, 15 Jul 2023 18:43:46 +0000 Subject: [PATCH 032/842] removes "sort-by-number" --- man/waybar-hyprland-workspaces.5.scd | 1 - 1 file changed, 1 deletion(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 88ca775b..95a5911f 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -50,7 +50,6 @@ Additional to workspace name matching, the following *format-icons* can be set. "active": "", "default": "" }, - "sort-by-number": true, "all-outputs": false, "show-special": false } From f3df15650a6fc60710c54ef94c3d284f7132069e Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 15 Jul 2023 23:48:12 +0200 Subject: [PATCH 033/842] use IPC for click events, clang-tidy fixes --- src/modules/hyprland/workspaces.cpp | 37 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 20881fe9..34eeffc6 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -163,13 +163,13 @@ Workspaces::~Workspaces() { std::lock_guard lg(mutex_); } -Workspace::Workspace(const Json::Value &value) { - id_ = value["id"].asInt(); - name_ = value["name"].asString(); - monitor_ = value["monitor"].asString(); // TODO:allow using monitor desc - windows_ = value["id"].asInt(); - active_ = 1; - is_special_ = 0; +Workspace::Workspace(const Json::Value &value) + : id_(value["id"].asInt()), + name_(value["name"].asString()), + monitor_(value["monitor"].asString()), // TODO:allow using monitor desc + windows_(value["id"].asInt()) { + active_ = true; + is_special_ = false; if (name_.find("name:") == 0) { name_ = name_.substr(5); @@ -253,16 +253,21 @@ std::string &Workspace::select_icon(std::map &icons_ma } auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { - if (id() > 0) { // normal - system(("hyprctl dispatch workspace " + std::to_string(id()) + " &> /dev/null").c_str()); - } else if (!is_special()) { // named normal - system(("hyprctl dispatch workspace name:" + name() + " &> /dev/null").c_str()); - } else if (id() != -99) { // named special - system(("hyprctl dispatch togglespecialworkspace name:" + name() + " &> /dev/null").c_str()); - } else { // special - system("hyprctl dispatch togglespecialworkspace special &> /dev/null"); + try { + if (id() > 0) { // normal + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } else if (!is_special()) { // named normal + gIPC->getSocket1Reply("dispatch workspace name" + name()); + } else if (id() != -99) { // named special + gIPC->getSocket1Reply("dispatch togglespecialworkspace name" + name()); + } else { // special + gIPC->getSocket1Reply("dispatch togglespecialworkspace special"); + } + return true; + } catch (const std::exception &e) { + spdlog::error("Failed to dispatch workspace: {}", e.what()); } - return 1; + return false; } } // namespace waybar::modules::hyprland From 7200b16520d85977566db92d3bfec8200ac73e46 Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sun, 16 Jul 2023 01:02:39 +0000 Subject: [PATCH 034/842] documentation --- man/waybar-hyprland-workspaces.5.scd | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 95a5911f..6e67e188 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -21,6 +21,16 @@ Addressed by *hyprland/workspaces* typeof: array ++ Based on the workspace id and state, the corresponding icon gets selected. See *icons*. +*show-special*: ++ + typeof: bool ++ + default: false ++ + 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. + # FORMAT REPLACEMENTS *{id}*: id of workspace assigned by compositor From ca0122c3cb8578443631b9853812cf7c5966d54c Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sun, 16 Jul 2023 01:18:41 +0000 Subject: [PATCH 035/842] workspaces.cpp --- src/modules/hyprland/workspaces.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 34eeffc6..b2a1d092 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -102,20 +102,20 @@ void Workspaces::onEvent(const std::string &ev) { } else if (eventName == "focusedmon") { active_workspace_name = payload.substr(payload.find(",") + 1); - } else if (eventName == "moveworkspace") { + } else if (eventName == "moveworkspace" && !all_outputs()) { std::string workspace = payload.substr(0, payload.find(",")); - std::string new_monitor = payload.substr(payload.find(",") + 1); - if (all_outputs()) { - if (bar_.output->name == new_monitor) { // TODO: implement this better - const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); - for (auto &workspace_json : workspaces_json) { - if (workspace_json["name"].asString() == workspace && - bar_.output->name == workspace_json["monitor"].asString()) - create_workspace(workspace_json); + 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 (auto &workspace_json : workspaces_json) { + if (workspace_json["name"].asString() == workspace && + bar_.output->name == workspace_json["monitor"].asString()) { + create_workspace(workspace_json); + break; } - } else { - workspaces_to_remove_.push_back(workspace); } + } else { + workspaces_to_remove_.push_back(workspace); } } @@ -166,7 +166,7 @@ Workspaces::~Workspaces() { Workspace::Workspace(const Json::Value &value) : id_(value["id"].asInt()), name_(value["name"].asString()), - monitor_(value["monitor"].asString()), // TODO:allow using monitor desc + output_(value["monitor"].asString()), // TODO:allow using monitor desc windows_(value["id"].asInt()) { active_ = true; is_special_ = false; @@ -177,8 +177,6 @@ Workspace::Workspace(const Json::Value &value) name_ = id_ == -99 ? name_ : name_.substr(13); is_special_ = 1; } - // spdlog::info("\nNew workspace:\n\tid:({})\n\tname:({})\n\tmonitor:({})\n\tspecial:({})\n", id_, - // name_, monitor_, is_special_ ? "yes" : "no"); //make less noisy button_.add_events(Gdk::BUTTON_PRESS_MASK); button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked), From 4f81e55e41931e75557e96a3d60afadfc91dbcd6 Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sun, 16 Jul 2023 01:20:30 +0000 Subject: [PATCH 036/842] workspaces.hpp --- include/modules/hyprland/workspaces.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 12590fcd..a2910f51 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -17,19 +17,19 @@ class Workspace { int id() const { return id_; }; std::string name() const { return name_; }; - std::string monitor() const { return monitor_; }; + std::string output() const { return output_; }; int active() const { return active_; }; bool is_special() const { return is_special_; }; auto handle_clicked(GdkEventButton* bt) -> bool; - void set_active(bool value) { active_ = value; }; + void set_active(bool value = true) { active_ = value; }; void update(const std::string& format, const std::string& icon); private: int id_; std::string name_; - std::string monitor_; + std::string output_; int windows_; bool active_; bool is_special_; From 5f0fa71f3204bd2ff1b905c36e05555e11a12ccd Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sun, 16 Jul 2023 01:43:25 +0000 Subject: [PATCH 037/842] moves createWorkspace to update() --- src/modules/hyprland/workspaces.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index b2a1d092..392f457e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -64,10 +64,12 @@ auto Workspaces::update() -> void { workspaces_to_remove_.clear(); - for (int &workspace_to_create : workspaces_to_create_) { + for (Json::Value &workspace_to_create : workspaces_to_create_) { create_workspace(workspace_to_create); } + workspaces_to_create_.clear(); + for (auto &workspace : workspaces_) { workspace->set_active(workspace->name() == active_workspace_name); std::string &workspace_icon = icons_map_[""]; @@ -92,7 +94,7 @@ void Workspaces::onEvent(const std::string &ev) { } else if (eventName == "createworkspace") { const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); - for (auto &workspace_json : workspaces_json) { + for (Json::Value workspace_json : workspaces_json) { if (workspace_json["name"].asString() == payload && (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && (workspace_json["name"].asString().find("special:") != 0 || show_special())) @@ -107,10 +109,10 @@ void Workspaces::onEvent(const std::string &ev) { 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 (auto &workspace_json : workspaces_json) { + for (Json::Value workspace_json : workspaces_json) { if (workspace_json["name"].asString() == workspace && bar_.output->name == workspace_json["monitor"].asString()) { - create_workspace(workspace_json); + workspaces_to_create_.push_back(workspace_json); break; } } @@ -122,7 +124,7 @@ void Workspaces::onEvent(const std::string &ev) { dp.emit(); } -void Workspaces::create_workspace(const Json::Value &value) { +void Workspaces::create_workspace(Json::Value &value) { workspaces_.push_back(std::make_unique(value)); Gtk::Button &new_workspace_button = workspaces_.back()->button(); box_.pack_start(new_workspace_button, false, false); @@ -146,7 +148,7 @@ void Workspaces::init() { active_workspace_name = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); - for (const Json::Value &workspace_json : workspaces_json) { + for (Json::Value workspace_json : workspaces_json) { if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && (workspace_json["name"].asString().find("special") != 0 || show_special())) create_workspace(workspace_json); From 6d24b22b21ccf779ace5032629fa1b2bd450dc74 Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sun, 16 Jul 2023 01:43:54 +0000 Subject: [PATCH 038/842] moves createWorkspace to update() --- include/modules/hyprland/workspaces.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index a2910f51..47df01f8 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -54,7 +54,7 @@ class Workspaces : public AModule, public EventHandler { private: void onEvent(const std::string&) override; void sort_workspaces(); - void create_workspace(const Json::Value& value); + void create_workspace(Json::Value& value); void remove_workspace(std::string name); bool all_outputs_ = false; @@ -65,7 +65,7 @@ class Workspaces : public AModule, public EventHandler { bool with_icon_; std::string active_workspace_name; std::vector> workspaces_; - std::vector workspaces_to_create_; + std::vector workspaces_to_create_; std::vector workspaces_to_remove_; std::mutex mutex_; const Bar& bar_; From 2bfc0e1da6f015b6c359c468385eb830cfdc6130 Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sun, 16 Jul 2023 01:49:46 +0000 Subject: [PATCH 039/842] moves createWorkspace to update() --- 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 392f457e..0002900a 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -97,8 +97,10 @@ 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()) && - (workspace_json["name"].asString().find("special:") != 0 || show_special())) - create_workspace(workspace_json); + (workspace_json["name"].asString().find("special:") != 0 || show_special())) { + workspaces_to_create_.push_back(workspace_json); + break; + } } } else if (eventName == "focusedmon") { From facb53e81f5f097e6c104f8efd092d461c458694 Mon Sep 17 00:00:00 2001 From: may Date: Sun, 16 Jul 2023 04:14:43 +0200 Subject: [PATCH 040/842] backlight: do not convert percent to string in fmt --- src/modules/backlight.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 58d14dde..a595073e 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -191,7 +191,7 @@ auto waybar::modules::Backlight::update() -> void { const uint8_t percent = best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max()); std::string desc = - fmt::format(fmt::runtime(format_), fmt::arg("percent", std::to_string(percent)), + fmt::format(fmt::runtime(format_), fmt::arg("percent", percent), fmt::arg("icon", getIcon(percent))); label_.set_markup(desc); getState(percent); @@ -202,7 +202,7 @@ auto waybar::modules::Backlight::update() -> void { } if (!tooltip_format.empty()) { label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), - fmt::arg("percent", std::to_string(percent)), + fmt::arg("percent", percent), fmt::arg("icon", getIcon(percent)))); } else { label_.set_tooltip_text(desc); From 2be0e966e181aa70df454baccd342fb1c096be6d Mon Sep 17 00:00:00 2001 From: Calvin Chu Date: Sun, 16 Jul 2023 16:40:54 +1000 Subject: [PATCH 041/842] hyprland/window: remove .empty css class for #window --- src/modules/hyprland/window.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index d01ac9dd..81c864d1 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -48,14 +48,7 @@ auto Window::update() -> void { std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title); std::string window_address = workspace_.last_window; - if (window_name != window_data_.title) { - if (window_name.empty()) { - label_.get_style_context()->add_class("empty"); - } else { - label_.get_style_context()->remove_class("empty"); - } - window_data_.title = window_name; - } + window_data_.title = window_name; if (!format_.empty()) { label_.show(); From 8687ed20681da0cee615330d803de5bdf5cec11f Mon Sep 17 00:00:00 2001 From: Calvin Chu Date: Sun, 16 Jul 2023 16:41:50 +1000 Subject: [PATCH 042/842] Update man for hyprland/window to replace #window.empty with window#waybar.empty #window --- man/waybar-hyprland-window.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd index 64e48e4e..5e7b4d5e 100644 --- a/man/waybar-hyprland-window.5.scd +++ b/man/waybar-hyprland-window.5.scd @@ -63,7 +63,7 @@ Invalid expressions (e.g., mismatched parentheses) are skipped. # STYLE - *#window* -- *#window.empty* When no windows are in the workspace +- *window#waybar.empty #window* When no windows are in the workspace The following classes are applied to the entire Waybar rather than just the window widget: From 0f6eff1f206276ca7a0cbaa8eb40103f8e4b5c8b Mon Sep 17 00:00:00 2001 From: Calvin Chu Date: Sun, 16 Jul 2023 21:58:15 +1000 Subject: [PATCH 043/842] hyprland: fix json parser runtime err from socket read ending early --- src/modules/hyprland/backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 79bc6375..6e586966 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -192,7 +192,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) { return ""; } response.append(buffer, sizeWritten); - } while (sizeWritten == 8192); + } while (sizeWritten > 0); close(SERVERSOCKET); return response; From c85738574cc71eccd97cf51fe101cfa53ef7f89f Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 16 Jul 2023 15:05:42 +0200 Subject: [PATCH 044/842] Use C++20 by default --- .github/workflows/linux.yml | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 1c00a5ed..d97612d5 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -13,7 +13,7 @@ jobs: - fedora - opensuse - gentoo - cpp_std: [c++17] + cpp_std: [c++20] include: - distro: fedora cpp_std: c++20 diff --git a/meson.build b/meson.build index f8b4a2d6..6e188c46 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,7 @@ project( license: 'MIT', meson_version: '>= 0.50.0', default_options : [ - 'cpp_std=c++17', + 'cpp_std=c++20', 'buildtype=release', 'default_library=static' ], From 62702a4878939cd523b99fc5a9abc9d4719f69ae Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 16 Jul 2023 15:18:18 +0200 Subject: [PATCH 045/842] fixed lint --- src/modules/backlight.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index a595073e..27871048 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -190,9 +190,8 @@ auto waybar::modules::Backlight::update() -> void { event_box_.show(); const uint8_t percent = best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max()); - std::string desc = - fmt::format(fmt::runtime(format_), fmt::arg("percent", percent), - fmt::arg("icon", getIcon(percent))); + std::string desc = fmt::format(fmt::runtime(format_), fmt::arg("percent", percent), + fmt::arg("icon", getIcon(percent))); label_.set_markup(desc); getState(percent); if (tooltipEnabled()) { From 2d0fdaeec6b62d4a245b4660a181b3b2c981494a Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Sun, 16 Jul 2023 18:22:14 +0000 Subject: [PATCH 046/842] special fix --- 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 0002900a..aad8eac9 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -263,7 +263,7 @@ auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { } else if (id() != -99) { // named special gIPC->getSocket1Reply("dispatch togglespecialworkspace name" + name()); } else { // special - gIPC->getSocket1Reply("dispatch togglespecialworkspace special"); + gIPC->getSocket1Reply("dispatch togglespecialworkspace"); } return true; } catch (const std::exception &e) { From f62b3d0e9dba3847e8029bdbc2fb71ecacc7c667 Mon Sep 17 00:00:00 2001 From: Patrick Nicolas Date: Sun, 16 Jul 2023 23:29:28 +0200 Subject: [PATCH 047/842] Ensure signal is disconnected in destructor --- include/util/sleeper_thread.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/util/sleeper_thread.hpp b/include/util/sleeper_thread.hpp index a724b1e8..861d5f1f 100644 --- a/include/util/sleeper_thread.hpp +++ b/include/util/sleeper_thread.hpp @@ -95,6 +95,7 @@ class SleeperThread { } ~SleeperThread() { + connection_.disconnect(); stop(); if (thread_.joinable()) { thread_.join(); From 2721e19ee6abe8c90cd938a74d6b84c9c66694c6 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 16 Jul 2023 14:47:14 +0200 Subject: [PATCH 048/842] small improvements --- include/modules/hyprland/workspaces.hpp | 4 ++-- src/modules/hyprland/workspaces.cpp | 32 ++++++++++++------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 47df01f8..b32dc41d 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -11,7 +11,7 @@ namespace waybar::modules::hyprland { class Workspace { public: - Workspace(const Json::Value& value); + Workspace(const Json::Value& workspace_data); std::string& select_icon(std::map& icons_map); Gtk::Button& button() { return button_; }; @@ -42,7 +42,7 @@ class Workspace { class Workspaces : public AModule, public EventHandler { public: Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); - virtual ~Workspaces(); + ~Workspaces() override; void update() override; void init(); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 0002900a..231a852e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -44,7 +44,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value } event_box_.add(box_); modulesReady = true; - if (!gIPC.get()) { + if (!gIPC) { gIPC = std::make_unique(); } @@ -104,11 +104,11 @@ void Workspaces::onEvent(const std::string &ev) { } } else if (eventName == "focusedmon") { - active_workspace_name = payload.substr(payload.find(",") + 1); + active_workspace_name = payload.substr(payload.find(',') + 1); } else if (eventName == "moveworkspace" && !all_outputs()) { - std::string workspace = payload.substr(0, payload.find(",")); - std::string new_output = payload.substr(payload.find(",") + 1); + 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) { @@ -167,19 +167,18 @@ Workspaces::~Workspaces() { std::lock_guard lg(mutex_); } -Workspace::Workspace(const Json::Value &value) - : id_(value["id"].asInt()), - name_(value["name"].asString()), - output_(value["monitor"].asString()), // TODO:allow using monitor desc - windows_(value["id"].asInt()) { - active_ = true; - is_special_ = false; - +Workspace::Workspace(const Json::Value &workspace_data) + : id_(workspace_data["id"].asInt()), + name_(workspace_data["name"].asString()), + output_(workspace_data["monitor"].asString()), // TODO:allow using monitor desc + windows_(workspace_data["id"].asInt()), + active_(true), + is_special_(false) { if (name_.find("name:") == 0) { name_ = name_.substr(5); } else if (name_.find("special") == 0) { name_ = id_ == -99 ? name_ : name_.substr(13); - is_special_ = 1; + is_special_ = true; } button_.add_events(Gdk::BUTTON_PRESS_MASK); @@ -191,7 +190,7 @@ Workspace::Workspace(const Json::Value &value) button_.add(content_); }; -void add_or_remove_class(Glib::RefPtr context, bool condition, +void add_or_remove_class(const Glib::RefPtr &context, bool condition, const std::string &class_name) { if (condition) { context->add_class(class_name); @@ -201,7 +200,7 @@ void add_or_remove_class(Glib::RefPtr context, bool condition } void Workspace::update(const std::string &format, const std::string &icon) { - Glib::RefPtr style_context = button_.get_style_context(); + auto style_context = button_.get_style_context(); add_or_remove_class(style_context, active(), "active"); label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), @@ -218,9 +217,8 @@ void Workspaces::sort_workspaces() { if (a->id() < 0 && b->id() < 0) { if ((a->is_special()) ^ (a->is_special())) { return a->id() > b->id(); - } else { - return a->id() < b->id(); } + return a->id() < b->id(); } if ((a->id() > 0) ^ (b->id() > 0)) { return a->id() > b->id(); From b2279c956509ad63e3141321ff995511f221a8d6 Mon Sep 17 00:00:00 2001 From: czM1K3 Date: Mon, 17 Jul 2023 22:20:50 +0200 Subject: [PATCH 049/842] Differencing keyboard layout variant for hyprland/language --- src/modules/hyprland/language.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index aa22a482..423e2b5c 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -38,7 +38,10 @@ auto Language::update() -> void { std::lock_guard lg(mutex_); std::string layoutName = std::string{}; - if (config_.isMember("format-" + layout_.short_description)) { + if (config_.isMember("format-" + layout_.short_description + "-" + layout_.variant)) { + const auto propName = "format-" + layout_.short_description + "-" + layout_.variant; + layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); + } else if (config_.isMember("format-" + layout_.short_description)) { const auto propName = "format-" + layout_.short_description; layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); } else { From 24d56023fd969741bf4448b87fc596555a6463af Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Mon, 17 Jul 2023 22:38:58 +0000 Subject: [PATCH 050/842] last fixes --- 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 b33c3472..f7b87bfa 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -97,7 +97,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()) && - (workspace_json["name"].asString().find("special:") != 0 || show_special())) { + (show_special() || workspace_json["name"].asString().find("special:") != 0)) { workspaces_to_create_.push_back(workspace_json); break; } @@ -223,7 +223,6 @@ void Workspaces::sort_workspaces() { if ((a->id() > 0) ^ (b->id() > 0)) { return a->id() > b->id(); } - spdlog::error("huh!!?"); return false; }); From 841a004acd72de91879582783832ede4d4dae7fb Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 18 Jul 2023 08:28:19 +0200 Subject: [PATCH 051/842] fix: lint --- src/modules/backlight.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index a595073e..27871048 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -190,9 +190,8 @@ auto waybar::modules::Backlight::update() -> void { event_box_.show(); const uint8_t percent = best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max()); - std::string desc = - fmt::format(fmt::runtime(format_), fmt::arg("percent", percent), - fmt::arg("icon", getIcon(percent))); + std::string desc = fmt::format(fmt::runtime(format_), fmt::arg("percent", percent), + fmt::arg("icon", getIcon(percent))); label_.set_markup(desc); getState(percent); if (tooltipEnabled()) { From 3ecd4030e3318863d2bab087d01c9921624f0269 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 18 Jul 2023 08:29:32 +0200 Subject: [PATCH 052/842] chore: v0.9.20 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index f8b4a2d6..85ce2c1d 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.19', + version: '0.9.20', license: 'MIT', meson_version: '>= 0.50.0', default_options : [ From 3e1176e8969c9110bd16ff8c57ccbd38ecdbd0c0 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 18 Jul 2023 08:35:46 +0200 Subject: [PATCH 053/842] fix: lint --- src/modules/sway/window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 04ed6e43..25e430a7 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -145,7 +145,8 @@ std::pair leafNodesInWorkspace(const Json::Value& node) { return {sum, floating_sum}; } -std::optional> getSingleChildNode(const Json::Value& node) { +std::optional> getSingleChildNode( + const Json::Value& node) { auto const& nodes = node["nodes"]; if (nodes.empty()) { if (node["type"].asString() == "workspace") From 8fdd456fa90ca1c191baf518581a456e1d18b9e3 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 18 Jul 2023 17:51:23 +0300 Subject: [PATCH 054/842] cava bump Signed-off-by: Viktar Lukashonak --- meson.build | 2 +- subprojects/cava.wrap | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index f8b4a2d6..d147943f 100644 --- a/meson.build +++ b/meson.build @@ -346,7 +346,7 @@ if get_option('experimental') endif cava = dependency('cava', - version : '>=0.8.4', + version : '>=0.8.5', required: get_option('cava'), fallback : ['cava', 'cava_dep'], not_found_message: 'cava is not found. Building waybar without cava') diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap index 59044b14..f6973c83 100644 --- a/subprojects/cava.wrap +++ b/subprojects/cava.wrap @@ -1,7 +1,7 @@ [wrap-file] -directory = cava-0.8.4 -source_url = https://github.com/LukashonakV/cava/archive/0.8.4.tar.gz -source_filename = cava-0.8.4.tar.gz -source_hash = 523353f446570277d40b8e1efb84468d70fdec53e1356a555c14bf466557a3ed +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 [provide] cava = cava_dep From 4dff1d4b2bd9038c22a329df04368fe36045d77b Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Wed, 19 Jul 2023 20:44:52 +0300 Subject: [PATCH 055/842] cava man page Signed-off-by: Viktar Lukashonak --- man/waybar-cava.5.scd | 182 ++++++++++++++++++++++++++++++++++++++++++ man/waybar.5.scd.in | 1 + meson.build | 1 + 3 files changed, 184 insertions(+) create mode 100644 man/waybar-cava.5.scd diff --git a/man/waybar-cava.5.scd b/man/waybar-cava.5.scd new file mode 100644 index 00000000..3d3762bf --- /dev/null +++ b/man/waybar-cava.5.scd @@ -0,0 +1,182 @@ +waybar-cava(5) "waybar-cava" "User Manual" + +# NAME + +cava + +# DESCRIPTION + +*cava* module for karlstav/cava project. See it on github: https://github.com/karlstav/cava. + + +# FILES + +$XDG_CONFIG_HOME/waybar/config ++ + Per user configuration file + +# ADDITIONAL FILES + +libcava lives in: + +. /usr/lib/libcava.so or /usr/lib64/libcava.so +. /usr/lib/pkgconfig/cava.pc or /usr/lib64/pkgconfig/cava.pc +. /usr/include/cava + +# CONFIGURATION + +[- *Option* +:- *Typeof* +:- *Default* +:- *Description* +|[ *cava_config* +:[ string +:[ +:< Path where cava configuration file is placed to +|[ *framerate* +:[ integer +:[ 30 +:[ rames per second. Is used as a replacement for *interval* +|[ *autosens* +:[ integer +:[ 1 +:[ Will attempt to decrease sensitivity if the bars peak +|[ *sensitivity* +:[ integer +:[ 100 +:[ Manual sensitivity in %. It's recommended to be omitted when *autosens* = 1 +|[ *bars* +:[ integer +:[ 12 +:[ The number of bars +|[ *lower_cutoff_freq* +:[ long integer +:[ 50 +:[ Lower cutoff frequencies for lowest bars the bandwidth of the visualizer +|[ *higher_cutoff_freq* +:[ long integer +:[ 10000 +:[ Higher cutoff frequencies for highest bars the bandwidth of the visualizer +|[ *sleep_timer* +:[ integer +:[ 5 +:[ Seconds with no input before cava main thread goes to sleep mode +|[ *method* +:[ string +:[ pulse +:[ Audio capturing method. Possible methods are: pipewire, pulse, alsa, fifo, sndio or shmem +|[ *source* +:[ string +:[ auto +:[ See cava configuration +|[ *sample_rate* +:[ long integer +:[ 44100 +:[ See cava configuration +|[ *sample_bits* +:[ integer +:[ 16 +:[ See cava configuration +|[ *stereo* +:[ bool +:[ true +:[ Visual channels +|[ *reverse* +:[ bool +:[ false +:[ Displays frequencies the other way around +|[ *bar_delimiter* +:[ integer +:[ 0 +:[ Each bar is separated by a delimiter. Use decimal value in ascii table(i.e. 59 = ";"). 0 means no delimiter +|[ *monstercat* +:[ bool +:[ false +:[ Disables or enables the so-called "Monstercat smoothing" with of without "waves" +|[ *waves* +:[ bool +:[ false +:[ Disables or enables the so-called "Monstercat smoothing" with of 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 +|[ *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 +|[ *ascii_max_range* +:[ integer +:[ 7 +:[ It's impossible to set it directly. The value is dictated by the number of icons in the array *format-icons* +|[ *data_format* +:[ string +:[ asci +:[ It's impossible to set it. Waybar sets it to = asci for internal needs +|[ *raw_target* +:[ string +:[ /dev/stdout +:[ It's impossible to set it. Waybar sets it to = /dev/stdout for internal needs + +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 + +# ACTIONS + +[- *String* +:- *Action* +|[ *mode* +:< Switch main cava thread and fetching audio source thread from/to pause/resume + +# DEPENDENCIES + +- iniparser +- fftw3 + +# SOLVING ISSUES + +. 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 + 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 + +# 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). ++ +So the order is: +. cava upstream +. libcava upstream. +In case when cava releases new version and you're wanna get it, it should be raised an issue to libcava(https://github.com/LukashonakV/cava) with title ++ +\[Bump\]x.x.x where x.x.x is cava release version. + +# EXAMPLES + +``` +"cava": { +// "cava_config": "$XDG_CONFIG_HOME/cava/cava.conf", + "framerate": 30, + "autosens": 1, +// "sensitivity": 100, + "bars": 14, + "lower_cutoff_freq": 50, + "higher_cutoff_freq": 10000, + "method": "pulse", + "source": "auto", + "stereo": true, + "reverse": false, + "bar_delimiter": 0, + "monstercat": false, + "waves": false, + "noise_reduction": 0.77, + "input_delay": 2, + "format-icons" : ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ], + "actions": { + "on-click-right": "mode" + } + }, +``` diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 0b3dc748..a8376697 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -268,6 +268,7 @@ Valid options for the (optional) "orientation" property are: "horizontal", "vert - *waybar-backlight(5)* - *waybar-battery(5)* - *waybar-bluetooth(5)* +- *waybar-cava(5)* - *waybar-clock(5)* - *waybar-cpu(5)* - *waybar-custom(5)* diff --git a/meson.build b/meson.build index f8b4a2d6..a4592b10 100644 --- a/meson.build +++ b/meson.build @@ -423,6 +423,7 @@ if scdoc.found() main_manpage_path, 'waybar-backlight.5.scd', 'waybar-battery.5.scd', + 'waybar-cava.5.scd', 'waybar-clock.5.scd', 'waybar-cpu.5.scd', 'waybar-custom.5.scd', From d3bcff31e5023aacacff94cacc2f947c023b1bf7 Mon Sep 17 00:00:00 2001 From: dmitry Date: Thu, 20 Jul 2023 22:56:15 +0300 Subject: [PATCH 056/842] add high-priority-named optiion --- include/modules/sway/workspaces.hpp | 1 + man/waybar-sway-workspaces.5.scd | 2 ++ src/modules/sway/workspaces.cpp | 22 ++++++++++++++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index d07edb49..0efffe64 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -41,6 +41,7 @@ class Workspaces : public AModule, public sigc::trackable { const Bar& bar_; std::vector workspaces_; + std::vector high_priority_named_; std::vector workspaces_order_; Gtk::Box box_; util::JsonParser parser_; diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 1e5f45db..c934a32a 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -102,6 +102,7 @@ Additional to workspace name matching, the following *format-icons* can be set. - *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 WORKSPACES @@ -134,6 +135,7 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge "3": "", "4": "", "5": "", + "high-priority-named": [ "1", "2" ], "urgent": "", "focused": "", "default": "" diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 90efe7a1..29eb6c9e 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -28,6 +28,11 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), bar_(bar), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 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()); + } + } box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -279,9 +284,22 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { } std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) { - std::vector keys = {"urgent", "focused", name, "visible", "default"}; + std::vector keys = {"high-priority-named", "urgent", "focused", name, "default"}; for (auto const &key : keys) { - if (key == "focused" || key == "visible" || key == "urgent") { + if (key == "high-priority-named") { + auto it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(), + [&](const std::string &member) { return member == name; }); + if (it != high_priority_named_.end()) { + return config_["format-icons"][name].asString(); + } + + it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(), + [&](const std::string &member) { return trimWorkspaceName(member) == trimWorkspaceName(name); }); + if (it != high_priority_named_.end()) { + return config_["format-icons"][trimWorkspaceName(name)].asString(); + } + } + if (key == "focused" || key == "urgent") { if (config_["format-icons"][key].isString() && node[key].asBool()) { return config_["format-icons"][key].asString(); } From 05efdb74f0d8191785be1030ab2ba539b6cc442d Mon Sep 17 00:00:00 2001 From: dmitry Date: Thu, 20 Jul 2023 22:57:33 +0300 Subject: [PATCH 057/842] format --- src/modules/sway/workspaces.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 29eb6c9e..a5e5fa75 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -287,17 +287,19 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node std::vector keys = {"high-priority-named", "urgent", "focused", name, "default"}; for (auto const &key : keys) { if (key == "high-priority-named") { - auto it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(), - [&](const std::string &member) { return member == name; }); - if (it != high_priority_named_.end()) { - return config_["format-icons"][name].asString(); - } + auto it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(), + [&](const std::string &member) { return member == name; }); + if (it != high_priority_named_.end()) { + return config_["format-icons"][name].asString(); + } - it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(), - [&](const std::string &member) { return trimWorkspaceName(member) == trimWorkspaceName(name); }); - if (it != high_priority_named_.end()) { - return config_["format-icons"][trimWorkspaceName(name)].asString(); - } + it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(), + [&](const std::string &member) { + return trimWorkspaceName(member) == trimWorkspaceName(name); + }); + if (it != high_priority_named_.end()) { + return config_["format-icons"][trimWorkspaceName(name)].asString(); + } } if (key == "focused" || key == "urgent") { if (config_["format-icons"][key].isString() && node[key].asBool()) { From 6dc33fe88ffb9812849c6f7dfd14f8d90c428a52 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 16 Jul 2023 16:01:35 +0200 Subject: [PATCH 058/842] Mediaplayer improvements --- resources/custom_modules/mediaplayer.py | 236 +++++++++++++++--------- src/modules/backlight.cpp | 5 +- 2 files changed, 147 insertions(+), 94 deletions(-) diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index 1630d97c..51a48373 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -1,89 +1,158 @@ #!/usr/bin/env python3 +import gi +gi.require_version("Playerctl", "2.0") +from gi.repository import Playerctl, GLib +from gi.repository.Playerctl import Player import argparse import logging import sys import signal import gi import json -gi.require_version('Playerctl', '2.0') -from gi.repository import Playerctl, GLib +import os +from typing import List logger = logging.getLogger(__name__) - -def write_output(text, player): - logger.info('Writing output') - - output = {'text': text, - 'class': 'custom-' + player.props.player_name, - 'alt': player.props.player_name} - - sys.stdout.write(json.dumps(output) + '\n') - sys.stdout.flush() - - -def on_play(player, status, manager): - logger.info('Received new playback status') - on_metadata(player, player.props.metadata, manager) - - -def on_metadata(player, metadata, manager): - logger.info('Received new metadata') - track_info = '' - - if player.props.player_name == 'spotify' and \ - 'mpris:trackid' in metadata.keys() and \ - ':ad:' in player.props.metadata['mpris:trackid']: - track_info = 'AD PLAYING' - elif player.get_artist() != '' and player.get_title() != '': - track_info = '{artist} - {title}'.format(artist=player.get_artist(), - title=player.get_title()) - else: - track_info = player.get_title() - - if player.props.status != 'Playing' and track_info: - track_info = ' ' + track_info - write_output(track_info, player) - - -def on_player_appeared(manager, player, selected_player=None): - if player is not None and (selected_player is None or player.name == selected_player): - init_player(manager, player) - else: - logger.debug("New player appeared, but it's not the selected player, skipping") - - -def on_player_vanished(manager, player): - logger.info('Player has vanished') - sys.stdout.write('\n') - sys.stdout.flush() - - -def init_player(manager, name): - logger.debug('Initialize player: {player}'.format(player=name.name)) - player = Playerctl.Player.new_from_name(name) - player.connect('playback-status', on_play, manager) - player.connect('metadata', on_metadata, manager) - manager.manage_player(player) - on_metadata(player, player.props.metadata, manager) - - def signal_handler(sig, frame): - logger.debug('Received signal to stop, exiting') - sys.stdout.write('\n') + logger.info("Received signal to stop, exiting") + sys.stdout.write("\n") sys.stdout.flush() # loop.quit() sys.exit(0) +class PlayerManager: + def __init__(self, selected_player=None): + self.manager = Playerctl.PlayerManager() + self.loop = GLib.MainLoop() + self.manager.connect( + "name-appeared", lambda *args: self.on_player_appeared(*args)) + self.manager.connect( + "player-vanished", lambda *args: self.on_player_vanished(*args)) + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + self.selected_player = selected_player + + self.init_players() + + def init_players(self): + for player in self.manager.props.player_names: + 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 + self.init_player(player) + + def run(self): + logger.info("Starting main loop") + self.loop.run() + + def init_player(self, player): + logger.info(f"Initialize new player: {player.name}") + player = Playerctl.Player.new_from_name(player) + player.connect("playback-status", + self.on_playback_status_changed, None) + player.connect("metadata", self.on_metadata_changed, None) + self.manager.manage_player(player) + self.on_metadata_changed(player, player.props.metadata) + + def get_players(self) -> List[Player]: + return self.manager.props.players + + def write_output(self, text, player): + logger.debug(f"Writing output: {text}") + + output = {"text": text, + "class": "custom-" + player.props.player_name, + "alt": player.props.player_name} + + sys.stdout.write(json.dumps(output) + "\n") + sys.stdout.flush() + + def clear_output(self): + sys.stdout.write("\n") + sys.stdout.flush() + + def on_playback_status_changed(self, player, status, _=None): + logger.debug(f"Playback status changed for player {player.props.player_name}: {status}") + self.on_metadata_changed(player, player.props.metadata) + + def get_first_playing_player(self): + players = self.get_players() + logger.debug(f"Getting first playing player from {len(players)} players") + if len(players) > 0: + # if any are playing, show the first one that is playing + # reverse order, so that the most recently added ones are preferred + for player in players[::-1]: + if player.props.status == "Playing": + return player + # if none are playing, show the first one + return players[0] + else: + logger.debug("No players found") + return None + + def show_most_important_player(self): + logger.debug("Showing most important player") + # show the currently playing player + # or else show the first paused player + # or else show nothing + current_player = self.get_first_playing_player() + if current_player is not None: + self.on_metadata_changed(current_player, current_player.props.metadata) + else: + self.clear_output() + + def on_metadata_changed(self, player, metadata, _=None): + logger.debug(f"Metadata changed for player {player.props.player_name}") + player_name = player.props.player_name + artist = player.get_artist() + title = player.get_title() + + track_info = "" + if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]: + track_info = "Advertisement" + elif artist is not None and title is not None: + track_info = f"{artist} - {title}" + else: + track_info = title + + if track_info: + if player.props.status == "Playing": + track_info = " " + track_info + else: + track_info = " " + track_info + # only print output if no other player is playing + current_playing = self.get_first_playing_player() + if current_playing is None or current_playing.props.player_name == player.props.player_name: + self.write_output(track_info, player) + else: + logger.debug(f"Other player {current_playing.props.player_name} is playing, skipping") + + def on_player_appeared(self, _, player): + logger.info(f"Player has appeared: {player.name}") + if player is not None and (self.selected_player is None or player.name == self.selected_player): + self.init_player(player) + else: + logger.debug( + "New player appeared, but it's not the selected player, skipping") + + def on_player_vanished(self, _, player): + logger.info(f"Player {player.props.player_name} has vanished") + self.show_most_important_player() + def parse_arguments(): parser = argparse.ArgumentParser() # Increase verbosity with every occurrence of -v - parser.add_argument('-v', '--verbose', action='count', default=0) + parser.add_argument("-v", "--verbose", action="count", default=0) - # Define for which player we're listening - parser.add_argument('--player') + # Define for which player we"re listening + parser.add_argument("--player") + + parser.add_argument("--enable-logging", action="store_true") return parser.parse_args() @@ -92,37 +161,22 @@ def main(): arguments = parse_arguments() # Initialize logging - logging.basicConfig(stream=sys.stderr, level=logging.DEBUG, - format='%(name)s %(levelname)s %(message)s') + if arguments.enable_logging: + logfile = os.path.join(os.path.dirname( + os.path.realpath(__file__)), "media-player.log") + logging.basicConfig(filename=logfile, level=logging.DEBUG, + format="%(asctime)s %(name)s %(levelname)s:%(lineno)d %(message)s") # Logging is set by default to WARN and higher. # With every occurrence of -v it's lowered by one logger.setLevel(max((3 - arguments.verbose) * 10, 0)) - # Log the sent command line arguments - logger.debug('Arguments received {}'.format(vars(arguments))) - - manager = Playerctl.PlayerManager() - loop = GLib.MainLoop() - - manager.connect('name-appeared', lambda *args: on_player_appeared(*args, arguments.player)) - manager.connect('player-vanished', on_player_vanished) - - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) - signal.signal(signal.SIGPIPE, signal.SIG_DFL) - - for player in manager.props.player_names: - if arguments.player is not None and arguments.player != player.name: - logger.debug('{player} is not the filtered player, skipping it' - .format(player=player.name) - ) - continue - - init_player(manager, player) - - loop.run() + logger.info("Creating player manager") + if arguments.player: + logger.info(f"Filtering for player: {arguments.player}") + player = PlayerManager(arguments.player) + player.run() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index a595073e..27871048 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -190,9 +190,8 @@ auto waybar::modules::Backlight::update() -> void { event_box_.show(); const uint8_t percent = best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max()); - std::string desc = - fmt::format(fmt::runtime(format_), fmt::arg("percent", percent), - fmt::arg("icon", getIcon(percent))); + std::string desc = fmt::format(fmt::runtime(format_), fmt::arg("percent", percent), + fmt::arg("icon", getIcon(percent))); label_.set_markup(desc); getState(percent); if (tooltipEnabled()) { From 4d9e0ea8029f769c8edb65007150de376b71a6f4 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Mon, 24 Jul 2023 01:21:30 +0300 Subject: [PATCH 059/842] time conversion between time zones Signed-off-by: Viktar Lukashonak --- include/modules/clock.hpp | 17 ++++-- src/modules/clock.cpp | 119 ++++++++++++++++---------------------- 2 files changed, 63 insertions(+), 73 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index fab38111..0fcd0af2 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -34,11 +34,18 @@ class Clock final : public ALabel { auto first_day_of_week() -> date::weekday; const date::time_zone* current_timezone(); - bool is_timezone_fixed(); - auto timezones_text(std::chrono::system_clock::time_point* now) -> std::string; + auto timezones_text(std::chrono::system_clock::time_point now) -> std::string; /*Calendar properties*/ WeeksSide cldWPos_{WeeksSide::HIDDEN}; + /* + 0 - calendar.format.months + 1 - calendar.format.weekdays + 2 - calendar.format.days + 3 - calendar.format.today + 4 - calendar.format.weeks + 5 - tooltip-format + */ std::map fmtMap_; CldMode cldMode_{CldMode::MONTH}; uint cldMonCols_{3}; // Count of the month in the row @@ -52,8 +59,10 @@ class Clock final : public ALabel { std::string cldMonCached_{}; date::day cldBaseDay_{0}; /*Calendar functions*/ - auto get_calendar(const date::zoned_seconds& now, const date::zoned_seconds& wtime) - -> 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/modules/clock.cpp b/src/modules/clock.cpp index 59963efd..b36b8258 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -17,9 +17,9 @@ 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) { + current_time_zone_idx_{0}, + is_calendar_in_tooltip_{false}, + is_timezoned_list_in_tooltip_{false} { if (config_["timezones"].isArray() && !config_["timezones"].empty()) { for (const auto& zone_name : config_["timezones"]) { if (!zone_name.isString()) continue; @@ -35,8 +35,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } } else if (config_["timezone"].isString()) { if (config_["timezone"].asString().empty()) - // nullptr means that local time should be shown - time_zones_.push_back(nullptr); + time_zones_.push_back(date::current_zone()); else try { time_zones_.push_back(date::locate_zone(config_["timezone"].asString())); @@ -48,20 +47,22 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) // If all timezones are parsed and no one is good if (!time_zones_.size()) { // nullptr means that local time should be shown - time_zones_.push_back(nullptr); + 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(); + std::string trimmed_format{config_["tooltip-format"].asString()}; 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) { + fmtMap_.insert({5, trimmed_format}); + + if (fmtMap_[5].find("{" + kCalendarPlaceholder + "}") != std::string::npos) { is_calendar_in_tooltip_ = true; } - if (trimmed_format.find("{" + KTimezonedTimeListPlaceholder + "}") != std::string::npos) { + if (fmtMap_[5].find("{" + KTimezonedTimeListPlaceholder + "}") != std::string::npos) { is_timezoned_list_in_tooltip_ = true; } } @@ -158,52 +159,33 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } const date::time_zone* waybar::modules::Clock::current_timezone() { - return is_timezone_fixed() ? time_zones_[current_time_zone_idx_] : date::current_zone(); -} - -bool waybar::modules::Clock::is_timezone_fixed() { - return time_zones_[current_time_zone_idx_] != nullptr; + return time_zones_[current_time_zone_idx_]; } auto waybar::modules::Clock::update() -> void { - const auto* time_zone = current_timezone(); - auto now = std::chrono::system_clock::now(); - auto ztime = date::zoned_time{time_zone, date::floor(now)}; + const auto* tz{current_timezone()}; + const date::zoned_time now{ + tz, 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_local_time() - date::floor(now.get_local_time())))}; - auto shifted_date = date::year_month_day{date::floor(now)} + cldCurrShift_; - if (cldCurrShift_.count()) { - shifted_date = date::year_month_day(shifted_date.year(), shifted_date.month(), date::day(1)); - } - auto now_shifted = date::sys_days{shifted_date} + (now - date::floor(now)); - auto shifted_ztime = date::zoned_time{time_zone, date::floor(now_shifted)}; - - std::string text{""}; - if (!is_timezone_fixed()) { - // As date dep is not fully compatible, prefer fmt - tzset(); - auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); - text = fmt::format(locale_, fmt::runtime(format_), localtime); - } else { - text = fmt::format(locale_, fmt::runtime(format_), ztime); - } - label_.set_markup(text); + label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now)); if (tooltipEnabled()) { - if (config_["tooltip-format"].isString()) { - std::string calendar_lines{""}; - std::string timezoned_time_lines{""}; - if (is_calendar_in_tooltip_) { - calendar_lines = get_calendar(ztime, shifted_ztime); - } - if (is_timezoned_list_in_tooltip_) { - timezoned_time_lines = timezones_text(&now); - } - auto tooltip_format = config_["tooltip-format"].asString(); - text = fmt::format(locale_, fmt::runtime(tooltip_format), shifted_ztime, - fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines), - fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines)); - label_.set_tooltip_markup(text); - } + 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 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); } // Call parent update @@ -219,15 +201,15 @@ auto waybar::modules::Clock::doAction(const std::string& name) -> void { } // The number of weeks in calendar month layout plus 1 more for calendar titles -unsigned cldRowsInMonth(date::year_month const ym, date::weekday const firstdow) { +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; } -auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow, unsigned const line) - -> const date::year_month_weekday { +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; @@ -235,8 +217,8 @@ auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow, return ymdw; } -auto getCalendarLine(date::year_month_day const currDate, date::year_month const ym, - unsigned const line, date::weekday const firstdow, +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; @@ -318,10 +300,9 @@ auto getCalendarLine(date::year_month_day const currDate, date::year_month const return res.str(); } -auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, - const date::zoned_seconds& wtime) -> std::string { - auto daypoint = date::floor(wtime.get_local_time()); - const auto ymd{date::year_month_day{daypoint}}; +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 { const auto ym{ymd.year() / ymd.month()}; const auto y{ymd.year()}; const auto d{ymd.day()}; @@ -329,9 +310,6 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, const auto maxRows{12 / cldMonCols_}; std::ostringstream os; std::ostringstream tmp; - // get currdate - daypoint = date::floor(now.get_local_time()); - const auto currDate{date::year_month_day{daypoint}}; if (cldMode_ == CldMode::YEAR) { if (y / date::month{1} / 1 == cldYearShift_) @@ -361,6 +339,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, 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_)); @@ -377,8 +356,9 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, if (line < ml[static_cast(ymTmp.month()) - 1u]) os << fmt::format(fmt::runtime(fmtMap_[4]), (line == 2) - ? date::sys_days{ymTmp / 1} - : date::sys_days{cldGetWeekForLine(ymTmp, firstdow, line)}) + ? date::zoned_seconds{tz, date::local_days{ymTmp / 1}} + : date::zoned_seconds{tz, date::local_days{cldGetWeekForLine( + ymTmp, firstdow, line)}}) << ' '; else os << std::string(cldWnLen_, ' '); @@ -387,7 +367,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, os << fmt::format( fmt::runtime((cldWPos_ != WeeksSide::LEFT || line == 0) ? "{:<{}}" : "{:>{}}"), - getCalendarLine(currDate, ymTmp, line, firstdow, &locale_), + getCalendarLine(today, ymTmp, line, firstdow, &locale_), (cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0))); // Week numbers on the right @@ -397,8 +377,9 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, os << ' ' << fmt::format(fmt::runtime(fmtMap_[4]), (line == 2) - ? date::sys_days{ymTmp / 1} - : date::sys_days{cldGetWeekForLine(ymTmp, firstdow, line)}); + ? date::zoned_seconds{tz, date::local_days{ymTmp / 1}} + : date::zoned_seconds{tz, date::local_days{cldGetWeekForLine( + ymTmp, firstdow, line)}}); else os << std::string(cldWnLen_, ' '); } @@ -421,7 +402,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, 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", ymd.day())))); + fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", d)))); if (cldMode_ == CldMode::YEAR) cldYearCached_ = os.str(); @@ -457,7 +438,7 @@ void waybar::modules::Clock::tz_down() { 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) +auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point now) -> std::string { if (time_zones_.size() == 1) { return ""; @@ -471,7 +452,7 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin if (!timezone) { timezone = date::current_zone(); } - auto ztime = date::zoned_time{timezone, date::floor(*now)}; + auto ztime = date::zoned_time{timezone, date::floor(now)}; os << fmt::format(locale_, fmt::runtime(format_), ztime) << '\n'; } return os.str(); From d01ce7d8129122d88667b2baf4a6727cb819401e Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Mon, 24 Jul 2023 10:16:38 +0300 Subject: [PATCH 060/842] Rewview changes. Signed-off-by: Viktar Lukashonak --- src/modules/clock.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index b36b8258..15d0319b 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -24,8 +24,8 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) for (const auto& zone_name : config_["timezones"]) { if (!zone_name.isString()) continue; if (zone_name.asString().empty()) - // nullptr means that local time should be shown - time_zones_.push_back(nullptr); + // local time should be shown + time_zones_.push_back(date::current_zone()); else try { time_zones_.push_back(date::locate_zone(zone_name.asString())); @@ -46,7 +46,6 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) // If all timezones are parsed and no one is good if (!time_zones_.size()) { - // nullptr means that local time should be shown time_zones_.push_back(date::current_zone()); } From dae7794bdfda81460beb26ad90a3beec2ab18bf2 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 25 Jul 2023 00:08:05 +0300 Subject: [PATCH 061/842] Clock. Narrow seconds precision Signed-off-by: Viktar Lukashonak --- src/modules/clock.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 15d0319b..ed21faa7 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -164,7 +164,9 @@ const date::time_zone* waybar::modules::Clock::current_timezone() { auto waybar::modules::Clock::update() -> void { const auto* tz{current_timezone()}; const date::zoned_time now{ - tz, std::chrono::system_clock::now()}; // Define local time is based on provided time zone + 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 From c71c0fca6e49745cf62f333da45f5f2707decfba Mon Sep 17 00:00:00 2001 From: MightyPlaza <123664421+MightyPlaza@users.noreply.github.com> Date: Tue, 25 Jul 2023 14:48:20 +0000 Subject: [PATCH 062/842] fix typo --- 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 f7b87bfa..8e5d1148 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -97,7 +97,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().find("special:") != 0)) { + (show_special() || workspace_json["name"].asString().find("special") != 0)) { workspaces_to_create_.push_back(workspace_json); break; } From 2b07dea3a6bb9d0e659f2c99318919023be4f370 Mon Sep 17 00:00:00 2001 From: MisterPine Date: Sat, 29 Jul 2023 00:30:33 +0200 Subject: [PATCH 063/842] Fix broken start behavior for `hyprland/window` --- src/modules/hyprland/window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 81c864d1..60de074c 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -26,6 +26,7 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) queryActiveWorkspace(); update(); + dp.emit(); // register for hyprland ipc gIPC->registerForIPC("activewindow", this); From 05e5a7e5fd63b15f5760ce34b987435706a0d0af Mon Sep 17 00:00:00 2001 From: MisterPine Date: Sat, 29 Jul 2023 00:35:01 +0200 Subject: [PATCH 064/842] Document icon for `hyprland/window` --- man/waybar-hyprland-window.5.scd | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd index 5e7b4d5e..3e1926b7 100644 --- a/man/waybar-hyprland-window.5.scd +++ b/man/waybar-hyprland-window.5.scd @@ -25,6 +25,16 @@ Addressed by *hyprland/window* typeof: bool ++ Show the active window of the monitor the bar belongs to, instead of the focused window. +*icon*: ++ + typeof: bool ++ + default: false ++ + Option to hide the application icon. + +*icon-size*: ++ + typeof: integer ++ + default: 24 ++ + Option to change the size of the application icon. + # FORMAT REPLACEMENTS See the output of "hyprctl clients" for examples From 106535e3ebdfd6a7731637c4df97631472f3f880 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 29 Jul 2023 23:46:35 +0300 Subject: [PATCH 065/842] tooltip-format spaces breaks pango format Signed-off-by: Viktar Lukashonak --- src/modules/clock.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index ed21faa7..3d6b8919 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -53,15 +53,15 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) // 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()); - fmtMap_.insert({5, trimmed_format}); - if (fmtMap_[5].find("{" + kCalendarPlaceholder + "}") != std::string::npos) { + if (trimmed_format.find("{" + kCalendarPlaceholder + "}") != std::string::npos) { is_calendar_in_tooltip_ = true; } - if (fmtMap_[5].find("{" + KTimezonedTimeListPlaceholder + "}") != std::string::npos) { + if (trimmed_format.find("{" + KTimezonedTimeListPlaceholder + "}") != std::string::npos) { is_timezoned_list_in_tooltip_ = true; } } From 6b7fd368638dc71f39a93eff21b5823769af9642 Mon Sep 17 00:00:00 2001 From: Diederik de Haas Date: Sun, 30 Jul 2023 09:44:16 +0200 Subject: [PATCH 066/842] man/waybar-clock: Fix typo and formatting typo: Adressed -> Addressed formatting: Add missing spaces --- man/waybar-clock.5.scd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 3c670566..bb039be4 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -37,13 +37,13 @@ $XDG_CONFIG_HOME/waybar/config ++ :[ list of strings :[ :[ A list of timezones (as in *timezone*) to use for time display, changed using - the scroll wheel. Do not specify *timezone* option when *timezones* is specified. + the scroll wheel. Do not specify *timezone* option when *timezones* is specified. "" represents the system's local timezone |[ *locale* :[ string :[ :[ A locale to be used to display the time. Intended to render times in custom - timezones with the proper language and format + timezones with the proper language and format |[ *max-length* :[ integer :[ @@ -104,14 +104,14 @@ View all valid format options in *strftime(3)* or have a look Date: Sun, 30 Jul 2023 09:55:11 +0200 Subject: [PATCH 067/842] man: Make NAME-ing consistent Tools like `apropos` and `whatis` use the NAME section to generate their database, so make sure every manpage has it. Also make sure they all have a brief description and make it consistent across the manpages. --- man/waybar-cava.5.scd | 2 +- man/waybar-clock.5.scd | 2 +- man/waybar-hyprland-workspaces.5.scd | 2 +- man/waybar-states.5.scd | 4 ++++ man/waybar-wlr-taskbar.5.scd | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/man/waybar-cava.5.scd b/man/waybar-cava.5.scd index 3d3762bf..7cbb10d5 100644 --- a/man/waybar-cava.5.scd +++ b/man/waybar-cava.5.scd @@ -2,7 +2,7 @@ waybar-cava(5) "waybar-cava" "User Manual" # NAME -cava +waybar - cava module # DESCRIPTION diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index bb039be4..e0bf2946 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -2,7 +2,7 @@ waybar-clock(5) "waybar-clock" "User Manual" # NAME -clock +waybar - clock module # DESCRIPTION diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 6e67e188..55b78b60 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -1,4 +1,4 @@ -waybar-wlr-workspaces(5) +waybar-hyprland-workspaces(5) # NAME diff --git a/man/waybar-states.5.scd b/man/waybar-states.5.scd index c1b78391..dea1beea 100644 --- a/man/waybar-states.5.scd +++ b/man/waybar-states.5.scd @@ -1,5 +1,9 @@ waybar-states(5) +# NAME + +waybar - states property + # OVERVIEW Some modules support 'states' which allows percentage values to be used as styling triggers to diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 2fc86938..6c724b08 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -2,7 +2,7 @@ waybar-wlr-taskbar(5) # NAME -wlroots - Taskbar module +waybar - wlr taskbar module # DESCRIPTION From 28635c1f6d580442709fc68d69c40d4950175e0d Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 30 Jul 2023 11:43:34 +0200 Subject: [PATCH 068/842] Fixed sway IPC compile warnings --- src/modules/sway/ipc/client.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index 5c3df7b2..4139a53b 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -1,6 +1,7 @@ #include "modules/sway/ipc/client.hpp" #include +#include #include @@ -17,12 +18,16 @@ Ipc::~Ipc() { if (fd_ > 0) { // To fail the IPC header - write(fd_, "close-sway-ipc", 14); + if (write(fd_, "close-sway-ipc", 14) == -1) { + spdlog::error("Failed to close sway IPC"); + } close(fd_); fd_ = -1; } if (fd_event_ > 0) { - write(fd_event_, "close-sway-ipc", 14); + if (write(fd_event_, "close-sway-ipc", 14) == -1) { + spdlog::error("Failed to close sway IPC event handler"); + } close(fd_event_); fd_event_ = -1; } From 600653538b778cf9528bb7a230428418b5b9220b Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sun, 23 Jul 2023 18:53:32 +0200 Subject: [PATCH 069/842] Persistent workspaces in hyprland/workspaces --- include/modules/hyprland/workspaces.hpp | 22 ++- src/modules/hyprland/workspaces.cpp | 204 ++++++++++++++++++++---- 2 files changed, 194 insertions(+), 32 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index b32dc41d..353edb7a 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -18,11 +18,15 @@ class Workspace { int id() const { return id_; }; std::string name() const { return name_; }; std::string output() const { return output_; }; - int active() const { return active_; }; + bool active() const { return active_; }; bool is_special() const { return is_special_; }; + bool is_persistent() const { return is_persistent_; }; + bool is_empty() const { return windows_ == 0; }; 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_windows(uint value) { windows_ = value; }; void update(const std::string& format, const std::string& icon); @@ -30,9 +34,10 @@ class Workspace { int id_; std::string name_; std::string output_; - int windows_; - bool active_; - bool is_special_; + uint windows_; + bool active_ = false; + bool is_special_ = false; + bool is_persistent_ = false; Gtk::Button button_; Gtk::Box content_; @@ -53,6 +58,7 @@ class Workspaces : public AModule, public EventHandler { private: void onEvent(const std::string&) override; + void update_window_count(); void sort_workspaces(); void create_workspace(Json::Value& value); void remove_workspace(std::string name); @@ -60,10 +66,16 @@ class Workspaces : public AModule, public EventHandler { bool all_outputs_ = false; bool show_special_ = false; + void fill_persistent_workspaces(); + void create_persistent_workspaces(); + std::vector persistent_workspaces_to_create_; + bool persistent_created_ = false; + std::string format_; std::map icons_map_; bool with_icon_; - std::string active_workspace_name; + uint64_t monitor_id_; + std::string active_workspace_name_; std::vector> workspaces_; std::vector workspaces_to_create_; std::vector workspaces_to_remove_; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 8e5d1148..787d812d 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -55,6 +55,9 @@ 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("openwindow", this); + gIPC->registerForIPC("closewindow", this); + gIPC->registerForIPC("movewindow", this); } auto Workspaces::update() -> void { @@ -71,7 +74,7 @@ auto Workspaces::update() -> void { workspaces_to_create_.clear(); for (auto &workspace : workspaces_) { - workspace->set_active(workspace->name() == active_workspace_name); + workspace->set_active(workspace->name() == active_workspace_name_); std::string &workspace_icon = icons_map_[""]; if (with_icon_) { workspace_icon = workspace->select_icon(icons_map_); @@ -87,7 +90,7 @@ void Workspaces::onEvent(const std::string &ev) { std::string payload = ev.substr(eventName.size() + 2); if (eventName == "workspace") { - active_workspace_name = payload; + active_workspace_name_ = payload; } else if (eventName == "destroyworkspace") { workspaces_to_remove_.push_back(payload); @@ -97,14 +100,14 @@ 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().find("special") != 0)) { + (show_special() || workspace_json["name"].asString().starts_with("special"))) { workspaces_to_create_.push_back(workspace_json); break; } } } else if (eventName == "focusedmon") { - active_workspace_name = payload.substr(payload.find(',') + 1); + active_workspace_name_ = payload.substr(payload.find(',') + 1); } else if (eventName == "moveworkspace" && !all_outputs()) { std::string workspace = payload.substr(0, payload.find(',')); @@ -121,13 +124,47 @@ void Workspaces::onEvent(const std::string &ev) { } else { workspaces_to_remove_.push_back(workspace); } + } else if (eventName == "openwindow" || eventName == "closewindow" || eventName == "movewindow") { + update_window_count(); } dp.emit(); } +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(), + [&](Json::Value const &x) { return x["name"].asString() == workspace->name(); }); + if (workspace_json != workspaces_json.end()) { + try { + workspace->set_windows((*workspace_json)["windows"].asUInt()); + } catch (const std::exception &e) { + spdlog::error("Failed to update window count: {}", e.what()); + } + } else { + workspace->set_windows(0); + } + } +} + void Workspaces::create_workspace(Json::Value &value) { - workspaces_.push_back(std::make_unique(value)); + // 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(); + 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; + } + + // create new workspace + workspaces_.emplace_back(std::make_unique(value)); Gtk::Button &new_workspace_button = workspaces_.back()->button(); box_.pack_start(new_workspace_button, false, false); sort_workspaces(); @@ -139,6 +176,12 @@ void Workspaces::remove_workspace(std::string name) { [&](std::unique_ptr &x) { return x->name() == name; }); if (workspace == 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 return; } @@ -146,16 +189,89 @@ void Workspaces::remove_workspace(std::string name) { workspaces_.erase(workspace); } +void Workspaces::fill_persistent_workspaces() { + if (config_["persistent_workspaces"].isObject() && !all_outputs()) { + const Json::Value persistent_workspaces = config_["persistent_workspaces"]; + const std::vector keys = persistent_workspaces.getMemberNames(); + + for (const std::string &key : keys) { + 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) { + int amount = value.asInt(); + spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, + bar_.output->name); + for (int i = 0; i < amount; i++) { + persistent_workspaces_to_create_.emplace_back( + 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; + } + } + } + } + } +} + +void Workspaces::create_persistent_workspaces() { + for (const std::string &workspace_name : persistent_workspaces_to_create_) { + Json::Value new_workspace; + try { + // numbered persistent workspaces get the name as ID + new_workspace["id"] = workspace_name == "special" ? -99 : std::stoi(workspace_name); + } catch (const std::exception &e) { + // named persistent workspaces start with ID=0 + new_workspace["id"] = 0; + } + new_workspace["name"] = workspace_name; + new_workspace["monitor"] = bar_.output->name; + new_workspace["windows"] = 0; + new_workspace["persistent"] = true; + + create_workspace(new_workspace); + } +} + void Workspaces::init() { - active_workspace_name = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + active_workspace_name_ = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + + // get monitor ID from name (used by persistent workspaces) + monitor_id_ = 0; + auto monitors = gIPC->getSocket1JsonReply("monitors"); + auto current_monitor = 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); + } else { + monitor_id_ = (*current_monitor)["id"].asInt(); + } + + fill_persistent_workspaces(); + create_persistent_workspaces(); 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().find("special") != 0 || show_special())) + (workspace_json["name"].asString().starts_with("special") || show_special())) create_workspace(workspace_json); } + update_window_count(); + sort_workspaces(); dp.emit(); @@ -171,16 +287,19 @@ Workspace::Workspace(const Json::Value &workspace_data) : id_(workspace_data["id"].asInt()), name_(workspace_data["name"].asString()), output_(workspace_data["monitor"].asString()), // TODO:allow using monitor desc - windows_(workspace_data["id"].asInt()), - active_(true), - is_special_(false) { - if (name_.find("name:") == 0) { + windows_(workspace_data["windows"].asInt()), + active_(true) { + if (name_.starts_with("name:")) { name_ = name_.substr(5); - } else if (name_.find("special") == 0) { - name_ = id_ == -99 ? name_ : name_.substr(13); + } else if (name_.starts_with("special")) { + name_ = id_ == -99 ? name_ : name_.substr(8); is_special_ = true; } + if (workspace_data.isMember("persistent")) { + is_persistent_ = 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); @@ -202,28 +321,45 @@ void add_or_remove_class(const Glib::RefPtr &context, bool co 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"); - 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("name", name()), fmt::arg("icon", icon))); } void Workspaces::sort_workspaces() { std::sort(workspaces_.begin(), workspaces_.end(), [](std::unique_ptr &a, std::unique_ptr &b) { - // normal -> named -> special -> named special + // normal -> named persistent -> named -> special -> named special + + // both normal (includes numbered persistent) => sort by ID if (a->id() > 0 && b->id() > 0) { return a->id() < b->id(); } - if (a->id() < 0 && b->id() < 0) { - if ((a->is_special()) ^ (a->is_special())) { - return a->id() > b->id(); - } - 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() > b->id(); + return a->id() > 0; } - return false; + + // 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 a->name() < b->name(); + } + + // sort non-special named workspaces by name (ID <= -1377) + return a->name() < b->name(); }); for (size_t i = 0; i < workspaces_.size(); ++i) { @@ -239,11 +375,25 @@ std::string &Workspace::select_icon(std::map &icons_ma } } + if (is_special()) { + auto special_icon_it = icons_map.find("special"); + if (special_icon_it != icons_map.end()) { + return special_icon_it->second; + } + } + auto named_icon_it = icons_map.find(std::to_string(id())); if (named_icon_it != icons_map.end()) { return named_icon_it->second; } + if (is_persistent()) { + auto persistent_icon_it = icons_map.find("persistent"); + if (persistent_icon_it != icons_map.end()) { + return persistent_icon_it->second; + } + } + auto default_icon_it = icons_map.find("default"); if (default_icon_it != icons_map.end()) { return default_icon_it->second; @@ -253,12 +403,12 @@ std::string &Workspace::select_icon(std::map &icons_ma auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { try { - if (id() > 0) { // normal + if (id() > 0) { // normal or numbered persistent gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); - } else if (!is_special()) { // named normal - gIPC->getSocket1Reply("dispatch workspace name" + name()); + } else if (!is_special()) { // named + gIPC->getSocket1Reply("dispatch workspace name:" + name()); } else if (id() != -99) { // named special - gIPC->getSocket1Reply("dispatch togglespecialworkspace name" + name()); + gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); } else { // special gIPC->getSocket1Reply("dispatch togglespecialworkspace"); } From a3904ff039a0539c855ddffe23c84067df0b7235 Mon Sep 17 00:00:00 2001 From: Diederik de Haas Date: Sun, 30 Jul 2023 17:33:02 +0200 Subject: [PATCH 070/842] man: Fix several whitespace formatting issues Fix the following whitespace formatting issues: - Indentation in scdoc source files should be done with tabs. - Lines where there (clearly) should be a line break, need to have '++' at the end, but several were missing them. - The scdoc manual (clearly) states that lines should be hard wrapped at 80 columns, but when line(s) are indented, that causes rendering issues. So lines where a line break was not clearly intended or clearly not intended, have been put onto 1 line to circumvent the rendering issue. Link: https://lists.sr.ht/~sircmpwn/public-inbox/%3C8251560.T7Z3S40VBb%40bagend%3E --- man/waybar-backlight.5.scd | 14 ++-- man/waybar-battery.5.scd | 36 ++++----- man/waybar-bluetooth.5.scd | 8 +- man/waybar-cava.5.scd | 52 ++++++------ man/waybar-clock.5.scd | 114 +++++++++++++------------- man/waybar-cpu.5.scd | 8 +- man/waybar-custom.5.scd | 27 +++---- man/waybar-disk.5.scd | 8 +- man/waybar-dwl-tags.5.scd | 18 ++--- man/waybar-hyprland-language.5.scd | 16 ++-- man/waybar-hyprland-submap.5.scd | 14 ++-- man/waybar-hyprland-window.5.scd | 10 +-- man/waybar-hyprland-workspaces.5.scd | 2 +- man/waybar-idle-inhibitor.5.scd | 18 ++--- man/waybar-image.5.scd | 14 ++-- man/waybar-inhibitor.5.scd | 4 +- man/waybar-jack.5.scd | 82 +++++++++---------- man/waybar-keyboard-state.5.scd | 14 ++-- man/waybar-memory.5.scd | 8 +- man/waybar-mpris.5.scd | 25 +++--- man/waybar-network.5.scd | 8 +- man/waybar-river-layout.5.scd | 40 ++++----- man/waybar-river-mode.5.scd | 52 ++++++------ man/waybar-river-tags.5.scd | 18 ++--- man/waybar-river-window.5.scd | 12 +-- man/waybar-sndio.5.scd | 16 ++-- man/waybar-sway-language.5.scd | 4 +- man/waybar-sway-mode.5.scd | 12 +-- man/waybar-sway-scratchpad.5.scd | 10 +-- man/waybar-sway-window.5.scd | 20 ++--- man/waybar-sway-workspaces.5.scd | 116 +++++++++++++-------------- man/waybar-temperature.5.scd | 8 +- man/waybar-tray.5.scd | 24 +++--- man/waybar-upower.5.scd | 8 +- man/waybar-wireplumber.5.scd | 6 +- man/waybar-wlr-workspaces.5.scd | 5 +- 36 files changed, 421 insertions(+), 430 deletions(-) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index ca3d922b..cbadb8b9 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -25,12 +25,12 @@ The *backlight* module displays the current backlight level. The maximum length in characters the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + 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. *rotate*: ++ typeof: integer ++ @@ -81,9 +81,9 @@ The *backlight* module displays the current backlight level. ``` "backlight": { - "device": "intel_backlight", - "format": "{percent}% {icon}", - "format-icons": ["", ""] + "device": "intel_backlight", + "format": "{percent}% {icon}", + "format-icons": ["", ""] } ``` diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index fabde59a..52bc9f64 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -23,9 +23,9 @@ The *battery* module displays the current capacity and state (eg. charging) of y Define the max percentage of the battery, for when you've set the battery to stop charging at a lower level to save it. For example, if you've set the battery to stop at 80% that will become the new 100%. *design-capacity*: ++ - typeof: bool ++ - default: false ++ - Option to use the battery design capacity instead of it's current maximal capacity. + typeof: bool ++ + default: false ++ + Option to use the battery design capacity instead of it's current maximal capacity. *interval*: ++ typeof: integer ++ @@ -56,12 +56,12 @@ The *battery* module displays the current capacity and state (eg. charging) of y The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + 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. *rotate*: ++ typeof: integer++ @@ -132,8 +132,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 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*. @@ -141,15 +141,15 @@ The *battery* module allows one to define custom formats based on up to two fact ``` "battery": { - "bat": "BAT2", - "interval": 60, - "states": { - "warning": 30, - "critical": 15 - }, - "format": "{capacity}% {icon}", - "format-icons": ["", "", "", "", ""], - "max-length": 25 + "bat": "BAT2", + "interval": 60, + "states": { + "warning": 30, + "critical": 15 + }, + "format": "{capacity}% {icon}", + "format-icons": ["", "", "", "", ""], + "max-length": 25 } ``` diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 6a5e71a7..cca7c35f 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -57,12 +57,12 @@ Addressed by *bluetooth* The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-cava.5.scd b/man/waybar-cava.5.scd index 7cbb10d5..5d62572e 100644 --- a/man/waybar-cava.5.scd +++ b/man/waybar-cava.5.scd @@ -12,7 +12,7 @@ waybar - cava module # FILES $XDG_CONFIG_HOME/waybar/config ++ - Per user configuration file + Per user configuration file # ADDITIONAL FILES @@ -139,10 +139,10 @@ 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 - 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 + 1. In such case for 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 # RISING ISSUES @@ -158,25 +158,25 @@ In case when cava releases new version and you're wanna get it, it should be rai ``` "cava": { -// "cava_config": "$XDG_CONFIG_HOME/cava/cava.conf", - "framerate": 30, - "autosens": 1, -// "sensitivity": 100, - "bars": 14, - "lower_cutoff_freq": 50, - "higher_cutoff_freq": 10000, - "method": "pulse", - "source": "auto", - "stereo": true, - "reverse": false, - "bar_delimiter": 0, - "monstercat": false, - "waves": false, - "noise_reduction": 0.77, - "input_delay": 2, - "format-icons" : ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ], - "actions": { - "on-click-right": "mode" - } - }, + //"cava_config": "$XDG_CONFIG_HOME/cava/cava.conf", + "framerate": 30, + "autosens": 1, + //"sensitivity": 100, + "bars": 14, + "lower_cutoff_freq": 50, + "higher_cutoff_freq": 10000, + "method": "pulse", + "source": "auto", + "stereo": true, + "reverse": false, + "bar_delimiter": 0, + "monstercat": false, + "waves": false, + "noise_reduction": 0.77, + "input_delay": 2, + "format-icons" : ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ], + "actions": { + "on-click-right": "mode" + } +}, ``` diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index e0bf2946..0e855afa 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -11,7 +11,7 @@ waybar - clock module # FILES $XDG_CONFIG_HOME/waybar/config ++ - Per user configuration file + Per user configuration file # CONFIGURATION @@ -164,9 +164,9 @@ View all valid format options in *strftime(3)* or have a look {calendar}", - "calendar": { - "mode" : "year", - "mode-mon-col" : 3, - "weeks-pos" : "right", - "on-scroll" : 1, - "on-click-right": "mode", - "format": { - "months": "{}", - "days": "{}", - "weeks": "W{}", - "weekdays": "{}", - "today": "{}" - } - }, - "actions": { - "on-click-right": "mode", - "on-click-forward": "tz_up", - "on-click-backward": "tz_down", - "on-scroll-up": "shift_up", - "on-scroll-down": "shift_down" - } + "format": "{:%H:%M}  ", + "format-alt": "{:%A, %B %d, %Y (%R)}  ", + "tooltip-format": "{calendar}", + "calendar": { + "mode" : "year", + "mode-mon-col" : 3, + "weeks-pos" : "right", + "on-scroll" : 1, + "on-click-right": "mode", + "format": { + "months": "{}", + "days": "{}", + "weeks": "W{}", + "weekdays": "{}", + "today": "{}" + } + }, + "actions": { + "on-click-right": "mode", + "on-click-forward": "tz_up", + "on-click-backward": "tz_down", + "on-scroll-up": "shift_up", + "on-scroll-down": "shift_down" + } }, ``` @@ -205,10 +205,10 @@ View all valid format options in *strftime(3)* or have a look {calendar}", - "calendar": { - "mode" : "year", - "mode-mon-col" : 3, - "weeks-pos" : "right", - "on-scroll" : 1, - "on-click-right": "mode", - "format": { - "months": "{}", - "days": "{}", - "weeks": "W{}", - "weekdays": "{}", - "today": "{}" - } - }, - "actions": { - "on-click-right": "mode", - "on-click-forward": "tz_up", - "on-click-backward": "tz_down", - "on-scroll-up": "shift_up", - "on-scroll-down": "shift_down" - } - }, + "format": "{:%H:%M}  ", + "format-alt": "{:%A, %B %d, %Y (%R)}  ", + "tooltip-format": "\n{calendar}", + "calendar": { + "mode" : "year", + "mode-mon-col" : 3, + "weeks-pos" : "right", + "on-scroll" : 1, + "on-click-right": "mode", + "format": { + "months": "{}", + "days": "{}", + "weeks": "W{}", + "weekdays": "{}", + "today": "{}" + } + }, + "actions": { + "on-click-right": "mode", + "on-click-forward": "tz_up", + "on-click-backward": "tz_down", + "on-scroll-up": "shift_up", + "on-scroll-down": "shift_down" + } +}, ``` # AUTHOR diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index e3545536..ebf5f07f 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -30,12 +30,12 @@ The *cpu* module displays the current cpu utilization. The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + 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. *rotate*: ++ typeof: integer ++ diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 3d30859d..aeb419f9 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -1,5 +1,4 @@ waybar-custom(5) - # NAME waybar - custom module @@ -19,14 +18,13 @@ Addressed by *custom/* *exec-if*: ++ typeof: string ++ - The path to a script, which determines if the script in *exec* should be executed. + The path to a script, which determines if the script in *exec* should be executed. ++ *exec* will be executed if the exit code of *exec-if* equals 0. *exec-on-event*: ++ typeof: bool ++ default: true ++ - If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after - executing the event command. + If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after executing the event command. *return-type*: ++ typeof: string ++ @@ -34,20 +32,19 @@ Addressed by *custom/* *interval*: ++ 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. + 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. *restart-interval*: ++ typeof: integer ++ - The restart interval (in seconds). - Can't be used with the *interval* option, so only with continuous scripts. + 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*. *signal*: ++ typeof: integer ++ - The signal number used to update the module. + The signal number used to update the module. ++ The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*. *format*: ++ @@ -68,12 +65,12 @@ Addressed by *custom/* The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index 586c9dbd..8d9b8191 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -40,12 +40,12 @@ Addressed by *disk* The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-dwl-tags.5.scd b/man/waybar-dwl-tags.5.scd index 06fb577f..c9f1162d 100644 --- a/man/waybar-dwl-tags.5.scd +++ b/man/waybar-dwl-tags.5.scd @@ -13,24 +13,24 @@ The *tags* module displays the current state of tags in dwl. Addressed by *dwl/tags* *num-tags*: ++ - typeof: uint ++ - default: 9 ++ - The number of tags that should be displayed. Max 32. + typeof: uint ++ + default: 9 ++ + The number of tags that should be displayed. Max 32. *tag-labels*: ++ - typeof: array ++ - The label to display for each tag. + typeof: array ++ + The label to display for each tag. *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. + 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. # EXAMPLE ``` "dwl/tags": { - "num-tags": 5 + "num-tags": 5 } ``` diff --git a/man/waybar-hyprland-language.5.scd b/man/waybar-hyprland-language.5.scd index 3e92def8..dba7dbca 100644 --- a/man/waybar-hyprland-language.5.scd +++ b/man/waybar-hyprland-language.5.scd @@ -18,12 +18,12 @@ Addressed by *hyprland/language* The format, how information should be displayed. *format-* ++ - typeof: string++ - Provide an alternative name to display per language where is the language of your choosing. Can be passed multiple times with multiple languages as shown by the example below. + typeof: string++ + Provide an alternative name to display per language where is the language of your choosing. Can be passed multiple times with multiple languages as shown by the example below. *keyboard-name*: ++ - typeof: string ++ - Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended. + typeof: string ++ + Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended. # FORMAT REPLACEMENTS @@ -41,10 +41,10 @@ Addressed by *hyprland/language* ``` "hyprland/language": { - "format": "Lang: {long}" - "format-en": "AMERICA, HELL YEAH!" - "format-tr": "As bayrakları" - "keyboard-name": "at-translated-set-2-keyboard" + "format": "Lang: {long}" + "format-en": "AMERICA, HELL YEAH!" + "format-tr": "As bayrakları" + "keyboard-name": "at-translated-set-2-keyboard" } ``` diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index a00a2762..51c23cb9 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -26,12 +26,12 @@ Addressed by *hyprland/submap* The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ @@ -71,9 +71,9 @@ Addressed by *hyprland/submap* ``` "hyprland/submap": { - "format": "✌️ {}", - "max-length": 8, - "tooltip": false + "format": "✌️ {}", + "max-length": 8, + "tooltip": false } ``` diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd index 3e1926b7..4e9c5d18 100644 --- a/man/waybar-hyprland-window.5.scd +++ b/man/waybar-hyprland-window.5.scd @@ -62,11 +62,11 @@ Invalid expressions (e.g., mismatched parentheses) are skipped. ``` "hyprland/window": { - "format": "{}", - "rewrite": { - "(.*) - Mozilla Firefox": "🌎 $1", - "(.*) - zsh": "> [$1]" - } + "format": "{}", + "rewrite": { + "(.*) - Mozilla Firefox": "🌎 $1", + "(.*) - zsh": "> [$1]" + } } ``` diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 55b78b60..c3deb52e 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -61,7 +61,7 @@ Additional to workspace name matching, the following *format-icons* can be set. "default": "" }, "all-outputs": false, - "show-special": false + "show-special": false } ``` diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index f85456d8..7be3b568 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -28,12 +28,12 @@ screensaving, also known as "presentation mode". The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ @@ -64,9 +64,9 @@ screensaving, also known as "presentation mode". Threshold to be used when scrolling. *start-activated*: ++ - typeof: bool ++ - default: *false* ++ - Whether the inhibit should be activated when starting waybar. + typeof: bool ++ + default: *false* ++ + Whether the inhibit should be activated when starting waybar. *timeout*: ++ typeof: double ++ @@ -97,8 +97,8 @@ screensaving, also known as "presentation mode". "idle_inhibitor": { "format": "{icon}", "format-icons": { - "activated": "", - "deactivated": "" + "activated": "", + "deactivated": "" }, "timeout": 30.5 } diff --git a/man/waybar-image.5.scd b/man/waybar-image.5.scd index d47dba39..401d0cd2 100644 --- a/man/waybar-image.5.scd +++ b/man/waybar-image.5.scd @@ -13,24 +13,26 @@ The *image* module displays an image from a path. *path*: ++ typeof: string ++ The path to the image. + *exec*: ++ typeof: string ++ - The path to the script, which should return image path file - it will only execute if the path is not set + The path to the script, which should return image path file. ++ + It will only execute if the path is not set + *size*: ++ typeof: integer ++ The width/height to render the image. *interval*: ++ typeof: integer ++ - The interval (in seconds) to re-render the image. - This is useful if the contents of *path* changes. + The interval (in seconds) to re-render the image. ++ + This is useful if the contents of *path* changes. ++ If no *interval* is defined, the image will only be rendered once. *signal*: ++ typeof: integer ++ - The signal number used to update the module. - This can be used instead of *interval* if the file changes irregularly. + The signal number used to update the module. ++ + This can be used instead of *interval* if the file changes irregularly. ++ The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*. *on-click*: ++ diff --git a/man/waybar-inhibitor.5.scd b/man/waybar-inhibitor.5.scd index 10d41bd5..bf37d351 100644 --- a/man/waybar-inhibitor.5.scd +++ b/man/waybar-inhibitor.5.scd @@ -85,8 +85,8 @@ See *systemd-inhibit*(1) for more information. "what": "handle-lid-switch", "format": "{icon}", "format-icons": { - "activated": "", - "deactivated": "" + "activated": "", + "deactivated": "" } } ``` diff --git a/man/waybar-jack.5.scd b/man/waybar-jack.5.scd index b8a4cebe..e04314a9 100644 --- a/man/waybar-jack.5.scd +++ b/man/waybar-jack.5.scd @@ -13,73 +13,73 @@ The *jack* module displays the current state of the JACK server. Addressed by *jack* *format*: ++ - typeof: string ++ - default: *{load}%* ++ - The format, how information should be displayed. This format is used when other formats aren't specified. + typeof: string ++ + default: *{load}%* ++ + The format, how information should be displayed. This format is used when other formats aren't specified. *format-connected*: ++ - typeof: string ++ - This format is used when the module is connected to the JACK server. + typeof: string ++ + This format is used when the module is connected to the JACK server. *format-disconnected*: ++ - typeof: string ++ - This format is used when the module is not connected to the JACK server. + typeof: string ++ + This format is used when the module is not connected to the JACK server. *format-xrun*: ++ - typeof: string ++ - This format is used for one polling interval, when the JACK server reports an xrun. + typeof: string ++ + This format is used for one polling interval, when the JACK server reports an xrun. *realtime*: ++ - typeof: bool ++ - default: *true* ++ - Option to drop real-time privileges for the JACK client opened by Waybar. + typeof: bool ++ + default: *true* ++ + Option to drop real-time privileges for the JACK client opened by Waybar. *tooltip*: ++ - typeof: bool ++ - default: *true* ++ - Option to disable tooltip on hover. + typeof: bool ++ + default: *true* ++ + Option to disable tooltip on hover. *tooltip-format*: ++ - typeof: string ++ - default: *{bufsize}/{samplerate} {latency}ms* ++ - The format of information displayed in the tooltip. + typeof: string ++ + default: *{bufsize}/{samplerate} {latency}ms* ++ + The format of information displayed in the tooltip. *interval*: ++ - typeof: integer ++ - default: 1 ++ - The interval in which the information gets polled. + typeof: integer ++ + default: 1 ++ + The interval in which the information gets polled. *rotate*: ++ - typeof: integer ++ - Positive value to rotate the text label. + typeof: integer ++ + Positive value to rotate the text label. *max-length*: ++ - typeof: integer ++ - The maximum length in character the module should display. + typeof: integer ++ + The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ - typeof: string ++ - Command to execute when clicked on the module. + typeof: string ++ + Command to execute when clicked on the module. *on-click-middle*: ++ - typeof: string ++ - Command to execute when middle-clicked on the module using mousewheel. + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. *on-click-right*: ++ - typeof: string ++ - Command to execute when you right clicked on the module. + typeof: string ++ + Command to execute when you right clicked on the module. *on-update*: ++ - typeof: string ++ - Command to execute when the module is updated. + typeof: string ++ + Command to execute when the module is updated. # FORMAT REPLACEMENTS @@ -97,10 +97,10 @@ Addressed by *jack* ``` "jack": { - "format": "DSP {}%", - "format-xrun": "{xruns} xruns", - "format-disconnected": "DSP off", - "realtime": true + "format": "DSP {}%", + "format-xrun": "{xruns} xruns", + "format-disconnected": "DSP off", + "realtime": true } ``` diff --git a/man/waybar-keyboard-state.5.scd b/man/waybar-keyboard-state.5.scd index fef46709..f07d6854 100644 --- a/man/waybar-keyboard-state.5.scd +++ b/man/waybar-keyboard-state.5.scd @@ -65,13 +65,13 @@ The following *format-icons* can be set. ``` "keyboard-state": { - "numlock": true, - "capslock": true, - "format": "{name} {icon}", - "format-icons": { - "locked": "", - "unlocked": "" - } + "numlock": true, + "capslock": true, + "format": "{name} {icon}", + "format-icons": { + "locked": "", + "unlocked": "" + } } ``` diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index 34e342f3..77e00638 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -40,12 +40,12 @@ Addressed by *memory* The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index ad5c1df5..2e445696 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -13,8 +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 currenly active player. *ignored-players*: ++ typeof: []string ++ @@ -49,18 +48,15 @@ The *mpris* module displays currently playing media via libplayerctl. *artist-len*: ++ typeof: integer ++ - Maximum length of the Artist tag (Wide/Fullwidth Unicode characters - count as two). Set to zero to hide the artist in `{dynamic}` tag. + Maximum length of the Artist tag (Wide/Fullwidth Unicode characters count as two). Set to zero to hide the artist in `{dynamic}` tag. *album-len*: ++ typeof: integer ++ - Maximum length of the Album tag (Wide/Fullwidth Unicode characters count - as two). Set to zero to hide the album in `{dynamic}` tag. + Maximum length of the Album tag (Wide/Fullwidth Unicode characters count as two). Set to zero to hide the album in `{dynamic}` tag. *title-len*: ++ typeof: integer ++ - Maximum length of the Title tag (Wide/Fullwidth Unicode characters count - as two). Set to zero to hide the title in `{dynamic}` tag. + Maximum length of the Title tag (Wide/Fullwidth Unicode characters count as two). Set to zero to hide the title in `{dynamic}` tag. *dynamic-len*: ++ typeof: integer ++ @@ -101,14 +97,12 @@ 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 ++ default: "…" ++ - This character will be used when any of the tags exceed their maximum - length. If you don't want to use an ellipsis, set this to empty string. + This character will be used when any of the tags exceed their maximum length. If you don't want to use an ellipsis, set this to empty string. *rotate*: ++ typeof: integer ++ @@ -124,7 +118,7 @@ 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. + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. ++ If the module is rotated, it will follow the flow of the text. *on-click*: ++ @@ -148,8 +142,7 @@ The *mpris* module displays currently playing media via libplayerctl. *status-icons*: ++ typeof: map[string]string ++ - Allows setting _{status-icon}_ based on player status (playing, paused, - stopped). + Allows setting _{status-icon}_ based on player status (playing, paused, stopped). # FORMAT REPLACEMENTS @@ -167,7 +160,7 @@ The *mpris* module displays currently playing media via libplayerctl. *{length}*: Length of the track, formatted as HH:MM:SS *{dynamic}*: Use _{artist}_, _{album}_, _{title}_ and _{length}_, automatically omit++ - empty values + empty values *{player_icon}*: Chooses an icon from _player-icons_ based on _{player}_ diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index a80737d4..0367aaa4 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -65,12 +65,12 @@ Addressed by *network* The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-river-layout.5.scd b/man/waybar-river-layout.5.scd index 5b18eee8..2a1206ce 100644 --- a/man/waybar-river-layout.5.scd +++ b/man/waybar-river-layout.5.scd @@ -15,45 +15,45 @@ It may not be set until a layout is first applied. Addressed by *river/layout* *format*: ++ - typeof: string ++ - default: {} ++ - The format, how information should be displayed. On {} data gets inserted. + typeof: string ++ + default: {} ++ + The format, how information should be displayed. On {} data gets inserted. *rotate*: ++ - typeof: integer ++ - Positive value to rotate the text label. + typeof: integer ++ + Positive value to rotate the text label. *max-length*: ++ - typeof: integer ++ - The maximum length in character the module should display. + typeof: integer ++ + The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ - typeof: string ++ - Command to execute when clicked on the module. + typeof: string ++ + Command to execute when clicked on the module. *on-click-middle*: ++ - typeof: string ++ - Command to execute when middle-clicked on the module using mousewheel. + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. *on-click-right*: ++ - typeof: string ++ - Command to execute when you right clicked on the module. + typeof: string ++ + Command to execute when you right clicked on the module. # EXAMPLE ``` "river/layout": { - "format": "{}", - "min-length": 4, - "align": "right" + "format": "{}", + "min-length": 4, + "align": "right" } ``` diff --git a/man/waybar-river-mode.5.scd b/man/waybar-river-mode.5.scd index 4dec7f55..8dfb0ec6 100644 --- a/man/waybar-river-mode.5.scd +++ b/man/waybar-river-mode.5.scd @@ -13,59 +13,59 @@ The *mode* module displays the current mapping mode of river. Addressed by *river/mode* *format*: ++ - typeof: string ++ - default: {} ++ - The format, how information should be displayed. On {} data gets inserted. + typeof: string ++ + default: {} ++ + The format, how information should be displayed. On {} data gets inserted. *rotate*: ++ - typeof: integer ++ - Positive value to rotate the text label. + typeof: integer ++ + Positive value to rotate the text label. *max-length*: ++ - typeof: integer ++ - The maximum length in character the module should display. + typeof: integer ++ + The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ - typeof: string ++ - Command to execute when clicked on the module. + typeof: string ++ + Command to execute when clicked on the module. *on-click-middle*: ++ - typeof: string ++ - Command to execute when middle-clicked on the module using mousewheel. + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. *on-click-right*: ++ - typeof: string ++ - Command to execute when you right clicked on the module. + typeof: string ++ + Command to execute when you right clicked on the module. *on-update*: ++ - typeof: string ++ - Command to execute when the module is updated. + typeof: string ++ + Command to execute when the module is updated. *on-scroll-up*: ++ - typeof: string ++ - Command to execute when scrolling up on the module. + typeof: string ++ + Command to execute when scrolling up on the module. *on-scroll-down*: ++ - typeof: string ++ - Command to execute when scrolling down on the module. + typeof: string ++ + Command to execute when scrolling down on the module. *smooth-scrolling-threshold*: ++ - typeof: double ++ - Threshold to be used when scrolling. + typeof: double ++ + Threshold to be used when scrolling. # EXAMPLES ``` "river/mode": { - "format": " {}" + "format": " {}" } ``` diff --git a/man/waybar-river-tags.5.scd b/man/waybar-river-tags.5.scd index 7814ee49..b117546d 100644 --- a/man/waybar-river-tags.5.scd +++ b/man/waybar-river-tags.5.scd @@ -13,24 +13,24 @@ The *tags* module displays the current state of tags in river. Addressed by *river/tags* *num-tags*: ++ - typeof: uint ++ - default: 9 ++ - The number of tags that should be displayed. Max 32. + typeof: uint ++ + default: 9 ++ + The number of tags that should be displayed. Max 32. *tag-labels*: ++ - typeof: array ++ - The label to display for each tag. + typeof: array ++ + The label to display for each tag. *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. + 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. # EXAMPLE ``` "river/tags": { - "num-tags": 5 + "num-tags": 5 } ``` diff --git a/man/waybar-river-window.5.scd b/man/waybar-river-window.5.scd index d0497a0f..2f01e5c3 100644 --- a/man/waybar-river-window.5.scd +++ b/man/waybar-river-window.5.scd @@ -26,12 +26,12 @@ Addressed by *river/window* The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ @@ -49,11 +49,11 @@ Addressed by *river/window* ``` "river/window": { - "format": "{}" + "format": "{}" } ``` # STYLE - *#window* -- *#window.focused* Applied when the output this module's bar belongs to is focused. \ No newline at end of file +- *#window.focused* Applied when the output this module's bar belongs to is focused. diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd index 90a73f48..01471392 100644 --- a/man/waybar-sndio.5.scd +++ b/man/waybar-sndio.5.scd @@ -27,12 +27,12 @@ cursor is over the module, and clicking on the module toggles mute. The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + 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. *scroll-step*: ++ typeof: int ++ @@ -58,12 +58,12 @@ cursor is over the module, and clicking on the module toggles mute. *on-scroll-up*: ++ typeof: string ++ - Command to execute when scrolling up on the module. + Command to execute when scrolling up on the module. ++ This replaces the default behaviour of volume control. *on-scroll-down*: ++ typeof: string ++ - Command to execute when scrolling down on the module. + Command to execute when scrolling down on the module. ++ This replaces the default behaviour of volume control. *smooth-scrolling-threshold*: ++ @@ -80,8 +80,8 @@ cursor is over the module, and clicking on the module toggles mute. ``` "sndio": { - "format": "{raw_value} 🎜", - "scroll-step": 3 + "format": "{raw_value} 🎜", + "scroll-step": 3 } ``` diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index 1c88314c..c257ed75 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -43,11 +43,11 @@ Addressed by *sway/language* ``` "sway/language": { - "format": "{}", + "format": "{}", }, "sway/language": { - "format": "{short} {variant}", + "format": "{short} {variant}", } ``` diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index 29eed4f5..2aca7b0c 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -26,12 +26,12 @@ Addressed by *sway/mode* The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ @@ -70,8 +70,8 @@ Addressed by *sway/mode* ``` "sway/mode": { - "format": " {}", - "max-length": 50 + "format": " {}", + "max-length": 50 } ``` diff --git a/man/waybar-sway-scratchpad.5.scd b/man/waybar-sway-scratchpad.5.scd index 11fc32c0..64c43db6 100644 --- a/man/waybar-sway-scratchpad.5.scd +++ b/man/waybar-sway-scratchpad.5.scd @@ -50,11 +50,11 @@ Addressed by *sway/scratchpad* ``` "sway/scratchpad": { - "format": "{icon} {count}", - "show-empty": false, - "format-icons": ["", ""], - "tooltip": true, - "tooltip-format": "{app}: {title}" + "format": "{icon} {count}", + "show-empty": false, + "format-icons": ["", ""], + "tooltip": true, + "tooltip-format": "{app}: {title}" } ``` diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 8091e249..ef137873 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -26,12 +26,12 @@ Addressed by *sway/window* The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ @@ -124,12 +124,12 @@ Invalid expressions (e.g., mismatched parentheses) are skipped. ``` "sway/window": { - "format": "{}", - "max-length": 50, - "rewrite": { - "(.*) - Mozilla Firefox": "🌎 $1", - "(.*) - zsh": "> [$1]" - } + "format": "{}", + "max-length": 50, + "rewrite": { + "(.*) - Mozilla Firefox": "🌎 $1", + "(.*) - zsh": "> [$1]" + } } ``` diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index c934a32a..82d858de 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -13,74 +13,74 @@ The *workspaces* module displays the currently used workspaces in Sway. Addressed by *sway/workspaces* *all-outputs*: ++ - typeof: bool ++ - default: false ++ - If set to false, workspaces will only be shown on the output they are on. If set to true all workspaces will be shown on every output. + typeof: bool ++ + default: false ++ + If set to false, workspaces will only be shown on the output they are on. If set to true all workspaces will be shown on every output. *format*: ++ - typeof: string ++ - default: {value} ++ - The format, how information should be displayed. + typeof: string ++ + default: {value} ++ + The format, how information should be displayed. *format-icons*: ++ - typeof: array ++ - Based on the workspace name and state, the corresponding icon gets selected. See *icons*. + typeof: array ++ + Based on the workspace name and state, the corresponding icon gets selected. See *icons*. *disable-scroll*: ++ - typeof: bool ++ - default: false ++ - If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled. + typeof: bool ++ + default: false ++ + If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled. *disable-click*: ++ - typeof: bool ++ - default: false ++ - If set to false, you can click to change workspace. If set to true this behaviour is disabled. + typeof: bool ++ + default: false ++ + If set to false, you can click to change workspace. If set to true this behaviour is disabled. *smooth-scrolling-threshold*: ++ - typeof: double ++ - Threshold to be used when scrolling. + typeof: double ++ + Threshold to be used when scrolling. *disable-scroll-wraparound*: ++ - typeof: bool ++ - default: false ++ - If set to false, scrolling on the workspace indicator will wrap around to the first workspace when reading the end, and vice versa. If set to true this behavior is disabled. + typeof: bool ++ + default: false ++ + If set to false, scrolling on the workspace indicator will wrap around to the first workspace when reading the end, and vice versa. If set to true this behavior is disabled. *enable-bar-scroll*: ++ - typeof: bool ++ - default: false ++ - If set to false, you can't scroll to cycle throughout workspaces from the entire bar. If set to true this behaviour is enabled. + typeof: bool ++ + default: false ++ + If set to false, you can't scroll to cycle throughout workspaces from the entire bar. If set to true this behaviour is enabled. *disable-markup*: ++ - typeof: bool ++ - default: false ++ - If set to true, button label will escape pango markup. + typeof: bool ++ + default: false ++ + If set to true, button label will escape pango markup. *current-only*: ++ - typeof: bool ++ - default: false ++ - If set to true. Only focused workspaces will be shown. + typeof: bool ++ + default: false ++ + If set to true. Only focused workspaces will be shown. *persistent_workspaces*: ++ - typeof: json (see below) ++ - default: empty ++ - Lists workspaces that should always be shown, even when non existent + typeof: json (see below) ++ + default: empty ++ + Lists workspaces that should always be shown, even when non existent *on-update*: ++ - typeof: string ++ - Command to execute when the module is updated. + typeof: string ++ + Command to execute when the module is updated. *disable-auto-back-and-forth*: ++ - typeof: bool ++ - Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration. + typeof: bool ++ + Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration. *alphabetical_sort*: ++ - typeof: bool ++ - Whether to sort workspaces alphabetically. Please note this can make "swaymsg workspace prev/next" move to workspaces inconsistent with the ordering shown in Waybar. + typeof: bool ++ + Whether to sort workspaces alphabetically. Please note this can make "swaymsg workspace prev/next" move to workspaces inconsistent with the ordering shown in Waybar. warp-on-scroll: ++ - typeof: bool ++ - 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. + typeof: bool ++ + 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. # FORMAT REPLACEMENTS @@ -112,11 +112,11 @@ 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 - } + "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 + } } ``` @@ -126,20 +126,20 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge ``` "sway/workspaces": { - "disable-scroll": true, - "all-outputs": true, - "format": "{name}: {icon}", - "format-icons": { - "1": "", - "2": "", - "3": "", - "4": "", - "5": "", + "disable-scroll": true, + "all-outputs": true, + "format": "{name}: {icon}", + "format-icons": { + "1": "", + "2": "", + "3": "", + "4": "", + "5": "", "high-priority-named": [ "1", "2" ], - "urgent": "", - "focused": "", - "default": "" - } + "urgent": "", + "focused": "", + "default": "" + } } ``` diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index cc689dc0..8cb7367c 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -66,12 +66,12 @@ Addressed by *temperature* The maximum length in characters the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-tray.5.scd b/man/waybar-tray.5.scd index bfd1f296..294a261d 100644 --- a/man/waybar-tray.5.scd +++ b/man/waybar-tray.5.scd @@ -6,32 +6,32 @@ waybar - tray module # DESCRIPTION -_WARNING_ *tray* is still in beta. There may me bugs. Breaking changes may occur. +_WARNING_ *tray* is still in beta. There may be bugs. Breaking changes may occur. # CONFIGURATION Addressed by *tray* *icon-size*: ++ - typeof: integer ++ - Defines the size of the tray icons. + typeof: integer ++ + Defines the size of the tray icons. *show-passive-items*: ++ - typeof: bool ++ - default: false ++ - Defines visibility of the tray icons with *Passive* status. + typeof: bool ++ + default: false ++ + Defines visibility of the tray icons with *Passive* status. *smooth-scrolling-threshold*: ++ typeof: double ++ Threshold to be used when scrolling. *spacing*: ++ - typeof: integer ++ - Defines the spacing between the tray icons. + typeof: integer ++ + Defines the spacing between the tray icons. *reverse-direction*: ++ - typeof: bool ++ - Defines if new app icons should be added in a reverse order + typeof: bool ++ + Defines if new app icons should be added in a reverse order *on-update*: ++ typeof: string ++ @@ -41,8 +41,8 @@ Addressed by *tray* ``` "tray": { - "icon-size": 21, - "spacing": 10 + "icon-size": 21, + "spacing": 10 } ``` diff --git a/man/waybar-upower.5.scd b/man/waybar-upower.5.scd index fc37b665..5ccda07c 100644 --- a/man/waybar-upower.5.scd +++ b/man/waybar-upower.5.scd @@ -12,10 +12,10 @@ compatible devices in the tooltip. # CONFIGURATION *native-path*: ++ - typeof: string ++ - default: ++ - The battery to monitor. Refer to the https://upower.freedesktop.org/docs/UpDevice.html#UpDevice--native-path ++ - Can be obtained using `upower --dump` + typeof: string ++ + default: ++ + The battery to monitor. Refer to the https://upower.freedesktop.org/docs/UpDevice.html#UpDevice--native-path ++ + Can be obtained using `upower --dump` *icon-size*: ++ typeof: integer ++ diff --git a/man/waybar-wireplumber.5.scd b/man/waybar-wireplumber.5.scd index 473df926..4d13b4f1 100644 --- a/man/waybar-wireplumber.5.scd +++ b/man/waybar-wireplumber.5.scd @@ -93,9 +93,9 @@ The *wireplumber* module displays the current volume reported by WirePlumber. ``` "wireplumber": { - "format": "{volume}%", - "format-muted": "", - "on-click": "helvum" + "format": "{volume}%", + "format-muted": "", + "on-click": "helvum" } ``` diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-wlr-workspaces.5.scd index 4a256f02..1c9f2d89 100644 --- a/man/waybar-wlr-workspaces.5.scd +++ b/man/waybar-wlr-workspaces.5.scd @@ -29,9 +29,8 @@ Addressed by *wlr/workspaces* *sort-by-coordinates*: ++ 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. + 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. *sort-by-number*: ++ typeof: bool ++ From b91adc9f2920d1988c06c8aaa3322be03bebbb6e Mon Sep 17 00:00:00 2001 From: Jacob Birkett Date: Mon, 31 Jul 2023 09:37:33 -0700 Subject: [PATCH 071/842] 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 072/842] 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 073/842] 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 074/842] 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 075/842] 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 076/842] 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 077/842] 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 078/842] 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 079/842] 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 080/842] 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 081/842] 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 082/842] 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 083/842] 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 084/842] 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 085/842] 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 086/842] 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 087/842] 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 088/842] 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 089/842] 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 090/842] 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 091/842] 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 092/842] 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 093/842] 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 094/842] 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 095/842] 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 096/842] 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 097/842] 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 098/842] 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 099/842] 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 100/842] 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 101/842] 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 102/842] 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 103/842] 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 104/842] 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 105/842] 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 106/842] 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 107/842] 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 108/842] 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 109/842] 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 110/842] 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 111/842] 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 112/842] 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 113/842] 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 114/842] 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 115/842] 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 116/842] 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 117/842] 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 118/842] 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 119/842] 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 120/842] 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 121/842] 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 122/842] 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 123/842] 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 124/842] 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 125/842] 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 126/842] 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 127/842] 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 128/842] 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 129/842] 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 130/842] 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 131/842] 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 132/842] 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 133/842] 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 134/842] 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 135/842] 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 136/842] 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 137/842] 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 138/842] 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 139/842] 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 140/842] 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 141/842] 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 142/842] 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 143/842] 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 144/842] 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 145/842] 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 146/842] 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 147/842] 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 148/842] 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 149/842] 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 150/842] 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 151/842] 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 152/842] 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 153/842] 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 154/842] 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 155/842] 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 156/842] 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 157/842] 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 158/842] 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 159/842] 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 160/842] 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 161/842] 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 162/842] 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 163/842] 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 164/842] 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 165/842] 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 166/842] 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 167/842] 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 168/842] 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 169/842] 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 170/842] 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 171/842] 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 172/842] 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 173/842] 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 174/842] 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 175/842] 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 176/842] 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 177/842] 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 178/842] 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 179/842] 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 180/842] 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 181/842] 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 182/842] 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 183/842] 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 184/842] 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 185/842] 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 186/842] 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 187/842] 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 188/842] 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 189/842] 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 190/842] 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 191/842] 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 192/842] 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 193/842] 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 194/842] 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 195/842] 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 196/842] 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 197/842] 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 198/842] 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 199/842] 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 200/842] 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 201/842] 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 202/842] 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 203/842] 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 204/842] 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 205/842] 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 206/842] 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 207/842] 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 208/842] 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 209/842] 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 210/842] 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 211/842] 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 212/842] 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 213/842] 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 214/842] 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 215/842] 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 216/842] 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 217/842] 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 218/842] 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 219/842] 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 220/842] 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 221/842] 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 222/842] 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 223/842] 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 224/842] 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 225/842] 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 226/842] 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 227/842] 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 228/842] 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 229/842] 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 230/842] 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 231/842] 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 232/842] 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 233/842] 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 234/842] 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 235/842] 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 236/842] 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 237/842] 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 238/842] 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 239/842] 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 240/842] 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 241/842] 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 242/842] 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 243/842] 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 244/842] 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 245/842] 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 246/842] 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 247/842] 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 248/842] 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 249/842] 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 250/842] 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 251/842] 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 252/842] 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 253/842] 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 254/842] 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 255/842] 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 256/842] 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 257/842] 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 258/842] 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 259/842] 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 260/842] 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 261/842] 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 262/842] 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 c420b40668de351226c03b1b3822aae529dc4d21 Mon Sep 17 00:00:00 2001 From: Yaroslav Chvanov Date: Sat, 4 Nov 2023 18:17:45 +0300 Subject: [PATCH 263/842] refactor(backlight): use concrete types for some helper functions This fixes linking of the best_device() function with 'mold' linker. --- include/util/backlight_backend.hpp | 14 +--- src/util/backlight_backend.cpp | 122 ++++++++++++++--------------- 2 files changed, 63 insertions(+), 73 deletions(-) diff --git a/include/util/backlight_backend.hpp b/include/util/backlight_backend.hpp index 8dcb8958..20925b52 100644 --- a/include/util/backlight_backend.hpp +++ b/include/util/backlight_backend.hpp @@ -20,7 +20,7 @@ std::scoped_lock lock((backend).udev_thread_mutex_); \ __devices = (backend).devices_; \ } \ - auto varname = (backend).best_device(__devices.cbegin(), __devices.cend(), preferred_device); + auto varname = (backend).best_device(__devices, preferred_device); namespace waybar::util { @@ -61,16 +61,10 @@ class BacklightBackend { void set_scaled_brightness(std::string preferred_device, int brightness); int get_scaled_brightness(std::string preferred_device); - template - static void upsert_device(ForwardIt first, ForwardIt last, Inserter inserter, udev_device *dev); - - template - static void enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, udev *udev); - bool is_login_proxy_initialized() const { return static_cast(login_proxy_); } - template - static const BacklightDevice *best_device(ForwardIt first, ForwardIt last, std::string_view); + static const BacklightDevice *best_device(const std::vector &devices, + std::string_view); std::vector devices_; std::mutex udev_thread_mutex_; @@ -90,4 +84,4 @@ class BacklightBackend { static constexpr int EPOLL_MAX_EVENTS = 16; }; -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index 1512103c..60d5ca3a 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -73,6 +73,54 @@ void check_nn(const void *ptr, const char *message = "ptr was null") { namespace waybar::util { +static void upsert_device(std::vector &devices, udev_device *dev) { + const char *name = udev_device_get_sysname(dev); + check_nn(name); + + const char *actual_brightness_attr = + strncmp(name, "amdgpu_bl", 9) == 0 || strcmp(name, "apple-panel-bl") == 0 + ? "brightness" + : "actual_brightness"; + + const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr); + const char *max = udev_device_get_sysattr_value(dev, "max_brightness"); + const char *power = udev_device_get_sysattr_value(dev, "bl_power"); + + auto found = std::find_if(devices.begin(), devices.end(), [name](const BacklightDevice &device) { + return device.name() == name; + }); + if (found != devices.end()) { + if (actual != nullptr) { + found->set_actual(std::stoi(actual)); + } + if (max != nullptr) { + found->set_max(std::stoi(max)); + } + if (power != nullptr) { + found->set_powered(std::stoi(power) == 0); + } + } else { + const int actual_int = actual == nullptr ? 0 : std::stoi(actual); + const int max_int = max == nullptr ? 0 : std::stoi(max); + const bool power_bool = power == nullptr ? true : std::stoi(power) == 0; + devices.emplace_back(name, actual_int, max_int, power_bool); + } +} + +static void enumerate_devices(std::vector &devices, udev *udev) { + std::unique_ptr enumerate{udev_enumerate_new(udev)}; + udev_enumerate_add_match_subsystem(enumerate.get(), "backlight"); + udev_enumerate_scan_devices(enumerate.get()); + udev_list_entry *enum_devices = udev_enumerate_get_list_entry(enumerate.get()); + udev_list_entry *dev_list_entry; + udev_list_entry_foreach(dev_list_entry, enum_devices) { + const char *path = udev_list_entry_get_name(dev_list_entry); + std::unique_ptr dev{udev_device_new_from_syspath(udev, path)}; + check_nn(dev.get(), "dev new failed"); + upsert_device(devices, dev.get()); + } +} + BacklightDevice::BacklightDevice(std::string name, int actual, int max, bool powered) : name_(name), actual_(actual), max_(max), powered_(powered) {} @@ -95,8 +143,7 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, : on_updated_cb_(on_updated_cb), polling_interval_(interval), previous_best_({}) { std::unique_ptr udev_check{udev_new()}; check_nn(udev_check.get(), "Udev check new failed"); - enumerate_devices(devices_.begin(), devices_.end(), std::back_inserter(devices_), - udev_check.get()); + enumerate_devices(devices_, udev_check.get()); if (devices_.empty()) { throw std::runtime_error("No backlight found"); } @@ -145,12 +192,12 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, check_eq(event.data.fd, udev_fd, "unexpected udev fd"); std::unique_ptr dev{udev_monitor_receive_device(mon.get())}; check_nn(dev.get(), "epoll dev was null"); - upsert_device(devices.begin(), devices.end(), std::back_inserter(devices), dev.get()); + upsert_device(devices, dev.get()); } // Refresh state if timed out if (event_count == 0) { - enumerate_devices(devices.begin(), devices.end(), std::back_inserter(devices), udev.get()); + enumerate_devices(devices, udev.get()); } { std::scoped_lock lock(udev_thread_mutex_); @@ -161,19 +208,20 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, }; } -template -const BacklightDevice *BacklightBackend::best_device(ForwardIt first, ForwardIt last, +const BacklightDevice *BacklightBackend::best_device(const std::vector &devices, std::string_view preferred_device) { const auto found = std::find_if( - first, last, [preferred_device](const auto &dev) { return dev.name() == preferred_device; }); - if (found != last) { + devices.begin(), devices.end(), + [preferred_device](const BacklightDevice &dev) { return dev.name() == preferred_device; }); + if (found != devices.end()) { return &(*found); } const auto max = std::max_element( - first, last, [](const auto &l, const auto &r) { return l.get_max() < r.get_max(); }); + devices.begin(), devices.end(), + [](const BacklightDevice &l, const BacklightDevice &r) { return l.get_max() < r.get_max(); }); - return max == last ? nullptr : &(*max); + return max == devices.end() ? nullptr : &(*max); } const BacklightDevice *BacklightBackend::get_previous_best_device() { @@ -233,56 +281,4 @@ int BacklightBackend::get_scaled_brightness(std::string preferred_device) { return 0; } -template -void BacklightBackend::upsert_device(ForwardIt first, ForwardIt last, Inserter inserter, - udev_device *dev) { - const char *name = udev_device_get_sysname(dev); - check_nn(name); - - const char *actual_brightness_attr = - strncmp(name, "amdgpu_bl", 9) == 0 || strcmp(name, "apple-panel-bl") == 0 - ? "brightness" - : "actual_brightness"; - - const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr); - const char *max = udev_device_get_sysattr_value(dev, "max_brightness"); - const char *power = udev_device_get_sysattr_value(dev, "bl_power"); - - auto found = - std::find_if(first, last, [name](const auto &device) { return device.name() == name; }); - if (found != last) { - if (actual != nullptr) { - found->set_actual(std::stoi(actual)); - } - if (max != nullptr) { - found->set_max(std::stoi(max)); - } - if (power != nullptr) { - found->set_powered(std::stoi(power) == 0); - } - } else { - const int actual_int = actual == nullptr ? 0 : std::stoi(actual); - const int max_int = max == nullptr ? 0 : std::stoi(max); - const bool power_bool = power == nullptr ? true : std::stoi(power) == 0; - *inserter = BacklightDevice{name, actual_int, max_int, power_bool}; - ++inserter; - } -} - -template -void BacklightBackend::enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, - udev *udev) { - std::unique_ptr enumerate{udev_enumerate_new(udev)}; - udev_enumerate_add_match_subsystem(enumerate.get(), "backlight"); - udev_enumerate_scan_devices(enumerate.get()); - udev_list_entry *enum_devices = udev_enumerate_get_list_entry(enumerate.get()); - udev_list_entry *dev_list_entry; - udev_list_entry_foreach(dev_list_entry, enum_devices) { - const char *path = udev_list_entry_get_name(dev_list_entry); - std::unique_ptr dev{udev_device_new_from_syspath(udev, path)}; - check_nn(dev.get(), "dev new failed"); - upsert_device(first, last, inserter, dev.get()); - } -} - -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util From aa1f8a428b163538c68bdaa81afe6c54de3e09af Mon Sep 17 00:00:00 2001 From: Lukas Hannen Date: Mon, 6 Nov 2023 01:28:48 +0100 Subject: [PATCH 264/842] 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 265/842] 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 266/842] 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 267/842] 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 268/842] 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 269/842] 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 270/842] 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 271/842] 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 272/842] 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 273/842] 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 274/842] 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 275/842] 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 276/842] 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 277/842] 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 278/842] 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 279/842] 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 280/842] 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 281/842] 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 282/842] 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 283/842] 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 284/842] 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 285/842] 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 286/842] 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 287/842] 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 288/842] 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 289/842] 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 290/842] 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 291/842] 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 292/842] 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 293/842] 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 294/842] 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 295/842] 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 296/842] 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 297/842] 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 298/842] 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 299/842] 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 300/842] 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 301/842] 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 302/842] 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 303/842] 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 304/842] 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 305/842] 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 306/842] 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 307/842] 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 308/842] 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 309/842] 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 310/842] 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 311/842] 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 312/842] 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 313/842] 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 314/842] 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 315/842] 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 316/842] 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 317/842] 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 318/842] 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 319/842] 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 320/842] 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 321/842] 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 322/842] 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 323/842] 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 324/842] 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 325/842] 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 326/842] 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 94633c346ab2ed5360230667047b8b02e9b1585a Mon Sep 17 00:00:00 2001 From: Paul Rey Date: Thu, 25 Jan 2024 11:02:44 +0100 Subject: [PATCH 327/842] retrigger checks From 167f04a4dfab2b1d7834c7348185ce2b2ccb0dc8 Mon Sep 17 00:00:00 2001 From: Jay-716 <13422525511@163.com> Date: Thu, 25 Jan 2024 22:51:07 +0800 Subject: [PATCH 328/842] 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 329/842] 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 330/842] 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 331/842] * 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 332/842] 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 333/842] 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 334/842] 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 335/842] 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 336/842] 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 337/842] 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 338/842] 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 339/842] 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 340/842] 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 341/842] 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 342/842] 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 343/842] 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 344/842] 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 345/842] 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 346/842] 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 347/842] 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 348/842] 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 349/842] 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 350/842] 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 351/842] 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 352/842] 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 353/842] 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 354/842] 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 9ea470410f0e3d351ee0e5410e68ff21d64b6f53 Mon Sep 17 00:00:00 2001 From: alttabber Date: Tue, 13 Feb 2024 12:53:45 +0100 Subject: [PATCH 355/842] Add always on option for hyprland/submap --- include/modules/hyprland/submap.hpp | 3 +++ man/waybar-hyprland-submap.5.scd | 10 ++++++++++ src/modules/hyprland/submap.cpp | 29 ++++++++++++++++++++++++++--- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/include/modules/hyprland/submap.hpp b/include/modules/hyprland/submap.hpp index 4ff232ff..b70d236a 100644 --- a/include/modules/hyprland/submap.hpp +++ b/include/modules/hyprland/submap.hpp @@ -19,12 +19,15 @@ class Submap : public waybar::ALabel, public EventHandler { auto update() -> void override; private: + auto parseConfig(const Json::Value&) -> void; void onEvent(const std::string&) override; std::mutex mutex_; const Bar& bar_; util::JsonParser parser_; std::string submap_; + bool always_on_ = false; + std::string default_submap_ = "Default"; }; } // namespace waybar::modules::hyprland diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index 0dc0b11a..fdcb33a8 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -66,6 +66,16 @@ Addressed by *hyprland/submap* default: true ++ Option to disable tooltip on hover. +*always-on*: ++ + typeof: bool ++ + default: false ++ + Option to display the widget even when there's no active submap. + +*default-submap* ++ + typeof: string ++ + default: Default ++ + Option to set the submap name to display when not in an active submap. + # EXAMPLES diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index ce27fc9a..b9ae9f21 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -10,6 +10,8 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "submap", id, "{}", 0, true), bar_(bar) { modulesReady = true; + parseConfig(config); + if (!gIPC.get()) { gIPC = std::make_unique(); } @@ -17,6 +19,13 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) label_.hide(); ALabel::update(); + // Displays widget immediately if always_on_ assuming default submap + // Needs an actual way to retrive current submap on startup + if (always_on_) { + submap_ = default_submap_; + label_.get_style_context()->add_class(submap_); + } + // register for hyprland ipc gIPC->registerForIPC("submap", this); dp.emit(); @@ -28,6 +37,18 @@ Submap::~Submap() { std::lock_guard lg(mutex_); } +auto Submap::parseConfig(const Json::Value& config) -> void { + auto const alwaysOn = config["always-on"]; + if (alwaysOn.isBool()) { + always_on_ = alwaysOn.asBool(); + } + + auto const defaultSubmap = config["default-submap"]; + if (defaultSubmap.isString()) { + default_submap_ = defaultSubmap.asString(); + } +} + auto Submap::update() -> void { std::lock_guard lg(mutex_); @@ -54,16 +75,18 @@ void Submap::onEvent(const std::string& ev) { auto submapName = ev.substr(ev.find_last_of('>') + 1); submapName = waybar::util::sanitize_string(submapName); - if (!submap_.empty()){ + if (!submap_.empty()) { label_.get_style_context()->remove_class(submap_); } submap_ = submapName; + if (submap_.empty() && always_on_) { + submap_ = default_submap_; + } + 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 356/842] 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 357/842] 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 358/842] 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 359/842] 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 360/842] 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 361/842] 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 362/842] 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 363/842] 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 364/842] 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 365/842] 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 366/842] 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 367/842] 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 368/842] 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 369/842] 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 370/842] 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 371/842] 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 372/842] 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 373/842] 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 374/842] 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 375/842] 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 376/842] 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 377/842] 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 378/842] 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 379/842] 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 380/842] 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 381/842] 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 382/842] 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 383/842] 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 384/842] 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 385/842] 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 386/842] 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 387/842] 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 388/842] 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 389/842] 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 390/842] 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 391/842] 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 392/842] 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 393/842] 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 394/842] 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 395/842] 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 396/842] 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 397/842] 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 398/842] 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 399/842] 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 400/842] 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 401/842] 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 402/842] 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 403/842] 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 16079eae09944fe76e8ba78b1e8d834baf4fa289 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 25 Feb 2024 00:51:52 +0100 Subject: [PATCH 404/842] update m_output --- include/modules/hyprland/workspaces.hpp | 1 + src/modules/hyprland/workspaces.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 8d46b1a1..dac61d95 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -82,6 +82,7 @@ class Workspace { void setVisible(bool value = true) { m_isVisible = value; }; void setWindows(uint value) { m_windows = value; }; void setName(std::string const& value) { m_name = value; }; + void setOutput(std::string const& value) { m_output = value; }; bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); } void insertWindow(WindowCreationPayload create_window_paylod); std::string removeWindow(WindowAddress const& addr); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3e393121..ceb887ea 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -208,6 +208,7 @@ void Workspaces::doUpdate() { } spdlog::trace("Updating workspace states"); + auto IPC_workspaces = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { // active workspace->setActive(workspace->name() == m_activeWorkspaceName || @@ -226,6 +227,15 @@ void Workspaces::doUpdate() { if (m_withIcon) { workspaceIcon = workspace->selectIcon(m_iconsMap); } + + // update m_output + auto IPC_workspace = std::find_if(IPC_workspaces.begin(), IPC_workspaces.end(), [&workspace](auto &w) { + auto wNameRaw = w["name"].asString(); + auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; + return wName == workspace->name(); + }); + workspace->setOutput((*IPC_workspace)["monitor"].asString()); + workspace->update(m_format, workspaceIcon); } From 4cc2800a78cb930e74ee089329c6c0c2b24c7551 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 25 Feb 2024 00:52:33 +0100 Subject: [PATCH 405/842] add 'onThisMonitor' css class --- src/modules/hyprland/workspaces.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index ceb887ea..aa2db524 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -871,6 +871,7 @@ void Workspace::update(const std::string &format, const std::string &icon) { addOrRemoveClass(styleContext, isPersistent(), "persistent"); addOrRemoveClass(styleContext, isUrgent(), "urgent"); addOrRemoveClass(styleContext, isVisible(), "visible"); + addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "onThisMonitor"); std::string windows; auto windowSeparator = m_workspaceManager.getWindowSeparator(); From 2540c07f1d2080876d9d58a4f12dd2718267be73 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 24 Feb 2024 18:24:39 -0800 Subject: [PATCH 406/842] 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 407/842] 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 408/842] 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 409/842] 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 9bc8de88765a0c4fb76fd3fa8c0c59df0048e9b2 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sun, 25 Feb 2024 13:46:49 +0100 Subject: [PATCH 410/842] fix clang complaints --- src/modules/hyprland/workspaces.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index aa2db524..bae73d2e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -208,7 +208,7 @@ void Workspaces::doUpdate() { } spdlog::trace("Updating workspace states"); - auto IPC_workspaces = gIPC->getSocket1JsonReply("workspaces"); + auto updated_workspaces = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { // active workspace->setActive(workspace->name() == m_activeWorkspaceName || @@ -229,12 +229,13 @@ void Workspaces::doUpdate() { } // update m_output - auto IPC_workspace = std::find_if(IPC_workspaces.begin(), IPC_workspaces.end(), [&workspace](auto &w) { - auto wNameRaw = w["name"].asString(); - auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; - return wName == workspace->name(); - }); - workspace->setOutput((*IPC_workspace)["monitor"].asString()); + auto updated_workspace = + std::find_if(updated_workspaces.begin(), updated_workspaces.end(), [&workspace](auto &w) { + auto wNameRaw = w["name"].asString(); + auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; + return wName == workspace->name(); + }); + workspace->setOutput((*updated_workspace)["monitor"].asString()); workspace->update(m_format, workspaceIcon); } From 21089596448d7c1e6a75ad97b33916272f6019b3 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 25 Feb 2024 11:21:58 -0800 Subject: [PATCH 411/842] 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 412/842] 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 413/842] 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 414/842] 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 415/842] 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 416/842] 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 417/842] 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 418/842] 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 419/842] 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 420/842] 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 421/842] 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 422/842] 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 423/842] 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 424/842] 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 425/842] 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 426/842] 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 427/842] 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 428/842] 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 429/842] 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 430/842] 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 431/842] 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 432/842] 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 433/842] 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 434/842] 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 435/842] 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 436/842] 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 437/842] 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 df7f1fffcf56bcbd54d92c6085692236dd7ab968 Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Mon, 4 Mar 2024 13:17:30 +0100 Subject: [PATCH 438/842] feat(hyprland/workspaces): added options `move-to-monitor` and `active-per-monitor` --- include/modules/hyprland/workspaces.hpp | 4 ++ man/waybar-hyprland-workspaces.5.scd | 15 ++++++ src/modules/hyprland/workspaces.cpp | 61 ++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 91ea1653..df72f343 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -126,6 +126,8 @@ class Workspaces : public AModule, public EventHandler { auto allOutputs() const -> bool { return m_allOutputs; } auto showSpecial() const -> bool { return m_showSpecial; } auto activeOnly() const -> bool { return m_activeOnly; } + auto moveToMonitor() const -> bool { return m_moveToMonitor; } + auto activePerMonitor() const -> bool { return m_activePerMonitor; } auto getBarOutput() const -> std::string { return m_bar.output->name; } @@ -182,6 +184,8 @@ class Workspaces : public AModule, public EventHandler { bool m_allOutputs = false; bool m_showSpecial = false; bool m_activeOnly = false; + bool m_moveToMonitor = false; + bool m_activePerMonitor = false; Json::Value m_persistentWorkspaceConfig; // Map for windows stored in workspaces not present in the current bar. diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 12c1fe39..44218f9a 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -52,6 +52,21 @@ Addressed by *hyprland/workspaces* default: false ++ If set to true, only the active workspace will be shown. +*move-to-monitor*: ++ + typeof: bool ++ + default: false ++ + If set to true, open the workspace on the current monitor when clicking on a workspace button. + Otherwise, the workspace will open on the monitor where it was previously assigned. + Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland. + +*active-per-monitor*: ++ + typeof: bool ++ + default: false ++ + If set to true, each bar on each monitor will show its separate active + workspace being the currently focused workspace on this monitor. + Otherwise, all bars on all monitors will show the same active workspace + being the currently focused workspace on the currently focused monitor. + *ignore-workspaces*: ++ typeof: array ++ default: [] ++ diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 882e3806..458da823 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -83,6 +83,16 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { m_activeOnly = configActiveOnly.asBool(); } + auto configMoveToMonitor = config_["move-to-monitor"]; + if (configMoveToMonitor.isBool()) { + m_moveToMonitor = configMoveToMonitor.asBool(); + } + + auto configActivePerMonitor = config_["active-per-monitor"]; + if (configActivePerMonitor.isBool()) { + m_activePerMonitor = configActivePerMonitor.asBool(); + } + auto configSortBy = config_["sort-by"]; if (configSortBy.isString()) { auto sortByStr = configSortBy.asString(); @@ -321,7 +331,14 @@ void Workspaces::onEvent(const std::string &ev) { } void Workspaces::onWorkspaceActivated(std::string const &payload) { - m_activeWorkspaceName = payload; + if (!m_activePerMonitor) { + m_activeWorkspaceName = payload; + return; + } + auto activeWorkspace = gIPC->getSocket1JsonReply("activeworkspace"); + if (m_bar.output->name == activeWorkspace["monitor"].asString()) { + m_activeWorkspaceName = payload; + } } void Workspaces::onSpecialWorkspaceActivated(std::string const &payload) { @@ -374,6 +391,16 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { if (m_bar.output->name == monitorName) { Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); onWorkspaceCreated(workspaceName, clientsData); + if (m_activePerMonitor) { + for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { + if (m_bar.output->name == monitor["name"].asString()) { + auto ws = monitor["activeWorkspace"]; + if (ws.isObject() && (ws["name"].isString())) { + m_activeWorkspaceName = ws["name"].asString(); + } + } + } + } } else { spdlog::debug("Removing workspace because it was moved to another monitor: {}"); onWorkspaceDestroyed(workspaceName); @@ -399,10 +426,13 @@ void Workspaces::onWorkspaceRenamed(std::string const &payload) { void Workspaces::onMonitorFocused(std::string const &payload) { spdlog::trace("Monitor focused: {}", payload); - m_activeWorkspaceName = payload.substr(payload.find(',') + 1); + auto monitorName = payload.substr(0, payload.find(',')); + if (!m_activePerMonitor || m_bar.output->name == monitorName) { + m_activeWorkspaceName = payload.substr(payload.find(',') + 1); + } for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { - if (monitor["name"].asString() == payload.substr(0, payload.find(','))) { + if (monitor["name"].asString() == monitorName) { auto name = monitor["specialWorkspace"]["name"].asString(); m_activeSpecialWorkspaceName = !name.starts_with("special:") ? name : name.substr(8); } @@ -804,7 +834,18 @@ void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) } void Workspaces::init() { - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + if (!m_activePerMonitor) { + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + } else { + for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { + if (m_bar.output->name == monitor["name"].asString()) { + auto ws = monitor["activeWorkspace"]; + if (ws.isObject() && (ws["name"].isString())) { + m_activeWorkspaceName = ws["name"].asString(); + } + } + } + } initializeWorkspaces(); updateWindowCount(); @@ -1019,9 +1060,17 @@ bool Workspace::handleClicked(GdkEventButton *bt) const { if (bt->type == GDK_BUTTON_PRESS) { try { if (id() > 0) { // normal - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); + } else { + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } } else if (!isSpecial()) { // named (this includes persistent) - gIPC->getSocket1Reply("dispatch workspace name:" + name()); + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); + } else { + gIPC->getSocket1Reply("dispatch workspace name:" + name()); + } } else if (id() != -99) { // named special gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); } else { // special From 25b85800a55a218f01e030e497441cc0304dd013 Mon Sep 17 00:00:00 2001 From: Merlin Sievers Date: Mon, 4 Mar 2024 20:00:57 +0100 Subject: [PATCH 439/842] 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 ++ From 68889494d019705e87b519934b4625a2134336d7 Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Tue, 5 Mar 2024 11:12:07 +0100 Subject: [PATCH 440/842] Removed option `active-per-monitor` --- include/modules/hyprland/workspaces.hpp | 2 -- man/waybar-hyprland-workspaces.5.scd | 8 ----- src/modules/hyprland/workspaces.cpp | 44 +++---------------------- 3 files changed, 4 insertions(+), 50 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index df72f343..655bc460 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -127,7 +127,6 @@ class Workspaces : public AModule, public EventHandler { auto showSpecial() const -> bool { return m_showSpecial; } auto activeOnly() const -> bool { return m_activeOnly; } auto moveToMonitor() const -> bool { return m_moveToMonitor; } - auto activePerMonitor() const -> bool { return m_activePerMonitor; } auto getBarOutput() const -> std::string { return m_bar.output->name; } @@ -185,7 +184,6 @@ class Workspaces : public AModule, public EventHandler { bool m_showSpecial = false; bool m_activeOnly = false; bool m_moveToMonitor = false; - bool m_activePerMonitor = false; Json::Value m_persistentWorkspaceConfig; // Map for windows stored in workspaces not present in the current bar. diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 44218f9a..584beac1 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -59,14 +59,6 @@ Addressed by *hyprland/workspaces* Otherwise, the workspace will open on the monitor where it was previously assigned. Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland. -*active-per-monitor*: ++ - typeof: bool ++ - default: false ++ - If set to true, each bar on each monitor will show its separate active - workspace being the currently focused workspace on this monitor. - Otherwise, all bars on all monitors will show the same active workspace - being the currently focused workspace on the currently focused monitor. - *ignore-workspaces*: ++ typeof: array ++ default: [] ++ diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 458da823..3330fbc7 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -88,11 +88,6 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { m_moveToMonitor = configMoveToMonitor.asBool(); } - auto configActivePerMonitor = config_["active-per-monitor"]; - if (configActivePerMonitor.isBool()) { - m_activePerMonitor = configActivePerMonitor.asBool(); - } - auto configSortBy = config_["sort-by"]; if (configSortBy.isString()) { auto sortByStr = configSortBy.asString(); @@ -331,14 +326,7 @@ void Workspaces::onEvent(const std::string &ev) { } void Workspaces::onWorkspaceActivated(std::string const &payload) { - if (!m_activePerMonitor) { - m_activeWorkspaceName = payload; - return; - } - auto activeWorkspace = gIPC->getSocket1JsonReply("activeworkspace"); - if (m_bar.output->name == activeWorkspace["monitor"].asString()) { - m_activeWorkspaceName = payload; - } + m_activeWorkspaceName = payload; } void Workspaces::onSpecialWorkspaceActivated(std::string const &payload) { @@ -391,16 +379,6 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { if (m_bar.output->name == monitorName) { Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); onWorkspaceCreated(workspaceName, clientsData); - if (m_activePerMonitor) { - for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { - if (m_bar.output->name == monitor["name"].asString()) { - auto ws = monitor["activeWorkspace"]; - if (ws.isObject() && (ws["name"].isString())) { - m_activeWorkspaceName = ws["name"].asString(); - } - } - } - } } else { spdlog::debug("Removing workspace because it was moved to another monitor: {}"); onWorkspaceDestroyed(workspaceName); @@ -426,13 +404,10 @@ void Workspaces::onWorkspaceRenamed(std::string const &payload) { void Workspaces::onMonitorFocused(std::string const &payload) { spdlog::trace("Monitor focused: {}", payload); - auto monitorName = payload.substr(0, payload.find(',')); - if (!m_activePerMonitor || m_bar.output->name == monitorName) { - m_activeWorkspaceName = payload.substr(payload.find(',') + 1); - } + m_activeWorkspaceName = payload.substr(payload.find(',') + 1); for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { - if (monitor["name"].asString() == monitorName) { + if (monitor["name"].asString() == payload.substr(0, payload.find(','))) { auto name = monitor["specialWorkspace"]["name"].asString(); m_activeSpecialWorkspaceName = !name.starts_with("special:") ? name : name.substr(8); } @@ -834,18 +809,7 @@ void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) } void Workspaces::init() { - if (!m_activePerMonitor) { - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); - } else { - for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { - if (m_bar.output->name == monitor["name"].asString()) { - auto ws = monitor["activeWorkspace"]; - if (ws.isObject() && (ws["name"].isString())) { - m_activeWorkspaceName = ws["name"].asString(); - } - } - } - } + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); initializeWorkspaces(); updateWindowCount(); From 207e99876d7acfc43fff0bd5432ed4586ef80a4a Mon Sep 17 00:00:00 2001 From: Robin Ole Heinemann Date: Fri, 8 Mar 2024 21:48:27 +0100 Subject: [PATCH 441/842] feat: allow horizontal scroll --- src/AModule.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 9a9f1386..a451c3d6 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -51,7 +51,9 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: event_box_.add_events(Gdk::BUTTON_RELEASE_MASK); event_box_.signal_button_release_event().connect(sigc::mem_fun(*this, &AModule::handleRelease)); } - if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || enable_scroll) { + if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || + config_["on-scroll-left"].isString() || config_["on-scroll-right"].isString() || + enable_scroll) { event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll)); } @@ -179,6 +181,10 @@ bool AModule::handleScroll(GdkEventScroll* e) { eventName = "on-scroll-up"; else if (dir == SCROLL_DIR::DOWN) eventName = "on-scroll-down"; + else if (dir == SCROLL_DIR::LEFT) + eventName = "on-scroll-left"; + else if (dir == SCROLL_DIR::RIGHT) + eventName = "on-scroll-right"; // First call module actions this->AModule::doAction(eventName); From 7b3d155608d030094f813b5c1908e0017c202547 Mon Sep 17 00:00:00 2001 From: Jo De Boeck Date: Tue, 12 Mar 2024 00:09:47 +0200 Subject: [PATCH 442/842] Fix peristant workspaces for sway Fixes: #2998 Signed-off-by: Jo De Boeck --- src/modules/sway/workspaces.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 6464bf9a..68f1ac45 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -141,12 +141,12 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { for (const std::string &p_w_name : p_workspaces_names) { const Json::Value &p_w = p_workspaces[p_w_name]; - auto it = - std::find_if(payload.begin(), payload.end(), [&p_w_name](const Json::Value &node) { - return node["name"].asString() == p_w_name; - }); + auto it = std::find_if(workspaces_.begin(), workspaces_.end(), + [&p_w_name](const Json::Value &node) { + return node["name"].asString() == p_w_name; + }); - if (it != payload.end()) { + if (it != workspaces_.end()) { continue; // already displayed by some bar } @@ -156,7 +156,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { if (output.asString() == bar_.output->name) { Json::Value v; v["name"] = p_w_name; - v["target_output"] = bar_.output->name; + v["output"] = bar_.output->name; v["num"] = convertWorkspaceNameToNum(p_w_name); workspaces_.emplace_back(std::move(v)); break; @@ -166,7 +166,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { // Adding to all outputs Json::Value v; v["name"] = p_w_name; - v["target_output"] = ""; + v["output"] = ""; v["num"] = convertWorkspaceNameToNum(p_w_name); workspaces_.emplace_back(std::move(v)); } @@ -250,6 +250,9 @@ bool Workspaces::filterButtons() { } bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { + if (!node[flag].isBool()) { + return false; + } if (node[flag].asBool()) { return true; } From 32eac3ccb738691974121b77b4af0c47d1cbe524 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 13 Mar 2024 19:46:56 +0100 Subject: [PATCH 443/842] chore: 0.10.0 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 4ce7363d..e21ff262 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.24', + version: '0.10.0', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From 17734f0364f3390e439acc912e94148830dee9a2 Mon Sep 17 00:00:00 2001 From: Eldar Yusupov Date: Thu, 14 Mar 2024 23:07:45 +0300 Subject: [PATCH 444/842] Add dwl/window module --- README.md | 2 +- include/modules/dwl/window.hpp | 37 ++++++++++ man/waybar-dwl-window.5.scd | 118 ++++++++++++++++++++++++++++++ man/waybar.5.scd.in | 1 + meson.build | 2 + src/factory.cpp | 4 ++ src/modules/dwl/tags.cpp | 11 +-- src/modules/dwl/window.cpp | 126 +++++++++++++++++++++++++++++++++ 8 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 include/modules/dwl/window.hpp create mode 100644 man/waybar-dwl-window.5.scd create mode 100644 src/modules/dwl/window.cpp diff --git a/README.md b/README.md index 65be764c..c76c19d3 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ - Sway (Workspaces, Binding mode, Focused window name) - River (Mapping mode, Tags, Focused window name) - Hyprland (Window Icons, Workspaces, Focused window name) -- DWL (Tags) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc) +- DWL (Tags, Focused window name) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc) - Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Local time - Battery diff --git a/include/modules/dwl/window.hpp b/include/modules/dwl/window.hpp new file mode 100644 index 00000000..e4c96404 --- /dev/null +++ b/include/modules/dwl/window.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include + +#include "AAppIconLabel.hpp" +#include "bar.hpp" +#include "util/json.hpp" +#include "dwl-ipc-unstable-v2-client-protocol.h" + +namespace waybar::modules::dwl { + +class Window : public AAppIconLabel, public sigc::trackable { + public: + Window(const std::string&, const waybar::Bar&, const Json::Value&); + virtual ~Window() = default; + + void handle_layout(const uint32_t layout); + void handle_title(const char *title); + void handle_appid(const char *ppid); + void handle_layout_symbol(const char *layout_symbol); + void handle_frame(); + + struct zdwl_ipc_manager_v2 *status_manager_; + private: + const Bar& bar_; + + std::string title_; + std::string appid_; + std::string layout_symbol_; + uint32_t layout_; + + struct zdwl_ipc_output_v2 *output_status_; +}; + +} // namespace waybar::modules::dwl diff --git a/man/waybar-dwl-window.5.scd b/man/waybar-dwl-window.5.scd new file mode 100644 index 00000000..c2f5b93e --- /dev/null +++ b/man/waybar-dwl-window.5.scd @@ -0,0 +1,118 @@ +waybar-dwl-window(5) + +# NAME + +waybar - dwl window module + +# DESCRIPTION + +The *window* module displays the title of the currently focused window in DWL + +# CONFIGURATION + +Addressed by *dwl/window* + +*format*: ++ + typeof: string ++ + default: {title} ++ + The format, how information should be displayed. + +*rotate*: ++ + typeof: integer ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should accept. + +*align*: ++ + typeof: float ++ + The alignment of the label within the module, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*justify*: ++ + typeof: string ++ + The alignment of the text within the module's label, allowing options 'left', 'right', or 'center' to define the positioning. + +*on-click*: ++ + typeof: string ++ + Command to execute when clicked on the module. + +*on-click-middle*: ++ + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. + +*on-click-right*: ++ + typeof: string ++ + Command to execute when you right-click on the module. + +*on-update*: ++ + typeof: string ++ + Command to execute when the module is updated. + +*on-scroll-up*: ++ + typeof: string ++ + Command to execute when scrolling up on the module. + +*on-scroll-down*: ++ + typeof: string ++ + Command to execute when scrolling down on the module. + +*smooth-scrolling-threshold*: ++ + typeof: double ++ + Threshold to be used when scrolling. + +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to disable tooltip on hover. + +*rewrite*: ++ + typeof: object ++ + Rules to rewrite the module format output. See *rewrite rules*. + +*icon*: ++ + typeof: bool ++ + default: false ++ + Option to hide the application icon. + +*icon-size*: ++ + typeof: integer ++ + default: 24 ++ + Option to change the size of the application icon. + +# FORMAT REPLACEMENTS + +*{title}*: The title of the focused window. + +*{app_id}*: The app_id of the focused window. + +*{layout}*: The layout of the focused window. + +# REWRITE RULES + +*rewrite* is an object where keys are regular expressions and values are +rewrite rules if the expression matches. Rules may contain references to +captures of the expression. + +Regular expression and replacement follow ECMA-script rules. + +If no expression matches, the format output is left unchanged. + +Invalid expressions (e.g., mismatched parentheses) are skipped. + +# EXAMPLES + +``` +"dwl/window": { + "format": "{}", + "max-length": 50, + "rewrite": { + "(.*) - Mozilla Firefox": "🌎 $1", + "(.*) - zsh": "> [$1]" + } +} +``` diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 2d4de0c9..5fe30ca8 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -310,6 +310,7 @@ A group may hide all but one element, showing them only on mouse hover. In order - *waybar-custom(5)* - *waybar-disk(5)* - *waybar-dwl-tags(5)* +- *waybar-dwl-window(5)* - *waybar-gamemode(5)* - *waybar-hyprland-language(5)* - *waybar-hyprland-submap(5)* diff --git a/meson.build b/meson.build index e21ff262..74a037b5 100644 --- a/meson.build +++ b/meson.build @@ -293,7 +293,9 @@ endif if true add_project_arguments('-DHAVE_DWL', language: 'cpp') src_files += files('src/modules/dwl/tags.cpp') + src_files += files('src/modules/dwl/window.cpp') man_files += files('man/waybar-dwl-tags.5.scd') + man_files += files('man/waybar-dwl-window.5.scd') endif if true diff --git a/src/factory.cpp b/src/factory.cpp index 94076201..0549fe09 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -28,6 +28,7 @@ #endif #ifdef HAVE_DWL #include "modules/dwl/tags.hpp" +#include "modules/dwl/window.hpp" #endif #ifdef HAVE_HYPRLAND #include "modules/hyprland/language.hpp" @@ -187,6 +188,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "dwl/tags") { return new waybar::modules::dwl::Tags(id, bar_, config_[name]); } + if (ref == "dwl/window") { + return new waybar::modules::dwl::Window(id, bar_, config_[name]); + } #endif #ifdef HAVE_HYPRLAND if (ref == "hyprland/window") { diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index f36ece1d..942c269f 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -21,11 +21,11 @@ wl_array tags, layouts; static uint num_tags = 0; -void toggle_visibility(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { +static void toggle_visibility(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { // Intentionally empty } -void active(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t active) { +static void active(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t active) { // Intentionally empty } @@ -37,15 +37,15 @@ static void set_tag(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t tag : num_tags & ~(1 << tag); } -void set_layout_symbol(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *layout) { +static void set_layout_symbol(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *layout) { // Intentionally empty } -void title(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *title) { +static void title(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *title) { // Intentionally empty } -void dwl_frame(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { +static void dwl_frame(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { // Intentionally empty } @@ -97,6 +97,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con output_status_{nullptr} { struct wl_display *display = Client::inst()->wl_display; struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener_impl, this); wl_display_roundtrip(display); diff --git a/src/modules/dwl/window.cpp b/src/modules/dwl/window.cpp new file mode 100644 index 00000000..cfd47a05 --- /dev/null +++ b/src/modules/dwl/window.cpp @@ -0,0 +1,126 @@ +#include "modules/dwl/window.hpp" + +#include +#include +#include +#include +#include +#include + +#include "client.hpp" +#include "dwl-ipc-unstable-v2-client-protocol.h" + +#include "util/rewrite_string.hpp" + +namespace waybar::modules::dwl { + +static void toggle_visibility(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { + // Intentionally empty +} + +static void active(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t active) { + // Intentionally empty +} + +static void set_tag(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t tag, uint32_t state, + uint32_t clients, uint32_t focused) { + // Intentionally empty +} + +static void set_layout_symbol(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *layout) { + static_cast(data)->handle_layout_symbol(layout); +} + +static void title(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *title) { + static_cast(data)->handle_title(title); +} + +static void dwl_frame(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { + static_cast(data)->handle_frame(); +} + +static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t layout) { + static_cast(data)->handle_layout(layout); +} + +static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid){ + static_cast(data)->handle_appid(appid); +}; + +static const zdwl_ipc_output_v2_listener output_status_listener_impl{ + .toggle_visibility = toggle_visibility, + .active = active, + .tag = set_tag, + .layout = set_layout, + .title = title, + .appid = appid, + .layout_symbol = set_layout_symbol, + .frame = dwl_frame, +}; + +static void handle_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) { + if (std::strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) { + static_cast(data)->status_manager_ = static_cast( + (zdwl_ipc_manager_v2 *)wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 1)); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + /* Ignore event */ +} + +static const wl_registry_listener registry_listener_impl = {.global = handle_global, + .global_remove = handle_global_remove}; + +Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) + : AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar) { + struct wl_display *display = Client::inst()->wl_display; + struct wl_registry *registry = wl_display_get_registry(display); + + wl_registry_add_listener(registry, ®istry_listener_impl, this); + wl_display_roundtrip(display); + + if (!status_manager_) { + spdlog::error("dwl_status_manager_v2 not advertised"); + return; + } + + struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + output_status_ = zdwl_ipc_manager_v2_get_output(status_manager_, output); + zdwl_ipc_output_v2_add_listener(output_status_, &output_status_listener_impl, this); + zdwl_ipc_manager_v2_destroy(status_manager_); +} + +void Window::handle_title(const char *title) { + title_ = title; +} + +void Window::handle_appid(const char *appid) { + appid_ = appid; +} + +void Window::handle_layout_symbol(const char *layout_symbol) { + layout_symbol_ = layout_symbol; +} + +void Window::handle_layout(const uint32_t layout) { + layout_ = layout; +} + +void Window::handle_frame() { + label_.set_markup(waybar::util::rewriteString( + fmt::format( + fmt::runtime(format_), + fmt::arg("title", title_), + fmt::arg("layout", layout_symbol_), + fmt::arg("app_id", appid_)), + config_["rewrite"])); + updateAppIconName(appid_, ""); + updateAppIcon(); + if (tooltipEnabled()) { + label_.set_tooltip_text(title_); + } +} + +} // namespace waybar::modules::dwl From dcddddd3f1a1c1560a84caf791a21b4610f0fc7d Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Thu, 14 Mar 2024 00:58:33 -0700 Subject: [PATCH 445/842] fix(power-profiles-daemon): correctly set initial visibility The bus error when the daemon is not reachable prevents the initial update and keeps the module visible, as an empty section on the bar. Do the update explicitly before connecting to set initial visibility. While we at it, remove a couple of redundant `update()` calls. --- src/modules/power_profiles_daemon.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index ae3d7443..ac5f7a2a 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -42,6 +42,8 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", "net.hadess.PowerProfiles", sigc::mem_fun(*this, &PowerProfilesDaemon::busConnectedCb)); + // Schedule update to set the initial visibility + dp.emit(); } void PowerProfilesDaemon::busConnectedCb(Glib::RefPtr& r) { @@ -74,7 +76,6 @@ void PowerProfilesDaemon::getAllPropsCb(Glib::RefPtr& r) { powerProfilesProxy_->signal_properties_changed().connect( sigc::mem_fun(*this, &PowerProfilesDaemon::profileChangedCb)); populateInitState(); - dp.emit(); } catch (const std::exception& err) { spdlog::error("Failed to query power-profiles-daemon via dbus: {}", err.what()); } catch (const Glib::Error& err) { @@ -112,8 +113,6 @@ void PowerProfilesDaemon::populateInitState() { // Find the index of the current activated mode (to toggle) std::string str = profileStr.get(); switchToProfile(str); - - update(); } void PowerProfilesDaemon::profileChangedCb( @@ -128,7 +127,6 @@ void PowerProfilesDaemon::profileChangedCb( Glib::VariantBase::cast_dynamic>(activeProfileVariant->second) .get(); switchToProfile(activeProfile); - update(); } } } @@ -145,6 +143,7 @@ void PowerProfilesDaemon::switchToProfile(std::string const& str) { "Power profile daemon: can't find the active profile {} in the available profiles list", str); } + dp.emit(); } auto PowerProfilesDaemon::update() -> void { @@ -195,7 +194,7 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { void PowerProfilesDaemon::setPropCb(Glib::RefPtr& r) { try { auto _ = powerProfilesProxy_->call_finish(r); - update(); + dp.emit(); } catch (const std::exception& e) { spdlog::error("Failed to set the the active power profile: {}", e.what()); } catch (const Glib::Error& e) { From 4ccefa090280230f8c9c600d4e0ddd3f6bf14d07 Mon Sep 17 00:00:00 2001 From: luzpaz Date: Fri, 15 Mar 2024 11:05:55 -0400 Subject: [PATCH 446/842] README: tweak repology badge * Change repology badge header * Use 3 columns instead of 1 to display badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 65be764c..32fbfa69 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Waybar is available from a number of Linux distributions: -[![Packaging status](https://repology.org/badge/vertical-allrepos/waybar.svg)](https://repology.org/project/waybar/versions) +[![Packaging status](https://repology.org/badge/vertical-allrepos/waybar.svg?columns=3&header=Waybar%20Downstream%20Packaging)](https://repology.org/project/waybar/versions) An Ubuntu PPA with more recent versions is available [here](https://launchpad.net/~nschloe/+archive/ubuntu/waybar). From 2d122367267e2e46a4cb6da8c317d8f33d7a1ee4 Mon Sep 17 00:00:00 2001 From: Nicola Revelant Date: Fri, 15 Mar 2024 16:01:07 +0100 Subject: [PATCH 447/842] Use the correct thermal zone in FreeBSD --- src/modules/temperature.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index accab969..886ce14e 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -9,7 +9,7 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config) : ALabel(config, "temperature", id, "{temperatureC}°C", 10) { #if defined(__FreeBSD__) -// try to read sysctl? +// FreeBSD uses sysctlbyname instead of read from a file #else auto& hwmon_path = config_["hwmon-path"]; if (hwmon_path.isString()) { @@ -37,11 +37,19 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); } + + // check if file_path_ can be used to retrive the temperature std::ifstream temp(file_path_); if (!temp.is_open()) { throw std::runtime_error("Can't open " + file_path_); } + if (!temp.good()) { + temp.close(); + throw std::runtime_error("Can't read from " + file_path_); + } + temp.close(); #endif + thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); @@ -93,11 +101,10 @@ float waybar::modules::Temperature::getTemperature() { size_t size = sizeof temp; auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; - auto sysctl_thermal = fmt::format("hw.acpi.thermal.tz{}.temperature", zone); - if (sysctlbyname("hw.acpi.thermal.tz0.temperature", &temp, &size, NULL, 0) != 0) { + if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone), &temp, &size, NULL, 0) != 0) { throw std::runtime_error( - "sysctl hw.acpi.thermal.tz0.temperature or dev.cpu.0.temperature failed"); + fmt::format("sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone)); } auto temperature_c = ((float)temp - 2732) / 10; return temperature_c; @@ -110,6 +117,9 @@ float waybar::modules::Temperature::getTemperature() { std::string line; if (temp.good()) { getline(temp, line); + } else { + temp.close(); + throw std::runtime_error("Can't read from " + file_path_); } temp.close(); auto temperature_c = std::strtol(line.c_str(), nullptr, 10) / 1000.0; From e1f876b981196c5bc1c2936eff477d429d8a8c1d Mon Sep 17 00:00:00 2001 From: Nicola Revelant Date: Fri, 15 Mar 2024 16:36:54 +0100 Subject: [PATCH 448/842] Fix fmt::format: missing argument --- src/modules/temperature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 886ce14e..eb99d137 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -104,7 +104,7 @@ float waybar::modules::Temperature::getTemperature() { if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone), &temp, &size, NULL, 0) != 0) { throw std::runtime_error( - fmt::format("sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone)); + fmt::format("sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone, zone)); } auto temperature_c = ((float)temp - 2732) / 10; return temperature_c; From fbf66530a32ace31600c4cbbcbf6e04dced4cfc8 Mon Sep 17 00:00:00 2001 From: Nicola Revelant Date: Fri, 15 Mar 2024 16:52:52 +0100 Subject: [PATCH 449/842] Explicit convert from std::string to const char* --- src/modules/temperature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index eb99d137..439dd27c 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -102,7 +102,7 @@ float waybar::modules::Temperature::getTemperature() { auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; - if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone), &temp, &size, NULL, 0) != 0) { + if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone).c_str(), &temp, &size, NULL, 0) != 0) { throw std::runtime_error( fmt::format("sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone, zone)); } From 01ff7ebb36648bf7afa6ea32c7c32733023b7e95 Mon Sep 17 00:00:00 2001 From: Nicola Revelant Date: Fri, 15 Mar 2024 17:12:31 +0100 Subject: [PATCH 450/842] Fix clang-format to src/modules/temperature.cpp --- src/modules/temperature.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 439dd27c..f0629670 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -102,9 +102,10 @@ float waybar::modules::Temperature::getTemperature() { auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; - if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone).c_str(), &temp, &size, NULL, 0) != 0) { - throw std::runtime_error( - fmt::format("sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone, zone)); + if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone).c_str(), &temp, &size, + NULL, 0) != 0) { + throw std::runtime_error(fmt::format( + "sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone, zone)); } auto temperature_c = ((float)temp - 2732) / 10; return temperature_c; From c5a629939895915f01767c6ec06897e8463387a5 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 16 Mar 2024 20:10:05 +0800 Subject: [PATCH 451/842] fix:dwl tag crash when use wlr-randr enable monitor --- src/modules/dwl/tags.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index f36ece1d..6a07a878 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -155,6 +155,9 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con } Tags::~Tags() { + if(output_status_) { + zdwl_ipc_output_v2_destroy(output_status_); + } if (status_manager_) { zdwl_ipc_manager_v2_destroy(status_manager_); } From f014a7d2e5f08260501e24d93b44bb33a4bed861 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sat, 16 Mar 2024 21:22:01 +0100 Subject: [PATCH 452/842] man docs & different css class name --- man/waybar-hyprland-workspaces.5.scd | 1 + src/modules/hyprland/workspaces.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 12c1fe39..5646df58 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -158,3 +158,4 @@ Additional to workspace name matching, the following *format-icons* can be set. - *#workspaces button.persistent* - *#workspaces button.special* - *#workspaces button.urgent* +- *#workspaces button.hosting-monitor* (gets applied if workspace-monitor == waybar-monitor) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index bae73d2e..1b58b417 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -872,7 +872,7 @@ void Workspace::update(const std::string &format, const std::string &icon) { addOrRemoveClass(styleContext, isPersistent(), "persistent"); addOrRemoveClass(styleContext, isUrgent(), "urgent"); addOrRemoveClass(styleContext, isVisible(), "visible"); - addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "onThisMonitor"); + addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); std::string windows; auto windowSeparator = m_workspaceManager.getWindowSeparator(); From 736309ef1ff64b99c0f114178d6b83b4c69fb3b4 Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Sun, 17 Mar 2024 23:00:48 +0100 Subject: [PATCH 453/842] Fixed segfault --- src/modules/hyprland/workspaces.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 9e368306..3eb408ac 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -235,7 +235,10 @@ void Workspaces::doUpdate() { auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; return wName == workspace->name(); }); - workspace->setOutput((*updated_workspace)["monitor"].asString()); + + if (updated_workspace != updated_workspaces.end()) { + workspace->setOutput((*updated_workspace)["monitor"].asString()); + } workspace->update(m_format, workspaceIcon); } From bd8b215416cdca6ed0c929c18cede7dfb907edf0 Mon Sep 17 00:00:00 2001 From: Bartel Sielski Date: Mon, 18 Mar 2024 11:47:43 +0100 Subject: [PATCH 454/842] upower: Add 'low' and 'critical' CSS classes Add secondary CSS class based on the 'warning_level' field reported by UPower over D-Bus. This makes it possible to add custom styling when the battery is near empty. --- include/modules/upower/upower.hpp | 1 + src/modules/upower/upower.cpp | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index d763259b..8cea8c42 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -71,6 +71,7 @@ class UPower : public AModule { GDBusConnection *login1_connection; std::unique_ptr upower_tooltip; std::string lastStatus; + const char *lastWarningLevel; bool showAltText; bool showIcon = true; bool upowerRunning; diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index 3554d43b..c31f9ea7 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -8,6 +8,17 @@ #include "gtkmm/tooltip.h" #include "util/gtk_icon.hpp" +static const char* getDeviceWarningLevel(UpDeviceLevel level) { + switch (level) { + case UP_DEVICE_LEVEL_CRITICAL: + return "critical"; + case UP_DEVICE_LEVEL_LOW: + return "low"; + default: + return nullptr; + } +} + namespace waybar::modules::upower { UPower::UPower(const std::string& id, const Json::Value& config) : AModule(config, "upower", id), @@ -306,6 +317,7 @@ auto UPower::update() -> void { UpDeviceKind kind; UpDeviceState state; + UpDeviceLevel level = UP_DEVICE_LEVEL_UNKNOWN; double percentage; gint64 time_empty; gint64 time_full; @@ -318,7 +330,7 @@ auto UPower::update() -> void { if (displayDevice) { g_object_get(displayDevice, "kind", &kind, "state", &state, "percentage", &percentage, "icon-name", &icon_name, "time-to-empty", &time_empty, "time-to-full", &time_full, - NULL); + "warning-level", &level, NULL); /* Every Device which is handled by Upower and which is not * UP_DEVICE_KIND_UNKNOWN (0) or UP_DEVICE_KIND_LINE_POWER (1) is a Battery */ @@ -338,6 +350,15 @@ auto UPower::update() -> void { } lastStatus = status; + const char* warning_level = getDeviceWarningLevel(level); + if (lastWarningLevel && box_.get_style_context()->has_class(lastWarningLevel)) { + box_.get_style_context()->remove_class(lastWarningLevel); + } + if (warning_level && !box_.get_style_context()->has_class(warning_level)) { + box_.get_style_context()->add_class(warning_level); + } + lastWarningLevel = warning_level; + if (devices.size() == 0 && !displayDeviceValid && hideIfEmpty) { event_box_.set_visible(false); // Call parent update From bbb69bd977745fc9b4a5b7da345426da208423f3 Mon Sep 17 00:00:00 2001 From: Bartel Sielski Date: Mon, 18 Mar 2024 12:47:36 +0100 Subject: [PATCH 455/842] upower: Initialize variables There are code paths in which some of these variables were used but not initialized, causing undefined behavior. --- src/modules/upower/upower.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index c31f9ea7..ad4c4732 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -315,12 +315,12 @@ auto UPower::update() -> void { return; } - UpDeviceKind kind; - UpDeviceState state; + UpDeviceKind kind = UP_DEVICE_KIND_UNKNOWN; + UpDeviceState state = UP_DEVICE_STATE_UNKNOWN; UpDeviceLevel level = UP_DEVICE_LEVEL_UNKNOWN; - double percentage; - gint64 time_empty; - gint64 time_full; + double percentage = 0.0; + gint64 time_empty = 0; + gint64 time_full = 0; gchar* icon_name{(char*)'\0'}; std::string percentString{""}; std::string time_format{""}; From 67218d555437d97434b4288613cb79cb820af720 Mon Sep 17 00:00:00 2001 From: leiserfg Date: Mon, 18 Mar 2024 21:48:06 +0100 Subject: [PATCH 456/842] Make right-click to circle down ppd --- src/modules/power_profiles_daemon.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index ac5f7a2a..eaa47023 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -39,6 +39,7 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // adresses for compatibility sake. // // Revisit this in 2026, systems should be updated by then. + Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", "net.hadess.PowerProfiles", sigc::mem_fun(*this, &PowerProfilesDaemon::busConnectedCb)); @@ -175,9 +176,16 @@ auto PowerProfilesDaemon::update() -> void { bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { if (e->type == GdkEventType::GDK_BUTTON_PRESS && connected_) { - activeProfile_++; - if (activeProfile_ == availableProfiles_.end()) { - activeProfile_ = availableProfiles_.begin(); + if (e->button == 1) /* left click */ { + activeProfile_++; + if (activeProfile_ == availableProfiles_.end()) { + activeProfile_ = availableProfiles_.begin(); + } + } else { + if (activeProfile_ == availableProfiles_.begin()) { + activeProfile_ = availableProfiles_.end(); + } + activeProfile_--; } using VarStr = Glib::Variant; From 2ffd9a94a505a2e7e933ea8303f9cf2af33c35fe Mon Sep 17 00:00:00 2001 From: Jo De Boeck Date: Tue, 19 Mar 2024 07:40:35 +0200 Subject: [PATCH 457/842] Fix peristent class on buttons Fixes: #3009 Signed-off-by: Jo De Boeck --- src/modules/sway/workspaces.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 68f1ac45..8671288b 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -156,7 +156,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { if (output.asString() == bar_.output->name) { Json::Value v; v["name"] = p_w_name; - v["output"] = bar_.output->name; + v["target_output"] = bar_.output->name; v["num"] = convertWorkspaceNameToNum(p_w_name); workspaces_.emplace_back(std::move(v)); break; @@ -166,7 +166,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { // Adding to all outputs Json::Value v; v["name"] = p_w_name; - v["output"] = ""; + v["target_output"] = ""; v["num"] = convertWorkspaceNameToNum(p_w_name); workspaces_.emplace_back(std::move(v)); } @@ -313,7 +313,7 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("urgent"); } - if (hasFlag((*it), "target_output")) { + if ((*it)["target_output"].isString()) { button.get_style_context()->add_class("persistent"); } else { button.get_style_context()->remove_class("persistent"); From 856a34e16df9516939021c9bc42db674d0205227 Mon Sep 17 00:00:00 2001 From: hrdl <31923882+hrdl-github@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:52:40 +0100 Subject: [PATCH 458/842] Also consider floating nodes when checking for flags Fixes #3030 --- src/modules/sway/workspaces.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 68f1ac45..77e74465 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -261,6 +261,10 @@ bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { [&](auto const &e) { return hasFlag(e, flag); })) { return true; } + if (std::any_of(node["floating_nodes"].begin(), node["floating_nodes"].end(), + [&](auto const &e) { return hasFlag(e, flag); })) { + return true; + } return false; } From cf3389e5d736825b9abb075d5f13e71474cc709f Mon Sep 17 00:00:00 2001 From: wispl Date: Mon, 18 Mar 2024 19:37:09 -0400 Subject: [PATCH 459/842] Add empty workspace style for Sway --- src/modules/sway/workspaces.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 0c9b554f..0f85feed 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -322,6 +322,11 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("persistent"); } + if ((*it)["nodes"].size() == 0) { + button.get_style_context()->add_class("empty"); + } else { + button.get_style_context()->remove_class("empty"); + } if ((*it)["output"].isString()) { if (((*it)["output"].asString()) == bar_.output->name) { button.get_style_context()->add_class("current_output"); From 0fcf6bcebc7d366cf82baa1947ecc971f9cee961 Mon Sep 17 00:00:00 2001 From: wispl Date: Tue, 19 Mar 2024 22:56:19 -0400 Subject: [PATCH 460/842] Document sway workspace button.empty --- man/waybar-sway-workspaces.5.scd | 1 + 1 file changed, 1 insertion(+) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 3343b8d5..8f0ac858 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -182,5 +182,6 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge - *#workspaces button.focused* - *#workspaces button.urgent* - *#workspaces button.persistent* +- *#workspaces button.empty* - *#workspaces button.current_output* - *#workspaces button#sway-workspace-${name}* From 6d690ad48b142bb9882ba31796bf27e5e9913da8 Mon Sep 17 00:00:00 2001 From: Mauro Guida Date: Wed, 20 Mar 2024 13:28:35 +0100 Subject: [PATCH 461/842] fix(wlr/taskbar): crash on taskbar drag and drop event --- src/modules/wlr/taskbar.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 2709584b..d291a6a5 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -334,9 +334,7 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, } button.add_events(Gdk::BUTTON_PRESS_MASK); - button.signal_button_press_event().connect(sigc::mem_fun(*this, &Task::handle_clicked), false); - button.signal_button_release_event().connect(sigc::mem_fun(*this, &Task::handle_button_release), - false); + button.signal_button_release_event().connect(sigc::mem_fun(*this, &Task::handle_clicked), false); button.signal_motion_notify_event().connect(sigc::mem_fun(*this, &Task::handle_motion_notify), false); @@ -573,12 +571,8 @@ bool Task::handle_clicked(GdkEventButton *bt) { else spdlog::warn("Unknown action {}", action); - return true; -} - -bool Task::handle_button_release(GdkEventButton *bt) { drag_start_button = -1; - return false; + return true; } bool Task::handle_motion_notify(GdkEventMotion *mn) { From c841bf567b591eee1e5e00ce40d0039bedd3e14c Mon Sep 17 00:00:00 2001 From: Mauro Guida Date: Wed, 20 Mar 2024 15:03:25 +0100 Subject: [PATCH 462/842] fix(sway/workspaces): visible class doesn't work --- src/modules/sway/workspaces.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 0c9b554f..5c8014db 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -250,9 +250,6 @@ bool Workspaces::filterButtons() { } bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { - if (!node[flag].isBool()) { - return false; - } if (node[flag].asBool()) { return true; } From 2326727ccbf0456ccfd631e748955f7f67c44a4e Mon Sep 17 00:00:00 2001 From: Ryan Walklin Date: Thu, 15 Feb 2024 09:37:36 +1300 Subject: [PATCH 463/842] Update Wireplumber API to 0.5 The WP component loader API has changed to be asynchronous, so implement a (GAsyncReadyCallback)-based loader to manage them. Logging integration change was required for 0.5.0 RCs but not for the 0.5.0 release. Fix clang-tidy and clang-format warnings. Note these are significantly wider than the changes for 0.5.0 so optional beyond the existing patchset. --- include/modules/wireplumber.hpp | 5 +- meson.build | 2 +- src/modules/wireplumber.cpp | 194 ++++++++++++++++++-------------- 3 files changed, 117 insertions(+), 84 deletions(-) diff --git a/include/modules/wireplumber.hpp b/include/modules/wireplumber.hpp index 9bbf4d46..6255b95f 100644 --- a/include/modules/wireplumber.hpp +++ b/include/modules/wireplumber.hpp @@ -17,12 +17,15 @@ class Wireplumber : public ALabel { auto update() -> void override; private: - void loadRequiredApiModules(); + void asyncLoadRequiredApiModules(); void prepare(); void activatePlugins(); static void updateVolume(waybar::modules::Wireplumber* self, uint32_t id); static void updateNodeName(waybar::modules::Wireplumber* self, uint32_t id); static void onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self); + static void onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res, + waybar::modules::Wireplumber* self); + static void onMixerApiLoaded(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self); static void onObjectManagerInstalled(waybar::modules::Wireplumber* self); static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id); static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self); diff --git a/meson.build b/meson.build index e21ff262..12097608 100644 --- a/meson.build +++ b/meson.build @@ -92,7 +92,7 @@ libevdev = dependency('libevdev', required: get_option('libevdev')) libmpdclient = dependency('libmpdclient', required: get_option('mpd')) xkbregistry = dependency('xkbregistry') libjack = dependency('jack', required: get_option('jack')) -libwireplumber = dependency('wireplumber-0.4', required: get_option('wireplumber')) +libwireplumber = dependency('wireplumber-0.5', required: get_option('wireplumber')) libsndio = compiler.find_library('sndio', required: get_option('sndio')) if libsndio.found() diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index 51bb708d..bd019b62 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -18,31 +18,24 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val min_step_(0.0), node_id_(0) { wp_init(WP_INIT_PIPEWIRE); - wp_core_ = wp_core_new(NULL, NULL); + wp_core_ = wp_core_new(nullptr, nullptr, nullptr); apis_ = g_ptr_array_new_with_free_func(g_object_unref); om_ = wp_object_manager_new(); prepare(); - loadRequiredApiModules(); + spdlog::debug("[{}]: connecting to pipewire...", name_); - spdlog::debug("[{}]: connecting to pipewire...", this->name_); - - if (!wp_core_connect(wp_core_)) { - spdlog::error("[{}]: Could not connect to PipeWire", this->name_); + if (wp_core_connect(wp_core_) == 0) { + spdlog::error("[{}]: Could not connect to PipeWire", name_); throw std::runtime_error("Could not connect to PipeWire\n"); } - spdlog::debug("[{}]: connected!", this->name_); + spdlog::debug("[{}]: connected!", name_); g_signal_connect_swapped(om_, "installed", (GCallback)onObjectManagerInstalled, this); - activatePlugins(); - - dp.emit(); - - event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); - event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Wireplumber::handleScroll)); + asyncLoadRequiredApiModules(); } waybar::modules::Wireplumber::~Wireplumber() { @@ -63,32 +56,36 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* return; } - auto proxy = static_cast(wp_object_manager_lookup( - self->om_, WP_TYPE_GLOBAL_PROXY, WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", id, NULL)); + auto* proxy = static_cast(wp_object_manager_lookup(self->om_, WP_TYPE_GLOBAL_PROXY, + WP_CONSTRAINT_TYPE_G_PROPERTY, + "bound-id", "=u", id, nullptr)); - if (!proxy) { + if (proxy == nullptr) { auto err = fmt::format("Object '{}' not found\n", id); spdlog::error("[{}]: {}", self->name_, err); throw std::runtime_error(err); } g_autoptr(WpProperties) properties = - WP_IS_PIPEWIRE_OBJECT(proxy) ? wp_pipewire_object_get_properties(WP_PIPEWIRE_OBJECT(proxy)) - : wp_properties_new_empty(); - g_autoptr(WpProperties) global_p = wp_global_proxy_get_global_properties(WP_GLOBAL_PROXY(proxy)); + WP_IS_PIPEWIRE_OBJECT(proxy) != 0 + ? wp_pipewire_object_get_properties(WP_PIPEWIRE_OBJECT(proxy)) + : wp_properties_new_empty(); + g_autoptr(WpProperties) globalP = wp_global_proxy_get_global_properties(WP_GLOBAL_PROXY(proxy)); properties = wp_properties_ensure_unique_owner(properties); - wp_properties_add(properties, global_p); - wp_properties_set(properties, "object.id", NULL); - auto nick = wp_properties_get(properties, "node.nick"); - auto description = wp_properties_get(properties, "node.description"); + wp_properties_add(properties, globalP); + wp_properties_set(properties, "object.id", nullptr); + const auto* nick = wp_properties_get(properties, "node.nick"); + const auto* description = wp_properties_get(properties, "node.description"); - self->node_name_ = nick ? nick : description ? description : "Unknown node name"; + self->node_name_ = nick != nullptr ? nick + : description != nullptr ? description + : "Unknown node name"; spdlog::debug("[{}]: Updating node name to: {}", self->name_, self->node_name_); } void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self, uint32_t id) { spdlog::debug("[{}]: updating volume", self->name_); - GVariant* variant = NULL; + GVariant* variant = nullptr; if (!isValidNodeId(id)) { spdlog::error("[{}]: '{}' is not a valid node ID. Ignoring volume update.", self->name_, id); @@ -97,7 +94,7 @@ void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* se g_signal_emit_by_name(self->mixer_api_, "get-volume", id, &variant); - if (!variant) { + if (variant == nullptr) { auto err = fmt::format("Node {} does not support volume\n", id); spdlog::error("[{}]: {}", self->name_, err); throw std::runtime_error(err); @@ -115,9 +112,9 @@ void waybar::modules::Wireplumber::onMixerChanged(waybar::modules::Wireplumber* spdlog::debug("[{}]: (onMixerChanged) - id: {}", self->name_, id); g_autoptr(WpNode) node = static_cast(wp_object_manager_lookup( - self->om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", id, NULL)); + self->om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", id, nullptr)); - if (!node) { + if (node == nullptr) { spdlog::warn("[{}]: (onMixerChanged) - Object with id {} not found", self->name_, id); return; } @@ -140,49 +137,49 @@ void waybar::modules::Wireplumber::onMixerChanged(waybar::modules::Wireplumber* void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wireplumber* self) { spdlog::debug("[{}]: (onDefaultNodesApiChanged)", self->name_); - uint32_t default_node_id; - g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &default_node_id); + uint32_t defaultNodeId; + g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &defaultNodeId); - if (!isValidNodeId(default_node_id)) { + if (!isValidNodeId(defaultNodeId)) { spdlog::warn("[{}]: '{}' is not a valid node ID. Ignoring node change.", self->name_, - default_node_id); + defaultNodeId); return; } g_autoptr(WpNode) node = static_cast( wp_object_manager_lookup(self->om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", - "=u", default_node_id, NULL)); + "=u", defaultNodeId, nullptr)); - if (!node) { + if (node == nullptr) { spdlog::warn("[{}]: (onDefaultNodesApiChanged) - Object with id {} not found", self->name_, - default_node_id); + defaultNodeId); return; } - const gchar* default_node_name = + const gchar* defaultNodeName = wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(node), "node.name"); spdlog::debug( "[{}]: (onDefaultNodesApiChanged) - got the following default node: Node(name: {}, id: {})", - self->name_, default_node_name, default_node_id); + self->name_, defaultNodeName, defaultNodeId); - if (g_strcmp0(self->default_node_name_, default_node_name) == 0) { + if (g_strcmp0(self->default_node_name_, defaultNodeName) == 0) { spdlog::debug( "[{}]: (onDefaultNodesApiChanged) - Default node has not changed. Node(name: {}, id: {}). " "Ignoring.", - self->name_, self->default_node_name_, default_node_id); + self->name_, self->default_node_name_, defaultNodeId); return; } spdlog::debug( "[{}]: (onDefaultNodesApiChanged) - Default node changed to -> Node(name: {}, id: {})", - self->name_, default_node_name, default_node_id); + self->name_, defaultNodeName, defaultNodeId); g_free(self->default_node_name_); - self->default_node_name_ = g_strdup(default_node_name); - self->node_id_ = default_node_id; - updateVolume(self, default_node_id); - updateNodeName(self, default_node_id); + self->default_node_name_ = g_strdup(defaultNodeName); + self->node_id_ = defaultNodeId; + updateVolume(self, defaultNodeId); + updateNodeName(self, defaultNodeId); } void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wireplumber* self) { @@ -190,14 +187,14 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir self->def_nodes_api_ = wp_plugin_find(self->wp_core_, "default-nodes-api"); - if (!self->def_nodes_api_) { + if (self->def_nodes_api_ == nullptr) { spdlog::error("[{}]: default nodes api is not loaded.", self->name_); throw std::runtime_error("Default nodes API is not loaded\n"); } self->mixer_api_ = wp_plugin_find(self->wp_core_, "mixer-api"); - if (!self->mixer_api_) { + if (self->mixer_api_ == nullptr) { spdlog::error("[{}]: mixer api is not loaded.", self->name_); throw std::runtime_error("Mixer api is not loaded\n"); } @@ -206,7 +203,7 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir &self->default_node_name_); g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &self->node_id_); - if (self->default_node_name_) { + if (self->default_node_name_ != nullptr) { spdlog::debug("[{}]: (onObjectManagerInstalled) - default configured node name: {} and id: {}", self->name_, self->default_node_name_, self->node_id_); } @@ -221,11 +218,11 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir void waybar::modules::Wireplumber::onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self) { - auto plugin_name = wp_plugin_get_name(WP_PLUGIN(p)); - spdlog::debug("[{}]: onPluginActivated: {}", self->name_, plugin_name); - g_autoptr(GError) error = NULL; + const auto* pluginName = wp_plugin_get_name(WP_PLUGIN(p)); + spdlog::debug("[{}]: onPluginActivated: {}", self->name_, pluginName); + g_autoptr(GError) error = nullptr; - if (!wp_object_activate_finish(p, res, &error)) { + if (wp_object_activate_finish(p, res, &error) == 0) { spdlog::error("[{}]: error activating plugin: {}", self->name_, error->message); throw std::runtime_error(error->message); } @@ -240,7 +237,7 @@ void waybar::modules::Wireplumber::activatePlugins() { for (uint16_t i = 0; i < apis_->len; i++) { WpPlugin* plugin = static_cast(g_ptr_array_index(apis_, i)); pending_plugins_++; - wp_object_activate(WP_OBJECT(plugin), WP_PLUGIN_FEATURE_ENABLED, NULL, + wp_object_activate(WP_OBJECT(plugin), WP_PLUGIN_FEATURE_ENABLED, nullptr, (GAsyncReadyCallback)onPluginActivated, this); } } @@ -248,34 +245,67 @@ void waybar::modules::Wireplumber::activatePlugins() { void waybar::modules::Wireplumber::prepare() { spdlog::debug("[{}]: preparing object manager", name_); wp_object_manager_add_interest(om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", - "=s", "Audio/Sink", NULL); + "=s", "Audio/Sink", nullptr); } -void waybar::modules::Wireplumber::loadRequiredApiModules() { - spdlog::debug("[{}]: loading required modules", name_); - g_autoptr(GError) error = NULL; +void waybar::modules::Wireplumber::onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res, + waybar::modules::Wireplumber* self) { + gboolean success = FALSE; + g_autoptr(GError) error = nullptr; - if (!wp_core_load_component(wp_core_, "libwireplumber-module-default-nodes-api", "module", NULL, - &error)) { + spdlog::debug("[{}]: callback loading default node api module", self->name_); + + success = wp_core_load_component_finish(self->wp_core_, res, &error); + + if (success == FALSE) { + spdlog::error("[{}]: default nodes API load failed", self->name_); + throw std::runtime_error(error->message); + } + spdlog::debug("[{}]: loaded default nodes api", self->name_); + g_ptr_array_add(self->apis_, wp_plugin_find(self->wp_core_, "default-nodes-api")); + + spdlog::debug("[{}]: loading mixer api module", self->name_); + wp_core_load_component(self->wp_core_, "libwireplumber-module-mixer-api", "module", nullptr, + "mixer-api", nullptr, (GAsyncReadyCallback)onMixerApiLoaded, self); +} + +void waybar::modules::Wireplumber::onMixerApiLoaded(WpObject* p, GAsyncResult* res, + waybar::modules::Wireplumber* self) { + gboolean success = FALSE; + g_autoptr(GError) error = nullptr; + + success = wp_core_load_component_finish(self->wp_core_, res, nullptr); + + if (success == FALSE) { + spdlog::error("[{}]: mixer API load failed", self->name_); throw std::runtime_error(error->message); } - if (!wp_core_load_component(wp_core_, "libwireplumber-module-mixer-api", "module", NULL, - &error)) { - throw std::runtime_error(error->message); - } - - g_ptr_array_add(apis_, wp_plugin_find(wp_core_, "default-nodes-api")); - g_ptr_array_add(apis_, ({ - WpPlugin* p = wp_plugin_find(wp_core_, "mixer-api"); - g_object_set(G_OBJECT(p), "scale", 1 /* cubic */, NULL); + spdlog::debug("[{}]: loaded mixer API", self->name_); + g_ptr_array_add(self->apis_, ({ + WpPlugin* p = wp_plugin_find(self->wp_core_, "mixer-api"); + g_object_set(G_OBJECT(p), "scale", 1 /* cubic */, nullptr); p; })); + + self->activatePlugins(); + + self->dp.emit(); + + self->event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); + self->event_box_.signal_scroll_event().connect(sigc::mem_fun(*self, &Wireplumber::handleScroll)); +} + +void waybar::modules::Wireplumber::asyncLoadRequiredApiModules() { + spdlog::debug("[{}]: loading default nodes api module", name_); + wp_core_load_component(wp_core_, "libwireplumber-module-default-nodes-api", "module", nullptr, + "default-nodes-api", nullptr, (GAsyncReadyCallback)onDefaultNodesApiLoaded, + this); } auto waybar::modules::Wireplumber::update() -> void { auto format = format_; - std::string tooltip_format; + std::string tooltipFormat; if (muted_) { format = config_["format-muted"].isString() ? config_["format-muted"].asString() : format; @@ -292,12 +322,12 @@ auto waybar::modules::Wireplumber::update() -> void { getState(vol); if (tooltipEnabled()) { - if (tooltip_format.empty() && config_["tooltip-format"].isString()) { - tooltip_format = config_["tooltip-format"].asString(); + if (tooltipFormat.empty() && config_["tooltip-format"].isString()) { + tooltipFormat = config_["tooltip-format"].asString(); } - if (!tooltip_format.empty()) { - label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), + if (!tooltipFormat.empty()) { + label_.set_tooltip_text(fmt::format(fmt::runtime(tooltipFormat), fmt::arg("node_name", node_name_), fmt::arg("volume", vol), fmt::arg("icon", getIcon(vol)))); } else { @@ -317,31 +347,31 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) { if (dir == SCROLL_DIR::NONE) { return true; } - double max_volume = 1; + double maxVolume = 1; double step = 1.0 / 100.0; if (config_["scroll-step"].isDouble()) { step = config_["scroll-step"].asDouble() / 100.0; } if (config_["max-volume"].isDouble()) { - max_volume = config_["max-volume"].asDouble() / 100.0; + maxVolume = config_["max-volume"].asDouble() / 100.0; } if (step < min_step_) step = min_step_; - double new_vol = volume_; + double newVol = volume_; if (dir == SCROLL_DIR::UP) { - if (volume_ < max_volume) { - new_vol = volume_ + step; - if (new_vol > max_volume) new_vol = max_volume; + if (volume_ < maxVolume) { + newVol = volume_ + step; + if (newVol > maxVolume) newVol = maxVolume; } } else if (dir == SCROLL_DIR::DOWN) { if (volume_ > 0) { - new_vol = volume_ - step; - if (new_vol < 0) new_vol = 0; + newVol = volume_ - step; + if (newVol < 0) newVol = 0; } } - if (new_vol != volume_) { - GVariant* variant = g_variant_new_double(new_vol); + if (newVol != volume_) { + GVariant* variant = g_variant_new_double(newVol); gboolean ret; g_signal_emit_by_name(mixer_api_, "set-volume", node_id_, variant, &ret); } From fe0716bf398a69188a20f871918f555c40ae86d1 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 22 Mar 2024 23:13:10 +0100 Subject: [PATCH 464/842] fix: lint --- src/modules/dwl/tags.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index 6a07a878..036cb1b8 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -155,7 +155,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con } Tags::~Tags() { - if(output_status_) { + if (output_status_) { zdwl_ipc_output_v2_destroy(output_status_); } if (status_manager_) { From 19f3ce6f856c11735ba0781d8bf21a49f84c4840 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 22 Mar 2024 23:21:57 +0100 Subject: [PATCH 465/842] fix: lint --- include/modules/dwl/window.hpp | 7 ++++--- src/modules/dwl/window.cpp | 28 ++++++++-------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/include/modules/dwl/window.hpp b/include/modules/dwl/window.hpp index e4c96404..6b068360 100644 --- a/include/modules/dwl/window.hpp +++ b/include/modules/dwl/window.hpp @@ -6,14 +6,14 @@ #include "AAppIconLabel.hpp" #include "bar.hpp" -#include "util/json.hpp" #include "dwl-ipc-unstable-v2-client-protocol.h" +#include "util/json.hpp" namespace waybar::modules::dwl { class Window : public AAppIconLabel, public sigc::trackable { public: - Window(const std::string&, const waybar::Bar&, const Json::Value&); + Window(const std::string &, const waybar::Bar &, const Json::Value &); virtual ~Window() = default; void handle_layout(const uint32_t layout); @@ -23,8 +23,9 @@ class Window : public AAppIconLabel, public sigc::trackable { void handle_frame(); struct zdwl_ipc_manager_v2 *status_manager_; + private: - const Bar& bar_; + const Bar &bar_; std::string title_; std::string appid_; diff --git a/src/modules/dwl/window.cpp b/src/modules/dwl/window.cpp index cfd47a05..4f8b0281 100644 --- a/src/modules/dwl/window.cpp +++ b/src/modules/dwl/window.cpp @@ -9,7 +9,6 @@ #include "client.hpp" #include "dwl-ipc-unstable-v2-client-protocol.h" - #include "util/rewrite_string.hpp" namespace waybar::modules::dwl { @@ -43,7 +42,7 @@ static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t static_cast(data)->handle_layout(layout); } -static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid){ +static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid) { static_cast(data)->handle_appid(appid); }; @@ -73,7 +72,7 @@ static void handle_global_remove(void *data, struct wl_registry *registry, uint3 static const wl_registry_listener registry_listener_impl = {.global = handle_global, .global_remove = handle_global_remove}; -Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) +Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) : AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar) { struct wl_display *display = Client::inst()->wl_display; struct wl_registry *registry = wl_display_get_registry(display); @@ -92,29 +91,18 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) zdwl_ipc_manager_v2_destroy(status_manager_); } -void Window::handle_title(const char *title) { - title_ = title; -} +void Window::handle_title(const char *title) { title_ = title; } -void Window::handle_appid(const char *appid) { - appid_ = appid; -} +void Window::handle_appid(const char *appid) { appid_ = appid; } -void Window::handle_layout_symbol(const char *layout_symbol) { - layout_symbol_ = layout_symbol; -} +void Window::handle_layout_symbol(const char *layout_symbol) { layout_symbol_ = layout_symbol; } -void Window::handle_layout(const uint32_t layout) { - layout_ = layout; -} +void Window::handle_layout(const uint32_t layout) { layout_ = layout; } void Window::handle_frame() { label_.set_markup(waybar::util::rewriteString( - fmt::format( - fmt::runtime(format_), - fmt::arg("title", title_), - fmt::arg("layout", layout_symbol_), - fmt::arg("app_id", appid_)), + fmt::format(fmt::runtime(format_), fmt::arg("title", title_), + fmt::arg("layout", layout_symbol_), fmt::arg("app_id", appid_)), config_["rewrite"])); updateAppIconName(appid_, ""); updateAppIcon(); From 7cd2a6c00366998fcaa4f0d7f794529f9cf9c9bc Mon Sep 17 00:00:00 2001 From: Mauro Guida Date: Sat, 23 Mar 2024 13:02:39 +0100 Subject: [PATCH 466/842] fix(sway/workspaces): Visible class doesn't work on visible and empty workspaces --- src/modules/sway/workspaces.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 68b451ce..311073e0 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -304,7 +304,7 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("focused"); } - if (hasFlag((*it), "visible")) { + if (hasFlag((*it), "visible") || ((*it)["output"].isString() && (*it)["nodes"].size() == 0)) { button.get_style_context()->add_class("visible"); } else { button.get_style_context()->remove_class("visible"); From 70ef406d6b39e19a7dc9d4c060548a96a1d9fdbf Mon Sep 17 00:00:00 2001 From: Sano Date: Sat, 23 Mar 2024 18:39:22 +0100 Subject: [PATCH 467/842] check for group modules array in key conversion --- src/bar.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/bar.cpp b/src/bar.cpp index 31afcd43..872632ac 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -449,7 +449,17 @@ void waybar::Bar::setupAltFormatKeyForModuleList(const char* module_list_name) { Json::Value& modules = config[module_list_name]; for (const Json::Value& module_name : modules) { if (module_name.isString()) { - setupAltFormatKeyForModule(module_name.asString()); + auto ref = module_name.asString(); + if (ref.compare(0, 6, "group/") == 0 && ref.size() > 6) { + Json::Value& group_modules = config[ref]["modules"]; + for (const Json::Value& module_name : group_modules) { + if (module_name.isString()) { + setupAltFormatKeyForModule(module_name.asString()); + } + } + } else { + setupAltFormatKeyForModule(ref); + } } } } From abd7ca2a1efb451bd27c01687621d96e1a8e1cb8 Mon Sep 17 00:00:00 2001 From: encbar5 <8845322+encbar5@users.noreply.github.com> Date: Sat, 23 Mar 2024 08:22:19 -0500 Subject: [PATCH 468/842] Fix clock on-scroll value not being used for calendar, which was broken by 86a3898 --- include/modules/clock.hpp | 1 + src/modules/clock.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 8b597c4e..c50b7ae5 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -41,6 +41,7 @@ class Clock final : public ALabel { const int cldMonColLen_{20}; // calendar month column length WS cldWPos_{WS::HIDDEN}; // calendar week side to print months cldCurrShift_{0}; // calendar months shift + int cldShift_{1}; // calendar months shift factor year_month_day cldYearShift_; // calendar Year mode. Cached ymd std::string cldYearCached_; // calendar Year mode. Cached calendar year_month cldMonShift_; // calendar Month mode. Cached ym diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index b54a360f..e2cdf9fc 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -115,6 +115,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } else cldMonCols_ = 1; if (config_[kCldPlaceholder]["on-scroll"].isInt()) { + cldShift_ = config_[kCldPlaceholder]["on-scroll"].asInt(); event_box_.add_events(Gdk::LEAVE_NOTIFY_MASK); event_box_.signal_leave_notify_event().connect([this](GdkEventCrossing*) { cldCurrShift_ = months{0}; @@ -405,10 +406,10 @@ void waybar::modules::Clock::cldModeSwitch() { cldMode_ = (cldMode_ == CldMode::YEAR) ? CldMode::MONTH : CldMode::YEAR; } void waybar::modules::Clock::cldShift_up() { - cldCurrShift_ += (months)((cldMode_ == CldMode::YEAR) ? 12 : 1); + cldCurrShift_ += (months)((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; } void waybar::modules::Clock::cldShift_down() { - cldCurrShift_ -= (months)((cldMode_ == CldMode::YEAR) ? 12 : 1); + cldCurrShift_ -= (months)((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; } void waybar::modules::Clock::tz_up() { const auto tzSize{tzList_.size()}; @@ -468,4 +469,4 @@ auto waybar::modules::Clock::get_ordinal_date(const year_month_day& today) -> st res << "th"; } return res.str(); -} \ No newline at end of file +} From e3ceaf63d11968c171f9a80f485bd40baa2e6f90 Mon Sep 17 00:00:00 2001 From: cptpcrd <31829097+cptpcrd@users.noreply.github.com> Date: Sun, 24 Mar 2024 15:39:15 -0400 Subject: [PATCH 469/842] Fix reloading config Fully clear the configuration before reloading, so that when the config is read and merged in there are no existing values which mergeConfig refuses to overwrite. --- src/config.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config.cpp b/src/config.cpp index 45f5ee38..c43e5a63 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -162,6 +162,7 @@ void Config::load(const std::string &config) { } config_file_ = file.value(); spdlog::info("Using configuration file {}", config_file_); + config_ = Json::Value(); setupConfig(config_, config_file_, 0); } From 5056309963dec9e944da6b11a593e94bbe02db5f Mon Sep 17 00:00:00 2001 From: Lin Xianyi Date: Mon, 25 Mar 2024 22:47:37 +0800 Subject: [PATCH 470/842] nix: build against wireplumber 0.5 --- flake.lock | 6 +++--- nix/default.nix | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index 25f12644..7647478b 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1704538339, - "narHash": "sha256-1734d3mQuux9ySvwf6axRWZRBhtcZA9Q8eftD6EZg6U=", + "lastModified": 1711163522, + "narHash": "sha256-YN/Ciidm+A0fmJPWlHBGvVkcarYWSC+s3NTPk/P+q3c=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "46ae0210ce163b3cba6c7da08840c1d63de9c701", + "rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4", "type": "github" }, "original": { diff --git a/nix/default.nix b/nix/default.nix index bf8f2f21..986e84dd 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -25,6 +25,10 @@ in mesonFlags = lib.remove "-Dgtk-layer-shell=enabled" oldAttrs.mesonFlags; + buildInputs = (builtins.filter (p: p.pname != "wireplumber") oldAttrs.buildInputs) ++ [ + pkgs.wireplumber + ]; + postUnpack = '' pushd "$sourceRoot" cp -R --no-preserve=mode,ownership ${libcava.src} subprojects/cava-${libcava.version} @@ -32,4 +36,4 @@ in popd ''; } -)) \ No newline at end of file +)) From fe15530f34eb83792991c3a5dcabd0372f371439 Mon Sep 17 00:00:00 2001 From: Kuruyia Date: Mon, 25 Mar 2024 19:40:23 +0100 Subject: [PATCH 471/842] refactor(privacy): clean up the module --- include/util/pipewire/pipewire_backend.hpp | 17 ++- include/util/pipewire/privacy_node_info.hpp | 29 +--- meson.build | 3 +- src/modules/privacy/privacy.cpp | 4 - src/modules/privacy/privacy_item.cpp | 15 +- src/util/pipewire/pipewire_backend.cpp | 140 ++++++++++++++++++ src/util/pipewire/privacy_node_info.cpp | 56 +++++++ src/util/pipewire_backend.cpp | 155 -------------------- 8 files changed, 217 insertions(+), 202 deletions(-) create mode 100644 src/util/pipewire/pipewire_backend.cpp create mode 100644 src/util/pipewire/privacy_node_info.cpp delete mode 100644 src/util/pipewire_backend.cpp diff --git a/include/util/pipewire/pipewire_backend.hpp b/include/util/pipewire/pipewire_backend.hpp index 4e23b282..ac70a139 100644 --- a/include/util/pipewire/pipewire_backend.hpp +++ b/include/util/pipewire/pipewire_backend.hpp @@ -13,7 +13,8 @@ class PipewireBackend { pw_context* context_; pw_core* core_; - spa_hook registry_listener; + pw_registry* registry_; + spa_hook registryListener_; /* Hack to keep constructor inaccessible but still public. * This is required to be able to use std::make_shared. @@ -21,20 +22,22 @@ class PipewireBackend { * pointer because the destructor will manually free memory, and this could be * a problem with C++20's copy and move semantics. */ - struct private_constructor_tag {}; + struct PrivateConstructorTag {}; public: - std::mutex mutex_; - - pw_registry* registry; - sigc::signal privacy_nodes_changed_signal_event; std::unordered_map privacy_nodes; + std::mutex mutex_; static std::shared_ptr getInstance(); - PipewireBackend(private_constructor_tag tag); + // Handlers for PipeWire events + void handleRegistryEventGlobal(uint32_t id, uint32_t permissions, const char* type, + uint32_t version, const struct spa_dict* props); + void handleRegistryEventGlobalRemove(uint32_t id); + + PipewireBackend(PrivateConstructorTag tag); ~PipewireBackend(); }; } // namespace waybar::util::PipewireBackend diff --git a/include/util/pipewire/privacy_node_info.hpp b/include/util/pipewire/privacy_node_info.hpp index 3b7f446d..7b8df018 100644 --- a/include/util/pipewire/privacy_node_info.hpp +++ b/include/util/pipewire/privacy_node_info.hpp @@ -34,29 +34,12 @@ class PrivacyNodeInfo { void *data; - std::string get_name() { - const std::vector names{&application_name, &node_name}; - std::string name = "Unknown Application"; - for (auto &name_ : names) { - if (name_ != nullptr && name_->length() > 0) { - name = *name_; - name[0] = toupper(name[0]); - break; - } - } - return name; - } + std::string getName(); + std::string getIconName(); - std::string get_icon_name() { - const std::vector names{&application_icon_name, &pipewire_access_portal_app_id, - &application_name, &node_name}; - const std::string name = "application-x-executable-symbolic"; - for (auto &name_ : names) { - if (name_ != nullptr && name_->length() > 0 && DefaultGtkIconThemeWrapper::has_icon(*name_)) { - return *name_; - } - } - return name; - } + // Handlers for PipeWire events + void handleProxyEventDestroy(); + void handleNodeEventInfo(const struct pw_node_info *info); }; + } // namespace waybar::util::PipewireBackend diff --git a/meson.build b/meson.build index bbcde7f0..dfdf08a8 100644 --- a/meson.build +++ b/meson.build @@ -348,7 +348,8 @@ if pipewire.found() src_files += files( 'src/modules/privacy/privacy.cpp', 'src/modules/privacy/privacy_item.cpp', - 'src/util/pipewire_backend.cpp', + 'src/util/pipewire/pipewire_backend.cpp', + 'src/util/pipewire/privacy_node_info.cpp', ) man_files += files('man/waybar-privacy.5.scd') endif diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 64a1572b..b7eede75 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -1,12 +1,8 @@ #include "modules/privacy/privacy.hpp" -#include #include -#include #include -#include -#include #include #include "AModule.hpp" diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index a0a2da57..c5b617d5 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -1,23 +1,14 @@ #include "modules/privacy/privacy_item.hpp" -#include -#include -#include - -#include -#include #include #include #include "AModule.hpp" #include "glibmm/main.h" -#include "glibmm/priorities.h" -#include "gtkmm/enums.h" #include "gtkmm/label.h" #include "gtkmm/revealer.h" #include "gtkmm/tooltip.h" #include "sigc++/adaptors/bind.h" -#include "util/gtk_icon.hpp" #include "util/pipewire/privacy_node_info.hpp" namespace waybar::modules::privacy { @@ -108,12 +99,12 @@ void PrivacyItem::update_tooltip() { // Set device icon Gtk::Image *node_icon = new Gtk::Image(); node_icon->set_pixel_size(tooltipIconSize); - node_icon->set_from_icon_name(node->get_icon_name(), Gtk::ICON_SIZE_INVALID); + node_icon->set_from_icon_name(node->getIconName(), Gtk::ICON_SIZE_INVALID); box->add(*node_icon); // Set model - Gtk::Label *node_name = new Gtk::Label(node->get_name()); - box->add(*node_name); + auto *nodeName = new Gtk::Label(node->getName()); + box->add(*nodeName); tooltip_window.add(*box); } diff --git a/src/util/pipewire/pipewire_backend.cpp b/src/util/pipewire/pipewire_backend.cpp new file mode 100644 index 00000000..044b926f --- /dev/null +++ b/src/util/pipewire/pipewire_backend.cpp @@ -0,0 +1,140 @@ +#include "util/pipewire/pipewire_backend.hpp" + +#include "util/pipewire/privacy_node_info.hpp" + +namespace waybar::util::PipewireBackend { + +static void getNodeInfo(void *data_, const struct pw_node_info *info) { + auto *pNodeInfo = static_cast(data_); + pNodeInfo->handleNodeEventInfo(info); + + static_cast(pNodeInfo->data)->privacy_nodes_changed_signal_event.emit(); +} + +static const struct pw_node_events NODE_EVENTS = { + .version = PW_VERSION_NODE_EVENTS, + .info = getNodeInfo, +}; + +static void proxyDestroy(void *data) { + static_cast(data)->handleProxyEventDestroy(); +} + +static const struct pw_proxy_events PROXY_EVENTS = { + .version = PW_VERSION_PROXY_EVENTS, + .destroy = proxyDestroy, +}; + +static void registryEventGlobal(void *_data, uint32_t id, uint32_t permissions, const char *type, + uint32_t version, const struct spa_dict *props) { + static_cast(_data)->handleRegistryEventGlobal(id, permissions, type, version, + props); +} + +static void registryEventGlobalRemove(void *_data, uint32_t id) { + static_cast(_data)->handleRegistryEventGlobalRemove(id); +} + +static const struct pw_registry_events REGISTRY_EVENTS = { + .version = PW_VERSION_REGISTRY_EVENTS, + .global = registryEventGlobal, + .global_remove = registryEventGlobalRemove, +}; + +PipewireBackend::PipewireBackend(PrivateConstructorTag tag) + : mainloop_(nullptr), context_(nullptr), core_(nullptr) { + pw_init(nullptr, nullptr); + mainloop_ = pw_thread_loop_new("waybar", nullptr); + if (mainloop_ == nullptr) { + throw std::runtime_error("pw_thread_loop_new() failed."); + } + context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0); + if (context_ == nullptr) { + throw std::runtime_error("pa_context_new() failed."); + } + core_ = pw_context_connect(context_, nullptr, 0); + if (core_ == nullptr) { + throw std::runtime_error("pw_context_connect() failed"); + } + registry_ = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0); + + spa_zero(registryListener_); + pw_registry_add_listener(registry_, ®istryListener_, ®ISTRY_EVENTS, this); + if (pw_thread_loop_start(mainloop_) < 0) { + throw std::runtime_error("pw_thread_loop_start() failed."); + } +} + +PipewireBackend::~PipewireBackend() { + if (registry_ != nullptr) { + pw_proxy_destroy((struct pw_proxy *)registry_); + } + + spa_zero(registryListener_); + + if (core_ != nullptr) { + pw_core_disconnect(core_); + } + + if (context_ != nullptr) { + pw_context_destroy(context_); + } + + if (mainloop_ != nullptr) { + pw_thread_loop_stop(mainloop_); + pw_thread_loop_destroy(mainloop_); + } +} + +std::shared_ptr PipewireBackend::getInstance() { + PrivateConstructorTag tag; + return std::make_shared(tag); +} + +void PipewireBackend::handleRegistryEventGlobal(uint32_t id, uint32_t permissions, const char *type, + uint32_t version, const struct spa_dict *props) { + if (props == nullptr || strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return; + + const char *lookupStr = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); + if (lookupStr == nullptr) return; + std::string mediaClass = lookupStr; + enum PrivacyNodeType mediaType = PRIVACY_NODE_TYPE_NONE; + if (mediaClass == "Stream/Input/Video") { + mediaType = PRIVACY_NODE_TYPE_VIDEO_INPUT; + } else if (mediaClass == "Stream/Input/Audio") { + mediaType = PRIVACY_NODE_TYPE_AUDIO_INPUT; + } else if (mediaClass == "Stream/Output/Audio") { + mediaType = PRIVACY_NODE_TYPE_AUDIO_OUTPUT; + } else { + return; + } + + auto *proxy = (pw_proxy *)pw_registry_bind(registry_, id, type, version, sizeof(PrivacyNodeInfo)); + + if (proxy == nullptr) return; + + auto *pNodeInfo = (PrivacyNodeInfo *)pw_proxy_get_user_data(proxy); + pNodeInfo->id = id; + pNodeInfo->data = this; + pNodeInfo->type = mediaType; + pNodeInfo->media_class = mediaClass; + + pw_proxy_add_listener(proxy, &pNodeInfo->proxy_listener, &PROXY_EVENTS, pNodeInfo); + + pw_proxy_add_object_listener(proxy, &pNodeInfo->object_listener, &NODE_EVENTS, pNodeInfo); + + privacy_nodes.insert_or_assign(id, pNodeInfo); +} + +void PipewireBackend::handleRegistryEventGlobalRemove(uint32_t id) { + mutex_.lock(); + auto iter = privacy_nodes.find(id); + if (iter != privacy_nodes.end()) { + privacy_nodes.erase(id); + } + mutex_.unlock(); + + privacy_nodes_changed_signal_event.emit(); +} + +} // namespace waybar::util::PipewireBackend diff --git a/src/util/pipewire/privacy_node_info.cpp b/src/util/pipewire/privacy_node_info.cpp new file mode 100644 index 00000000..739dc528 --- /dev/null +++ b/src/util/pipewire/privacy_node_info.cpp @@ -0,0 +1,56 @@ +#include "util/pipewire/privacy_node_info.hpp" + +namespace waybar::util::PipewireBackend { + +std::string PrivacyNodeInfo::getName() { + const std::vector names{&application_name, &node_name}; + std::string name = "Unknown Application"; + for (const auto &item : names) { + if (item != nullptr && !item->empty()) { + name = *item; + name[0] = toupper(name[0]); + break; + } + } + return name; +} + +std::string PrivacyNodeInfo::getIconName() { + const std::vector names{&application_icon_name, &pipewire_access_portal_app_id, + &application_name, &node_name}; + std::string name = "application-x-executable-symbolic"; + for (const auto &item : names) { + if (item != nullptr && !item->empty() && DefaultGtkIconThemeWrapper::has_icon(*item)) { + return *item; + } + } + return name; +} + +void PrivacyNodeInfo::handleProxyEventDestroy() { + spa_hook_remove(&proxy_listener); + spa_hook_remove(&object_listener); +} + +void PrivacyNodeInfo::handleNodeEventInfo(const struct pw_node_info *info) { + state = info->state; + + const struct spa_dict_item *item; + spa_dict_for_each(item, info->props) { + if (strcmp(item->key, PW_KEY_CLIENT_ID) == 0) { + client_id = strtoul(item->value, nullptr, 10); + } else if (strcmp(item->key, PW_KEY_MEDIA_NAME) == 0) { + media_name = item->value; + } else if (strcmp(item->key, PW_KEY_NODE_NAME) == 0) { + node_name = item->value; + } else if (strcmp(item->key, PW_KEY_APP_NAME) == 0) { + application_name = item->value; + } else if (strcmp(item->key, "pipewire.access.portal.app_id") == 0) { + pipewire_access_portal_app_id = item->value; + } else if (strcmp(item->key, PW_KEY_APP_ICON_NAME) == 0) { + application_icon_name = item->value; + } + } +} + +} // namespace waybar::util::PipewireBackend diff --git a/src/util/pipewire_backend.cpp b/src/util/pipewire_backend.cpp deleted file mode 100644 index 5fe3ba62..00000000 --- a/src/util/pipewire_backend.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "util/pipewire/pipewire_backend.hpp" - -#include "util/pipewire/privacy_node_info.hpp" - -namespace waybar::util::PipewireBackend { - -static void get_node_info(void *data_, const struct pw_node_info *info) { - PrivacyNodeInfo *p_node_info = static_cast(data_); - PipewireBackend *backend = (PipewireBackend *)p_node_info->data; - - p_node_info->state = info->state; - - const struct spa_dict_item *item; - spa_dict_for_each(item, info->props) { - if (strcmp(item->key, PW_KEY_CLIENT_ID) == 0) { - p_node_info->client_id = strtoul(item->value, NULL, 10); - } else if (strcmp(item->key, PW_KEY_MEDIA_NAME) == 0) { - p_node_info->media_name = item->value; - } else if (strcmp(item->key, PW_KEY_NODE_NAME) == 0) { - p_node_info->node_name = item->value; - } else if (strcmp(item->key, PW_KEY_APP_NAME) == 0) { - p_node_info->application_name = item->value; - } else if (strcmp(item->key, "pipewire.access.portal.app_id") == 0) { - p_node_info->pipewire_access_portal_app_id = item->value; - } else if (strcmp(item->key, PW_KEY_APP_ICON_NAME) == 0) { - p_node_info->application_icon_name = item->value; - } - } - - backend->privacy_nodes_changed_signal_event.emit(); -} - -static const struct pw_node_events node_events = { - .version = PW_VERSION_NODE_EVENTS, - .info = get_node_info, -}; - -static void proxy_destroy(void *data) { - PrivacyNodeInfo *node = (PrivacyNodeInfo *)data; - - spa_hook_remove(&node->proxy_listener); - spa_hook_remove(&node->object_listener); -} - -static const struct pw_proxy_events proxy_events = { - .version = PW_VERSION_PROXY_EVENTS, - .destroy = proxy_destroy, -}; - -static void registry_event_global(void *_data, uint32_t id, uint32_t permissions, const char *type, - uint32_t version, const struct spa_dict *props) { - if (!props || strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return; - - const char *lookup_str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); - if (!lookup_str) return; - std::string media_class = lookup_str; - enum PrivacyNodeType media_type = PRIVACY_NODE_TYPE_NONE; - if (media_class == "Stream/Input/Video") { - media_type = PRIVACY_NODE_TYPE_VIDEO_INPUT; - } else if (media_class == "Stream/Input/Audio") { - media_type = PRIVACY_NODE_TYPE_AUDIO_INPUT; - } else if (media_class == "Stream/Output/Audio") { - media_type = PRIVACY_NODE_TYPE_AUDIO_OUTPUT; - } else { - return; - } - - PipewireBackend *backend = static_cast(_data); - struct pw_proxy *proxy = - (pw_proxy *)pw_registry_bind(backend->registry, id, type, version, sizeof(PrivacyNodeInfo)); - - if (!proxy) return; - - PrivacyNodeInfo *p_node_info = (PrivacyNodeInfo *)pw_proxy_get_user_data(proxy); - p_node_info->id = id; - p_node_info->data = backend; - p_node_info->type = media_type; - p_node_info->media_class = media_class; - - pw_proxy_add_listener(proxy, &p_node_info->proxy_listener, &proxy_events, p_node_info); - - pw_proxy_add_object_listener(proxy, &p_node_info->object_listener, &node_events, p_node_info); - - backend->privacy_nodes.insert_or_assign(id, p_node_info); -} - -static void registry_event_global_remove(void *_data, uint32_t id) { - auto backend = static_cast(_data); - - backend->mutex_.lock(); - auto iter = backend->privacy_nodes.find(id); - if (iter != backend->privacy_nodes.end()) { - backend->privacy_nodes.erase(id); - } - backend->mutex_.unlock(); - - backend->privacy_nodes_changed_signal_event.emit(); -} - -static const struct pw_registry_events registry_events = { - .version = PW_VERSION_REGISTRY_EVENTS, - .global = registry_event_global, - .global_remove = registry_event_global_remove, -}; - -PipewireBackend::PipewireBackend(private_constructor_tag tag) - : mainloop_(nullptr), context_(nullptr), core_(nullptr) { - pw_init(nullptr, nullptr); - mainloop_ = pw_thread_loop_new("waybar", nullptr); - if (mainloop_ == nullptr) { - throw std::runtime_error("pw_thread_loop_new() failed."); - } - context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0); - if (context_ == nullptr) { - throw std::runtime_error("pa_context_new() failed."); - } - core_ = pw_context_connect(context_, nullptr, 0); - if (core_ == nullptr) { - throw std::runtime_error("pw_context_connect() failed"); - } - registry = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0); - - spa_zero(registry_listener); - pw_registry_add_listener(registry, ®istry_listener, ®istry_events, this); - if (pw_thread_loop_start(mainloop_) < 0) { - throw std::runtime_error("pw_thread_loop_start() failed."); - } -} - -PipewireBackend::~PipewireBackend() { - if (registry != nullptr) { - pw_proxy_destroy((struct pw_proxy *)registry); - } - - spa_zero(registry_listener); - - if (core_ != nullptr) { - pw_core_disconnect(core_); - } - - if (context_ != nullptr) { - pw_context_destroy(context_); - } - - if (mainloop_ != nullptr) { - pw_thread_loop_stop(mainloop_); - pw_thread_loop_destroy(mainloop_); - } -} - -std::shared_ptr PipewireBackend::getInstance() { - private_constructor_tag tag; - return std::make_shared(tag); -} -} // namespace waybar::util::PipewireBackend From 9b4fc6d16b51adbf2d6e931c290555493ba5560a Mon Sep 17 00:00:00 2001 From: kvark Date: Thu, 28 Mar 2024 14:42:44 +0700 Subject: [PATCH 472/842] fix(sway/workspaces): floating_nodes and focused icon Floating nodes are not taken into account for visible and empty workspaces And fix focused icon (#3095) --- src/modules/sway/workspaces.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 311073e0..12765333 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -299,12 +299,13 @@ auto Workspaces::update() -> void { if (needReorder) { box_.reorder_child(button, it - workspaces_.begin()); } + bool noNodes = (*it)["nodes"].empty() && (*it)["floating_nodes"].empty(); if (hasFlag((*it), "focused")) { button.get_style_context()->add_class("focused"); } else { button.get_style_context()->remove_class("focused"); } - if (hasFlag((*it), "visible") || ((*it)["output"].isString() && (*it)["nodes"].size() == 0)) { + if (hasFlag((*it), "visible") || ((*it)["output"].isString() && noNodes )) { button.get_style_context()->add_class("visible"); } else { button.get_style_context()->remove_class("visible"); @@ -319,7 +320,7 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("persistent"); } - if ((*it)["nodes"].size() == 0) { + if (noNodes) { button.get_style_context()->add_class("empty"); } else { button.get_style_context()->remove_class("empty"); @@ -406,7 +407,7 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node } } if (key == "focused" || key == "urgent") { - if (config_["format-icons"][key].isString() && node[key].asBool()) { + if (config_["format-icons"][key].isString() && hasFlag(node, key)) { return config_["format-icons"][key].asString(); } } else if (config_["format-icons"]["persistent"].isString() && From 245043f9e7469a5b837cb8d8387bd045ead3a30d Mon Sep 17 00:00:00 2001 From: Bruce Mills Date: Mon, 1 Apr 2024 14:30:31 -0400 Subject: [PATCH 473/842] taskbar: search user directories first for desktop files --- src/modules/wlr/taskbar.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index d291a6a5..6e3e4e08 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -30,6 +30,9 @@ namespace waybar::modules::wlr { static std::vector search_prefix() { std::vector prefixes = {""}; + std::string home_dir = std::getenv("HOME"); + prefixes.push_back(home_dir + "/.local/share/"); + auto xdg_data_dirs = std::getenv("XDG_DATA_DIRS"); if (!xdg_data_dirs) { prefixes.emplace_back("/usr/share/"); @@ -47,9 +50,6 @@ static std::vector search_prefix() { } while (end != std::string::npos); } - std::string home_dir = std::getenv("HOME"); - prefixes.push_back(home_dir + "/.local/share/"); - for (auto &p : prefixes) spdlog::debug("Using 'desktop' search path prefix: {}", p); return prefixes; From 3d15b96429fa0299e17869b9ac690169b4900228 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Tue, 9 Apr 2024 10:02:33 -0400 Subject: [PATCH 474/842] Add waybar-styles(5) manual page waybar(5) describes the configuration syntax but doesn't mention how the stylesheets are handled. This documentation would have been helpful to me as i figured out how to configure waybar. --- man/waybar-styles.5.scd.in | 34 ++++++++++++++++++++++++++++++++++ man/waybar.5.scd.in | 3 +++ meson.build | 8 ++++++++ 3 files changed, 45 insertions(+) create mode 100644 man/waybar-styles.5.scd.in diff --git a/man/waybar-styles.5.scd.in b/man/waybar-styles.5.scd.in new file mode 100644 index 00000000..ddc4c3c9 --- /dev/null +++ b/man/waybar-styles.5.scd.in @@ -0,0 +1,34 @@ +waybar-styles(5) + +# NAME + +waybar-styles - using stylesheets for waybar + +# DESCRIPTION + +Waybar uses Cascading Style Sheets (CSS) to configure its appearance. + +It uses the first file found in this search order: + +- *$XDG_CONFIG_HOME/waybar/style.css* +- *~/.config/waybar/style.css* +- *~/waybar/style.css* +- */etc/xdg/waybar/style.css* +- *@sysconfdir@/xdg/waybar/style.css* + +# EXAMPLE + +An example user-controlled stylesheet that just changes the color of the clock to be green on black, while keeping the rest of the system config the same would be: + +``` +@import url("file:///etc/xdg/waybar/style.css") + +#clock { + background: #000000; + color: #00ff00; +} +``` + +# SEE ALSO + +- *waybar(5)* diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 5fe30ca8..53613e4a 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -19,6 +19,8 @@ Valid locations for this file are: A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config.jsonc Also, a minimal example configuration can be found at the bottom of this man page. +The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(5)* for details. + # BAR CONFIGURATION *layer* ++ @@ -347,3 +349,4 @@ A group may hide all but one element, showing them only on mouse hover. In order # SEE ALSO *sway-output(5)* +*waybar-styles(5)" diff --git a/meson.build b/meson.build index dfdf08a8..92d1ead4 100644 --- a/meson.build +++ b/meson.build @@ -539,6 +539,14 @@ if scdoc.found() } ) + man_files += configure_file( + input: 'man/waybar-styles.5.scd.in', + output: 'waybar-styles.5.scd', + configuration: { + 'sysconfdir': prefix / sysconfdir + } + ) + fs = import('fs') mandir = get_option('mandir') foreach file : man_files From f68ac9119a9140901f535f458221728818c0e188 Mon Sep 17 00:00:00 2001 From: Hristo Venev Date: Tue, 9 Apr 2024 20:33:53 +0300 Subject: [PATCH 475/842] Use $TZ for local time if it is set libstdc++ doesn't. --- include/modules/clock.hpp | 3 +++ src/modules/clock.cpp | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index c50b7ae5..e7c3872c 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -52,6 +52,9 @@ class Clock final : public ALabel { auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) -> const std::string; + // get local time zone + auto local_zone() -> const time_zone*; + // time zoned time in tooltip const bool tzInTooltip_; // if need to print time zones text std::vector tzList_; // time zones list diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index e2cdf9fc..83537405 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -87,7 +87,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) fmtMap_.insert({3, config_[kCldPlaceholder]["format"]["today"].asString()}); cldBaseDay_ = year_month_day{ - floor(zoned_time{current_zone(), system_clock::now()}.get_local_time())} + floor(zoned_time{local_zone(), system_clock::now()}.get_local_time())} .day(); } else fmtMap_.insert({3, "{}"}); @@ -131,7 +131,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } auto waybar::modules::Clock::update() -> void { - const auto* tz = tzList_[tzCurrIdx_] != nullptr ? tzList_[tzCurrIdx_] : current_zone(); + const auto* tz = tzList_[tzCurrIdx_] != nullptr ? tzList_[tzCurrIdx_] : local_zone(); const zoned_time now{tz, floor(system_clock::now())}; label_.set_markup(fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(now))); @@ -168,7 +168,7 @@ auto waybar::modules::Clock::getTZtext(sys_seconds now) -> std::string { std::stringstream os; for (size_t tz_idx{0}; tz_idx < tzList_.size(); ++tz_idx) { if (static_cast(tz_idx) == tzCurrIdx_) continue; - const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : current_zone(); + const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : local_zone(); auto zt{zoned_time{tz, now}}; os << fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(zt)) << '\n'; } @@ -393,6 +393,18 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea return os.str(); } +auto waybar::modules::Clock::local_zone() -> const time_zone* { + const char* tz_name = getenv("TZ"); + if (tz_name) { + try { + return locate_zone(tz_name); + } catch (const std::runtime_error& e) { + spdlog::warn("Timezone: {0}. {1}", tz_name, e.what()); + } + } + return current_zone(); +} + // Actions handler auto waybar::modules::Clock::doAction(const std::string& name) -> void { if (actionMap_[name]) { From a9088c7e7d019a3eff27db8c55db8414e0638941 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 10 Apr 2024 10:20:21 +0200 Subject: [PATCH 476/842] fix: lint --- src/modules/sway/workspaces.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 12765333..8a3b9c84 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -305,7 +305,7 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("focused"); } - if (hasFlag((*it), "visible") || ((*it)["output"].isString() && noNodes )) { + if (hasFlag((*it), "visible") || ((*it)["output"].isString() && noNodes)) { button.get_style_context()->add_class("visible"); } else { button.get_style_context()->remove_class("visible"); @@ -320,7 +320,7 @@ auto Workspaces::update() -> void { } else { button.get_style_context()->remove_class("persistent"); } - if (noNodes) { + if (noNodes) { button.get_style_context()->add_class("empty"); } else { button.get_style_context()->remove_class("empty"); From 43511992d94ef5524c8a251373a70fc9eb1ff970 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:00:50 +0200 Subject: [PATCH 477/842] feat(battery): Add {cycles} format replacement --- include/modules/battery.hpp | 2 +- man/waybar-battery.5.scd | 2 ++ src/modules/battery.cpp | 27 +++++++++++++++++++++------ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 7955e598..6f09043b 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -32,7 +32,7 @@ class Battery : public ALabel { void refreshBatteries(); void worker(); const std::string getAdapterStatus(uint8_t capacity) const; - const std::tuple getInfos(); + const std::tuple getInfos(); const std::string formatTimeRemaining(float hoursRemaining); void setBarClass(std::string&); diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index e359ea2e..284803b0 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -119,6 +119,8 @@ The *battery* module displays the current capacity and state (eg. charging) of y *{time}*: Estimate of time until full or empty. Note that this is based on the power draw at the last refresh time, not an average. +*{cycles}*: Amount of charge cycles the highest-capacity battery has seen. *(Linux only)* + # TIME FORMAT The *battery* module allows you to define how time should be formatted via *format-time*. diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 9003db6e..06df5db2 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -181,7 +181,7 @@ static bool status_gt(const std::string& a, const std::string& b) { return false; } -const std::tuple waybar::modules::Battery::getInfos() { +const std::tuple waybar::modules::Battery::getInfos() { std::lock_guard guard(battery_list_mutex_); try { @@ -252,6 +252,9 @@ const std::tuple waybar::modules::Battery::g uint32_t time_to_full_now = 0; bool time_to_full_now_exists = false; + uint32_t largest_design_capacity = 0; + uint16_t main_bat_cycle_count = 0; + std::string status = "Unknown"; for (auto const& item : batteries_) { auto bat = item.first; @@ -353,6 +356,16 @@ const std::tuple waybar::modules::Battery::g std::ifstream(bat / "energy_full_design") >> energy_full_design; } + bool is_main_battery = charge_full_design >= largest_design_capacity; + uint16_t cycle_count = 0; + if (fs::exists(bat / "cycle_count")) { + std::ifstream(bat / "cycle_count") >> cycle_count; + } + if (is_main_battery && (cycle_count > main_bat_cycle_count)) { + largest_design_capacity = charge_full_design; + main_bat_cycle_count = cycle_count; + } + if (!voltage_now_exists) { if (power_now_exists && current_now_exists && current_now != 0) { voltage_now_exists = true; @@ -573,11 +586,11 @@ const std::tuple waybar::modules::Battery::g // still charging but not yet done if (cap == 100 && status == "Charging") status = "Full"; - return {cap, time_remaining, status, total_power / 1e6}; + return {cap, time_remaining, status, total_power / 1e6, main_bat_cycle_count}; #endif } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); - return {0, 0, "Unknown", 0}; + return {0, 0, "Unknown", 0, 0}; } } @@ -633,7 +646,7 @@ auto waybar::modules::Battery::update() -> void { return; } #endif - auto [capacity, time_remaining, status, power] = getInfos(); + auto [capacity, time_remaining, status, power, cycles] = getInfos(); if (status == "Unknown") { status = getAdapterStatus(capacity); } @@ -666,7 +679,8 @@ auto waybar::modules::Battery::update() -> void { label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), fmt::arg("timeTo", tooltip_text_default), fmt::arg("power", power), fmt::arg("capacity", capacity), - fmt::arg("time", time_remaining_formatted))); + fmt::arg("time", time_remaining_formatted), + fmt::arg("cycles", cycles))); } if (!old_status_.empty()) { label_.get_style_context()->remove_class(old_status_); @@ -687,7 +701,8 @@ auto waybar::modules::Battery::update() -> void { auto icons = std::vector{status + "-" + state, status, state}; label_.set_markup(fmt::format( fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted))); + fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), + fmt::arg("cycles", cycles))); } // Call parent update ALabel::update(); From 7f1e623f77a6b99e38a5cfc2839d0dcf59f2c393 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:30:47 +0200 Subject: [PATCH 478/842] style: Refactor battery cycle count choosing --- src/modules/battery.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 06df5db2..cc6595dc 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -361,9 +361,12 @@ const std::tuple waybar::modules:: if (fs::exists(bat / "cycle_count")) { std::ifstream(bat / "cycle_count") >> cycle_count; } - if (is_main_battery && (cycle_count > main_bat_cycle_count)) { + if (is_main_battery) { largest_design_capacity = charge_full_design; - main_bat_cycle_count = cycle_count; + + if (cycle_count > main_bat_cycle_count) { + main_bat_cycle_count = cycle_count; + } } if (!voltage_now_exists) { From a59593fde1ccb7caef46f69a1d1fa7a9fc13f7fb Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 10 Apr 2024 18:19:55 +0200 Subject: [PATCH 479/842] feat(battery): Add {health} format replacement --- include/modules/battery.hpp | 2 +- man/waybar-battery.5.scd | 2 ++ src/modules/battery.cpp | 22 +++++++++++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 6f09043b..0468bff1 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -32,7 +32,7 @@ class Battery : public ALabel { void refreshBatteries(); void worker(); const std::string getAdapterStatus(uint8_t capacity) const; - const std::tuple getInfos(); + const std::tuple getInfos(); const std::string formatTimeRemaining(float hoursRemaining); void setBarClass(std::string&); diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 284803b0..25c7caca 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -121,6 +121,8 @@ The *battery* module displays the current capacity and state (eg. charging) of y *{cycles}*: Amount of charge cycles the highest-capacity battery has seen. *(Linux only)* +*{health}*: The percentage of the highest-capacity battery's original maximum charge it can still hold. + # TIME FORMAT The *battery* module allows you to define how time should be formatted via *format-time*. diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index cc6595dc..6d3caa27 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -181,7 +181,7 @@ static bool status_gt(const std::string& a, const std::string& b) { return false; } -const std::tuple waybar::modules::Battery::getInfos() { +const std::tuple waybar::modules::Battery::getInfos() { std::lock_guard guard(battery_list_mutex_); try { @@ -254,6 +254,7 @@ const std::tuple waybar::modules:: uint32_t largest_design_capacity = 0; uint16_t main_bat_cycle_count = 0; + float main_bat_health_percent = 0.0f; std::string status = "Unknown"; for (auto const& item : batteries_) { @@ -367,6 +368,16 @@ const std::tuple waybar::modules:: if (cycle_count > main_bat_cycle_count) { main_bat_cycle_count = cycle_count; } + + std::string name; + std::ifstream(bat / "model_name") >> name; + spdlog::info("{} | full: {}, full_design: {}", name, energy_full, energy_full_design); + if (charge_full_exists && charge_full_design_exists) { + float bat_health_percent = ((float)charge_full_design / charge_full) * 100; + if (main_bat_health_percent == 0.0f || bat_health_percent < main_bat_health_percent) { + main_bat_health_percent = bat_health_percent; + } + } } if (!voltage_now_exists) { @@ -589,11 +600,11 @@ const std::tuple waybar::modules:: // still charging but not yet done if (cap == 100 && status == "Charging") status = "Full"; - return {cap, time_remaining, status, total_power / 1e6, main_bat_cycle_count}; + return {cap, time_remaining, status, total_power / 1e6, main_bat_cycle_count, main_bat_health_percent}; #endif } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); - return {0, 0, "Unknown", 0, 0}; + return {0, 0, "Unknown", 0, 0, 0.0f}; } } @@ -649,7 +660,7 @@ auto waybar::modules::Battery::update() -> void { return; } #endif - auto [capacity, time_remaining, status, power, cycles] = getInfos(); + auto [capacity, time_remaining, status, power, cycles, health] = getInfos(); if (status == "Unknown") { status = getAdapterStatus(capacity); } @@ -683,7 +694,8 @@ auto waybar::modules::Battery::update() -> void { fmt::arg("timeTo", tooltip_text_default), fmt::arg("power", power), fmt::arg("capacity", capacity), fmt::arg("time", time_remaining_formatted), - fmt::arg("cycles", cycles))); + fmt::arg("cycles", cycles), + fmt::arg("health", fmt::format("{:.3}", health)))); } if (!old_status_.empty()) { label_.get_style_context()->remove_class(old_status_); From 805faa47e63686d359843535e4557358f18c2c0e Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 10 Apr 2024 18:48:03 +0200 Subject: [PATCH 480/842] style: Remove debug output Oops --- src/modules/battery.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 6d3caa27..e65a2dcf 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -369,9 +369,6 @@ const std::tuple waybar::mo main_bat_cycle_count = cycle_count; } - std::string name; - std::ifstream(bat / "model_name") >> name; - spdlog::info("{} | full: {}, full_design: {}", name, energy_full, energy_full_design); if (charge_full_exists && charge_full_design_exists) { float bat_health_percent = ((float)charge_full_design / charge_full) * 100; if (main_bat_health_percent == 0.0f || bat_health_percent < main_bat_health_percent) { From 24690248dbdb33323de3b99c51f5706bf8652736 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Thu, 11 Apr 2024 02:40:04 +0200 Subject: [PATCH 481/842] fix: Calculate battery health the right way around I even did this originally, then got confused when my battery in particular showed 102% and, instead of checking the values I calculate with, just decided to do the stupid thing and do maths the wrong around --- src/modules/battery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index e65a2dcf..df3712a6 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -370,7 +370,7 @@ const std::tuple waybar::mo } if (charge_full_exists && charge_full_design_exists) { - float bat_health_percent = ((float)charge_full_design / charge_full) * 100; + float bat_health_percent = ((float)charge_full / charge_full_design) * 100; if (main_bat_health_percent == 0.0f || bat_health_percent < main_bat_health_percent) { main_bat_health_percent = bat_health_percent; } From cd3d588abdc23e19acf2452ad9bd08ae21de0ec0 Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Fri, 12 Apr 2024 11:33:29 +0200 Subject: [PATCH 482/842] [hyprland/workspaces] Fix active workspace not getting updated on multi monitor setups --- src/modules/hyprland/workspaces.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 5d7436e7..671a4a8b 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -318,7 +318,7 @@ void Workspaces::onEvent(const std::string &ev) { onWorkspaceCreated(payload); } else if (eventName == "focusedmon") { onMonitorFocused(payload); - } else if (eventName == "moveworkspace" && !allOutputs()) { + } else if (eventName == "moveworkspace") { onWorkspaceMoved(payload); } else if (eventName == "openwindow") { onWindowOpened(payload); @@ -387,6 +387,13 @@ void Workspaces::onWorkspaceCreated(std::string const &workspaceName, void Workspaces::onWorkspaceMoved(std::string const &payload) { spdlog::debug("Workspace moved: {}", payload); + + // Update active workspace + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + + if (allOutputs()) + return; + std::string workspaceName = payload.substr(0, payload.find(',')); std::string monitorName = payload.substr(payload.find(',') + 1); From 084b561d5bc93a07a3f5777bef3357cf405e60cb Mon Sep 17 00:00:00 2001 From: Tom Benham Date: Fri, 12 Apr 2024 11:35:41 +0200 Subject: [PATCH 483/842] [hyprland/workspaces] Update window count and sort workspaces AFTER their creation --- src/modules/hyprland/workspaces.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 671a4a8b..3c03c708 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -194,6 +194,10 @@ void Workspaces::doUpdate() { for (auto &[workspaceData, clientsData] : m_workspacesToCreate) { createWorkspace(workspaceData, clientsData); } + if (!m_workspacesToCreate.empty()) { + updateWindowCount(); + sortWorkspaces(); + } m_workspacesToCreate.clear(); // get all active workspaces @@ -391,8 +395,7 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { // Update active workspace m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); - if (allOutputs()) - return; + if (allOutputs()) return; std::string workspaceName = payload.substr(0, payload.find(',')); std::string monitorName = payload.substr(payload.find(',') + 1); @@ -833,8 +836,6 @@ void Workspaces::init() { m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); initializeWorkspaces(); - updateWindowCount(); - sortWorkspaces(); dp.emit(); } From 421ba6e31a6c1d454a6b6f5ab9e61209682b1418 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Fri, 12 Apr 2024 18:48:54 +0200 Subject: [PATCH 484/842] fix: Add dummy information for battery cycles,health on FreeBSD --- src/modules/battery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index df3712a6..4facaaca 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -234,7 +234,7 @@ const std::tuple waybar::mo } // spdlog::info("{} {} {} {}", capacity,time,status,rate); - return {capacity, time / 60.0, status, rate}; + return {capacity, time / 60.0, status, rate, 0, 0.0F}; #elif defined(__linux__) uint32_t total_power = 0; // μW From 986b348bc721fb9d78221a5dc720556e08a8c9fb Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Sat, 13 Apr 2024 13:11:14 +0200 Subject: [PATCH 485/842] style: Change new variables to camelCase --- src/modules/battery.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 4facaaca..d2a24457 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -252,9 +252,9 @@ const std::tuple waybar::mo uint32_t time_to_full_now = 0; bool time_to_full_now_exists = false; - uint32_t largest_design_capacity = 0; - uint16_t main_bat_cycle_count = 0; - float main_bat_health_percent = 0.0f; + uint32_t largestDesignCapacity = 0; + uint16_t mainBatCycleCount = 0; + float mainBatHealthPercent = 0.0F; std::string status = "Unknown"; for (auto const& item : batteries_) { @@ -357,22 +357,22 @@ const std::tuple waybar::mo std::ifstream(bat / "energy_full_design") >> energy_full_design; } - bool is_main_battery = charge_full_design >= largest_design_capacity; + bool is_main_battery = charge_full_design >= largestDesignCapacity; uint16_t cycle_count = 0; if (fs::exists(bat / "cycle_count")) { std::ifstream(bat / "cycle_count") >> cycle_count; } if (is_main_battery) { - largest_design_capacity = charge_full_design; + largestDesignCapacity = charge_full_design; - if (cycle_count > main_bat_cycle_count) { - main_bat_cycle_count = cycle_count; + if (cycle_count > mainBatCycleCount) { + mainBatCycleCount = cycle_count; } if (charge_full_exists && charge_full_design_exists) { float bat_health_percent = ((float)charge_full / charge_full_design) * 100; - if (main_bat_health_percent == 0.0f || bat_health_percent < main_bat_health_percent) { - main_bat_health_percent = bat_health_percent; + if (mainBatHealthPercent == 0.0f || bat_health_percent < main_bat_health_percent) { + mainBatHealthPercent = bat_health_percent; } } } @@ -597,7 +597,7 @@ const std::tuple waybar::mo // still charging but not yet done if (cap == 100 && status == "Charging") status = "Full"; - return {cap, time_remaining, status, total_power / 1e6, main_bat_cycle_count, main_bat_health_percent}; + return {cap, time_remaining, status, total_power / 1e6, mainBatCycleCount, mainBatHealthPercent}; #endif } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); From da47c94480459085ef1214639ae88d2bbe3f694c Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Sat, 13 Apr 2024 13:18:50 +0200 Subject: [PATCH 486/842] fix: Also use camelCase for usages of new vars --- src/modules/battery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index d2a24457..f9f3084e 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -371,7 +371,7 @@ const std::tuple waybar::mo if (charge_full_exists && charge_full_design_exists) { float bat_health_percent = ((float)charge_full / charge_full_design) * 100; - if (mainBatHealthPercent == 0.0f || bat_health_percent < main_bat_health_percent) { + if (mainBatHealthPercent == 0.0f || bat_health_percent < mainBatHealthPercent) { mainBatHealthPercent = bat_health_percent; } } From 3d54a6002d9521ab835570522aa514e2816d2477 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Sat, 13 Apr 2024 13:19:54 +0200 Subject: [PATCH 487/842] style: Remove superfluous 'const' on getInfo() was here before, but is an easy fix for a clang-tidy warning --- include/modules/battery.hpp | 2 +- src/modules/battery.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 0468bff1..8e1a2ad2 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -32,7 +32,7 @@ class Battery : public ALabel { void refreshBatteries(); void worker(); const std::string getAdapterStatus(uint8_t capacity) const; - const std::tuple getInfos(); + std::tuple getInfos(); const std::string formatTimeRemaining(float hoursRemaining); void setBarClass(std::string&); diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index f9f3084e..d4bd3e2d 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -181,7 +181,7 @@ static bool status_gt(const std::string& a, const std::string& b) { return false; } -const std::tuple waybar::modules::Battery::getInfos() { +std::tuple waybar::modules::Battery::getInfos() { std::lock_guard guard(battery_list_mutex_); try { From 74773885c6166d190a294ad945b1f1d4d5f007ec Mon Sep 17 00:00:00 2001 From: hrdl <31923882+hrdl-github@users.noreply.github.com> Date: Tue, 9 Apr 2024 00:20:18 +0200 Subject: [PATCH 488/842] Pipewire backend: use pipewire thread lock Fixes #3047. --- src/util/pipewire/pipewire_backend.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/util/pipewire/pipewire_backend.cpp b/src/util/pipewire/pipewire_backend.cpp index 044b926f..5bb7c19a 100644 --- a/src/util/pipewire/pipewire_backend.cpp +++ b/src/util/pipewire/pipewire_backend.cpp @@ -48,12 +48,17 @@ PipewireBackend::PipewireBackend(PrivateConstructorTag tag) if (mainloop_ == nullptr) { throw std::runtime_error("pw_thread_loop_new() failed."); } + + pw_thread_loop_lock(mainloop_); + context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0); if (context_ == nullptr) { + pw_thread_loop_unlock(mainloop_); throw std::runtime_error("pa_context_new() failed."); } core_ = pw_context_connect(context_, nullptr, 0); if (core_ == nullptr) { + pw_thread_loop_unlock(mainloop_); throw std::runtime_error("pw_context_connect() failed"); } registry_ = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0); @@ -61,11 +66,17 @@ PipewireBackend::PipewireBackend(PrivateConstructorTag tag) spa_zero(registryListener_); pw_registry_add_listener(registry_, ®istryListener_, ®ISTRY_EVENTS, this); if (pw_thread_loop_start(mainloop_) < 0) { + pw_thread_loop_unlock(mainloop_); throw std::runtime_error("pw_thread_loop_start() failed."); } + pw_thread_loop_unlock(mainloop_); } PipewireBackend::~PipewireBackend() { + if (mainloop_ != nullptr) { + pw_thread_loop_lock(mainloop_); + } + if (registry_ != nullptr) { pw_proxy_destroy((struct pw_proxy *)registry_); } @@ -81,6 +92,7 @@ PipewireBackend::~PipewireBackend() { } if (mainloop_ != nullptr) { + pw_thread_loop_unlock(mainloop_); pw_thread_loop_stop(mainloop_); pw_thread_loop_destroy(mainloop_); } From 133dfc2e8526c8965d7e1517cfc5f89aaa1791ae Mon Sep 17 00:00:00 2001 From: Raphael Nestler Date: Mon, 15 Apr 2024 13:50:41 +0200 Subject: [PATCH 489/842] Remove unused variable in Workspaces::updateWindows --- src/modules/sway/workspaces.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 8a3b9c84..925448c2 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -266,7 +266,6 @@ bool Workspaces::hasFlag(const Json::Value &node, const std::string &flag) { } void Workspaces::updateWindows(const Json::Value &node, std::string &windows) { - auto format = config_["window-format"].asString(); if ((node["type"].asString() == "con" || node["type"].asString() == "floating_con") && node["name"].isString()) { std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1); From fb88c06d78cebe49310530268fee3c928fe5c239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Peter=20Dhall=C3=A9?= Date: Mon, 15 Apr 2024 21:59:35 +0200 Subject: [PATCH 490/842] calendar: add shift_reset action --- include/modules/clock.hpp | 2 ++ src/modules/clock.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index e7c3872c..9e10fb85 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -73,6 +73,7 @@ class Clock final : public ALabel { void cldModeSwitch(); void cldShift_up(); void cldShift_down(); + void cldShift_reset(); void tz_up(); void tz_down(); // Module Action Map @@ -80,6 +81,7 @@ class Clock final : public ALabel { {"mode", &waybar::modules::Clock::cldModeSwitch}, {"shift_up", &waybar::modules::Clock::cldShift_up}, {"shift_down", &waybar::modules::Clock::cldShift_down}, + {"shift_reset", &waybar::modules::Clock::cldShift_reset}, {"tz_up", &waybar::modules::Clock::tz_up}, {"tz_down", &waybar::modules::Clock::tz_down}}; }; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 83537405..9f26b51f 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -423,6 +423,9 @@ void waybar::modules::Clock::cldShift_up() { void waybar::modules::Clock::cldShift_down() { cldCurrShift_ -= (months)((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; } +void waybar::modules::Clock::cldShift_reset() { + cldCurrShift_ = (months)0; +} void waybar::modules::Clock::tz_up() { const auto tzSize{tzList_.size()}; if (tzSize == 1) return; From 67bf98a93e60c6db4f24f54d7505fe6a9f2dfca7 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 17 Apr 2024 14:46:35 +0200 Subject: [PATCH 491/842] style: Change more var names to camelCase --- src/modules/battery.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index d4bd3e2d..69f1b84d 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -357,22 +357,21 @@ std::tuple waybar::modules: std::ifstream(bat / "energy_full_design") >> energy_full_design; } - bool is_main_battery = charge_full_design >= largestDesignCapacity; - uint16_t cycle_count = 0; + uint16_t cycleCount = 0; if (fs::exists(bat / "cycle_count")) { - std::ifstream(bat / "cycle_count") >> cycle_count; + std::ifstream(bat / "cycle_count") >> cycleCount; } - if (is_main_battery) { + if (charge_full_design >= largestDesignCapacity) { largestDesignCapacity = charge_full_design; - if (cycle_count > mainBatCycleCount) { - mainBatCycleCount = cycle_count; + if (cycleCount > mainBatCycleCount) { + mainBatCycleCount = cycleCount; } if (charge_full_exists && charge_full_design_exists) { - float bat_health_percent = ((float)charge_full / charge_full_design) * 100; - if (mainBatHealthPercent == 0.0f || bat_health_percent < mainBatHealthPercent) { - mainBatHealthPercent = bat_health_percent; + float batHealthPercent = ((float)charge_full / charge_full_design) * 100; + if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) { + mainBatHealthPercent = batHealthPercent; } } } From 8ef4ddd7efa2b29a455f38dbb7eeddf4002f304d Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Apr 2024 08:34:02 +0200 Subject: [PATCH 492/842] fix: lint --- src/modules/battery.cpp | 25 +++++++++++++------------ src/modules/clock.cpp | 4 +--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 69f1b84d..8dae43d5 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -181,7 +181,8 @@ static bool status_gt(const std::string& a, const std::string& b) { return false; } -std::tuple waybar::modules::Battery::getInfos() { +std::tuple +waybar::modules::Battery::getInfos() { std::lock_guard guard(battery_list_mutex_); try { @@ -596,7 +597,8 @@ std::tuple waybar::modules: // still charging but not yet done if (cap == 100 && status == "Charging") status = "Full"; - return {cap, time_remaining, status, total_power / 1e6, mainBatCycleCount, mainBatHealthPercent}; + return { + cap, time_remaining, status, total_power / 1e6, mainBatCycleCount, mainBatHealthPercent}; #endif } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); @@ -686,12 +688,11 @@ auto waybar::modules::Battery::update() -> void { } else if (config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } - label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), - fmt::arg("timeTo", tooltip_text_default), - fmt::arg("power", power), fmt::arg("capacity", capacity), - fmt::arg("time", time_remaining_formatted), - fmt::arg("cycles", cycles), - fmt::arg("health", fmt::format("{:.3}", health)))); + label_.set_tooltip_text( + fmt::format(fmt::runtime(tooltip_format), fmt::arg("timeTo", tooltip_text_default), + fmt::arg("power", power), fmt::arg("capacity", capacity), + fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles), + fmt::arg("health", fmt::format("{:.3}", health)))); } if (!old_status_.empty()) { label_.get_style_context()->remove_class(old_status_); @@ -710,10 +711,10 @@ auto waybar::modules::Battery::update() -> void { } else { event_box_.show(); auto icons = std::vector{status + "-" + state, status, state}; - label_.set_markup(fmt::format( - fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), - fmt::arg("cycles", cycles))); + label_.set_markup( + fmt::format(fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), + fmt::arg("icon", getIcon(capacity, icons)), + fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles))); } // Call parent update ALabel::update(); diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 9f26b51f..92af70dc 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -423,9 +423,7 @@ void waybar::modules::Clock::cldShift_up() { void waybar::modules::Clock::cldShift_down() { cldCurrShift_ -= (months)((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; } -void waybar::modules::Clock::cldShift_reset() { - cldCurrShift_ = (months)0; -} +void waybar::modules::Clock::cldShift_reset() { cldCurrShift_ = (months)0; } void waybar::modules::Clock::tz_up() { const auto tzSize{tzList_.size()}; if (tzSize == 1) return; From 2673a5a4f1e2cee9f2edee57ca278697fdfdaf50 Mon Sep 17 00:00:00 2001 From: joesri <166829758+joesri@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:17:30 +0000 Subject: [PATCH 493/842] Escape tooltip in custom module --- src/modules/custom.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 5e5d7019..296a3b04 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -214,13 +214,19 @@ void waybar::modules::Custom::parseOutputRaw() { if (i == 0) { if (config_["escape"].isBool() && config_["escape"].asBool()) { text_ = Glib::Markup::escape_text(validated_line); + tooltip_ = Glib::Markup::escape_text(validated_line); } else { text_ = validated_line; + tooltip_ = validated_line; } tooltip_ = validated_line; class_.clear(); } else if (i == 1) { - tooltip_ = validated_line; + if (config_["escape"].isBool() && config_["escape"].asBool()) { + tooltip_ = Glib::Markup::escape_text(validated_line); + } else { + tooltip_ = validated_line; + } } else if (i == 2) { class_.push_back(validated_line); } else { @@ -246,7 +252,11 @@ void waybar::modules::Custom::parseOutputJson() { } else { alt_ = parsed["alt"].asString(); } - tooltip_ = parsed["tooltip"].asString(); + if (config_["escape"].isBool() && config_["escape"].asBool()) { + tooltip_ = Glib::Markup::escape_text(parsed["tooltip"].asString()); + } else { + tooltip_ = parsed["tooltip"].asString(); + } if (parsed["class"].isString()) { class_.push_back(parsed["class"].asString()); } else if (parsed["class"].isArray()) { From f75b2ae91f25a28462f6c3d54eaf0a3e851d210c Mon Sep 17 00:00:00 2001 From: vawvaw Date: Thu, 18 Apr 2024 21:52:53 +0200 Subject: [PATCH 494/842] sway/workspaces: Fix scroll on unfocused monitor --- src/modules/sway/workspaces.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 925448c2..7517dc26 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -434,9 +434,16 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { } std::string name; { + bool alloutputs = config_["all-outputs"].asBool(); std::lock_guard lock(mutex_); - auto it = std::find_if(workspaces_.begin(), workspaces_.end(), - [](const auto &workspace) { return hasFlag(workspace, "focused"); }); + auto it = + std::find_if(workspaces_.begin(), workspaces_.end(), [alloutputs](const auto &workspace) { + if (alloutputs) { + return hasFlag(workspace, "focused"); + } + bool noNodes = workspace["nodes"].empty() && workspace["floating_nodes"].empty(); + return hasFlag(workspace, "visible") || (workspace["output"].isString() && noNodes); + }); if (it == workspaces_.end()) { return true; } From 937bf2ba5d5a9b00148519a694e2fa5c2fb4b4ae Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 19 Apr 2024 06:21:10 +0200 Subject: [PATCH 495/842] fix: lint --- src/modules/custom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 296a3b04..ec3bb3fa 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -226,7 +226,7 @@ void waybar::modules::Custom::parseOutputRaw() { tooltip_ = Glib::Markup::escape_text(validated_line); } else { tooltip_ = validated_line; - } + } } else if (i == 2) { class_.push_back(validated_line); } else { From 6c1125c1feaf81957e16cca924d08b56e3eb841e Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" Date: Wed, 17 Apr 2024 22:23:59 +0200 Subject: [PATCH 496/842] feat(#2989): (optional) hover for all modules --- include/AModule.hpp | 2 ++ man/waybar-styles.5.scd.in | 10 ++++++++++ resources/style.css | 5 +++++ src/AModule.cpp | 17 +++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/include/AModule.hpp b/include/AModule.hpp index c15efb00..ea692ff8 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -38,6 +38,8 @@ class AModule : public IModule { Gtk::EventBox event_box_; virtual bool handleToggle(GdkEventButton *const &ev); + virtual bool handleMouseEnter(GdkEventCrossing *const &ev); + virtual bool handleMouseLeave(GdkEventCrossing *const &ev); virtual bool handleScroll(GdkEventScroll *); virtual bool handleRelease(GdkEventButton *const &ev); diff --git a/man/waybar-styles.5.scd.in b/man/waybar-styles.5.scd.in index ddc4c3c9..0af393ef 100644 --- a/man/waybar-styles.5.scd.in +++ b/man/waybar-styles.5.scd.in @@ -29,6 +29,16 @@ An example user-controlled stylesheet that just changes the color of the clock t } ``` +## Hover-effect + +You can apply special styling to any module for when the cursor hovers it. + +``` +#clock:hover { + background-color: #ffffff; +} +``` + # SEE ALSO - *waybar(5)* diff --git a/resources/style.css b/resources/style.css index b5859390..7e830285 100644 --- a/resources/style.css +++ b/resources/style.css @@ -48,6 +48,11 @@ button:hover { box-shadow: inset 0 -3px #ffffff; } +/* you can set a style on hover for any module like this */ +#pulseaudio:hover { + background-color: #a37800; +} + #workspaces button { padding: 0 5px; background-color: transparent; diff --git a/src/AModule.cpp b/src/AModule.cpp index a451c3d6..a285da77 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -27,6 +27,9 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); } + event_box_.signal_enter_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseEnter)); + event_box_.signal_leave_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave)); + // configure events' user commands // hasUserEvent is true if any element from eventMap_ is satisfying the condition in the lambda bool hasUserEvent = @@ -83,6 +86,20 @@ auto AModule::doAction(const std::string& name) -> void { } } +bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { + if (auto* module = event_box_.get_child(); module != nullptr) { + module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + } + return true; +} + +bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { + if (auto* module = event_box_.get_child(); module != nullptr) { + module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + } + return true; +} + bool AModule::handleToggle(GdkEventButton* const& e) { return handleUserEvent(e); } bool AModule::handleRelease(GdkEventButton* const& e) { return handleUserEvent(e); } From 2123995b03d41537cb48d5ecbb17d8fc240490c7 Mon Sep 17 00:00:00 2001 From: drendog <53359960+drendog@users.noreply.github.com> Date: Sun, 21 Apr 2024 18:02:26 +0200 Subject: [PATCH 497/842] fix: update clock tooltip without placeholders scenario --- src/modules/clock.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 92af70dc..2901c0d1 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -152,6 +152,8 @@ auto waybar::modules::Clock::update() -> void { std::regex_replace(tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); tlpText_ = std::regex_replace(tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); + } else { + tlpText_ = tlpFmt_; } tlpText_ = fmt_lib::vformat(locale_, tlpText_, fmt_lib::make_format_args(shiftedNow)); From 501e63fba63b244b589faf5fe2489a7e440a9ae0 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 Apr 2024 07:53:22 +0200 Subject: [PATCH 498/842] chore: 0.10.1 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 92d1ead4..ae388b5a 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.10.0', + version: '0.10.1', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From 5b7d0a28109611a92334bc3686c15c1bdc059628 Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" Date: Tue, 23 Apr 2024 16:18:54 +0200 Subject: [PATCH 499/842] fix(#3162): hover event did not propagate causing issues --- src/AModule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index a285da77..399a23e4 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -90,14 +90,14 @@ bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - return true; + return false; } bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - return true; + return false; } bool AModule::handleToggle(GdkEventButton* const& e) { return handleUserEvent(e); } From a04016e0b6033b783e4b706e764aa8b5466f81c1 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 Apr 2024 17:59:08 +0200 Subject: [PATCH 500/842] chore: 0.10.2 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ae388b5a..ec23923a 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.10.1', + version: '0.10.2', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From efa7dc7ba49c0479cdbe247049052491b5b0b9ef Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:34:35 +0200 Subject: [PATCH 501/842] fix(battery): Register health replacement for main format --- src/modules/battery.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 8dae43d5..e9fa3db2 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -714,7 +714,8 @@ auto waybar::modules::Battery::update() -> void { label_.set_markup( fmt::format(fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), fmt::arg("icon", getIcon(capacity, icons)), - fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles))); + fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles), + fmt::arg("health", fmt::format("{:.3}", health)))); } // Call parent update ALabel::update(); From 54a85ea15fa8bcda0eeeba428728ed5d7670dbef Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:56:24 +0000 Subject: [PATCH 502/842] style: Apply clang-format change At least I hope I copy-pased it correctly --- src/modules/battery.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index e9fa3db2..7566e33e 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -712,10 +712,10 @@ auto waybar::modules::Battery::update() -> void { event_box_.show(); auto icons = std::vector{status + "-" + state, status, state}; label_.set_markup( - fmt::format(fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), - fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles), - fmt::arg("health", fmt::format("{:.3}", health)))); + label_.set_markup(fmt::format( + fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), + fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), + fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health)))); } // Call parent update ALabel::update(); From 8d962430dd7c058ca77ebb037b4763baa47f7a8b Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:02:03 +0000 Subject: [PATCH 503/842] fix(battery): Remove duplicate line This is what happens when you copy-paste from GitHub actions --- src/modules/battery.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 7566e33e..8a9c80d9 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -711,11 +711,10 @@ auto waybar::modules::Battery::update() -> void { } else { event_box_.show(); auto icons = std::vector{status + "-" + state, status, state}; - label_.set_markup( - label_.set_markup(fmt::format( - fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), - fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health)))); + label_.set_markup(fmt::format( + fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), + fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), + fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health)))); } // Call parent update ALabel::update(); From 5c4e36881916760d0741b193c1339fb62980ee3f Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:05:11 +0000 Subject: [PATCH 504/842] style(battery): Indent level It's now *inconsistent* in the file, but clang-tidy should be happy, sooo... --- src/modules/battery.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 8a9c80d9..5d822f10 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -712,9 +712,9 @@ auto waybar::modules::Battery::update() -> void { event_box_.show(); auto icons = std::vector{status + "-" + state, status, state}; label_.set_markup(fmt::format( - fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), - fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health)))); + fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power), + fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted), + fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health)))); } // Call parent update ALabel::update(); From 57197b8e016b5ad42f4ae39b4860afa857b47a70 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:53:04 +0200 Subject: [PATCH 505/842] feat(battery): Also support energy_full (instead of charge_full) --- src/modules/battery.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 5d822f10..43816d9c 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -374,6 +374,11 @@ waybar::modules::Battery::getInfos() { if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) { mainBatHealthPercent = batHealthPercent; } + } else if (energy_full_exists && energy_full_design_exists) { + float batHealthPercent = ((float)energy_full / energy_full_design) * 100; + if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) { + mainBatHealthPercent = batHealthPercent; + } } } From a2c5a8215ba996acada6ac9229767847fbc1dc85 Mon Sep 17 00:00:00 2001 From: Kiri <56218513+kiriDevs@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:07:20 +0200 Subject: [PATCH 506/842] style(battery): Capitalize float 'F' suffix --- src/modules/battery.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 43816d9c..327420ae 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -371,12 +371,12 @@ waybar::modules::Battery::getInfos() { if (charge_full_exists && charge_full_design_exists) { float batHealthPercent = ((float)charge_full / charge_full_design) * 100; - if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) { + if (mainBatHealthPercent == 0.0F || batHealthPercent < mainBatHealthPercent) { mainBatHealthPercent = batHealthPercent; } } else if (energy_full_exists && energy_full_design_exists) { float batHealthPercent = ((float)energy_full / energy_full_design) * 100; - if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) { + if (mainBatHealthPercent == 0.0F || batHealthPercent < mainBatHealthPercent) { mainBatHealthPercent = batHealthPercent; } } From 9a3044a54f1d38a3618cb6369ebbcf6a91e9d996 Mon Sep 17 00:00:00 2001 From: Milo Mordaunt Date: Wed, 24 Apr 2024 18:15:40 -0400 Subject: [PATCH 507/842] Cursor change to indicate module clickability (#3108) * Indicate clickability on mouse hover * Avoid messy overrides situation * Update AModule.cpp * Update AModule.cpp * Update AModule.cpp * Update AModule.cpp --------- Co-authored-by: Alexis Rouillard --- include/AModule.hpp | 3 +++ src/AModule.cpp | 26 +++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index ea692ff8..58076655 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -37,6 +37,8 @@ class AModule : public IModule { const Json::Value &config_; Gtk::EventBox event_box_; + virtual void setCursor(Gdk::CursorType const c); + virtual bool handleToggle(GdkEventButton *const &ev); virtual bool handleMouseEnter(GdkEventCrossing *const &ev); virtual bool handleMouseLeave(GdkEventCrossing *const &ev); @@ -46,6 +48,7 @@ class AModule : public IModule { private: bool handleUserEvent(GdkEventButton *const &ev); const bool isTooltip; + bool hasUserEvents_; std::vector pid_; gdouble distance_scrolled_y_; gdouble distance_scrolled_x_; diff --git a/src/AModule.cpp b/src/AModule.cpp index 399a23e4..082e6233 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -15,6 +15,7 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: distance_scrolled_x_(0.0) { // Configure module action Map const Json::Value actions{config_["actions"]}; + for (Json::Value::const_iterator it = actions.begin(); it != actions.end(); ++it) { if (it.key().isString() && it->isString()) if (eventActionMap_.count(it.key().asString()) == 0) { @@ -31,17 +32,20 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: event_box_.signal_leave_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave)); // configure events' user commands - // hasUserEvent is true if any element from eventMap_ is satisfying the condition in the lambda - bool hasUserEvent = + // hasUserEvents is true if any element from eventMap_ is satisfying the condition in the lambda + bool hasUserEvents = std::find_if(eventMap_.cbegin(), eventMap_.cend(), [&config](const auto& eventEntry) { // True if there is any non-release type event return eventEntry.first.second != GdkEventType::GDK_BUTTON_RELEASE && config[eventEntry.second].isString(); }) != eventMap_.cend(); - if (enable_click || hasUserEvent) { + if (enable_click || hasUserEvents) { + hasUserEvents_ = true; event_box_.add_events(Gdk::BUTTON_PRESS_MASK); event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle)); + } else { + hasUserEvents_ = false; } bool hasReleaseEvent = @@ -86,10 +90,21 @@ auto AModule::doAction(const std::string& name) -> void { } } + +void AModule::setCursor(Gdk::CursorType c) { + auto cursor = Gdk::Cursor::create(Gdk::HAND2); + auto gdk_window = event_box_.get_window(); + gdk_window->set_cursor(cursor); +} + bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } + + if (hasUserEvents_) { + setCursor(Gdk::HAND2); + } return false; } @@ -97,6 +112,10 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } + + if (hasUserEvents_) { + setCursor(Gdk::ARROW); + } return false; } @@ -108,6 +127,7 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) { std::string format{}; const std::map, std::string>::const_iterator& rec{ eventMap_.find(std::pair(e->button, e->type))}; + if (rec != eventMap_.cend()) { // First call module actions this->AModule::doAction(rec->second); From 61ac7e4e107ac17bb872eba8062dcda18f38abaa Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 25 Apr 2024 00:16:15 +0200 Subject: [PATCH 508/842] fix: lint --- src/AModule.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 082e6233..bca8ec61 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -90,7 +90,6 @@ auto AModule::doAction(const std::string& name) -> void { } } - void AModule::setCursor(Gdk::CursorType c) { auto cursor = Gdk::Cursor::create(Gdk::HAND2); auto gdk_window = event_box_.get_window(); @@ -101,7 +100,7 @@ bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - + if (hasUserEvents_) { setCursor(Gdk::HAND2); } @@ -112,7 +111,7 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - + if (hasUserEvents_) { setCursor(Gdk::ARROW); } From 2481f7a2924d18ce83bc2bfc2434a06349a7bb32 Mon Sep 17 00:00:00 2001 From: clayton craft Date: Thu, 25 Apr 2024 01:36:43 -0700 Subject: [PATCH 509/842] upower: fix segfault by initializing lastWarningLevel (#3171) fixes bd8b215416cdca6ed0c929c18cede7dfb907edf0 --- include/modules/upower/upower.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index 8cea8c42..a5eb7209 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -71,7 +71,7 @@ class UPower : public AModule { GDBusConnection *login1_connection; std::unique_ptr upower_tooltip; std::string lastStatus; - const char *lastWarningLevel; + const char *lastWarningLevel = nullptr; bool showAltText; bool showIcon = true; bool upowerRunning; From f41458ea24a57bb71b629089396c31fe4dd97f1c Mon Sep 17 00:00:00 2001 From: Tuur Vanhoutte <4633209+zjeffer@users.noreply.github.com> Date: Mon, 29 Apr 2024 19:46:28 +0200 Subject: [PATCH 510/842] Fix Hyprland socketpath changed to XDG_RUNTIME_DIR (#3183) --- src/modules/hyprland/backend.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 05db94ec..98eb8b90 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -9,11 +9,30 @@ #include #include +#include #include #include namespace waybar::modules::hyprland { +std::filesystem::path getSocketFolder(const char* instanceSig) { + // socket path, specified by EventManager of Hyprland + static std::filesystem::path socketFolder; + if (!socketFolder.empty()) { + return socketFolder; + } + + std::filesystem::path xdgRuntimeDir = std::filesystem::path(getenv("XDG_RUNTIME_DIR")); + if (!xdgRuntimeDir.empty() && std::filesystem::exists(xdgRuntimeDir / "hypr")) { + socketFolder = xdgRuntimeDir / "hypr"; + } else { + spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr"); + socketFolder = std::filesystem::temp_directory_path() / "hypr"; + } + socketFolder = socketFolder / instanceSig; + return socketFolder; +} + void IPC::startIPC() { // will start IPC and relay events to parseIPC @@ -40,9 +59,7 @@ void IPC::startIPC() { addr.sun_family = AF_UNIX; - // socket path, specified by EventManager of Hyprland - std::string socketPath = "/tmp/hypr/" + std::string(his) + "/.socket2.sock"; - + auto socketPath = getSocketFolder(his) / ".socket2.sock"; strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); addr.sun_path[sizeof(addr.sun_path) - 1] = 0; @@ -142,12 +159,10 @@ std::string IPC::getSocket1Reply(const std::string& rq) { return ""; } - std::string instanceSigStr = std::string(instanceSig); - sockaddr_un serverAddress = {0}; serverAddress.sun_family = AF_UNIX; - std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock"; + std::string socketPath = getSocketFolder(instanceSig) / ".socket.sock"; // Use snprintf to copy the socketPath string into serverAddress.sun_path if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < From 79ae530bd29cb561d6f48773e894dd62fe353b7f Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Thu, 2 May 2024 06:31:40 +0000 Subject: [PATCH 511/842] pipewire: unbreak build on FreeBSD (#3193) --- .github/workflows/freebsd.yml | 2 +- include/util/pipewire/pipewire_backend.hpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 7b27fdb6..0b628d19 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -29,7 +29,7 @@ jobs: sudo pkg install -y git # subprojects/date sudo pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \ libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ - pkgconf pulseaudio scdoc sndio spdlog wayland-protocols upower \ + pkgconf pipewire pulseaudio scdoc sndio spdlog wayland-protocols upower \ libinotify meson build -Dman-pages=enabled ninja -C build diff --git a/include/util/pipewire/pipewire_backend.hpp b/include/util/pipewire/pipewire_backend.hpp index ac70a139..90fb2bb2 100644 --- a/include/util/pipewire/pipewire_backend.hpp +++ b/include/util/pipewire/pipewire_backend.hpp @@ -2,6 +2,8 @@ #include +#include + #include "util/backend_common.hpp" #include "util/pipewire/privacy_node_info.hpp" From 0b6476da32d181ee6b2cabdc5205a46a90521a75 Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" <31956036+haug1@users.noreply.github.com> Date: Thu, 2 May 2024 22:09:21 +0200 Subject: [PATCH 512/842] fix: set cursor appropriately on user event hover (#3195) --- src/AModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index bca8ec61..915c8603 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -91,7 +91,7 @@ auto AModule::doAction(const std::string& name) -> void { } void AModule::setCursor(Gdk::CursorType c) { - auto cursor = Gdk::Cursor::create(Gdk::HAND2); + auto cursor = Gdk::Cursor::create(c); auto gdk_window = event_box_.get_window(); gdk_window->set_cursor(cursor); } From 50476edc98d091573eef14b6c145251456712d4b Mon Sep 17 00:00:00 2001 From: Jacob Birkett Date: Thu, 2 May 2024 23:31:39 -0700 Subject: [PATCH 513/842] Nix Flake: Fix overlay (again) (#3196) --- flake.nix | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/flake.nix b/flake.nix index ebaeb81f..1ef5b09c 100644 --- a/flake.nix +++ b/flake.nix @@ -46,22 +46,25 @@ }; }); - overlays.default = final: prev: { - waybar = final.callPackage ./nix/default.nix { - # take the first "version: '...'" from meson.build - version = - (builtins.head (builtins.split "'" - (builtins.elemAt - (builtins.split " version: '" (builtins.readFile ./meson.build)) - 2))) - + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); + overlays = { + default = self.overlays.waybar; + waybar = final: prev: { + waybar = final.callPackage ./nix/default.nix { + waybar = prev.waybar; + # take the first "version: '...'" from meson.build + version = + (builtins.head (builtins.split "'" + (builtins.elemAt + (builtins.split " version: '" (builtins.readFile ./meson.build)) + 2))) + + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); + }; }; }; - packages = genSystems (pkgs: - let packages = self.overlays.default pkgs pkgs; - in packages // { - default = packages.waybar; - }); + packages = genSystems (pkgs: { + default = self.packages.${pkgs.stdenv.hostPlatform.system}.waybar; + inherit (pkgs) waybar; + }); }; } From 231d6972d7a023e9358ab7deda509baac49006cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E8=8A=AD=E8=80=81=E7=88=B9?= <37040069+stanly0726@users.noreply.github.com> Date: Fri, 3 May 2024 14:47:41 +0800 Subject: [PATCH 514/842] fix: custom module mediaplayer doesn't respect argument (#3198) fix custom module mediaplayer which doesn't consider --exclude argument on player appear --- resources/custom_modules/mediaplayer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index 4aea4171..acc47496 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -136,6 +136,10 @@ class PlayerManager: def on_player_appeared(self, _, player): logger.info(f"Player has appeared: {player.name}") + if player.name in self.excluded_player: + logger.debug( + "New player appeared, but it's in exclude player list, skipping") + return if player is not None and (self.selected_player is None or player.name == self.selected_player): self.init_player(player) else: From c4e0c569aa74d771d62dbbedb52326871492ef59 Mon Sep 17 00:00:00 2001 From: Bintang <96517350+spitulax@users.noreply.github.com> Date: Mon, 6 May 2024 15:46:10 +0700 Subject: [PATCH 515/842] flake: fix overlay not actually being applied (#3208) --- flake.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 1ef5b09c..571c4934 100644 --- a/flake.nix +++ b/flake.nix @@ -16,7 +16,12 @@ "x86_64-linux" "aarch64-linux" ] - (system: func (import nixpkgs { inherit system; })); + (system: func (import nixpkgs { + inherit system; + overlays = with self.overlays; [ + waybar + ]; + })); mkDate = longDate: (lib.concatStringsSep "-" [ (builtins.substring 0 4 longDate) From 8e8ce0c6bcacb000c91fb2ef1308b11bab518518 Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" <31956036+haug1@users.noreply.github.com> Date: Mon, 6 May 2024 10:47:25 +0200 Subject: [PATCH 516/842] feat(#3182): style tray icon on hover (#3203) --- include/modules/sni/item.hpp | 2 ++ src/modules/sni/item.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index 1043157c..ebc08d45 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -76,6 +76,8 @@ class Item : public sigc::trackable { void makeMenu(); bool handleClick(GdkEventButton* const& /*ev*/); bool handleScroll(GdkEventScroll* const&); + bool handleMouseEnter(GdkEventCrossing* const&); + bool handleMouseLeave(GdkEventCrossing* const&); // smooth scrolling threshold gdouble scroll_threshold_ = 0; diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index c3de2357..b5c0dd85 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -8,6 +8,7 @@ #include #include +#include "gdk/gdk.h" #include "util/format.hpp" #include "util/gtk_icon.hpp" @@ -57,6 +58,8 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf event_box.add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick)); event_box.signal_scroll_event().connect(sigc::mem_fun(*this, &Item::handleScroll)); + event_box.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Item::handleMouseEnter)); + event_box.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Item::handleMouseLeave)); // initial visibility event_box.show_all(); event_box.set_visible(show_passive_); @@ -69,6 +72,16 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf cancellable_, interface); } +bool Item::handleMouseEnter(GdkEventCrossing* const& e) { + event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + return false; +} + +bool Item::handleMouseLeave(GdkEventCrossing* const& e) { + event_box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + return false; +} + void Item::onConfigure(GdkEventConfigure* ev) { this->updateImage(); } void Item::proxyReady(Glib::RefPtr& result) { From a453ea3c70195803e9962a97f9243c0c78d4ecdc Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" <31956036+haug1@users.noreply.github.com> Date: Mon, 6 May 2024 10:47:52 +0200 Subject: [PATCH 517/842] fix(#3210): tooltip-format on custom modules not working in some cases (#3213) --- include/modules/custom.hpp | 1 + src/modules/custom.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/modules/custom.hpp b/include/modules/custom.hpp index 2c7ba8a8..6c17c6e4 100644 --- a/include/modules/custom.hpp +++ b/include/modules/custom.hpp @@ -35,6 +35,7 @@ class Custom : public ALabel { std::string id_; std::string alt_; std::string tooltip_; + const bool tooltip_format_enabled_; std::vector class_; int percentage_; FILE* fp_; diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index ec3bb3fa..45e849cc 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -10,6 +10,7 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id, name_(name), output_name_(output_name), id_(id), + tooltip_format_enabled_{config_["tooltip-format"].isString()}, percentage_(0), fp_(nullptr), pid_(-1) { @@ -166,16 +167,16 @@ auto waybar::modules::Custom::update() -> void { } else { label_.set_markup(str); if (tooltipEnabled()) { - if (text_ == tooltip_) { - if (label_.get_tooltip_markup() != str) { - label_.set_tooltip_markup(str); - } - } else if (config_["tooltip-format"].isString()) { + if (tooltip_format_enabled_) { auto tooltip = config_["tooltip-format"].asString(); tooltip = fmt::format(fmt::runtime(tooltip), text_, fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); label_.set_tooltip_markup(tooltip); + } else if (text_ == tooltip_) { + if (label_.get_tooltip_markup() != str) { + label_.set_tooltip_markup(str); + } } else { if (label_.get_tooltip_markup() != tooltip_) { label_.set_tooltip_markup(tooltip_); From fc6d708fb63497064a791b4f451eb561fe3abb37 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 May 2024 10:50:55 +0200 Subject: [PATCH 518/842] chore: disable cland-tidy for now --- .github/workflows/{clang-tidy.yml => clang-tidy.yml.bak} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{clang-tidy.yml => clang-tidy.yml.bak} (100%) diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml.bak similarity index 100% rename from .github/workflows/clang-tidy.yml rename to .github/workflows/clang-tidy.yml.bak From e7779b545855bac1e24db65deefc830fd0fbd66b Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" <31956036+haug1@users.noreply.github.com> Date: Mon, 6 May 2024 10:51:03 +0200 Subject: [PATCH 519/842] feat(#3174): hover for whole group (#3201) * feat(#3174): hover for whole group * fix: target eventbox for class also * fix: actually no reason to add handler, just override AModule * fix: actually remove existing handler as well drawer functionality still works from my testing. anything else to think abotu? * revert: keep id and class on original box * refactor: clang-format group.hpp * dev: try stop workflow --- include/group.hpp | 14 ++++++-------- src/group.cpp | 45 +++++++++++++++++---------------------------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index 67cf4385..564d2eb5 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -11,15 +11,13 @@ namespace waybar { class Group : public AModule { public: - Group(const std::string&, const std::string&, const Json::Value&, bool); + Group(const std::string &, const std::string &, const Json::Value &, bool); virtual ~Group() = default; auto update() -> void override; - operator Gtk::Widget&() override; + operator Gtk::Widget &() override; - virtual Gtk::Box& getBox(); - void addWidget(Gtk::Widget& widget); - - bool handleMouseHover(GdkEventCrossing* const& e); + virtual Gtk::Box &getBox(); + void addWidget(Gtk::Widget &widget); protected: Gtk::Box box; @@ -28,8 +26,8 @@ class Group : public AModule { bool is_first_widget = true; bool is_drawer = false; std::string add_class_to_drawer_children; - - void addHoverHandlerTo(Gtk::Widget& widget); + bool handleMouseEnter(GdkEventCrossing *const &ev) override; + bool handleMouseLeave(GdkEventCrossing *const &ev) override; }; } // namespace waybar diff --git a/src/group.cpp b/src/group.cpp index 262cae65..b3261735 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -4,7 +4,7 @@ #include -#include "gdkmm/device.h" +#include "gtkmm/enums.h" #include "gtkmm/widget.h" namespace waybar { @@ -78,30 +78,23 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& } else { box.pack_start(revealer); } - - addHoverHandlerTo(revealer); - } -} - -bool Group::handleMouseHover(GdkEventCrossing* const& e) { - switch (e->type) { - case GDK_ENTER_NOTIFY: - revealer.set_reveal_child(true); - break; - case GDK_LEAVE_NOTIFY: - revealer.set_reveal_child(false); - break; - default: - break; } - return true; + event_box_.add(box); } -void Group::addHoverHandlerTo(Gtk::Widget& widget) { - widget.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK); - widget.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseHover)); - widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseHover)); +bool Group::handleMouseEnter(GdkEventCrossing* const& e) { + box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + revealer.set_reveal_child(true); + return false; +} + + + +bool Group::handleMouseLeave(GdkEventCrossing* const& e) { + box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + revealer.set_reveal_child(false); + return false; } auto Group::update() -> void { @@ -113,17 +106,13 @@ Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer void Group::addWidget(Gtk::Widget& widget) { getBox().pack_start(widget, false, false); - if (is_drawer) { - // Necessary because of GTK's hitbox detection - addHoverHandlerTo(widget); - if (!is_first_widget) { - widget.get_style_context()->add_class(add_class_to_drawer_children); - } + if (is_drawer && !is_first_widget) { + widget.get_style_context()->add_class(add_class_to_drawer_children); } is_first_widget = false; } -Group::operator Gtk::Widget&() { return box; } +Group::operator Gtk::Widget&() { return event_box_; } } // namespace waybar From df1a9c5509543cc2dbfed5f0230918ae88473b81 Mon Sep 17 00:00:00 2001 From: Eldar Yusupov Date: Mon, 6 May 2024 11:51:14 +0300 Subject: [PATCH 520/842] Remove listener when window is destroyed (#3215) --- include/modules/dwl/window.hpp | 2 +- src/modules/dwl/window.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/modules/dwl/window.hpp b/include/modules/dwl/window.hpp index 6b068360..43586399 100644 --- a/include/modules/dwl/window.hpp +++ b/include/modules/dwl/window.hpp @@ -14,7 +14,7 @@ namespace waybar::modules::dwl { class Window : public AAppIconLabel, public sigc::trackable { public: Window(const std::string &, const waybar::Bar &, const Json::Value &); - virtual ~Window() = default; + ~Window(); void handle_layout(const uint32_t layout); void handle_title(const char *title); diff --git a/src/modules/dwl/window.cpp b/src/modules/dwl/window.cpp index 4f8b0281..870d87e4 100644 --- a/src/modules/dwl/window.cpp +++ b/src/modules/dwl/window.cpp @@ -80,7 +80,7 @@ Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) wl_registry_add_listener(registry, ®istry_listener_impl, this); wl_display_roundtrip(display); - if (!status_manager_) { + if (status_manager_ == nullptr) { spdlog::error("dwl_status_manager_v2 not advertised"); return; } @@ -91,6 +91,12 @@ Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) zdwl_ipc_manager_v2_destroy(status_manager_); } +Window::~Window() { + if (output_status_ != nullptr) { + zdwl_ipc_output_v2_destroy(output_status_); + } +} + void Window::handle_title(const char *title) { title_ = title; } void Window::handle_appid(const char *appid) { appid_ = appid; } From 0572e02d7e04fbac20c5f881ef778ef5e6b8dd67 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 May 2024 10:51:30 +0200 Subject: [PATCH 521/842] fix: lint --- src/group.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/group.cpp b/src/group.cpp index b3261735..9f707dc9 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -89,8 +89,6 @@ bool Group::handleMouseEnter(GdkEventCrossing* const& e) { return false; } - - bool Group::handleMouseLeave(GdkEventCrossing* const& e) { box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); revealer.set_reveal_child(false); From e627879b1656ec7352e6382f80ee16d90b377aaf Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 May 2024 10:54:52 +0200 Subject: [PATCH 522/842] chore: 0.10.3 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ec23923a..a57b17f8 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.10.2', + version: '0.10.3', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From 29917fb073c957f5610e951d3c0c3e21eb4f377c Mon Sep 17 00:00:00 2001 From: Tuur Vanhoutte <4633209+zjeffer@users.noreply.github.com> Date: Tue, 7 May 2024 08:26:05 +0200 Subject: [PATCH 523/842] Fix hyprland/language events not working with keyboard names with commas in them (#3224) --- src/modules/hyprland/language.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 549faf73..49b20218 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -63,7 +63,7 @@ auto Language::update() -> void { void Language::onEvent(const std::string& ev) { std::lock_guard lg(mutex_); std::string kbName(begin(ev) + ev.find_last_of('>') + 1, begin(ev) + ev.find_first_of(',')); - auto layoutName = ev.substr(ev.find_first_of(',') + 1); + auto layoutName = ev.substr(ev.find_last_of(',') + 1); if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString()) return; // ignore From 2ead1bbf84ff0fdb7234b1d9332c3b3a3bb8b799 Mon Sep 17 00:00:00 2001 From: ViktarL <23121044+LukashonakV@users.noreply.github.com> Date: Tue, 7 May 2024 11:29:52 +0300 Subject: [PATCH 524/842] Upower refactoring (#3220) Signed-off-by: Viktar Lukashonak --- include/modules/upower.hpp | 92 +++++ include/modules/upower/upower.hpp | 82 ---- include/modules/upower/upower_tooltip.hpp | 33 -- meson.build | 5 +- src/factory.cpp | 4 +- src/modules/upower.cpp | 479 ++++++++++++++++++++++ src/modules/upower/upower.cpp | 418 ------------------- src/modules/upower/upower_tooltip.cpp | 160 -------- 8 files changed, 574 insertions(+), 699 deletions(-) create mode 100644 include/modules/upower.hpp delete mode 100644 include/modules/upower/upower.hpp delete mode 100644 include/modules/upower/upower_tooltip.hpp create mode 100644 src/modules/upower.cpp delete mode 100644 src/modules/upower/upower.cpp delete mode 100644 src/modules/upower/upower_tooltip.cpp diff --git a/include/modules/upower.hpp b/include/modules/upower.hpp new file mode 100644 index 00000000..b4450df2 --- /dev/null +++ b/include/modules/upower.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include + +#include + +#include "AIconLabel.hpp" + +namespace waybar::modules { + +class UPower final : public AIconLabel { + public: + UPower(const std::string &, const Json::Value &); + virtual ~UPower(); + auto update() -> void override; + + private: + const std::string NO_BATTERY{"battery-missing-symbolic"}; + + // Config + bool showIcon_{true}; + bool hideIfEmpty_{true}; + int iconSize_{20}; + int tooltip_spacing_{4}; + int tooltip_padding_{4}; + Gtk::Box contentBox_; // tooltip box + std::string tooltipFormat_; + + // UPower device info + struct upDevice_output { + UpDevice *upDevice{NULL}; + double percentage{0.0}; + double temperature{0.0}; + guint64 time_full{0u}; + guint64 time_empty{0u}; + gchar *icon_name{(char *)'\0'}; + bool upDeviceValid{false}; + UpDeviceState state; + UpDeviceKind kind; + char *nativePath{(char *)'\0'}; + char *model{(char *)'\0'}; + }; + + // Technical variables + std::string nativePath_; + std::string lastStatus_; + Glib::ustring label_markup_; + std::mutex mutex_; + Glib::RefPtr gtkTheme_; + + // Technical functions + void addDevice(UpDevice *); + void removeDevice(const gchar *); + void removeDevices(); + void resetDevices(); + void setDisplayDevice(); + const Glib::ustring getText(const upDevice_output &upDevice_, const std::string &format); + bool queryTooltipCb(int, int, bool, const Glib::RefPtr &); + + // DBUS variables + guint watcherID_; + Glib::RefPtr conn_; + guint subscrID_{0u}; + + // UPower variables + UpClient *upClient_; + upDevice_output upDevice_; // Device to display + typedef std::unordered_map Devices; + Devices devices_; + bool upRunning_{true}; + + // DBus callbacks + void getConn_cb(Glib::RefPtr &result); + void onAppear(const Glib::RefPtr &, const Glib::ustring &, + const Glib::ustring &); + void onVanished(const Glib::RefPtr &, const Glib::ustring &); + void prepareForSleep_cb(const Glib::RefPtr &connection, + const Glib::ustring &sender_name, const Glib::ustring &object_path, + const Glib::ustring &interface_name, const Glib::ustring &signal_name, + const Glib::VariantContainerBase ¶meters); + + // UPower callbacks + static void deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data); + static void deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data); + static void deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer user_data); + // UPower secondary functions + void getUpDeviceInfo(upDevice_output &upDevice_); +}; + +} // namespace waybar::modules diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp deleted file mode 100644 index a5eb7209..00000000 --- a/include/modules/upower/upower.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include - -#include "ALabel.hpp" -#include "glibconfig.h" -#include "gtkmm/box.h" -#include "gtkmm/image.h" -#include "gtkmm/label.h" -#include "modules/upower/upower_tooltip.hpp" - -namespace waybar::modules::upower { - -class UPower : public AModule { - public: - UPower(const std::string &, const Json::Value &); - virtual ~UPower(); - auto update() -> void override; - - private: - typedef std::unordered_map Devices; - - const std::string DEFAULT_FORMAT = "{percentage}"; - const std::string DEFAULT_FORMAT_ALT = "{percentage} {time}"; - - static void deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data); - static void deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data); - static void deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer user_data); - static void prepareForSleep_cb(GDBusConnection *system_bus, const gchar *sender_name, - const gchar *object_path, const gchar *interface_name, - const gchar *signal_name, GVariant *parameters, - gpointer user_data); - static void upowerAppear(GDBusConnection *conn, const gchar *name, const gchar *name_owner, - gpointer data); - static void upowerDisappear(GDBusConnection *connection, const gchar *name, gpointer user_data); - - void removeDevice(const gchar *objectPath); - void addDevice(UpDevice *device); - void setDisplayDevice(); - void resetDevices(); - void removeDevices(); - bool show_tooltip_callback(int, int, bool, const Glib::RefPtr &tooltip); - bool handleToggle(GdkEventButton *const &) override; - std::string timeToString(gint64 time); - - const std::string getDeviceStatus(UpDeviceState &state); - - Gtk::Box box_; - Gtk::Image icon_; - Gtk::Label label_; - - // Config - bool hideIfEmpty = true; - bool tooltip_enabled = true; - uint tooltip_spacing = 4; - uint tooltip_padding = 4; - uint iconSize = 20; - std::string format = DEFAULT_FORMAT; - std::string format_alt = DEFAULT_FORMAT_ALT; - - Devices devices; - std::mutex m_Mutex; - UpClient *client; - UpDevice *displayDevice = nullptr; - guint login1_id; - GDBusConnection *login1_connection; - std::unique_ptr upower_tooltip; - std::string lastStatus; - const char *lastWarningLevel = nullptr; - bool showAltText; - bool showIcon = true; - bool upowerRunning; - guint upowerWatcher_id; - std::string nativePath_; -}; - -} // namespace waybar::modules::upower diff --git a/include/modules/upower/upower_tooltip.hpp b/include/modules/upower/upower_tooltip.hpp deleted file mode 100644 index bc99abed..00000000 --- a/include/modules/upower/upower_tooltip.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "gtkmm/box.h" -#include "gtkmm/label.h" -#include "gtkmm/window.h" - -namespace waybar::modules::upower { - -class UPowerTooltip : public Gtk::Window { - private: - typedef std::unordered_map Devices; - - const std::string getDeviceIcon(UpDeviceKind& kind); - - std::unique_ptr contentBox; - - uint iconSize; - uint tooltipSpacing; - uint tooltipPadding; - - public: - UPowerTooltip(uint iconSize, uint tooltipSpacing, uint tooltipPadding); - virtual ~UPowerTooltip(); - - uint updateTooltip(Devices& devices); -}; - -} // namespace waybar::modules::upower diff --git a/meson.build b/meson.build index a57b17f8..d7a5a4ee 100644 --- a/meson.build +++ b/meson.build @@ -335,10 +335,7 @@ endif if (upower_glib.found() and not get_option('logind').disabled()) add_project_arguments('-DHAVE_UPOWER', language: 'cpp') - src_files += files( - 'src/modules/upower/upower.cpp', - 'src/modules/upower/upower_tooltip.cpp', - ) + src_files += files('src/modules/upower.cpp') man_files += files('man/waybar-upower.5.scd') endif diff --git a/src/factory.cpp b/src/factory.cpp index 0549fe09..ca10ef95 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -70,7 +70,7 @@ #include "modules/gamemode.hpp" #endif #ifdef HAVE_UPOWER -#include "modules/upower/upower.hpp" +#include "modules/upower.hpp" #endif #ifdef HAVE_PIPEWIRE #include "modules/privacy/privacy.hpp" @@ -130,7 +130,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, #endif #ifdef HAVE_UPOWER if (ref == "upower") { - return new waybar::modules::upower::UPower(id, config_[name]); + return new waybar::modules::UPower(id, config_[name]); } #endif #ifdef HAVE_PIPEWIRE diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp new file mode 100644 index 00000000..73c0af08 --- /dev/null +++ b/src/modules/upower.cpp @@ -0,0 +1,479 @@ +#include "modules/upower.hpp" + +#include +#include +#include + +namespace waybar::modules { + +UPower::UPower(const std::string &id, const Json::Value &config) + : AIconLabel(config, "upower", id, "{percentage}", 0, true, true, true) { + box_.set_name(name_); + box_.set_spacing(0); + box_.set_has_tooltip(AModule::tooltipEnabled()); + // Tooltip box + contentBox_.set_orientation((box_.get_orientation() == Gtk::ORIENTATION_HORIZONTAL) + ? Gtk::ORIENTATION_VERTICAL + : Gtk::ORIENTATION_HORIZONTAL); + // Get current theme + gtkTheme_ = Gtk::IconTheme::get_default(); + + // Icon Size + if (config_["icon-size"].isInt()) { + iconSize_ = config_["icon-size"].asInt(); + } + image_.set_pixel_size(iconSize_); + + // Show icon only when "show-icon" isn't set to false + if (config_["show-icon"].isBool()) showIcon_ = config_["show-icon"].asBool(); + if (!showIcon_) box_.remove(image_); + // Device user wants + if (config_["native-path"].isString()) nativePath_ = config_["native-path"].asString(); + + // Hide If Empty + if (config_["hide-if-empty"].isBool()) hideIfEmpty_ = config_["hide-if-empty"].asBool(); + + // Tooltip Spacing + if (config_["tooltip-spacing"].isInt()) tooltip_spacing_ = config_["tooltip-spacing"].asInt(); + + // Tooltip Padding + if (config_["tooltip-padding"].isInt()) { + tooltip_padding_ = config_["tooltip-padding"].asInt(); + contentBox_.set_margin_top(tooltip_padding_); + contentBox_.set_margin_bottom(tooltip_padding_); + contentBox_.set_margin_left(tooltip_padding_); + contentBox_.set_margin_right(tooltip_padding_); + } + + // Tooltip Format + if (config_["tooltip-format"].isString()) tooltipFormat_ = config_["tooltip-format"].asString(); + + // Start watching DBUS + watcherID_ = Gio::DBus::watch_name( + Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.UPower", + sigc::mem_fun(*this, &UPower::onAppear), sigc::mem_fun(*this, &UPower::onVanished), + Gio::DBus::BusNameWatcherFlags::BUS_NAME_WATCHER_FLAGS_AUTO_START); + // Get DBus async connect + Gio::DBus::Connection::get(Gio::DBus::BusType::BUS_TYPE_SYSTEM, + sigc::mem_fun(*this, &UPower::getConn_cb)); + + // Make UPower client + GError **gErr = NULL; + upClient_ = up_client_new_full(NULL, gErr); + if (upClient_ == NULL) + spdlog::error("Upower. UPower client connection error. {}", (*gErr)->message); + + // Subscribe UPower events + g_signal_connect(upClient_, "device-added", G_CALLBACK(deviceAdded_cb), this); + g_signal_connect(upClient_, "device-removed", G_CALLBACK(deviceRemoved_cb), this); + + // Subscribe tooltip query events + box_.set_has_tooltip(); + box_.signal_query_tooltip().connect(sigc::mem_fun(*this, &UPower::queryTooltipCb), false); + + resetDevices(); + setDisplayDevice(); + // Update the widget + dp.emit(); +} + +UPower::~UPower() { + if (upDevice_.upDevice != NULL) g_object_unref(upDevice_.upDevice); + if (upClient_ != NULL) g_object_unref(upClient_); + if (subscrID_ > 0u) { + conn_->signal_unsubscribe(subscrID_); + subscrID_ = 0u; + } + Gio::DBus::unwatch_name(watcherID_); + watcherID_ = 0u; + removeDevices(); +} + +static const std::string getDeviceStatus(UpDeviceState &state) { + switch (state) { + case UP_DEVICE_STATE_CHARGING: + case UP_DEVICE_STATE_PENDING_CHARGE: + return "charging"; + case UP_DEVICE_STATE_DISCHARGING: + case UP_DEVICE_STATE_PENDING_DISCHARGE: + return "discharging"; + case UP_DEVICE_STATE_FULLY_CHARGED: + return "full"; + case UP_DEVICE_STATE_EMPTY: + return "empty"; + default: + return "unknown-status"; + } +} + +static const std::string getDeviceIcon(UpDeviceKind &kind) { + switch (kind) { + case UP_DEVICE_KIND_LINE_POWER: + return "ac-adapter-symbolic"; + case UP_DEVICE_KIND_BATTERY: + return "battery-symbolic"; + case UP_DEVICE_KIND_UPS: + return "uninterruptible-power-supply-symbolic"; + case UP_DEVICE_KIND_MONITOR: + return "video-display-symbolic"; + case UP_DEVICE_KIND_MOUSE: + return "input-mouse-symbolic"; + case UP_DEVICE_KIND_KEYBOARD: + return "input-keyboard-symbolic"; + case UP_DEVICE_KIND_PDA: + return "pda-symbolic"; + case UP_DEVICE_KIND_PHONE: + return "phone-symbolic"; + case UP_DEVICE_KIND_MEDIA_PLAYER: + return "multimedia-player-symbolic"; + case UP_DEVICE_KIND_TABLET: + return "computer-apple-ipad-symbolic"; + case UP_DEVICE_KIND_COMPUTER: + return "computer-symbolic"; + case UP_DEVICE_KIND_GAMING_INPUT: + return "input-gaming-symbolic"; + case UP_DEVICE_KIND_PEN: + return "input-tablet-symbolic"; + case UP_DEVICE_KIND_TOUCHPAD: + return "input-touchpad-symbolic"; + case UP_DEVICE_KIND_MODEM: + return "modem-symbolic"; + case UP_DEVICE_KIND_NETWORK: + return "network-wired-symbolic"; + case UP_DEVICE_KIND_HEADSET: + return "audio-headset-symbolic"; + case UP_DEVICE_KIND_HEADPHONES: + return "audio-headphones-symbolic"; + case UP_DEVICE_KIND_OTHER_AUDIO: + case UP_DEVICE_KIND_SPEAKERS: + return "audio-speakers-symbolic"; + case UP_DEVICE_KIND_VIDEO: + return "camera-web-symbolic"; + case UP_DEVICE_KIND_PRINTER: + return "printer-symbolic"; + case UP_DEVICE_KIND_SCANNER: + return "scanner-symbolic"; + case UP_DEVICE_KIND_CAMERA: + return "camera-photo-symbolic"; + case UP_DEVICE_KIND_BLUETOOTH_GENERIC: + return "bluetooth-active-symbolic"; + case UP_DEVICE_KIND_TOY: + case UP_DEVICE_KIND_REMOTE_CONTROL: + case UP_DEVICE_KIND_WEARABLE: + case UP_DEVICE_KIND_LAST: + default: + return "battery-symbolic"; + } +} + +static std::string secondsToString(const std::chrono::seconds sec) { + const auto ds{std::chrono::duration_cast(sec)}; + const auto hrs{std::chrono::duration_cast(sec - ds)}; + const auto min{std::chrono::duration_cast(sec - ds - hrs)}; + std::string_view strRet{(ds.count() > 0) ? "{D}d {H}h {M}min" + : (hrs.count() > 0) ? "{H}h {M}min" + : (min.count() > 0) ? "{M}min" + : ""}; + spdlog::debug( + "UPower::secondsToString(). seconds: \"{0}\", minutes: \"{1}\", hours: \"{2}\", \ +days: \"{3}\", strRet: \"{4}\"", + sec.count(), min.count(), hrs.count(), ds.count(), strRet); + return fmt::format(fmt::runtime(strRet), fmt::arg("D", ds.count()), fmt::arg("H", hrs.count()), + fmt::arg("M", min.count())); +} + +auto UPower::update() -> void { + std::lock_guard guard{mutex_}; + // Don't update widget if the UPower service isn't running + if (!upRunning_) { + if (hideIfEmpty_) box_.hide(); + return; + } + + getUpDeviceInfo(upDevice_); + + if (upDevice_.upDevice == NULL && hideIfEmpty_) { + box_.hide(); + return; + } + /* Every Device which is handled by Upower and which is not + * UP_DEVICE_KIND_UNKNOWN (0) or UP_DEVICE_KIND_LINE_POWER (1) is a Battery + */ + const bool upDeviceValid{upDevice_.kind != UpDeviceKind::UP_DEVICE_KIND_UNKNOWN && + upDevice_.kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER}; + // Get CSS status + const auto status{getDeviceStatus(upDevice_.state)}; + // Remove last status if it exists + if (!lastStatus_.empty() && box_.get_style_context()->has_class(lastStatus_)) + box_.get_style_context()->remove_class(lastStatus_); + if (!box_.get_style_context()->has_class(status)) box_.get_style_context()->add_class(status); + lastStatus_ = status; + + if (devices_.size() == 0 && !upDeviceValid && hideIfEmpty_) { + box_.hide(); + // Call parent update + AModule::update(); + return; + } + + label_.set_markup(getText(upDevice_, format_)); + // Set icon + if (upDevice_.icon_name == NULL || !gtkTheme_->has_icon(upDevice_.icon_name)) + upDevice_.icon_name = (char *)NO_BATTERY.c_str(); + image_.set_from_icon_name(upDevice_.icon_name, Gtk::ICON_SIZE_INVALID); + + box_.show(); + + // Call parent update + ALabel::update(); +} + +void UPower::getConn_cb(Glib::RefPtr &result) { + try { + conn_ = Gio::DBus::Connection::get_finish(result); + // Subscribe DBUs events + subscrID_ = conn_->signal_subscribe(sigc::mem_fun(*this, &UPower::prepareForSleep_cb), + "org.freedesktop.login1", "org.freedesktop.login1.Manager", + "PrepareForSleep", "/org/freedesktop/login1"); + + } catch (const Glib::Error &e) { + spdlog::error("Upower. DBus connection error. {}", e.what().c_str()); + } +} + +void UPower::onAppear(const Glib::RefPtr &conn, const Glib::ustring &name, + const Glib::ustring &name_owner) { + upRunning_ = true; +} + +void UPower::onVanished(const Glib::RefPtr &conn, + const Glib::ustring &name) { + upRunning_ = false; +} + +void UPower::prepareForSleep_cb(const Glib::RefPtr &connection, + const Glib::ustring &sender_name, const Glib::ustring &object_path, + const Glib::ustring &interface_name, + const Glib::ustring &signal_name, + const Glib::VariantContainerBase ¶meters) { + if (parameters.is_of_type(Glib::VariantType("(b)"))) { + Glib::Variant sleeping; + parameters.get_child(sleeping, 0); + if (!sleeping.get()) { + resetDevices(); + setDisplayDevice(); + // Update the widget + dp.emit(); + } + } +} + +void UPower::deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data) { + UPower *up{static_cast(data)}; + up->addDevice(device); + up->setDisplayDevice(); + // Update the widget + up->dp.emit(); +} + +void UPower::deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data) { + UPower *up{static_cast(data)}; + up->removeDevice(objectPath); + up->setDisplayDevice(); + // Update the widget + up->dp.emit(); +} + +void UPower::deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer data) { + UPower *up{static_cast(data)}; + // Update the widget + up->dp.emit(); +} + +void UPower::addDevice(UpDevice *device) { + std::lock_guard guard{mutex_}; + + if (G_IS_OBJECT(device)) { + const gchar *objectPath{up_device_get_object_path(device)}; + + // Due to the device getting cleared after this event is fired, we + // create a new object pointing to its objectPath + device = up_device_new(); + upDevice_output upDevice{.upDevice = device}; + gboolean ret{up_device_set_object_path_sync(device, objectPath, NULL, NULL)}; + if (!ret) { + g_object_unref(G_OBJECT(device)); + return; + } + + if (devices_.find(objectPath) != devices_.cend()) { + auto upDevice{devices_[objectPath]}; + if (G_IS_OBJECT(upDevice.upDevice)) g_object_unref(upDevice.upDevice); + devices_.erase(objectPath); + } + + g_signal_connect(device, "notify", G_CALLBACK(deviceNotify_cb), this); + devices_.emplace(Devices::value_type(objectPath, upDevice)); + } +} + +void UPower::removeDevice(const gchar *objectPath) { + std::lock_guard guard{mutex_}; + if (devices_.find(objectPath) != devices_.cend()) { + auto upDevice{devices_[objectPath]}; + if (G_IS_OBJECT(upDevice.upDevice)) g_object_unref(upDevice.upDevice); + devices_.erase(objectPath); + } +} + +void UPower::removeDevices() { + std::lock_guard guard{mutex_}; + if (!devices_.empty()) { + auto it{devices_.cbegin()}; + while (it != devices_.cend()) { + if (G_IS_OBJECT(it->second.upDevice)) g_object_unref(it->second.upDevice); + devices_.erase(it++); + } + } +} + +// Removes all devices and adds the current devices +void UPower::resetDevices() { + // Remove all devices + removeDevices(); + + // Adds all devices + GPtrArray *newDevices = up_client_get_devices2(upClient_); + if (newDevices != NULL) + for (guint i{0}; i < newDevices->len; ++i) { + UpDevice *device{(UpDevice *)g_ptr_array_index(newDevices, i)}; + if (device && G_IS_OBJECT(device)) addDevice(device); + } +} + +void UPower::setDisplayDevice() { + std::lock_guard guard{mutex_}; + + if (nativePath_.empty()) { + upDevice_.upDevice = up_client_get_display_device(upClient_); + getUpDeviceInfo(upDevice_); + } else { + g_ptr_array_foreach( + up_client_get_devices2(upClient_), + [](gpointer data, gpointer user_data) { + upDevice_output upDevice; + auto thisPtr{static_cast(user_data)}; + upDevice.upDevice = static_cast(data); + thisPtr->getUpDeviceInfo(upDevice); + if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { + // Unref current upDevice + if (thisPtr->upDevice_.upDevice) g_object_unref(thisPtr->upDevice_.upDevice); + // Reassign new upDevice + thisPtr->upDevice_ = upDevice; + } + }, + this); + } + + if (upDevice_.upDevice) + g_signal_connect(upDevice_.upDevice, "notify", G_CALLBACK(deviceNotify_cb), this); +} + +void UPower::getUpDeviceInfo(upDevice_output &upDevice_) { + if (upDevice_.upDevice && G_IS_OBJECT(upDevice_.upDevice)) { + g_object_get(upDevice_.upDevice, "kind", &upDevice_.kind, "state", &upDevice_.state, + "percentage", &upDevice_.percentage, "icon-name", &upDevice_.icon_name, + "time-to-empty", &upDevice_.time_empty, "time-to-full", &upDevice_.time_full, + "temperature", &upDevice_.temperature, "native-path", &upDevice_.nativePath, + "model", &upDevice_.model, NULL); + spdlog::debug( + "UPower. getUpDeviceInfo. kind: \"{0}\". state: \"{1}\". percentage: \"{2}\". \ +icon_name: \"{3}\". time-to-empty: \"{4}\". time-to-full: \"{5}\". temperature: \"{6}\". \ +native_path: \"{7}\". model: \"{8}\"", + fmt::format_int(upDevice_.kind).str(), fmt::format_int(upDevice_.state).str(), + upDevice_.percentage, upDevice_.icon_name, upDevice_.time_empty, upDevice_.time_full, + upDevice_.temperature, upDevice_.nativePath, upDevice_.model); + } +} + +const Glib::ustring UPower::getText(const upDevice_output &upDevice_, const std::string &format) { + Glib::ustring ret{""}; + if (upDevice_.upDevice) { + std::string timeStr{""}; + switch (upDevice_.state) { + case UP_DEVICE_STATE_CHARGING: + case UP_DEVICE_STATE_PENDING_CHARGE: + timeStr = secondsToString(std::chrono::seconds(upDevice_.time_full)); + break; + case UP_DEVICE_STATE_DISCHARGING: + case UP_DEVICE_STATE_PENDING_DISCHARGE: + timeStr = secondsToString(std::chrono::seconds(upDevice_.time_empty)); + break; + default: + break; + } + + ret = fmt::format( + fmt::runtime(format), + fmt::arg("percentage", std::to_string((int)std::round(upDevice_.percentage)) + '%'), + fmt::arg("time", timeStr), + fmt::arg("temperature", fmt::format("{:-.2g}C", upDevice_.temperature)), + fmt::arg("model", upDevice_.model), fmt::arg("native-path", upDevice_.nativePath)); + } + + return ret; +} + +bool UPower::queryTooltipCb(int x, int y, bool keyboard_tooltip, + const Glib::RefPtr &tooltip) { + std::lock_guard guard{mutex_}; + + // Clear content box + contentBox_.forall([this](Gtk::Widget &wg) { contentBox_.remove(wg); }); + + // Fill content box with the content + for (auto pairDev : devices_) { + // Get device info + getUpDeviceInfo(pairDev.second); + + if (pairDev.second.kind != UpDeviceKind::UP_DEVICE_KIND_UNKNOWN && + pairDev.second.kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER) { + // Make box record + Gtk::Box *boxRec{new Gtk::Box{box_.get_orientation(), tooltip_spacing_}}; + contentBox_.add(*boxRec); + Gtk::Box *boxDev{new Gtk::Box{box_.get_orientation()}}; + Gtk::Box *boxUsr{new Gtk::Box{box_.get_orientation()}}; + boxRec->add(*boxDev); + boxRec->add(*boxUsr); + // Construct device box + // Set icon from kind + std::string iconNameDev{getDeviceIcon(pairDev.second.kind)}; + if (!gtkTheme_->has_icon(iconNameDev)) iconNameDev = (char *)NO_BATTERY.c_str(); + Gtk::Image *iconDev{new Gtk::Image{}}; + iconDev->set_from_icon_name(iconNameDev, Gtk::ICON_SIZE_INVALID); + iconDev->set_pixel_size(iconSize_); + boxDev->add(*iconDev); + // Set label from model + Gtk::Label *labelDev{new Gtk::Label{pairDev.second.model}}; + boxDev->add(*labelDev); + // Construct user box + // Set icon from icon state + if (pairDev.second.icon_name == NULL || !gtkTheme_->has_icon(pairDev.second.icon_name)) + pairDev.second.icon_name = (char *)NO_BATTERY.c_str(); + Gtk::Image *iconTooltip{new Gtk::Image{}}; + iconTooltip->set_from_icon_name(pairDev.second.icon_name, Gtk::ICON_SIZE_INVALID); + iconTooltip->set_pixel_size(iconSize_); + boxUsr->add(*iconTooltip); + // Set markup text + Gtk::Label *labelTooltip{new Gtk::Label{}}; + labelTooltip->set_markup(getText(pairDev.second, tooltipFormat_)); + boxUsr->add(*labelTooltip); + } + } + tooltip->set_custom(contentBox_); + contentBox_.show_all(); + + return true; +} + +} // namespace waybar::modules diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp deleted file mode 100644 index ad4c4732..00000000 --- a/src/modules/upower/upower.cpp +++ /dev/null @@ -1,418 +0,0 @@ -#include "modules/upower/upower.hpp" - -#include - -#include -#include - -#include "gtkmm/tooltip.h" -#include "util/gtk_icon.hpp" - -static const char* getDeviceWarningLevel(UpDeviceLevel level) { - switch (level) { - case UP_DEVICE_LEVEL_CRITICAL: - return "critical"; - case UP_DEVICE_LEVEL_LOW: - return "low"; - default: - return nullptr; - } -} - -namespace waybar::modules::upower { -UPower::UPower(const std::string& id, const Json::Value& config) - : AModule(config, "upower", id), - box_(Gtk::ORIENTATION_HORIZONTAL, 0), - icon_(), - label_(), - devices(), - m_Mutex(), - client(), - showAltText(false) { - // Show icon only when "show-icon" isn't set to false - if (config_["show-icon"].isBool()) { - showIcon = config_["show-icon"].asBool(); - } - - if (showIcon) { - box_.pack_start(icon_); - } - - box_.pack_start(label_); - box_.set_name(name_); - event_box_.add(box_); - - // Device user wants - if (config_["native-path"].isString()) nativePath_ = config_["native-path"].asString(); - // Icon Size - if (config_["icon-size"].isUInt()) { - iconSize = config_["icon-size"].asUInt(); - } - icon_.set_pixel_size(iconSize); - - // Hide If Empty - if (config_["hide-if-empty"].isBool()) { - hideIfEmpty = config_["hide-if-empty"].asBool(); - } - - // Format - if (config_["format"].isString()) { - format = config_["format"].asString(); - } - - // Format Alt - if (config_["format-alt"].isString()) { - format_alt = config_["format-alt"].asString(); - } - - // Tooltip Spacing - if (config_["tooltip-spacing"].isUInt()) { - tooltip_spacing = config_["tooltip-spacing"].asUInt(); - } - - // Tooltip Padding - if (config_["tooltip-padding"].isUInt()) { - tooltip_padding = config_["tooltip-padding"].asUInt(); - } - - // Tooltip - if (config_["tooltip"].isBool()) { - tooltip_enabled = config_["tooltip"].asBool(); - } - box_.set_has_tooltip(tooltip_enabled); - if (tooltip_enabled) { - // Sets the window to use when showing the tooltip - upower_tooltip = std::make_unique(iconSize, tooltip_spacing, tooltip_padding); - box_.set_tooltip_window(*upower_tooltip); - box_.signal_query_tooltip().connect(sigc::mem_fun(*this, &UPower::show_tooltip_callback)); - } - - upowerWatcher_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM, "org.freedesktop.UPower", - G_BUS_NAME_WATCHER_FLAGS_AUTO_START, upowerAppear, - upowerDisappear, this, NULL); - - client = up_client_new_full(NULL, NULL); - if (client == NULL) { - throw std::runtime_error("Unable to create UPower client!"); - } - - // Connect to Login1 PrepareForSleep signal - login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); - if (!login1_connection) { - throw std::runtime_error("Unable to connect to the SYSTEM Bus!..."); - } else { - login1_id = g_dbus_connection_signal_subscribe( - login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager", - "PrepareForSleep", "/org/freedesktop/login1", NULL, G_DBUS_SIGNAL_FLAGS_NONE, - prepareForSleep_cb, this, NULL); - } - - event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &UPower::handleToggle)); - - g_signal_connect(client, "device-added", G_CALLBACK(deviceAdded_cb), this); - g_signal_connect(client, "device-removed", G_CALLBACK(deviceRemoved_cb), this); - - resetDevices(); - setDisplayDevice(); -} - -UPower::~UPower() { - if (displayDevice != NULL) g_object_unref(displayDevice); - if (client != NULL) g_object_unref(client); - if (login1_id > 0) { - g_dbus_connection_signal_unsubscribe(login1_connection, login1_id); - login1_id = 0; - } - g_bus_unwatch_name(upowerWatcher_id); - removeDevices(); -} - -void UPower::deviceAdded_cb(UpClient* client, UpDevice* device, gpointer data) { - UPower* up = static_cast(data); - up->addDevice(device); - up->setDisplayDevice(); - // Update the widget - up->dp.emit(); -} -void UPower::deviceRemoved_cb(UpClient* client, const gchar* objectPath, gpointer data) { - UPower* up = static_cast(data); - up->removeDevice(objectPath); - up->setDisplayDevice(); - // Update the widget - up->dp.emit(); -} -void UPower::deviceNotify_cb(UpDevice* device, GParamSpec* pspec, gpointer data) { - UPower* up = static_cast(data); - // Update the widget - up->dp.emit(); -} -void UPower::prepareForSleep_cb(GDBusConnection* system_bus, const gchar* sender_name, - const gchar* object_path, const gchar* interface_name, - const gchar* signal_name, GVariant* parameters, gpointer data) { - if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)"))) { - gboolean sleeping; - g_variant_get(parameters, "(b)", &sleeping); - - if (!sleeping) { - UPower* up = static_cast(data); - up->resetDevices(); - up->setDisplayDevice(); - } - } -} -void UPower::upowerAppear(GDBusConnection* conn, const gchar* name, const gchar* name_owner, - gpointer data) { - UPower* up = static_cast(data); - up->upowerRunning = true; - up->event_box_.set_visible(true); -} -void UPower::upowerDisappear(GDBusConnection* conn, const gchar* name, gpointer data) { - UPower* up = static_cast(data); - up->upowerRunning = false; - up->event_box_.set_visible(false); -} - -void UPower::removeDevice(const gchar* objectPath) { - std::lock_guard guard(m_Mutex); - if (devices.find(objectPath) != devices.end()) { - UpDevice* device = devices[objectPath]; - if (G_IS_OBJECT(device)) { - g_object_unref(device); - } - devices.erase(objectPath); - } -} - -void UPower::addDevice(UpDevice* device) { - if (G_IS_OBJECT(device)) { - const gchar* objectPath = up_device_get_object_path(device); - - // Due to the device getting cleared after this event is fired, we - // create a new object pointing to its objectPath - gboolean ret; - device = up_device_new(); - ret = up_device_set_object_path_sync(device, objectPath, NULL, NULL); - if (!ret) { - g_object_unref(G_OBJECT(device)); - return; - } - - std::lock_guard guard(m_Mutex); - - if (devices.find(objectPath) != devices.end()) { - UpDevice* device = devices[objectPath]; - if (G_IS_OBJECT(device)) { - g_object_unref(device); - } - devices.erase(objectPath); - } - - g_signal_connect(device, "notify", G_CALLBACK(deviceNotify_cb), this); - devices.emplace(Devices::value_type(objectPath, device)); - } -} - -void UPower::setDisplayDevice() { - std::lock_guard guard(m_Mutex); - - if (nativePath_.empty()) - displayDevice = up_client_get_display_device(client); - else { - g_ptr_array_foreach( - up_client_get_devices2(client), - [](gpointer data, gpointer user_data) { - UpDevice* device{static_cast(data)}; - UPower* thisPtr{static_cast(user_data)}; - gchar* nativePath; - if (!thisPtr->displayDevice) { - g_object_get(device, "native-path", &nativePath, NULL); - if (!std::strcmp(nativePath, thisPtr->nativePath_.c_str())) - thisPtr->displayDevice = device; - } - }, - this); - } - - if (displayDevice) g_signal_connect(displayDevice, "notify", G_CALLBACK(deviceNotify_cb), this); -} - -void UPower::removeDevices() { - std::lock_guard guard(m_Mutex); - if (!devices.empty()) { - auto it = devices.cbegin(); - while (it != devices.cend()) { - if (G_IS_OBJECT(it->second)) { - g_object_unref(it->second); - } - devices.erase(it++); - } - } -} - -/** Removes all devices and adds the current devices */ -void UPower::resetDevices() { - // Removes all devices - removeDevices(); - - // Adds all devices - GPtrArray* newDevices = up_client_get_devices2(client); - for (guint i = 0; i < newDevices->len; i++) { - UpDevice* device = (UpDevice*)g_ptr_array_index(newDevices, i); - if (device && G_IS_OBJECT(device)) addDevice(device); - } - - // Update the widget - dp.emit(); -} - -bool UPower::show_tooltip_callback(int, int, bool, const Glib::RefPtr& tooltip) { - return true; -} - -const std::string UPower::getDeviceStatus(UpDeviceState& state) { - switch (state) { - case UP_DEVICE_STATE_CHARGING: - case UP_DEVICE_STATE_PENDING_CHARGE: - return "charging"; - case UP_DEVICE_STATE_DISCHARGING: - case UP_DEVICE_STATE_PENDING_DISCHARGE: - return "discharging"; - case UP_DEVICE_STATE_FULLY_CHARGED: - return "full"; - case UP_DEVICE_STATE_EMPTY: - return "empty"; - default: - return "unknown-status"; - } -} - -bool UPower::handleToggle(GdkEventButton* const& event) { - std::lock_guard guard(m_Mutex); - showAltText = !showAltText; - return AModule::handleToggle(event); -} - -std::string UPower::timeToString(gint64 time) { - if (time == 0) return ""; - float hours = (float)time / 3600; - float hours_fixed = static_cast(static_cast(hours * 10)) / 10; - float minutes = static_cast(static_cast(hours * 60 * 10)) / 10; - if (hours_fixed >= 1) { - return fmt::format("{H} h", fmt::arg("H", hours_fixed)); - } else { - return fmt::format("{M} min", fmt::arg("M", minutes)); - } -} - -auto UPower::update() -> void { - std::lock_guard guard(m_Mutex); - - // Don't update widget if the UPower service isn't running - if (!upowerRunning) { - if (hideIfEmpty) { - event_box_.set_visible(false); - } - return; - } - - UpDeviceKind kind = UP_DEVICE_KIND_UNKNOWN; - UpDeviceState state = UP_DEVICE_STATE_UNKNOWN; - UpDeviceLevel level = UP_DEVICE_LEVEL_UNKNOWN; - double percentage = 0.0; - gint64 time_empty = 0; - gint64 time_full = 0; - gchar* icon_name{(char*)'\0'}; - std::string percentString{""}; - std::string time_format{""}; - - bool displayDeviceValid{false}; - - if (displayDevice) { - g_object_get(displayDevice, "kind", &kind, "state", &state, "percentage", &percentage, - "icon-name", &icon_name, "time-to-empty", &time_empty, "time-to-full", &time_full, - "warning-level", &level, NULL); - /* Every Device which is handled by Upower and which is not - * UP_DEVICE_KIND_UNKNOWN (0) or UP_DEVICE_KIND_LINE_POWER (1) is a Battery - */ - displayDeviceValid = (kind != UpDeviceKind::UP_DEVICE_KIND_UNKNOWN && - kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER); - } - - // CSS status class - const std::string status = getDeviceStatus(state); - // Remove last status if it exists - if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) { - box_.get_style_context()->remove_class(lastStatus); - } - // Add the new status class to the Box - if (!box_.get_style_context()->has_class(status)) { - box_.get_style_context()->add_class(status); - } - lastStatus = status; - - const char* warning_level = getDeviceWarningLevel(level); - if (lastWarningLevel && box_.get_style_context()->has_class(lastWarningLevel)) { - box_.get_style_context()->remove_class(lastWarningLevel); - } - if (warning_level && !box_.get_style_context()->has_class(warning_level)) { - box_.get_style_context()->add_class(warning_level); - } - lastWarningLevel = warning_level; - - if (devices.size() == 0 && !displayDeviceValid && hideIfEmpty) { - event_box_.set_visible(false); - // Call parent update - AModule::update(); - return; - } - - event_box_.set_visible(true); - - if (displayDeviceValid) { - // Tooltip - if (tooltip_enabled) { - uint tooltipCount = upower_tooltip->updateTooltip(devices); - // Disable the tooltip if there aren't any devices in the tooltip - box_.set_has_tooltip(!devices.empty() && tooltipCount > 0); - } - - // Set percentage - percentString = std::to_string(int(percentage + 0.5)) + "%"; - - // Label format - switch (state) { - case UP_DEVICE_STATE_CHARGING: - case UP_DEVICE_STATE_PENDING_CHARGE: - time_format = timeToString(time_full); - break; - case UP_DEVICE_STATE_DISCHARGING: - case UP_DEVICE_STATE_PENDING_DISCHARGE: - time_format = timeToString(time_empty); - break; - default: - break; - } - } - std::string label_format = - fmt::format(fmt::runtime(showAltText ? format_alt : format), - fmt::arg("percentage", percentString), fmt::arg("time", time_format)); - // Only set the label text if it doesn't only contain spaces - bool onlySpaces = true; - for (auto& character : label_format) { - if (character == ' ') continue; - onlySpaces = false; - break; - } - label_.set_markup(onlySpaces ? "" : label_format); - - // Set icon - if (icon_name == NULL || !DefaultGtkIconThemeWrapper::has_icon(icon_name)) { - icon_name = (char*)"battery-missing-symbolic"; - } - icon_.set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID); - - // Call parent update - AModule::update(); -} - -} // namespace waybar::modules::upower diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp deleted file mode 100644 index 1a653f85..00000000 --- a/src/modules/upower/upower_tooltip.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "modules/upower/upower_tooltip.hpp" - -#include "gtkmm/box.h" -#include "gtkmm/enums.h" -#include "gtkmm/image.h" -#include "gtkmm/label.h" -#include "util/gtk_icon.hpp" - -namespace waybar::modules::upower { -UPowerTooltip::UPowerTooltip(uint iconSize_, uint tooltipSpacing_, uint tooltipPadding_) - : Gtk::Window(), - contentBox(std::make_unique(Gtk::ORIENTATION_VERTICAL)), - iconSize(iconSize_), - tooltipSpacing(tooltipSpacing_), - tooltipPadding(tooltipPadding_) { - // Sets the Tooltip Padding - contentBox->set_margin_top(tooltipPadding); - contentBox->set_margin_bottom(tooltipPadding); - contentBox->set_margin_left(tooltipPadding); - contentBox->set_margin_right(tooltipPadding); - - add(*contentBox); - contentBox->show(); -} - -UPowerTooltip::~UPowerTooltip() {} - -uint UPowerTooltip::updateTooltip(Devices& devices) { - // Removes all old devices - for (auto child : contentBox->get_children()) { - delete child; - } - - uint deviceCount = 0; - // Adds all valid devices - for (auto pair : devices) { - UpDevice* device = pair.second; - std::string objectPath = pair.first; - - if (!G_IS_OBJECT(device)) continue; - - Gtk::Box* box = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, tooltipSpacing); - - UpDeviceKind kind; - double percentage; - gchar* native_path; - gchar* model; - gchar* icon_name; - - g_object_get(device, "kind", &kind, "percentage", &percentage, "native-path", &native_path, - "model", &model, "icon-name", &icon_name, NULL); - - // Skip Line_Power and BAT0 devices - if (kind == UP_DEVICE_KIND_LINE_POWER || native_path == NULL || strlen(native_path) == 0 || - strcmp(native_path, "BAT0") == 0) - continue; - - Gtk::Box* modelBox = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL); - box->add(*modelBox); - // Set device icon - std::string deviceIconName = getDeviceIcon(kind); - Gtk::Image* deviceIcon = new Gtk::Image(); - deviceIcon->set_pixel_size(iconSize); - if (!DefaultGtkIconThemeWrapper::has_icon(deviceIconName)) { - deviceIconName = "battery-missing-symbolic"; - } - deviceIcon->set_from_icon_name(deviceIconName, Gtk::ICON_SIZE_INVALID); - modelBox->add(*deviceIcon); - - // Set model - if (model == NULL) model = (gchar*)""; - Gtk::Label* modelLabel = new Gtk::Label(model); - modelBox->add(*modelLabel); - - Gtk::Box* chargeBox = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL); - box->add(*chargeBox); - - // Set icon - Gtk::Image* icon = new Gtk::Image(); - icon->set_pixel_size(iconSize); - if (icon_name == NULL || !DefaultGtkIconThemeWrapper::has_icon(icon_name)) { - icon_name = (char*)"battery-missing-symbolic"; - } - icon->set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID); - chargeBox->add(*icon); - - // Set percentage - std::string percentString = std::to_string(int(percentage + 0.5)) + "%"; - Gtk::Label* percentLabel = new Gtk::Label(percentString); - chargeBox->add(*percentLabel); - - contentBox->add(*box); - - deviceCount++; - } - - contentBox->show_all(); - return deviceCount; -} - -const std::string UPowerTooltip::getDeviceIcon(UpDeviceKind& kind) { - switch (kind) { - case UP_DEVICE_KIND_LINE_POWER: - return "ac-adapter-symbolic"; - case UP_DEVICE_KIND_BATTERY: - return "battery"; - case UP_DEVICE_KIND_UPS: - return "uninterruptible-power-supply-symbolic"; - case UP_DEVICE_KIND_MONITOR: - return "video-display-symbolic"; - case UP_DEVICE_KIND_MOUSE: - return "input-mouse-symbolic"; - case UP_DEVICE_KIND_KEYBOARD: - return "input-keyboard-symbolic"; - case UP_DEVICE_KIND_PDA: - return "pda-symbolic"; - case UP_DEVICE_KIND_PHONE: - return "phone-symbolic"; - case UP_DEVICE_KIND_MEDIA_PLAYER: - return "multimedia-player-symbolic"; - case UP_DEVICE_KIND_TABLET: - return "computer-apple-ipad-symbolic"; - case UP_DEVICE_KIND_COMPUTER: - return "computer-symbolic"; - case UP_DEVICE_KIND_GAMING_INPUT: - return "input-gaming-symbolic"; - case UP_DEVICE_KIND_PEN: - return "input-tablet-symbolic"; - case UP_DEVICE_KIND_TOUCHPAD: - return "input-touchpad-symbolic"; - case UP_DEVICE_KIND_MODEM: - return "modem-symbolic"; - case UP_DEVICE_KIND_NETWORK: - return "network-wired-symbolic"; - case UP_DEVICE_KIND_HEADSET: - return "audio-headset-symbolic"; - case UP_DEVICE_KIND_HEADPHONES: - return "audio-headphones-symbolic"; - case UP_DEVICE_KIND_OTHER_AUDIO: - case UP_DEVICE_KIND_SPEAKERS: - return "audio-speakers-symbolic"; - case UP_DEVICE_KIND_VIDEO: - return "camera-web-symbolic"; - case UP_DEVICE_KIND_PRINTER: - return "printer-symbolic"; - case UP_DEVICE_KIND_SCANNER: - return "scanner-symbolic"; - case UP_DEVICE_KIND_CAMERA: - return "camera-photo-symbolic"; - case UP_DEVICE_KIND_BLUETOOTH_GENERIC: - return "bluetooth-active-symbolic"; - case UP_DEVICE_KIND_TOY: - case UP_DEVICE_KIND_REMOTE_CONTROL: - case UP_DEVICE_KIND_WEARABLE: - case UP_DEVICE_KIND_LAST: - default: - return "battery-symbolic"; - } -} -} // namespace waybar::modules::upower From e298bf922fbef2f6e3cd3717d3d3710b802fcc4a Mon Sep 17 00:00:00 2001 From: giskard Date: Wed, 8 May 2024 21:00:52 +0800 Subject: [PATCH 525/842] temperature: allow hwmon-path-abs as array --- src/modules/temperature.cpp | 49 ++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index f0629670..922fa639 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -1,6 +1,9 @@ #include "modules/temperature.hpp" +#include + #include +#include #if defined(__FreeBSD__) #include @@ -11,26 +14,32 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val #if defined(__FreeBSD__) // FreeBSD uses sysctlbyname instead of read from a file #else - auto& hwmon_path = config_["hwmon-path"]; - if (hwmon_path.isString()) { - file_path_ = hwmon_path.asString(); - } else if (hwmon_path.isArray()) { - // if hwmon_path is an array, loop to find first valid item - for (auto& item : hwmon_path) { - auto path = item.asString(); - if (std::filesystem::exists(path)) { - file_path_ = path; - break; - } - } - } else if (config_["hwmon-path-abs"].isString() && config_["input-filename"].isString()) { - for (const auto& hwmon : - std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())) { - if (hwmon.path().filename().string().starts_with("hwmon")) { - file_path_ = hwmon.path().string() + "/" + config_["input-filename"].asString(); - break; - } - } + auto traverseAsArray = [](const Json::Value& value, auto&& check_set_path) { + if (value.isString()) + check_set_path(value.asString()); + else if (value.isArray()) + for (const auto& item : value) + if (check_set_path(item.asString())) break; + }; + + // if hwmon_path is an array, loop to find first valid item + traverseAsArray(config_["hwmon-path"], [this](const std::string& path) { + if (!std::filesystem::exists(path)) return false; + file_path_ = path; + return true; + }); + + if (file_path_.empty() && config_["input-filename"].isString()) { + // fallback to hwmon_paths-abs + traverseAsArray(config_["hwmon-path-abs"], [this](const std::string& path) { + if (!std::filesystem::is_directory(path)) return false; + return std::ranges::any_of( + std::filesystem::directory_iterator(path), [this](const auto& hwmon) { + if (!hwmon.path().filename().string().starts_with("hwmon")) return false; + file_path_ = hwmon.path().string() + "/" + config_["input-filename"].asString(); + return true; + }); + }); } if (file_path_.empty()) { From b980dab6df22468b27b0e85f9da3343fcdff5bf6 Mon Sep 17 00:00:00 2001 From: giskard Date: Wed, 8 May 2024 21:24:54 +0800 Subject: [PATCH 526/842] doc: update waybar-temperature manual page --- man/waybar-temperature.5.scd | 1 + 1 file changed, 1 insertion(+) diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index ff2168ea..87b60d38 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -25,6 +25,7 @@ Addressed by *temperature* *hwmon-path-abs*: ++ typeof: string ++ The path of the hwmon-directory of the device, e.g. */sys/devices/pci0000:00/0000:00:18.3/hwmon*. (Note that the subdirectory *hwmon/hwmon#*, where *#* is a number is not part of the path!) Has to be used together with *input-filename*. + This can also be an array of strings, for which, it just works like *hwmon-path*. *input-filename*: ++ typeof: string ++ From 884b909e7d4c8f670785ba3fac80ec931922ab50 Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Thu, 9 May 2024 17:28:08 +0200 Subject: [PATCH 527/842] =?UTF-8?q?=E2=9C=A8=20add=20GtkMenu=20to=20the=20?= =?UTF-8?q?AModule=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit You can configure what key launch the menu with the "menu" element in the config, the xml file that describes the menu with the "menu-file" element in the config, and the actions of each buttons with the "menu-actions" field. --- include/AModule.hpp | 5 +++++ src/AModule.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/AModule.hpp b/include/AModule.hpp index 58076655..903e00b8 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "IModule.hpp" @@ -52,6 +53,10 @@ class AModule : public IModule { std::vector pid_; gdouble distance_scrolled_y_; gdouble distance_scrolled_x_; + GObject* menu_; + std::map submenus_; + std::map menuActionsMap_; + static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data); std::map eventActionMap_; static const inline std::map, std::string> eventMap_{ {std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"}, diff --git a/src/AModule.cpp b/src/AModule.cpp index 915c8603..709a1e38 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -27,6 +27,24 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: else spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); } + // If a GTKMenu is requested in the config + if (config_["menu"].isString()) { + // Create the GTKMenu widget + GtkBuilder* builder = gtk_builder_new_from_file(config_["menu-file"].asString().c_str()); + menu_ = gtk_builder_get_object(builder, "menu"); + submenus_ = std::map(); + menuActionsMap_ = std::map(); + // Linking actions to the GTKMenu based on + for (Json::Value::const_iterator it = config_["menu-actions"].begin(); it != config_["menu-actions"].end(); ++it) { + std::string key = it.key().asString(); + submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); + menuActionsMap_[key] = it->asString(); + g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), (gpointer) menuActionsMap_[key].c_str()); + + } + // Enable click + enable_click = true; + } event_box_.signal_enter_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseEnter)); event_box_.signal_leave_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave)); @@ -66,6 +84,10 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: } } +void AModule::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) { + waybar::util::command::res res = waybar::util::command::exec((char*) data, "TLP"); +} + AModule::~AModule() { for (const auto& pid : pid_) { if (pid != -1) { @@ -133,6 +155,14 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) { format = rec->second; } + + // Check if the event is the one specified for the "menu" option + if (rec->second == config_["menu"].asString()) { + // Popup the menu + gtk_widget_show_all(GTK_WIDGET(menu_)); + gtk_menu_popup_at_pointer (GTK_MENU(menu_), reinterpret_cast(e)); + + } // Second call user scripts if (!format.empty()) { if (config_[format].isString()) From 3b87b830761615df7ed74b3877405dddb48255dc Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Thu, 9 May 2024 18:34:26 +0200 Subject: [PATCH 528/842] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20move=20GMenu=20to?= =?UTF-8?q?=20ALabel=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/ALabel.hpp | 4 ++++ include/AModule.hpp | 5 +---- src/ALabel.cpp | 22 +++++++++++++++++++++- src/AModule.cpp | 22 ---------------------- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/include/ALabel.hpp b/include/ALabel.hpp index 888c65a8..8855a3ed 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -27,6 +27,10 @@ class ALabel : public AModule { bool handleToggle(GdkEventButton *const &e) override; virtual std::string getState(uint8_t value, bool lesser = false); + + std::map submenus_; + std::map menuActionsMap_; + static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data); }; } // namespace waybar diff --git a/include/AModule.hpp b/include/AModule.hpp index 903e00b8..961f2a7f 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -45,6 +45,7 @@ class AModule : public IModule { virtual bool handleMouseLeave(GdkEventCrossing *const &ev); virtual bool handleScroll(GdkEventScroll *); virtual bool handleRelease(GdkEventButton *const &ev); + GObject* menu_; private: bool handleUserEvent(GdkEventButton *const &ev); @@ -53,10 +54,6 @@ class AModule : public IModule { std::vector pid_; gdouble distance_scrolled_y_; gdouble distance_scrolled_x_; - GObject* menu_; - std::map submenus_; - std::map menuActionsMap_; - static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data); std::map eventActionMap_; static const inline std::map, std::string> eventMap_{ {std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"}, diff --git a/src/ALabel.cpp b/src/ALabel.cpp index b8d39df5..4befb5b5 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -9,7 +9,7 @@ namespace waybar { ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id, const std::string& format, uint16_t interval, bool ellipsize, bool enable_click, bool enable_scroll) - : AModule(config, name, id, config["format-alt"].isString() || enable_click, enable_scroll), + : AModule(config, name, id, config["format-alt"].isString() || config["menu"].isString() || enable_click, enable_scroll), format_(config_["format"].isString() ? config_["format"].asString() : format), interval_(config_["interval"] == "once" ? std::chrono::seconds::max() @@ -51,6 +51,22 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st } } + // If a GTKMenu is requested in the config + if (config_["menu"].isString()) { + // Create the GTKMenu widget + GtkBuilder* builder = gtk_builder_new_from_file(config_["menu-file"].asString().c_str()); + menu_ = gtk_builder_get_object(builder, "menu"); + submenus_ = std::map(); + menuActionsMap_ = std::map(); + // Linking actions to the GTKMenu based on + for (Json::Value::const_iterator it = config_["menu-actions"].begin(); it != config_["menu-actions"].end(); ++it) { + std::string key = it.key().asString(); + submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); + menuActionsMap_[key] = it->asString(); + g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), (gpointer) menuActionsMap_[key].c_str()); + } + } + if (config_["justify"].isString()) { auto justify_str = config_["justify"].asString(); if (justify_str == "left") { @@ -125,6 +141,10 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { return AModule::handleToggle(e); } +void ALabel::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) { + waybar::util::command::res res = waybar::util::command::exec((char*) data, "GtkMenu"); +} + std::string ALabel::getState(uint8_t value, bool lesser) { if (!config_["states"].isObject()) { return ""; diff --git a/src/AModule.cpp b/src/AModule.cpp index 709a1e38..b2cf2fca 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -27,24 +27,6 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: else spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); } - // If a GTKMenu is requested in the config - if (config_["menu"].isString()) { - // Create the GTKMenu widget - GtkBuilder* builder = gtk_builder_new_from_file(config_["menu-file"].asString().c_str()); - menu_ = gtk_builder_get_object(builder, "menu"); - submenus_ = std::map(); - menuActionsMap_ = std::map(); - // Linking actions to the GTKMenu based on - for (Json::Value::const_iterator it = config_["menu-actions"].begin(); it != config_["menu-actions"].end(); ++it) { - std::string key = it.key().asString(); - submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); - menuActionsMap_[key] = it->asString(); - g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), (gpointer) menuActionsMap_[key].c_str()); - - } - // Enable click - enable_click = true; - } event_box_.signal_enter_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseEnter)); event_box_.signal_leave_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave)); @@ -84,10 +66,6 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: } } -void AModule::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) { - waybar::util::command::res res = waybar::util::command::exec((char*) data, "TLP"); -} - AModule::~AModule() { for (const auto& pid : pid_) { if (pid != -1) { From 21751b2faab327e839f3d0d24960539679220eb4 Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Thu, 9 May 2024 20:59:25 +0200 Subject: [PATCH 529/842] =?UTF-8?q?=F0=9F=8E=A8=20clang-tidy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/ALabel.hpp | 4 ++-- include/AModule.hpp | 4 ++-- src/ALabel.cpp | 12 ++++++++---- src/AModule.cpp | 3 +-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/include/ALabel.hpp b/include/ALabel.hpp index 8855a3ed..a1aae9da 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -28,9 +28,9 @@ class ALabel : public AModule { bool handleToggle(GdkEventButton *const &e) override; virtual std::string getState(uint8_t value, bool lesser = false); - std::map submenus_; + std::map submenus_; std::map menuActionsMap_; - static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data); + static void handleGtkMenuEvent(GtkMenuItem *menuitem, gpointer data); }; } // namespace waybar diff --git a/include/AModule.hpp b/include/AModule.hpp index 961f2a7f..90f34187 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -2,9 +2,9 @@ #include #include +#include #include #include -#include #include "IModule.hpp" @@ -45,7 +45,7 @@ class AModule : public IModule { virtual bool handleMouseLeave(GdkEventCrossing *const &ev); virtual bool handleScroll(GdkEventScroll *); virtual bool handleRelease(GdkEventButton *const &ev); - GObject* menu_; + GObject *menu_; private: bool handleUserEvent(GdkEventButton *const &ev); diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 4befb5b5..56850617 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -9,7 +9,9 @@ namespace waybar { ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id, const std::string& format, uint16_t interval, bool ellipsize, bool enable_click, bool enable_scroll) - : AModule(config, name, id, config["format-alt"].isString() || config["menu"].isString() || enable_click, enable_scroll), + : AModule(config, name, id, + config["format-alt"].isString() || config["menu"].isString() || enable_click, + enable_scroll), format_(config_["format"].isString() ? config_["format"].asString() : format), interval_(config_["interval"] == "once" ? std::chrono::seconds::max() @@ -59,11 +61,13 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st submenus_ = std::map(); menuActionsMap_ = std::map(); // Linking actions to the GTKMenu based on - for (Json::Value::const_iterator it = config_["menu-actions"].begin(); it != config_["menu-actions"].end(); ++it) { + for (Json::Value::const_iterator it = config_["menu-actions"].begin(); + it != config_["menu-actions"].end(); ++it) { std::string key = it.key().asString(); submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); menuActionsMap_[key] = it->asString(); - g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), (gpointer) menuActionsMap_[key].c_str()); + g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), + (gpointer)menuActionsMap_[key].c_str()); } } @@ -142,7 +146,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { } void ALabel::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) { - waybar::util::command::res res = waybar::util::command::exec((char*) data, "GtkMenu"); + waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu"); } std::string ALabel::getState(uint8_t value, bool lesser) { diff --git a/src/AModule.cpp b/src/AModule.cpp index b2cf2fca..77b82cee 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -138,8 +138,7 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) { if (rec->second == config_["menu"].asString()) { // Popup the menu gtk_widget_show_all(GTK_WIDGET(menu_)); - gtk_menu_popup_at_pointer (GTK_MENU(menu_), reinterpret_cast(e)); - + gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast(e)); } // Second call user scripts if (!format.empty()) { From 5fe99ea0e16b1e5c8986edae9abd5b106ffc3bb6 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 10 May 2024 00:00:47 +0300 Subject: [PATCH 530/842] Upower. Fix segmentation fault Signed-off-by: Viktar Lukashonak --- include/modules/upower.hpp | 1 + src/modules/upower.cpp | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/modules/upower.hpp b/include/modules/upower.hpp index b4450df2..d3499232 100644 --- a/include/modules/upower.hpp +++ b/include/modules/upower.hpp @@ -49,6 +49,7 @@ class UPower final : public AIconLabel { Glib::ustring label_markup_; std::mutex mutex_; Glib::RefPtr gtkTheme_; + bool sleeping_; // Technical functions void addDevice(UpDevice *); diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 73c0af08..7f0b3446 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -7,7 +7,7 @@ namespace waybar::modules { UPower::UPower(const std::string &id, const Json::Value &config) - : AIconLabel(config, "upower", id, "{percentage}", 0, true, true, true) { + : AIconLabel(config, "upower", id, "{percentage}", 0, true, true, true), sleeping_{false} { box_.set_name(name_); box_.set_spacing(0); box_.set_has_tooltip(AModule::tooltipEnabled()); @@ -185,7 +185,7 @@ days: \"{3}\", strRet: \"{4}\"", auto UPower::update() -> void { std::lock_guard guard{mutex_}; // Don't update widget if the UPower service isn't running - if (!upRunning_) { + if (!upRunning_ || sleeping_) { if (hideIfEmpty_) box_.hide(); return; } @@ -262,9 +262,11 @@ void UPower::prepareForSleep_cb(const Glib::RefPtr &conne if (!sleeping.get()) { resetDevices(); setDisplayDevice(); + sleeping_ = false; // Update the widget dp.emit(); - } + } else + sleeping_ = true; } } @@ -355,6 +357,9 @@ void UPower::setDisplayDevice() { std::lock_guard guard{mutex_}; if (nativePath_.empty()) { + // Unref current upDevice + if (upDevice_.upDevice != NULL) g_object_unref(upDevice_.upDevice); + upDevice_.upDevice = up_client_get_display_device(upClient_); getUpDeviceInfo(upDevice_); } else { @@ -367,7 +372,7 @@ void UPower::setDisplayDevice() { thisPtr->getUpDeviceInfo(upDevice); if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { // Unref current upDevice - if (thisPtr->upDevice_.upDevice) g_object_unref(thisPtr->upDevice_.upDevice); + if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); // Reassign new upDevice thisPtr->upDevice_ = upDevice; } @@ -375,12 +380,12 @@ void UPower::setDisplayDevice() { this); } - if (upDevice_.upDevice) + if (upDevice_.upDevice != NULL) g_signal_connect(upDevice_.upDevice, "notify", G_CALLBACK(deviceNotify_cb), this); } void UPower::getUpDeviceInfo(upDevice_output &upDevice_) { - if (upDevice_.upDevice && G_IS_OBJECT(upDevice_.upDevice)) { + if (upDevice_.upDevice != NULL && G_IS_OBJECT(upDevice_.upDevice)) { g_object_get(upDevice_.upDevice, "kind", &upDevice_.kind, "state", &upDevice_.state, "percentage", &upDevice_.percentage, "icon-name", &upDevice_.icon_name, "time-to-empty", &upDevice_.time_empty, "time-to-full", &upDevice_.time_full, @@ -398,7 +403,7 @@ native_path: \"{7}\". model: \"{8}\"", const Glib::ustring UPower::getText(const upDevice_output &upDevice_, const std::string &format) { Glib::ustring ret{""}; - if (upDevice_.upDevice) { + if (upDevice_.upDevice != NULL) { std::string timeStr{""}; switch (upDevice_.state) { case UP_DEVICE_STATE_CHARGING: From ff84c6dbafe25e0fb85417c4c65061e942c4f21d Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 11 May 2024 15:58:54 +0200 Subject: [PATCH 531/842] fix debian dockerfile --- Dockerfiles/debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 0745935e..f479062d 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -34,7 +34,7 @@ RUN apt update && \ libudev-dev \ libupower-glib-dev \ libwayland-dev \ - libwireplumber-0.4-dev \ + libwireplumber-0.5-dev \ libxkbcommon-dev \ libxkbregistry-dev \ locales \ From 49afcdf71589ea1f321631b6de2070f85eb583b4 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 11 May 2024 16:16:02 +0200 Subject: [PATCH 532/842] Add GitHub action for nightly Dockerfiles building --- .github/workflows/docker.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 00000000..12927fb0 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,31 @@ +name: Build and Push Docker Image + +on: + schedule: + # run every night at midnight + - cron: '0 0 * * *' + +jobs: + build-and-push: + runs-on: ubuntu-latest + strategy: + matrix: + os: [ 'alpine', 'archlinux', 'debian', 'fedora', 'gentoo', 'opensuse' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: Dockerfiles/${{ matrix.os }} + push: true + tags: alexays/${{ matrix.os }}:latest \ No newline at end of file From 1828a94b6c2e2bb7301e88639a4634b8d8ba5639 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 11 May 2024 15:58:45 +0200 Subject: [PATCH 533/842] clang-tidy: comment case styling options --- .clang-tidy | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index f74eae65..3d4cf260 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -15,15 +15,15 @@ Checks: > -readability-redundant-member-init, -readability-redundant-string-init, -readability-identifier-length -CheckOptions: - - { key: readability-identifier-naming.NamespaceCase, value: lower_case } - - { key: readability-identifier-naming.ClassCase, value: CamelCase } - - { key: readability-identifier-naming.StructCase, value: CamelCase } - - { key: readability-identifier-naming.FunctionCase, value: camelBack } - - { key: readability-identifier-naming.VariableCase, value: camelBack } - - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack } - - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ } - - { key: readability-identifier-naming.EnumCase, value: CamelCase } - - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } - - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } - - { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE } +# CheckOptions: +# - { key: readability-identifier-naming.NamespaceCase, value: lower_case } +# - { key: readability-identifier-naming.ClassCase, value: CamelCase } +# - { key: readability-identifier-naming.StructCase, value: CamelCase } +# - { key: readability-identifier-naming.FunctionCase, value: camelBack } +# - { key: readability-identifier-naming.VariableCase, value: camelBack } +# - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack } +# - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ } +# - { key: readability-identifier-naming.EnumCase, value: CamelCase } +# - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } +# - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } +# - { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE } From e27488b48c98c97ba28725f07b4501490e742d75 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 11 May 2024 15:58:12 +0200 Subject: [PATCH 534/842] clang-tidy improvements in privacy module --- include/modules/privacy/privacy.hpp | 3 --- include/modules/privacy/privacy_item.hpp | 3 --- src/modules/privacy/privacy.cpp | 22 +++++++++++----------- src/modules/privacy/privacy_item.cpp | 5 +---- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/include/modules/privacy/privacy.hpp b/include/modules/privacy/privacy.hpp index b8e76768..d7656d31 100644 --- a/include/modules/privacy/privacy.hpp +++ b/include/modules/privacy/privacy.hpp @@ -1,10 +1,7 @@ #pragma once -#include -#include #include -#include "ALabel.hpp" #include "gtkmm/box.h" #include "modules/privacy/privacy_item.hpp" #include "util/pipewire/pipewire_backend.hpp" diff --git a/include/modules/privacy/privacy_item.hpp b/include/modules/privacy/privacy_item.hpp index a0e3038b..836bd994 100644 --- a/include/modules/privacy/privacy_item.hpp +++ b/include/modules/privacy/privacy_item.hpp @@ -2,9 +2,6 @@ #include -#include -#include -#include #include #include "gtkmm/box.h" diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index b7eede75..d3e3d4b2 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -6,7 +6,6 @@ #include #include "AModule.hpp" -#include "gtkmm/image.h" #include "modules/privacy/privacy_item.hpp" namespace waybar::modules::privacy { @@ -46,30 +45,29 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st // Initialize each privacy module Json::Value modules = config_["modules"]; // Add Screenshare and Mic usage as default modules if none are specified - if (!modules.isArray() || modules.size() == 0) { + if (!modules.isArray() || modules.empty()) { modules = Json::Value(Json::arrayValue); - for (auto& type : {"screenshare", "audio-in"}) { + for (const auto& type : {"screenshare", "audio-in"}) { Json::Value obj = Json::Value(Json::objectValue); obj["type"] = type; modules.append(obj); } } - for (uint i = 0; i < modules.size(); i++) { - const Json::Value& module_config = modules[i]; + for (const auto& module_config : modules) { if (!module_config.isObject() || !module_config["type"].isString()) continue; const std::string type = module_config["type"].asString(); if (type == "screenshare") { - auto item = + auto* item = Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_VIDEO_INPUT, &nodes_screenshare, pos, iconSize, transition_duration); box_.add(*item); } else if (type == "audio-in") { - auto item = + auto* item = Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_INPUT, &nodes_audio_in, pos, iconSize, transition_duration); box_.add(*item); } else if (type == "audio-out") { - auto item = + auto* item = Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_OUTPUT, &nodes_audio_out, pos, iconSize, transition_duration); box_.add(*item); @@ -117,11 +115,13 @@ void Privacy::onPrivacyNodesChanged() { auto Privacy::update() -> void { mutex_.lock(); - bool screenshare, audio_in, audio_out; + bool screenshare = false; + bool audio_in = false; + bool audio_out = false; for (Gtk::Widget* widget : box_.get_children()) { - PrivacyItem* module = dynamic_cast(widget); - if (!module) continue; + auto* module = dynamic_cast(widget); + if (module == nullptr) continue; switch (module->privacy_type) { case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: screenshare = !nodes_screenshare.empty(); diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index c5b617d5..a38b95a4 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -1,14 +1,11 @@ #include "modules/privacy/privacy_item.hpp" #include -#include -#include "AModule.hpp" #include "glibmm/main.h" #include "gtkmm/label.h" #include "gtkmm/revealer.h" #include "gtkmm/tooltip.h" -#include "sigc++/adaptors/bind.h" #include "util/pipewire/privacy_node_info.hpp" namespace waybar::modules::privacy { @@ -89,7 +86,7 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac void PrivacyItem::update_tooltip() { // Removes all old nodes - for (auto child : tooltip_window.get_children()) { + for (auto *child : tooltip_window.get_children()) { delete child; } From ba8a88acfb0103b4762e3598525377c744b79ac1 Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Tue, 14 May 2024 08:16:10 +0200 Subject: [PATCH 535/842] Do not try to compare a string that may be a null-pointer --- src/modules/upower.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 7f0b3446..69e5b79e 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -370,6 +370,8 @@ void UPower::setDisplayDevice() { auto thisPtr{static_cast(user_data)}; upDevice.upDevice = static_cast(data); thisPtr->getUpDeviceInfo(upDevice); + if (upDevice.nativePath == nullptr) + return; if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { // Unref current upDevice if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); From 3c075bcc53cd7ff1744dcb23e6c3345b28603af0 Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Tue, 14 May 2024 08:26:44 +0200 Subject: [PATCH 536/842] Fixed formatting --- src/modules/upower.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 69e5b79e..fbbd6c4d 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -370,8 +370,7 @@ void UPower::setDisplayDevice() { auto thisPtr{static_cast(user_data)}; upDevice.upDevice = static_cast(data); thisPtr->getUpDeviceInfo(upDevice); - if (upDevice.nativePath == nullptr) - return; + if (upDevice.nativePath == nullptr) return; if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { // Unref current upDevice if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); From 6413f25b8d3c274f1238090268c7242bc724586a Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Tue, 14 May 2024 10:13:22 +0200 Subject: [PATCH 537/842] Add config option to select UPower device based on device model. --- include/modules/upower.hpp | 1 + man/waybar-upower.5.scd | 6 ++++++ src/modules/upower.cpp | 23 +++++++++++++++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/include/modules/upower.hpp b/include/modules/upower.hpp index d3499232..60a276db 100644 --- a/include/modules/upower.hpp +++ b/include/modules/upower.hpp @@ -45,6 +45,7 @@ class UPower final : public AIconLabel { // Technical variables std::string nativePath_; + std::string model_; std::string lastStatus_; Glib::ustring label_markup_; std::mutex mutex_; diff --git a/man/waybar-upower.5.scd b/man/waybar-upower.5.scd index 5e2a8eb8..2ae5f17e 100644 --- a/man/waybar-upower.5.scd +++ b/man/waybar-upower.5.scd @@ -17,6 +17,12 @@ compatible devices in the tooltip. The battery to monitor. Refer to the https://upower.freedesktop.org/docs/UpDevice.html#UpDevice--native-path ++ Can be obtained using `upower --dump` +*model*: ++ + typeof: string ++ + default: ++ + The battery to monitor, based on the model. (this option is ignored if *native-path* is given). ++ + Can be obtained using `upower --dump` + *icon-size*: ++ typeof: integer ++ default: 20 ++ diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index fbbd6c4d..b13dcc44 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -29,6 +29,8 @@ UPower::UPower(const std::string &id, const Json::Value &config) if (!showIcon_) box_.remove(image_); // Device user wants if (config_["native-path"].isString()) nativePath_ = config_["native-path"].asString(); + // Device model user wants + if (config_["model"].isString()) model_ = config_["model"].asString(); // Hide If Empty if (config_["hide-if-empty"].isBool()) hideIfEmpty_ = config_["hide-if-empty"].asBool(); @@ -356,13 +358,13 @@ void UPower::resetDevices() { void UPower::setDisplayDevice() { std::lock_guard guard{mutex_}; - if (nativePath_.empty()) { + if (nativePath_.empty() && model_.empty()) { // Unref current upDevice if (upDevice_.upDevice != NULL) g_object_unref(upDevice_.upDevice); upDevice_.upDevice = up_client_get_display_device(upClient_); getUpDeviceInfo(upDevice_); - } else { + } else if (!nativePath_.empty()) { g_ptr_array_foreach( up_client_get_devices2(upClient_), [](gpointer data, gpointer user_data) { @@ -379,6 +381,23 @@ void UPower::setDisplayDevice() { } }, this); + } else { // if `nativePath_` is empty, but `model_` is not. + g_ptr_array_foreach( + up_client_get_devices2(upClient_), + [](gpointer data, gpointer user_data) { + upDevice_output upDevice; + auto thisPtr {static_cast(user_data)}; + upDevice.upDevice = static_cast(data); + thisPtr->getUpDeviceInfo(upDevice); + if (upDevice.model == nullptr) return; + if (0 == std::strcmp(upDevice.model, thisPtr->model_.c_str())) { + // Unref current upDevice + if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); + // Reassign new upDevice + thisPtr->upDevice_ = upDevice; + } + }, + this); } if (upDevice_.upDevice != NULL) From 28ef5b7db26c3d17347fde18e05c49ea3fd3b54c Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Tue, 14 May 2024 10:21:24 +0200 Subject: [PATCH 538/842] Fix formatting --- src/modules/upower.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index b13dcc44..6a3b9091 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -381,12 +381,12 @@ void UPower::setDisplayDevice() { } }, this); - } else { // if `nativePath_` is empty, but `model_` is not. + } else { // if `nativePath_` is empty, but `model_` is not. g_ptr_array_foreach( up_client_get_devices2(upClient_), [](gpointer data, gpointer user_data) { upDevice_output upDevice; - auto thisPtr {static_cast(user_data)}; + auto thisPtr{static_cast(user_data)}; upDevice.upDevice = static_cast(data); thisPtr->getUpDeviceInfo(upDevice); if (upDevice.model == nullptr) return; From d2a719d67c5427dffc3e431c41b96b60399576e6 Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Thu, 16 May 2024 12:37:53 +0200 Subject: [PATCH 539/842] Redo to minimize code duplication. --- src/modules/upower.cpp | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 6a3b9091..552495f8 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -364,7 +364,7 @@ void UPower::setDisplayDevice() { upDevice_.upDevice = up_client_get_display_device(upClient_); getUpDeviceInfo(upDevice_); - } else if (!nativePath_.empty()) { + } else { g_ptr_array_foreach( up_client_get_devices2(upClient_), [](gpointer data, gpointer user_data) { @@ -372,30 +372,22 @@ void UPower::setDisplayDevice() { auto thisPtr{static_cast(user_data)}; upDevice.upDevice = static_cast(data); thisPtr->getUpDeviceInfo(upDevice); - if (upDevice.nativePath == nullptr) return; - if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { - // Unref current upDevice - if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); - // Reassign new upDevice - thisPtr->upDevice_ = upDevice; - } - }, - this); - } else { // if `nativePath_` is empty, but `model_` is not. - g_ptr_array_foreach( - up_client_get_devices2(upClient_), - [](gpointer data, gpointer user_data) { - upDevice_output upDevice; - auto thisPtr{static_cast(user_data)}; - upDevice.upDevice = static_cast(data); - thisPtr->getUpDeviceInfo(upDevice); - if (upDevice.model == nullptr) return; - if (0 == std::strcmp(upDevice.model, thisPtr->model_.c_str())) { - // Unref current upDevice - if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); - // Reassign new upDevice - thisPtr->upDevice_ = upDevice; + upDevice_output displayDevice{NULL}; + if (!thisPtr->nativePath_.empty()) { + if (upDevice.nativePath == nullptr) return; + if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) { + displayDevice = upDevice; + } + } else { + if (upDevice.model == nullptr) return; + if (0 == std::strcmp(upDevice.model, thisPtr->model_.c_str())) { + displayDevice = upDevice; + } } + // Unref current upDevice + if (displayDevice.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); + // Reassign new upDevice + thisPtr->upDevice_ = displayDevice; }, this); } From b288fdf8c1f0ac3b8a9f2e3995fc3b59201daaf5 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 17 May 2024 20:17:33 +0300 Subject: [PATCH 540/842] ISSUE#2240. Clock Gtk::Label as a calendar tooltip Signed-off-by: Viktar Lukashonak --- include/modules/clock.hpp | 8 +++-- src/modules/clock.cpp | 74 +++++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 9e10fb85..c212ec8b 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -21,10 +21,12 @@ class Clock final : public ALabel { auto doAction(const std::string&) -> void override; private: - const std::locale locale_; + const std::locale m_locale_; // tooltip - const std::string tlpFmt_; - std::string tlpText_{""}; // tooltip text to print + const std::string m_tlpFmt_; + std::string m_tlpText_{""}; // tooltip text to print + const Glib::RefPtr m_tooltip_; // tooltip as a separate Gtk::Label + bool query_tlp_cb(int, int, bool, const Glib::RefPtr& tooltip); // Calendar const bool cldInTooltip_; // calendar in tooltip /* diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 2901c0d1..9f279711 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,5 +1,6 @@ #include "modules/clock.hpp" +#include #include #include @@ -18,13 +19,14 @@ namespace fmt_lib = waybar::util::date::format; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), - locale_{std::locale(config_["locale"].isString() ? config_["locale"].asString() : "")}, - tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""}, - cldInTooltip_{tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, - tzInTooltip_{tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, + m_locale_{std::locale(config_["locale"].isString() ? config_["locale"].asString() : "")}, + m_tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""}, + m_tooltip_{new Gtk::Label()}, + cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, + tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, tzCurrIdx_{0}, - ordInTooltip_{tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { - tlpText_ = tlpFmt_; + ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { + m_tlpText_ = m_tlpFmt_; if (config_["timezones"].isArray() && !config_["timezones"].empty()) { for (const auto& zone_name : config_["timezones"]) { @@ -124,17 +126,26 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } } + label_.set_has_tooltip(true); + label_.signal_query_tooltip().connect(sigc::mem_fun(*this, &Clock::query_tlp_cb)); + thread_ = [this] { dp.emit(); thread_.sleep_for(interval_ - system_clock::now().time_since_epoch() % interval_); }; } +bool waybar::modules::Clock::query_tlp_cb(int, int, bool, + const Glib::RefPtr& tooltip) { + tooltip->set_custom(*m_tooltip_.get()); + return true; +} + auto waybar::modules::Clock::update() -> void { const auto* tz = tzList_[tzCurrIdx_] != nullptr ? tzList_[tzCurrIdx_] : local_zone(); const zoned_time now{tz, floor(system_clock::now())}; - label_.set_markup(fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(now))); + label_.set_markup(fmt_lib::vformat(m_locale_, format_, fmt_lib::make_format_args(now))); if (tooltipEnabled()) { const year_month_day today{floor(now.get_local_time())}; @@ -147,18 +158,19 @@ auto waybar::modules::Clock::update() -> void { if (ordInTooltip_) ordText_ = get_ordinal_date(shiftedDay); if (tzInTooltip_ || cldInTooltip_ || ordInTooltip_) { // std::vformat doesn't support named arguments. - tlpText_ = std::regex_replace(tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_); - tlpText_ = - std::regex_replace(tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); - tlpText_ = - std::regex_replace(tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); + m_tlpText_ = + std::regex_replace(m_tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_); + m_tlpText_ = + std::regex_replace(m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); + m_tlpText_ = + std::regex_replace(m_tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); } else { - tlpText_ = tlpFmt_; + m_tlpText_ = m_tlpFmt_; } - tlpText_ = fmt_lib::vformat(locale_, tlpText_, fmt_lib::make_format_args(shiftedNow)); - - label_.set_tooltip_markup(tlpText_); + m_tlpText_ = fmt_lib::vformat(m_locale_, m_tlpText_, fmt_lib::make_format_args(shiftedNow)); + m_tooltip_->set_markup(m_tlpText_); + label_.trigger_tooltip_query(); } ALabel::update(); @@ -172,7 +184,7 @@ auto waybar::modules::Clock::getTZtext(sys_seconds now) -> std::string { if (static_cast(tz_idx) == tzCurrIdx_) continue; const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : local_zone(); auto zt{zoned_time{tz, now}}; - os << fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(zt)) << '\n'; + os << fmt_lib::vformat(m_locale_, format_, fmt_lib::make_format_args(zt)) << '\n'; } return os.str(); @@ -190,13 +202,13 @@ auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsi } auto getCalendarLine(const year_month_day& currDate, const year_month ym, const unsigned line, - const weekday& firstdow, const std::locale* const locale_) -> std::string { + const weekday& firstdow, const std::locale* const m_locale_) -> std::string { std::ostringstream os; switch (line) { // Print month and year title case 0: { - os << date::format(*locale_, "{:L%B %Y}", ym); + os << date::format(*m_locale_, "{:L%B %Y}", ym); break; } // Print weekday names title @@ -206,7 +218,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const Glib::ustring::size_type wdLen{0}; int clen{0}; do { - wdStr = date::format(*locale_, "{:L%a}", wd); + wdStr = date::format(*m_locale_, "{:L%a}", wd); clen = ustring_clen(wdStr); wdLen = wdStr.length(); while (clen > 2) { @@ -229,7 +241,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const os << std::string((wd - firstdow).count() * 3, ' '); if (currDate != ym / d) - os << date::format(*locale_, "{:L%e}", d); + os << date::format(*m_locale_, "{:L%e}", d); else os << "{today}"; @@ -237,7 +249,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const ++d; if (currDate != ym / d) - os << date::format(*locale_, " {:L%e}", d); + os << date::format(*m_locale_, " {:L%e}", d); else os << " {today}"; } @@ -252,13 +264,13 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const auto wd{firstdow}; if (currDate != ym / d) - os << date::format(*locale_, "{:L%e}", d); + os << date::format(*m_locale_, "{:L%e}", d); else os << "{today}"; while (++wd != firstdow && ++d <= dlast) { if (currDate != ym / d) - os << date::format(*locale_, " {:L%e}", d); + os << date::format(*m_locale_, " {:L%e}", d); else os << " {today}"; } @@ -328,7 +340,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea if (line > 1) { if (line < ml[(unsigned)ymTmp.month() - 1u]) { os << fmt_lib::vformat( - locale_, fmtMap_[4], + m_locale_, fmtMap_[4], fmt_lib::make_format_args( (line == 2) ? static_cast( @@ -344,7 +356,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea os << Glib::ustring::format((cldWPos_ != WS::LEFT || line == 0) ? std::left : std::right, std::setfill(L' '), std::setw(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)), - getCalendarLine(today, ymTmp, line, firstdow, &locale_)); + getCalendarLine(today, ymTmp, line, firstdow, &m_locale_)); // Week numbers on the right if (cldWPos_ == WS::RIGHT && line > 0) { @@ -352,7 +364,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea if (line < ml[(unsigned)ymTmp.month() - 1u]) os << ' ' << fmt_lib::vformat( - locale_, fmtMap_[4], + m_locale_, fmtMap_[4], fmt_lib::make_format_args( (line == 2) ? static_cast( zoned_seconds{tz, local_days{ymTmp / 1}}) @@ -368,7 +380,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea // Apply user's formats if (line < 2) tmp << fmt_lib::vformat( - locale_, fmtMap_[line], + m_locale_, fmtMap_[line], fmt_lib::make_format_args(static_cast(os.str()))); else tmp << os.str(); @@ -380,10 +392,10 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea } os << std::regex_replace( - fmt_lib::vformat(locale_, fmtMap_[2], + fmt_lib::vformat(m_locale_, fmtMap_[2], fmt_lib::make_format_args(static_cast(tmp.str()))), std::regex("\\{today\\}"), - fmt_lib::vformat(locale_, fmtMap_[3], + fmt_lib::vformat(m_locale_, fmtMap_[3], fmt_lib::make_format_args( static_cast(date::format("{:L%e}", d))))); @@ -450,7 +462,7 @@ using deleting_unique_ptr = std::unique_ptr>; auto waybar::modules::Clock::first_day_of_week() -> weekday { #ifdef HAVE_LANGINFO_1STDAY deleting_unique_ptr::type, freelocale> posix_locale{ - newlocale(LC_ALL, locale_.name().c_str(), nullptr)}; + newlocale(LC_ALL, m_locale_.name().c_str(), nullptr)}; if (posix_locale) { const auto i{(int)((std::intptr_t)nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get()))}; const weekday wd{year_month_day{year(i / 10000) / month(i / 100 % 100) / day(i % 100)}}; From 5a1454ab310bf4abebf7a93c35f5b29d462938f6 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 18 May 2024 11:28:10 +0300 Subject: [PATCH 541/842] Cava. $XDG_CONFIG_HOME validation Signed-off-by: Viktar Lukashonak --- src/modules/cava.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 07227546..a98e5a67 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -12,7 +12,12 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) std::string strPath{config_["cava_config"].asString()}; const std::string fnd{"XDG_CONFIG_HOME"}; const std::string::size_type npos{strPath.find("$" + fnd)}; - if (npos != std::string::npos) strPath.replace(npos, fnd.length() + 1, getenv(fnd.c_str())); + if (npos != std::string::npos) { + if (const char* xdg = getenv(fnd.c_str())) + strPath.replace(npos, fnd.length() + 1, xdg); + else + spdlog::warn("Module {0}. Environment variable \"${1}\" not found", name_, fnd); + } strcpy(cfgPath, strPath.data()); } // Load cava config From b61ea62732a51e564ded6e7d4d37cd4796b014f2 Mon Sep 17 00:00:00 2001 From: wmlhwl Date: Sun, 19 May 2024 13:53:09 +0200 Subject: [PATCH 542/842] change layer for mode invisible to nullopt --- include/bar.hpp | 3 ++- src/bar.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 6dc3c03d..2f225de6 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -41,7 +42,7 @@ struct bar_margins { }; struct bar_mode { - bar_layer layer; + std::optional layer; bool exclusive; bool passthrough; bool visible; diff --git a/src/bar.cpp b/src/bar.cpp index 872632ac..5efe5889 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -43,7 +43,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // .visible = true}}, {"invisible", {// - .layer = bar_layer::BOTTOM, + .layer = std::nullopt, .exclusive = false, .passthrough = true, .visible = false}}, @@ -59,7 +59,7 @@ const std::string Bar::MODE_INVISIBLE = "invisible"; const std::string_view DEFAULT_BAR_ID = "bar-0"; /* Deserializer for enum bar_layer */ -void from_json(const Json::Value& j, bar_layer& l) { +void from_json(const Json::Value& j, std::optional& l) { if (j == "bottom") { l = bar_layer::BOTTOM; } else if (j == "top") { @@ -316,13 +316,13 @@ void waybar::Bar::setMode(const std::string& mode) { void waybar::Bar::setMode(const struct bar_mode& mode) { auto* gtk_window = window.gobj(); - auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM; - if (mode.layer == bar_layer::TOP) { - layer = GTK_LAYER_SHELL_LAYER_TOP; + if (mode.layer == bar_layer::BOTTOM) { + gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_BOTTOM); + } else if (mode.layer == bar_layer::TOP) { + gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_TOP); } else if (mode.layer == bar_layer::OVERLAY) { - layer = GTK_LAYER_SHELL_LAYER_OVERLAY; + gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_OVERLAY); } - gtk_layer_set_layer(gtk_window, layer); if (mode.exclusive) { gtk_layer_auto_exclusive_zone_enable(gtk_window); From b8e68b0e6301869d04c065905c7b811d3790ce9d Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Wed, 22 May 2024 12:18:23 +0800 Subject: [PATCH 543/842] (hyprland) fix crash when failed to parse IPC message IPC messages are parsed in a dedicated thread, and the thread terminates when an exception is not caught, which causes the waybar process to crash with SIGABORT. While this issue might be related to Hyprland, it is really annoying to see waybar crash. It would be better to catch those exceptions and report errors instead of crashing. --- src/modules/hyprland/backend.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 98eb8b90..1b47ff7d 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -86,7 +86,14 @@ void IPC::startIPC() { std::string messageReceived(buffer.data()); messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n')); spdlog::debug("hyprland IPC received {}", messageReceived); - parseIPC(messageReceived); + + try { + parseIPC(messageReceived); + } catch (std::exception& e) { + spdlog::warn("Failed to parse IPC message: {}, reason: {}", messageReceived, e.what()); + } catch (...) { + throw; + } std::this_thread::sleep_for(std::chrono::milliseconds(1)); } From 60a613ae5106e215ce1b95268bb6f8cdbfc3a2d7 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Thu, 23 May 2024 16:14:06 +0300 Subject: [PATCH 544/842] cava bump: 0.10.2 Signed-off-by: Viktar Lukashonak --- meson.build | 2 +- src/modules/cava.cpp | 13 +------------ subprojects/cava.wrap | 8 ++++---- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/meson.build b/meson.build index d7a5a4ee..102bb878 100644 --- a/meson.build +++ b/meson.build @@ -464,7 +464,7 @@ if get_option('experimental') endif cava = dependency('cava', - version : '>=0.10.1', + version : '>=0.10.2', required: get_option('cava'), fallback : ['cava', 'cava_dep'], not_found_message: 'cava is not found. Building waybar without cava') diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index a98e5a67..431ce5f1 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -8,18 +8,7 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) char cfgPath[PATH_MAX]; cfgPath[0] = '\0'; - if (config_["cava_config"].isString()) { - std::string strPath{config_["cava_config"].asString()}; - const std::string fnd{"XDG_CONFIG_HOME"}; - const std::string::size_type npos{strPath.find("$" + fnd)}; - if (npos != std::string::npos) { - if (const char* xdg = getenv(fnd.c_str())) - strPath.replace(npos, fnd.length() + 1, xdg); - else - spdlog::warn("Module {0}. Environment variable \"${1}\" not found", name_, fnd); - } - strcpy(cfgPath, strPath.data()); - } + if (config_["cava_config"].isString()) strcpy(cfgPath, config_["cava_config"].asString().data()); // Load cava config error_.length = 0; diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap index 19383d11..275ba114 100644 --- a/subprojects/cava.wrap +++ b/subprojects/cava.wrap @@ -1,7 +1,7 @@ [wrap-file] -directory = cava-0.10.1 -source_url = https://github.com/LukashonakV/cava/archive/0.10.1.tar.gz -source_filename = cava-0.10.1.tar.gz -source_hash = ae8c7339908d6febeac5ab8df4576c03c9fdbca6c8e8975daf9ce68b57038bb5 +directory = cava-0.10.2 +source_url = https://github.com/LukashonakV/cava/archive/0.10.2.tar.gz +source_filename = cava-0.10.2.tar.gz +source_hash = dff78c4787c9843583086408a0a6e5bde7a5dee1fa17ae526847366846cb19c3 [provide] cava = cava_dep From d012124c03a9d1c547478b9b3c72928fcb485806 Mon Sep 17 00:00:00 2001 From: Unreal Hoang Date: Fri, 24 May 2024 09:18:25 +0900 Subject: [PATCH 545/842] cava bump: 0.10.2 for nix --- nix/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/default.nix b/nix/default.nix index 986e84dd..9ce39a9b 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -5,12 +5,12 @@ }: let libcava = rec { - version = "0.10.1"; + version = "0.10.2"; src = pkgs.fetchFromGitHub { owner = "LukashonakV"; repo = "cava"; rev = version; - hash = "sha256-iIYKvpOWafPJB5XhDOSIW9Mb4I3A4pcgIIPQdQYEqUw="; + hash = "sha256-jU7RQV2txruu/nUUl0TzjK4nai7G38J1rcTjO7UXumY="; }; }; in From e4353e548ad566d9ad3d7e7d0472cbfeae2870aa Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:21:23 -0500 Subject: [PATCH 546/842] .gitignore: add .ccls-cache --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 68bc0dc4..b486237e 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ packagecache # Nix result result-* + +.ccls-cache From 9fe51af6b09a37cd7253012b3e24e8b5f8ef3c0b Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 10:58:03 -0500 Subject: [PATCH 547/842] hyprland/workspaces: break up parseConfig --- include/modules/hyprland/workspaces.hpp | 10 +++ src/modules/hyprland/workspaces.cpp | 87 ++++++++++++++----------- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 82872348..41c4b986 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -145,7 +145,17 @@ class Workspaces : public AModule, public EventHandler { Json::Value const& clientsData = Json::Value::nullRef); void removeWorkspace(std::string const& name); void setUrgentWorkspace(std::string const& windowaddress); + + // Config void parseConfig(const Json::Value& config); + auto populateIconsMap(const Json::Value& formatIcons) -> void; + auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) -> void; + auto populateSortByConfig(const Json::Value& config) -> void; + auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; + auto populatePersistentWorkspacesConfig(const Json::Value& config) -> void; + auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; + auto populateWindowRewriteConfig(const Json::Value& config) -> void; + void registerIpc(); // workspace events diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3c03c708..c7bffa94 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -55,54 +55,63 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value } auto Workspaces::parseConfig(const Json::Value &config) -> void { - const Json::Value &configFormat = config["format"]; - + const auto &configFormat = config["format"]; m_format = configFormat.isString() ? configFormat.asString() : "{name}"; m_withIcon = m_format.find("{icon}") != std::string::npos; if (m_withIcon && m_iconsMap.empty()) { - Json::Value formatIcons = config["format-icons"]; - for (std::string &name : formatIcons.getMemberNames()) { - m_iconsMap.emplace(name, formatIcons[name].asString()); - } - m_iconsMap.emplace("", ""); + populateIconsMap(config["format-icons"]); } - auto configAllOutputs = config_["all-outputs"]; - if (configAllOutputs.isBool()) { - m_allOutputs = configAllOutputs.asBool(); - } + populateBoolConfig(config, "all-outputs", m_allOutputs); + populateBoolConfig(config, "show-special", m_showSpecial); + populateBoolConfig(config, "active-only", m_activeOnly); + populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); - auto configShowSpecial = config_["show-special"]; - if (configShowSpecial.isBool()) { - m_showSpecial = configShowSpecial.asBool(); - } + populateSortByConfig(config); - auto configActiveOnly = config_["active-only"]; - if (configActiveOnly.isBool()) { - m_activeOnly = configActiveOnly.asBool(); - } + populateIgnoreWorkspacesConfig(config); - auto configMoveToMonitor = config_["move-to-monitor"]; - if (configMoveToMonitor.isBool()) { - m_moveToMonitor = configMoveToMonitor.asBool(); - } + populatePersistentWorkspacesConfig(config); - auto configSortBy = config_["sort-by"]; + populateFormatWindowSeparatorConfig(config); + + populateWindowRewriteConfig(config); +} + +auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { + for (const auto &name : formatIcons.getMemberNames()) { + m_iconsMap.emplace(name, formatIcons[name].asString()); + } + m_iconsMap.emplace("", ""); +} + +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) + -> void { + auto configValue = config[key]; + if (configValue.isBool()) { + member = configValue.asBool(); + } +} + +auto Workspaces::populateSortByConfig(const Json::Value &config) -> void { + auto configSortBy = config["sort-by"]; if (configSortBy.isString()) { auto sortByStr = configSortBy.asString(); try { m_sortBy = m_enumParser.parseStringToEnum(sortByStr, m_sortMap); } catch (const std::invalid_argument &e) { - // Handle the case where the string is not a valid enum representation. m_sortBy = SortMethod::DEFAULT; - g_warning("Invalid string representation for sort-by. Falling back to default sort method."); + spdlog::warn( + "Invalid string representation for sort-by. Falling back to default sort method."); } } +} - Json::Value ignoreWorkspaces = config["ignore-workspaces"]; +auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> void { + auto ignoreWorkspaces = config["ignore-workspaces"]; if (ignoreWorkspaces.isArray()) { - for (Json::Value &workspaceRegex : ignoreWorkspaces) { + for (const auto &workspaceRegex : ignoreWorkspaces) { if (workspaceRegex.isString()) { std::string ruleString = workspaceRegex.asString(); try { @@ -116,29 +125,31 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { } } } +} - if (config_["persistent_workspaces"].isObject()) { +auto Workspaces::populatePersistentWorkspacesConfig(const Json::Value &config) -> void { + if (config.isMember("persistent-workspaces") || config.isMember("persistent_workspaces")) { spdlog::warn( "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); + m_persistentWorkspaceConfig = + config.get("persistent-workspaces", config.get("persistent_workspaces", Json::Value())); } +} - if (config_["persistent-workspaces"].isObject() || config_["persistent_workspaces"].isObject()) { - m_persistentWorkspaceConfig = config_["persistent-workspaces"].isObject() - ? config_["persistent-workspaces"] - : config_["persistent_workspaces"]; - } - - const Json::Value &formatWindowSeparator = config["format-window-separator"]; +auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void { + auto formatWindowSeparator = config["format-window-separator"]; m_formatWindowSeparator = formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " "; +} - const Json::Value &windowRewrite = config["window-rewrite"]; +auto Workspaces::populateWindowRewriteConfig(const Json::Value &config) -> void { + const auto &windowRewrite = config["window-rewrite"]; if (!windowRewrite.isObject()) { spdlog::debug("window-rewrite is not defined or is not an object, using default rules."); return; } - const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"]; + const auto &windowRewriteDefaultConfig = config["window-rewrite-default"]; std::string windowRewriteDefault = windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; From d73051e98063ec03b95356f1eb3a3ee319f3f15a Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 13:53:39 -0500 Subject: [PATCH 548/842] hyprland/workspaces: break up doUpdate --- include/modules/hyprland/workspaces.hpp | 6 ++ src/modules/hyprland/workspaces.cpp | 77 +++++++++++++------------ 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 41c4b986..3c19bc4b 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -181,7 +181,13 @@ class Workspaces : public AModule, public EventHandler { int windowRewritePriorityFunction(std::string const& window_rule); + // Update methods void doUpdate(); + void removeWorkspacesToRemove(); + void createWorkspacesToCreate(); + std::vector getVisibleWorkspaces(); + void updateWorkspaceStates(const std::vector& visibleWorkspaces); + bool updateWindowsToCreate(); void extendOrphans(int workspaceId, Json::Value const& clientsJson); void registerOrphanWindow(WindowCreationPayload create_window_payload); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index c7bffa94..b535794f 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -69,13 +69,9 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); populateSortByConfig(config); - populateIgnoreWorkspacesConfig(config); - populatePersistentWorkspacesConfig(config); - populateFormatWindowSeparatorConfig(config); - populateWindowRewriteConfig(config); } @@ -195,14 +191,31 @@ auto Workspaces::registerIpc() -> void { void Workspaces::doUpdate() { std::unique_lock lock(m_mutex); - // remove workspaces that wait to be removed - for (auto &elem : m_workspacesToRemove) { - removeWorkspace(elem); + removeWorkspacesToRemove(); + createWorkspacesToCreate(); + + std::vector visibleWorkspaces = getVisibleWorkspaces(); + + updateWorkspaceStates(visibleWorkspaces); + updateWindowCount(); + sortWorkspaces(); + + bool anyWindowCreated = updateWindowsToCreate(); + + if (anyWindowCreated) { + dp.emit(); + } +} + +void Workspaces::removeWorkspacesToRemove() { + for (const auto &workspaceName : m_workspacesToRemove) { + removeWorkspace(workspaceName); } m_workspacesToRemove.clear(); +} - // add workspaces that wait to be created - for (auto &[workspaceData, clientsData] : m_workspacesToCreate) { +void Workspaces::createWorkspacesToCreate() { + for (const auto &[workspaceData, clientsData] : m_workspacesToCreate) { createWorkspace(workspaceData, clientsData); } if (!m_workspacesToCreate.empty()) { @@ -210,63 +223,55 @@ void Workspaces::doUpdate() { sortWorkspaces(); } m_workspacesToCreate.clear(); +} - // get all active workspaces - spdlog::trace("Getting active workspaces"); - auto monitors = gIPC->getSocket1JsonReply("monitors"); +std::vector Workspaces::getVisibleWorkspaces() { std::vector visibleWorkspaces; - for (Json::Value &monitor : monitors) { + auto monitors = gIPC->getSocket1JsonReply("monitors"); + for (const auto &monitor : monitors) { auto ws = monitor["activeWorkspace"]; - if (ws.isObject() && (ws["name"].isString())) { + if (ws.isObject() && ws["name"].isString()) { visibleWorkspaces.push_back(ws["name"].asString()); } auto sws = monitor["specialWorkspace"]; auto name = sws["name"].asString(); - if (sws.isObject() && (sws["name"].isString()) && !name.empty()) { + if (sws.isObject() && sws["name"].isString() && !name.empty()) { visibleWorkspaces.push_back(!name.starts_with("special:") ? name : name.substr(8)); } } + return visibleWorkspaces; +} - spdlog::trace("Updating workspace states"); - auto updated_workspaces = gIPC->getSocket1JsonReply("workspaces"); +void Workspaces::updateWorkspaceStates(const std::vector &visibleWorkspaces) { + auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { - // active workspace->setActive(workspace->name() == m_activeWorkspaceName || workspace->name() == m_activeSpecialWorkspaceName); - // disable urgency if workspace is active if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { workspace->setUrgent(false); } - - // visible workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), workspace->name()) != visibleWorkspaces.end()); - - // set workspace icon std::string &workspaceIcon = m_iconsMap[""]; if (m_withIcon) { workspaceIcon = workspace->selectIcon(m_iconsMap); } - - // update m_output - auto updated_workspace = - std::find_if(updated_workspaces.begin(), updated_workspaces.end(), [&workspace](auto &w) { + auto updatedWorkspace = std::find_if( + updatedWorkspaces.begin(), updatedWorkspaces.end(), [&workspace](const auto &w) { auto wNameRaw = w["name"].asString(); auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; return wName == workspace->name(); }); - - if (updated_workspace != updated_workspaces.end()) { - workspace->setOutput((*updated_workspace)["monitor"].asString()); + if (updatedWorkspace != updatedWorkspaces.end()) { + workspace->setOutput((*updatedWorkspace)["monitor"].asString()); } - workspace->update(m_format, workspaceIcon); } +} - spdlog::trace("Updating window count"); +bool Workspaces::updateWindowsToCreate() { bool anyWindowCreated = false; std::vector notCreated; - for (auto &windowPayload : m_windowsToCreate) { bool created = false; for (auto &workspace : m_workspaces) { @@ -285,13 +290,9 @@ void Workspaces::doUpdate() { } } } - - if (anyWindowCreated) { - dp.emit(); - } - m_windowsToCreate.clear(); m_windowsToCreate = notCreated; + return anyWindowCreated; } auto Workspaces::update() -> void { From 07c91c200a284f308f2e621b5a360b175f33374e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:07:12 -0500 Subject: [PATCH 549/842] hyprland/workspaces: break up headers --- .../hyprland/windowcreationpayload.hpp | 61 ++++++++++++ include/modules/hyprland/workspace.hpp | 88 ++++++++++++++++++ include/modules/hyprland/workspaces.hpp | 93 +------------------ 3 files changed, 151 insertions(+), 91 deletions(-) create mode 100644 include/modules/hyprland/windowcreationpayload.hpp create mode 100644 include/modules/hyprland/workspace.hpp diff --git a/include/modules/hyprland/windowcreationpayload.hpp b/include/modules/hyprland/windowcreationpayload.hpp new file mode 100644 index 00000000..45229ed4 --- /dev/null +++ b/include/modules/hyprland/windowcreationpayload.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "modules/hyprland/backend.hpp" +#include "util/enum.hpp" +#include "util/regex_collection.hpp" + +using WindowAddress = std::string; + +namespace waybar::modules::hyprland { + +class Workspaces; + +class WindowCreationPayload { + public: + WindowCreationPayload(std::string workspace_name, WindowAddress window_address, + std::string window_repr); + WindowCreationPayload(std::string workspace_name, WindowAddress window_address, + std::string window_class, std::string window_title); + WindowCreationPayload(Json::Value const& client_data); + + int incrementTimeSpentUncreated(); + bool isEmpty(Workspaces& workspace_manager); + bool reprIsReady() const { return std::holds_alternative(m_window); } + std::string repr(Workspaces& workspace_manager); + + std::string getWorkspaceName() const { return m_workspaceName; } + WindowAddress getAddress() const { return m_windowAddress; } + + void moveToWorksace(std::string& new_workspace_name); + + private: + void clearAddr(); + void clearWorkspaceName(); + + using Repr = std::string; + using ClassAndTitle = std::pair; + std::variant m_window; + + WindowAddress m_windowAddress; + std::string m_workspaceName; + + int m_timeSpentUncreated = 0; +}; + +} // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/workspace.hpp b/include/modules/hyprland/workspace.hpp new file mode 100644 index 00000000..f1fea4e8 --- /dev/null +++ b/include/modules/hyprland/workspace.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "modules/hyprland/backend.hpp" +#include "modules/hyprland/windowcreationpayload.hpp" +#include "util/enum.hpp" +#include "util/regex_collection.hpp" + +using WindowAddress = std::string; + +namespace waybar::modules::hyprland { + +class Workspaces; +class Workspace { + public: + explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager, + const Json::Value& clients_data = Json::Value::nullRef); + std::string& selectIcon(std::map& icons_map); + Gtk::Button& button() { return m_button; }; + + int id() const { return m_id; }; + std::string name() const { return m_name; }; + std::string output() const { return m_output; }; + bool isActive() const { return m_isActive; }; + bool isSpecial() const { return m_isSpecial; }; + bool isPersistent() const { return m_isPersistentRule || m_isPersistentConfig; }; + bool isPersistentConfig() const { return m_isPersistentConfig; }; + bool isPersistentRule() const { return m_isPersistentRule; }; + bool isVisible() const { return m_isVisible; }; + bool isEmpty() const { return m_windows == 0; }; + bool isUrgent() const { return m_isUrgent; }; + + bool handleClicked(GdkEventButton* bt) const; + void setActive(bool value = true) { m_isActive = value; }; + void setPersistentRule(bool value = true) { m_isPersistentRule = value; }; + void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; }; + void setUrgent(bool value = true) { m_isUrgent = value; }; + void setVisible(bool value = true) { m_isVisible = value; }; + void setWindows(uint value) { m_windows = value; }; + void setName(std::string const& value) { m_name = value; }; + void setOutput(std::string const& value) { m_output = value; }; + bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); } + void insertWindow(WindowCreationPayload create_window_paylod); + std::string removeWindow(WindowAddress const& addr); + void initializeWindowMap(const Json::Value& clients_data); + + bool onWindowOpened(WindowCreationPayload const& create_window_paylod); + std::optional closeWindow(WindowAddress const& addr); + + void update(const std::string& format, const std::string& icon); + + private: + Workspaces& m_workspaceManager; + + int m_id; + std::string m_name; + std::string m_output; + uint m_windows; + bool m_isActive = false; + bool m_isSpecial = false; + bool m_isPersistentRule = false; // represents the persistent state in hyprland + bool m_isPersistentConfig = false; // represents the persistent state in the Waybar config + bool m_isUrgent = false; + bool m_isVisible = false; + + std::map m_windowMap; + + Gtk::Button m_button; + Gtk::Box m_content; + Gtk::Label m_label; +}; + +} // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 3c19bc4b..23e3e27f 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -17,6 +17,8 @@ #include "AModule.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" +#include "modules/hyprland/windowcreationpayload.hpp" +#include "modules/hyprland/workspace.hpp" #include "util/enum.hpp" #include "util/regex_collection.hpp" @@ -26,97 +28,6 @@ namespace waybar::modules::hyprland { class Workspaces; -class WindowCreationPayload { - public: - WindowCreationPayload(std::string workspace_name, WindowAddress window_address, - std::string window_repr); - WindowCreationPayload(std::string workspace_name, WindowAddress window_address, - std::string window_class, std::string window_title); - WindowCreationPayload(Json::Value const& client_data); - - int incrementTimeSpentUncreated(); - bool isEmpty(Workspaces& workspace_manager); - bool reprIsReady() const { return std::holds_alternative(m_window); } - std::string repr(Workspaces& workspace_manager); - - std::string getWorkspaceName() const { return m_workspaceName; } - WindowAddress getAddress() const { return m_windowAddress; } - - void moveToWorksace(std::string& new_workspace_name); - - private: - void clearAddr(); - void clearWorkspaceName(); - - using Repr = std::string; - using ClassAndTitle = std::pair; - std::variant m_window; - - WindowAddress m_windowAddress; - std::string m_workspaceName; - - int m_timeSpentUncreated = 0; -}; - -class Workspace { - public: - explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager, - const Json::Value& clients_data = Json::Value::nullRef); - std::string& selectIcon(std::map& icons_map); - Gtk::Button& button() { return m_button; }; - - int id() const { return m_id; }; - std::string name() const { return m_name; }; - std::string output() const { return m_output; }; - bool isActive() const { return m_isActive; }; - bool isSpecial() const { return m_isSpecial; }; - bool isPersistent() const { return m_isPersistentRule || m_isPersistentConfig; }; - bool isPersistentConfig() const { return m_isPersistentConfig; }; - bool isPersistentRule() const { return m_isPersistentRule; }; - bool isVisible() const { return m_isVisible; }; - bool isEmpty() const { return m_windows == 0; }; - bool isUrgent() const { return m_isUrgent; }; - - bool handleClicked(GdkEventButton* bt) const; - void setActive(bool value = true) { m_isActive = value; }; - void setPersistentRule(bool value = true) { m_isPersistentRule = value; }; - void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; }; - void setUrgent(bool value = true) { m_isUrgent = value; }; - void setVisible(bool value = true) { m_isVisible = value; }; - void setWindows(uint value) { m_windows = value; }; - void setName(std::string const& value) { m_name = value; }; - void setOutput(std::string const& value) { m_output = value; }; - bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); } - void insertWindow(WindowCreationPayload create_window_paylod); - std::string removeWindow(WindowAddress const& addr); - void initializeWindowMap(const Json::Value& clients_data); - - bool onWindowOpened(WindowCreationPayload const& create_window_paylod); - std::optional closeWindow(WindowAddress const& addr); - - void update(const std::string& format, const std::string& icon); - - private: - Workspaces& m_workspaceManager; - - int m_id; - std::string m_name; - std::string m_output; - uint m_windows; - bool m_isActive = false; - bool m_isSpecial = false; - bool m_isPersistentRule = false; // represents the persistent state in hyprland - bool m_isPersistentConfig = false; // represents the persistent state in the Waybar config - bool m_isUrgent = false; - bool m_isVisible = false; - - std::map m_windowMap; - - Gtk::Button m_button; - Gtk::Box m_content; - Gtk::Label m_label; -}; - class Workspaces : public AModule, public EventHandler { public: Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); From 56319a470588a2110204c8fc954f477c52a11a7e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:21:07 -0500 Subject: [PATCH 550/842] hyprland/workspaces: break up implementations --- meson.build | 2 + .../hyprland/windowcreationpayload.cpp | 110 +++++++ src/modules/hyprland/workspace.cpp | 213 +++++++++++++ src/modules/hyprland/workspaces.cpp | 294 ------------------ 4 files changed, 325 insertions(+), 294 deletions(-) create mode 100644 src/modules/hyprland/windowcreationpayload.cpp create mode 100644 src/modules/hyprland/workspace.cpp diff --git a/meson.build b/meson.build index 102bb878..1af8e2b2 100644 --- a/meson.build +++ b/meson.build @@ -305,7 +305,9 @@ if true 'src/modules/hyprland/language.cpp', 'src/modules/hyprland/submap.cpp', 'src/modules/hyprland/window.cpp', + 'src/modules/hyprland/workspace.cpp', 'src/modules/hyprland/workspaces.cpp', + 'src/modules/hyprland/windowcreationpayload.cpp', ) man_files += files( 'man/waybar-hyprland-language.5.scd', diff --git a/src/modules/hyprland/windowcreationpayload.cpp b/src/modules/hyprland/windowcreationpayload.cpp new file mode 100644 index 00000000..261edcbc --- /dev/null +++ b/src/modules/hyprland/windowcreationpayload.cpp @@ -0,0 +1,110 @@ +#include "modules/hyprland/windowcreationpayload.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#include "modules/hyprland/workspaces.hpp" +#include "util/regex_collection.hpp" + +namespace waybar::modules::hyprland { +WindowCreationPayload::WindowCreationPayload(std::string workspace_name, + WindowAddress window_address, std::string window_repr) + : m_window(std::move(window_repr)), + m_windowAddress(std::move(window_address)), + m_workspaceName(std::move(workspace_name)) { + clearAddr(); + clearWorkspaceName(); +} + +WindowCreationPayload::WindowCreationPayload(std::string workspace_name, + WindowAddress window_address, std::string window_class, + std::string window_title) + : m_window(std::make_pair(std::move(window_class), std::move(window_title))), + m_windowAddress(std::move(window_address)), + m_workspaceName(std::move(workspace_name)) { + clearAddr(); + clearWorkspaceName(); +} + +WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data) + : m_window(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), + m_windowAddress(client_data["address"].asString()), + m_workspaceName(client_data["workspace"]["name"].asString()) { + clearAddr(); + clearWorkspaceName(); +} + +std::string WindowCreationPayload::repr(Workspaces &workspace_manager) { + if (std::holds_alternative(m_window)) { + return std::get(m_window); + } + if (std::holds_alternative(m_window)) { + auto [window_class, window_title] = std::get(m_window); + return workspace_manager.getRewrite(window_class, window_title); + } + // Unreachable + spdlog::error("WorkspaceWindow::repr: Unreachable"); + throw std::runtime_error("WorkspaceWindow::repr: Unreachable"); +} + +bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) { + if (std::holds_alternative(m_window)) { + return std::get(m_window).empty(); + } + if (std::holds_alternative(m_window)) { + auto [window_class, window_title] = std::get(m_window); + return (window_class.empty() && + (!workspace_manager.windowRewriteConfigUsesTitle() || window_title.empty())); + } + // Unreachable + spdlog::error("WorkspaceWindow::isEmpty: Unreachable"); + throw std::runtime_error("WorkspaceWindow::isEmpty: Unreachable"); +} + +int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; } + +void WindowCreationPayload::clearAddr() { + // substr(2, ...) is necessary because Hyprland's JSON follows this format: + // 0x{ADDR} + // While Hyprland's IPC follows this format: + // {ADDR} + static const std::string ADDR_PREFIX = "0x"; + static const int ADDR_PREFIX_LEN = ADDR_PREFIX.length(); + + if (m_windowAddress.starts_with(ADDR_PREFIX)) { + m_windowAddress = + m_windowAddress.substr(ADDR_PREFIX_LEN, m_windowAddress.length() - ADDR_PREFIX_LEN); + } +} + +void WindowCreationPayload::clearWorkspaceName() { + // The workspace name may optionally feature "special:" at the beginning. + // If so, we need to remove it because the workspace is saved WITHOUT the + // special qualifier. The reasoning is that not all of Hyprland's IPC events + // use this qualifier, so it's better to be consistent about our uses. + + static const std::string SPECIAL_QUALIFIER_PREFIX = "special:"; + static const int SPECIAL_QUALIFIER_PREFIX_LEN = SPECIAL_QUALIFIER_PREFIX.length(); + + if (m_workspaceName.starts_with(SPECIAL_QUALIFIER_PREFIX)) { + m_workspaceName = m_workspaceName.substr( + SPECIAL_QUALIFIER_PREFIX_LEN, m_workspaceName.length() - SPECIAL_QUALIFIER_PREFIX_LEN); + } + + std::size_t spaceFound = m_workspaceName.find(' '); + if (spaceFound != std::string::npos) { + m_workspaceName.erase(m_workspaceName.begin() + spaceFound, m_workspaceName.end()); + } +} + +void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) { + m_workspaceName = new_workspace_name; +} + +} // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp new file mode 100644 index 00000000..1e3c05f3 --- /dev/null +++ b/src/modules/hyprland/workspace.cpp @@ -0,0 +1,213 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "modules/hyprland/workspaces.hpp" +#include "util/regex_collection.hpp" + +namespace waybar::modules::hyprland { + +void Workspace::initializeWindowMap(const Json::Value &clients_data) { + m_windowMap.clear(); + for (auto client : clients_data) { + if (client["workspace"]["id"].asInt() == id()) { + insertWindow({client}); + } + } +} + +void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { + if (!create_window_paylod.isEmpty(m_workspaceManager)) { + m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager); + } +}; + +std::string Workspace::removeWindow(WindowAddress const &addr) { + std::string windowRepr = m_windowMap[addr]; + m_windowMap.erase(addr); + return windowRepr; +} + +bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) { + if (create_window_paylod.getWorkspaceName() == name()) { + insertWindow(create_window_paylod); + return true; + } + return false; +} + +std::optional Workspace::closeWindow(WindowAddress const &addr) { + if (m_windowMap.contains(addr)) { + return removeWindow(addr); + } + return std::nullopt; +} + +Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager, + const Json::Value &clients_data) + : m_workspaceManager(workspace_manager), + m_id(workspace_data["id"].asInt()), + m_name(workspace_data["name"].asString()), + m_output(workspace_data["monitor"].asString()), // TODO:allow using monitor desc + m_windows(workspace_data["windows"].asInt()), + m_isActive(true), + m_isPersistentRule(workspace_data["persistent-rule"].asBool()), + m_isPersistentConfig(workspace_data["persistent-config"].asBool()) { + if (m_name.starts_with("name:")) { + m_name = m_name.substr(5); + } else if (m_name.starts_with("special")) { + m_name = m_id == -99 ? m_name : m_name.substr(8); + m_isSpecial = true; + } + + m_button.add_events(Gdk::BUTTON_PRESS_MASK); + m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked), + false); + + m_button.set_relief(Gtk::RELIEF_NONE); + m_content.set_center_widget(m_label); + m_button.add(m_content); + + initializeWindowMap(clients_data); +} + +void addOrRemoveClass(const Glib::RefPtr &context, bool condition, + const std::string &class_name) { + if (condition) { + context->add_class(class_name); + } else { + context->remove_class(class_name); + } +} + +void Workspace::update(const std::string &format, const std::string &icon) { + // clang-format off + if (this->m_workspaceManager.activeOnly() && \ + !this->isActive() && \ + !this->isPersistent() && \ + !this->isVisible() && \ + !this->isSpecial()) { + // clang-format on + // if activeOnly is true, hide if not active, persistent, visible or special + m_button.hide(); + return; + } + m_button.show(); + + auto styleContext = m_button.get_style_context(); + addOrRemoveClass(styleContext, isActive(), "active"); + addOrRemoveClass(styleContext, isSpecial(), "special"); + addOrRemoveClass(styleContext, isEmpty(), "empty"); + addOrRemoveClass(styleContext, isPersistent(), "persistent"); + addOrRemoveClass(styleContext, isUrgent(), "urgent"); + addOrRemoveClass(styleContext, isVisible(), "visible"); + addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); + + std::string windows; + auto windowSeparator = m_workspaceManager.getWindowSeparator(); + + bool isNotFirst = false; + + for (auto &[_pid, window_repr] : m_windowMap) { + if (isNotFirst) { + windows.append(windowSeparator); + } + isNotFirst = true; + windows.append(window_repr); + } + + m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), + fmt::arg("name", name()), fmt::arg("icon", icon), + fmt::arg("windows", windows))); +} + +std::string &Workspace::selectIcon(std::map &icons_map) { + spdlog::trace("Selecting icon for workspace {}", name()); + if (isUrgent()) { + auto urgentIconIt = icons_map.find("urgent"); + if (urgentIconIt != icons_map.end()) { + return urgentIconIt->second; + } + } + + if (isActive()) { + auto activeIconIt = icons_map.find("active"); + if (activeIconIt != icons_map.end()) { + return activeIconIt->second; + } + } + + if (isSpecial()) { + auto specialIconIt = icons_map.find("special"); + if (specialIconIt != icons_map.end()) { + return specialIconIt->second; + } + } + + auto namedIconIt = icons_map.find(name()); + if (namedIconIt != icons_map.end()) { + return namedIconIt->second; + } + + if (isVisible()) { + auto visibleIconIt = icons_map.find("visible"); + if (visibleIconIt != icons_map.end()) { + return visibleIconIt->second; + } + } + + if (isEmpty()) { + auto emptyIconIt = icons_map.find("empty"); + if (emptyIconIt != icons_map.end()) { + return emptyIconIt->second; + } + } + + if (isPersistent()) { + auto persistentIconIt = icons_map.find("persistent"); + if (persistentIconIt != icons_map.end()) { + return persistentIconIt->second; + } + } + + auto defaultIconIt = icons_map.find("default"); + if (defaultIconIt != icons_map.end()) { + return defaultIconIt->second; + } + + return m_name; +} + +bool Workspace::handleClicked(GdkEventButton *bt) const { + if (bt->type == GDK_BUTTON_PRESS) { + try { + if (id() > 0) { // normal + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); + } else { + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } + } else if (!isSpecial()) { // named (this includes persistent) + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); + } else { + gIPC->getSocket1Reply("dispatch workspace name:" + name()); + } + } else if (id() != -99) { // named special + gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); + } else { // special + gIPC->getSocket1Reply("dispatch togglespecialworkspace"); + } + return true; + } catch (const std::exception &e) { + spdlog::error("Failed to dispatch workspace: {}", e.what()); + } + } + return false; +} +} // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index b535794f..2074ad54 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -589,42 +589,6 @@ void Workspaces::updateWindowCount() { } } -void Workspace::initializeWindowMap(const Json::Value &clients_data) { - m_windowMap.clear(); - for (auto client : clients_data) { - if (client["workspace"]["id"].asInt() == id()) { - insertWindow({client}); - } - } -} - -void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { - if (!create_window_paylod.isEmpty(m_workspaceManager)) { - m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager); - } -}; - -std::string Workspace::removeWindow(WindowAddress const &addr) { - std::string windowRepr = m_windowMap[addr]; - m_windowMap.erase(addr); - return windowRepr; -} - -bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) { - if (create_window_paylod.getWorkspaceName() == name()) { - insertWindow(create_window_paylod); - return true; - } - return false; -} - -std::optional Workspace::closeWindow(WindowAddress const &addr) { - if (m_windowMap.contains(addr)) { - return removeWindow(addr); - } - return std::nullopt; -} - void Workspaces::createWorkspace(Json::Value const &workspace_data, Json::Value const &clients_data) { auto workspaceName = workspace_data["name"].asString(); @@ -857,84 +821,6 @@ Workspaces::~Workspaces() { std::lock_guard lg(m_mutex); } -Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager, - const Json::Value &clients_data) - : m_workspaceManager(workspace_manager), - m_id(workspace_data["id"].asInt()), - m_name(workspace_data["name"].asString()), - m_output(workspace_data["monitor"].asString()), // TODO:allow using monitor desc - m_windows(workspace_data["windows"].asInt()), - m_isActive(true), - m_isPersistentRule(workspace_data["persistent-rule"].asBool()), - m_isPersistentConfig(workspace_data["persistent-config"].asBool()) { - if (m_name.starts_with("name:")) { - m_name = m_name.substr(5); - } else if (m_name.starts_with("special")) { - m_name = m_id == -99 ? m_name : m_name.substr(8); - m_isSpecial = true; - } - - m_button.add_events(Gdk::BUTTON_PRESS_MASK); - m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked), - false); - - m_button.set_relief(Gtk::RELIEF_NONE); - m_content.set_center_widget(m_label); - m_button.add(m_content); - - initializeWindowMap(clients_data); -} - -void addOrRemoveClass(const Glib::RefPtr &context, bool condition, - const std::string &class_name) { - if (condition) { - context->add_class(class_name); - } else { - context->remove_class(class_name); - } -} - -void Workspace::update(const std::string &format, const std::string &icon) { - // clang-format off - if (this->m_workspaceManager.activeOnly() && \ - !this->isActive() && \ - !this->isPersistent() && \ - !this->isVisible() && \ - !this->isSpecial()) { - // clang-format on - // if activeOnly is true, hide if not active, persistent, visible or special - m_button.hide(); - return; - } - m_button.show(); - - auto styleContext = m_button.get_style_context(); - addOrRemoveClass(styleContext, isActive(), "active"); - addOrRemoveClass(styleContext, isSpecial(), "special"); - addOrRemoveClass(styleContext, isEmpty(), "empty"); - addOrRemoveClass(styleContext, isPersistent(), "persistent"); - addOrRemoveClass(styleContext, isUrgent(), "urgent"); - addOrRemoveClass(styleContext, isVisible(), "visible"); - addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); - - std::string windows; - auto windowSeparator = m_workspaceManager.getWindowSeparator(); - - bool isNotFirst = false; - - for (auto &[_pid, window_repr] : m_windowMap) { - if (isNotFirst) { - windows.append(windowSeparator); - } - isNotFirst = true; - windows.append(window_repr); - } - - m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), - fmt::arg("name", name()), fmt::arg("icon", icon), - fmt::arg("windows", windows))); -} - void Workspaces::sortWorkspaces() { std::sort(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr &a, std::unique_ptr &b) { @@ -998,91 +884,6 @@ void Workspaces::sortWorkspaces() { } } -std::string &Workspace::selectIcon(std::map &icons_map) { - spdlog::trace("Selecting icon for workspace {}", name()); - if (isUrgent()) { - auto urgentIconIt = icons_map.find("urgent"); - if (urgentIconIt != icons_map.end()) { - return urgentIconIt->second; - } - } - - if (isActive()) { - auto activeIconIt = icons_map.find("active"); - if (activeIconIt != icons_map.end()) { - return activeIconIt->second; - } - } - - if (isSpecial()) { - auto specialIconIt = icons_map.find("special"); - if (specialIconIt != icons_map.end()) { - return specialIconIt->second; - } - } - - auto namedIconIt = icons_map.find(name()); - if (namedIconIt != icons_map.end()) { - return namedIconIt->second; - } - - if (isVisible()) { - auto visibleIconIt = icons_map.find("visible"); - if (visibleIconIt != icons_map.end()) { - return visibleIconIt->second; - } - } - - if (isEmpty()) { - auto emptyIconIt = icons_map.find("empty"); - if (emptyIconIt != icons_map.end()) { - return emptyIconIt->second; - } - } - - if (isPersistent()) { - auto persistentIconIt = icons_map.find("persistent"); - if (persistentIconIt != icons_map.end()) { - return persistentIconIt->second; - } - } - - auto defaultIconIt = icons_map.find("default"); - if (defaultIconIt != icons_map.end()) { - return defaultIconIt->second; - } - - return m_name; -} - -bool Workspace::handleClicked(GdkEventButton *bt) const { - if (bt->type == GDK_BUTTON_PRESS) { - try { - if (id() > 0) { // normal - if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); - } else { - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); - } - } else if (!isSpecial()) { // named (this includes persistent) - if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); - } else { - gIPC->getSocket1Reply("dispatch workspace name:" + name()); - } - } else if (id() != -99) { // named special - gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); - } else { // special - gIPC->getSocket1Reply("dispatch togglespecialworkspace"); - } - return true; - } catch (const std::exception &e) { - spdlog::error("Failed to dispatch workspace: {}", e.what()); - } - } - return false; -} - void Workspaces::setUrgentWorkspace(std::string const &windowaddress) { const Json::Value clientsJson = gIPC->getSocket1JsonReply("clients"); int workspaceId = -1; @@ -1113,99 +914,4 @@ std::string Workspaces::getRewrite(std::string window_class, std::string window_ return fmt::format(fmt::runtime(rewriteRule), fmt::arg("class", window_class), fmt::arg("title", window_title)); } - -WindowCreationPayload::WindowCreationPayload(std::string workspace_name, - WindowAddress window_address, std::string window_repr) - : m_window(std::move(window_repr)), - m_windowAddress(std::move(window_address)), - m_workspaceName(std::move(workspace_name)) { - clearAddr(); - clearWorkspaceName(); -} - -WindowCreationPayload::WindowCreationPayload(std::string workspace_name, - WindowAddress window_address, std::string window_class, - std::string window_title) - : m_window(std::make_pair(std::move(window_class), std::move(window_title))), - m_windowAddress(std::move(window_address)), - m_workspaceName(std::move(workspace_name)) { - clearAddr(); - clearWorkspaceName(); -} - -WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data) - : m_window(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), - m_windowAddress(client_data["address"].asString()), - m_workspaceName(client_data["workspace"]["name"].asString()) { - clearAddr(); - clearWorkspaceName(); -} - -std::string WindowCreationPayload::repr(Workspaces &workspace_manager) { - if (std::holds_alternative(m_window)) { - return std::get(m_window); - } - if (std::holds_alternative(m_window)) { - auto [window_class, window_title] = std::get(m_window); - return workspace_manager.getRewrite(window_class, window_title); - } - // Unreachable - spdlog::error("WorkspaceWindow::repr: Unreachable"); - throw std::runtime_error("WorkspaceWindow::repr: Unreachable"); -} - -bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) { - if (std::holds_alternative(m_window)) { - return std::get(m_window).empty(); - } - if (std::holds_alternative(m_window)) { - auto [window_class, window_title] = std::get(m_window); - return (window_class.empty() && - (!workspace_manager.windowRewriteConfigUsesTitle() || window_title.empty())); - } - // Unreachable - spdlog::error("WorkspaceWindow::isEmpty: Unreachable"); - throw std::runtime_error("WorkspaceWindow::isEmpty: Unreachable"); -} - -int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; } - -void WindowCreationPayload::clearAddr() { - // substr(2, ...) is necessary because Hyprland's JSON follows this format: - // 0x{ADDR} - // While Hyprland's IPC follows this format: - // {ADDR} - static const std::string ADDR_PREFIX = "0x"; - static const int ADDR_PREFIX_LEN = ADDR_PREFIX.length(); - - if (m_windowAddress.starts_with(ADDR_PREFIX)) { - m_windowAddress = - m_windowAddress.substr(ADDR_PREFIX_LEN, m_windowAddress.length() - ADDR_PREFIX_LEN); - } -} - -void WindowCreationPayload::clearWorkspaceName() { - // The workspace name may optionally feature "special:" at the beginning. - // If so, we need to remove it because the workspace is saved WITHOUT the - // special qualifier. The reasoning is that not all of Hyprland's IPC events - // use this qualifier, so it's better to be consistent about our uses. - - static const std::string SPECIAL_QUALIFIER_PREFIX = "special:"; - static const int SPECIAL_QUALIFIER_PREFIX_LEN = SPECIAL_QUALIFIER_PREFIX.length(); - - if (m_workspaceName.starts_with(SPECIAL_QUALIFIER_PREFIX)) { - m_workspaceName = m_workspaceName.substr( - SPECIAL_QUALIFIER_PREFIX_LEN, m_workspaceName.length() - SPECIAL_QUALIFIER_PREFIX_LEN); - } - - std::size_t spaceFound = m_workspaceName.find(' '); - if (spaceFound != std::string::npos) { - m_workspaceName.erase(m_workspaceName.begin() + spaceFound, m_workspaceName.end()); - } -} - -void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) { - m_workspaceName = new_workspace_name; -} - } // namespace waybar::modules::hyprland From 82ae474002d673f39d5ed3410fae334b0177bd74 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:29:03 -0500 Subject: [PATCH 551/842] hyprland/workspace: sort methods --- src/modules/hyprland/workspace.cpp | 191 +++++++++++++++-------------- 1 file changed, 96 insertions(+), 95 deletions(-) diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index 1e3c05f3..694d3b0d 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -12,42 +12,6 @@ namespace waybar::modules::hyprland { -void Workspace::initializeWindowMap(const Json::Value &clients_data) { - m_windowMap.clear(); - for (auto client : clients_data) { - if (client["workspace"]["id"].asInt() == id()) { - insertWindow({client}); - } - } -} - -void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { - if (!create_window_paylod.isEmpty(m_workspaceManager)) { - m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager); - } -}; - -std::string Workspace::removeWindow(WindowAddress const &addr) { - std::string windowRepr = m_windowMap[addr]; - m_windowMap.erase(addr); - return windowRepr; -} - -bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) { - if (create_window_paylod.getWorkspaceName() == name()) { - insertWindow(create_window_paylod); - return true; - } - return false; -} - -std::optional Workspace::closeWindow(WindowAddress const &addr) { - if (m_windowMap.contains(addr)) { - return removeWindow(addr); - } - return std::nullopt; -} - Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager, const Json::Value &clients_data) : m_workspaceManager(workspace_manager), @@ -85,45 +49,68 @@ void addOrRemoveClass(const Glib::RefPtr &context, bool condi } } -void Workspace::update(const std::string &format, const std::string &icon) { - // clang-format off - if (this->m_workspaceManager.activeOnly() && \ - !this->isActive() && \ - !this->isPersistent() && \ - !this->isVisible() && \ - !this->isSpecial()) { - // clang-format on - // if activeOnly is true, hide if not active, persistent, visible or special - m_button.hide(); - return; +std::optional Workspace::closeWindow(WindowAddress const &addr) { + if (m_windowMap.contains(addr)) { + return removeWindow(addr); } - m_button.show(); + return std::nullopt; +} - auto styleContext = m_button.get_style_context(); - addOrRemoveClass(styleContext, isActive(), "active"); - addOrRemoveClass(styleContext, isSpecial(), "special"); - addOrRemoveClass(styleContext, isEmpty(), "empty"); - addOrRemoveClass(styleContext, isPersistent(), "persistent"); - addOrRemoveClass(styleContext, isUrgent(), "urgent"); - addOrRemoveClass(styleContext, isVisible(), "visible"); - addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); - - std::string windows; - auto windowSeparator = m_workspaceManager.getWindowSeparator(); - - bool isNotFirst = false; - - for (auto &[_pid, window_repr] : m_windowMap) { - if (isNotFirst) { - windows.append(windowSeparator); +bool Workspace::handleClicked(GdkEventButton *bt) const { + if (bt->type == GDK_BUTTON_PRESS) { + try { + if (id() > 0) { // normal + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); + } else { + gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + } + } else if (!isSpecial()) { // named (this includes persistent) + if (m_workspaceManager.moveToMonitor()) { + gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); + } else { + gIPC->getSocket1Reply("dispatch workspace name:" + name()); + } + } else if (id() != -99) { // named special + gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); + } else { // special + gIPC->getSocket1Reply("dispatch togglespecialworkspace"); + } + return true; + } catch (const std::exception &e) { + spdlog::error("Failed to dispatch workspace: {}", e.what()); } - isNotFirst = true; - windows.append(window_repr); } + return false; +} - m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), - fmt::arg("name", name()), fmt::arg("icon", icon), - fmt::arg("windows", windows))); +void Workspace::initializeWindowMap(const Json::Value &clients_data) { + m_windowMap.clear(); + for (auto client : clients_data) { + if (client["workspace"]["id"].asInt() == id()) { + insertWindow({client}); + } + } +} + +void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { + if (!create_window_paylod.isEmpty(m_workspaceManager)) { + m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager); + } +}; + +bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) { + if (create_window_paylod.getWorkspaceName() == name()) { + insertWindow(create_window_paylod); + return true; + } + return false; +} + +std::string Workspace::removeWindow(WindowAddress const &addr) { + std::string windowRepr = m_windowMap[addr]; + m_windowMap.erase(addr); + return windowRepr; } std::string &Workspace::selectIcon(std::map &icons_map) { @@ -183,31 +170,45 @@ std::string &Workspace::selectIcon(std::map &icons_map return m_name; } -bool Workspace::handleClicked(GdkEventButton *bt) const { - if (bt->type == GDK_BUTTON_PRESS) { - try { - if (id() > 0) { // normal - if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); - } else { - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); - } - } else if (!isSpecial()) { // named (this includes persistent) - if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); - } else { - gIPC->getSocket1Reply("dispatch workspace name:" + name()); - } - } else if (id() != -99) { // named special - gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); - } else { // special - gIPC->getSocket1Reply("dispatch togglespecialworkspace"); - } - return true; - } catch (const std::exception &e) { - spdlog::error("Failed to dispatch workspace: {}", e.what()); - } +void Workspace::update(const std::string &format, const std::string &icon) { + // clang-format off + if (this->m_workspaceManager.activeOnly() && \ + !this->isActive() && \ + !this->isPersistent() && \ + !this->isVisible() && \ + !this->isSpecial()) { + // clang-format on + // if activeOnly is true, hide if not active, persistent, visible or special + m_button.hide(); + return; } - return false; + m_button.show(); + + auto styleContext = m_button.get_style_context(); + addOrRemoveClass(styleContext, isActive(), "active"); + addOrRemoveClass(styleContext, isSpecial(), "special"); + addOrRemoveClass(styleContext, isEmpty(), "empty"); + addOrRemoveClass(styleContext, isPersistent(), "persistent"); + addOrRemoveClass(styleContext, isUrgent(), "urgent"); + addOrRemoveClass(styleContext, isVisible(), "visible"); + addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); + + std::string windows; + auto windowSeparator = m_workspaceManager.getWindowSeparator(); + + bool isNotFirst = false; + + for (auto &[_pid, window_repr] : m_windowMap) { + if (isNotFirst) { + windows.append(windowSeparator); + } + isNotFirst = true; + windows.append(window_repr); + } + + m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), + fmt::arg("name", name()), fmt::arg("icon", icon), + fmt::arg("windows", windows))); } + } // namespace waybar::modules::hyprland From 9ba9d57c8cd140035741633bacf7f3a8568112f9 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:30:31 -0500 Subject: [PATCH 552/842] hyprland/windowcreationpayload: sort methods --- .../hyprland/windowcreationpayload.cpp | 75 ++++++++++--------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/src/modules/hyprland/windowcreationpayload.cpp b/src/modules/hyprland/windowcreationpayload.cpp index 261edcbc..22febc59 100644 --- a/src/modules/hyprland/windowcreationpayload.cpp +++ b/src/modules/hyprland/windowcreationpayload.cpp @@ -13,6 +13,15 @@ #include "util/regex_collection.hpp" namespace waybar::modules::hyprland { + +WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data) + : m_window(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), + m_windowAddress(client_data["address"].asString()), + m_workspaceName(client_data["workspace"]["name"].asString()) { + clearAddr(); + clearWorkspaceName(); +} + WindowCreationPayload::WindowCreationPayload(std::string workspace_name, WindowAddress window_address, std::string window_repr) : m_window(std::move(window_repr)), @@ -32,43 +41,6 @@ WindowCreationPayload::WindowCreationPayload(std::string workspace_name, clearWorkspaceName(); } -WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data) - : m_window(std::make_pair(client_data["class"].asString(), client_data["title"].asString())), - m_windowAddress(client_data["address"].asString()), - m_workspaceName(client_data["workspace"]["name"].asString()) { - clearAddr(); - clearWorkspaceName(); -} - -std::string WindowCreationPayload::repr(Workspaces &workspace_manager) { - if (std::holds_alternative(m_window)) { - return std::get(m_window); - } - if (std::holds_alternative(m_window)) { - auto [window_class, window_title] = std::get(m_window); - return workspace_manager.getRewrite(window_class, window_title); - } - // Unreachable - spdlog::error("WorkspaceWindow::repr: Unreachable"); - throw std::runtime_error("WorkspaceWindow::repr: Unreachable"); -} - -bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) { - if (std::holds_alternative(m_window)) { - return std::get(m_window).empty(); - } - if (std::holds_alternative(m_window)) { - auto [window_class, window_title] = std::get(m_window); - return (window_class.empty() && - (!workspace_manager.windowRewriteConfigUsesTitle() || window_title.empty())); - } - // Unreachable - spdlog::error("WorkspaceWindow::isEmpty: Unreachable"); - throw std::runtime_error("WorkspaceWindow::isEmpty: Unreachable"); -} - -int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; } - void WindowCreationPayload::clearAddr() { // substr(2, ...) is necessary because Hyprland's JSON follows this format: // 0x{ADDR} @@ -103,8 +75,37 @@ void WindowCreationPayload::clearWorkspaceName() { } } +bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) { + if (std::holds_alternative(m_window)) { + return std::get(m_window).empty(); + } + if (std::holds_alternative(m_window)) { + auto [window_class, window_title] = std::get(m_window); + return (window_class.empty() && + (!workspace_manager.windowRewriteConfigUsesTitle() || window_title.empty())); + } + // Unreachable + spdlog::error("WorkspaceWindow::isEmpty: Unreachable"); + throw std::runtime_error("WorkspaceWindow::isEmpty: Unreachable"); +} + +int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; } + void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) { m_workspaceName = new_workspace_name; } +std::string WindowCreationPayload::repr(Workspaces &workspace_manager) { + if (std::holds_alternative(m_window)) { + return std::get(m_window); + } + if (std::holds_alternative(m_window)) { + auto [window_class, window_title] = std::get(m_window); + return workspace_manager.getRewrite(window_class, window_title); + } + // Unreachable + spdlog::error("WorkspaceWindow::repr: Unreachable"); + throw std::runtime_error("WorkspaceWindow::repr: Unreachable"); +} + } // namespace waybar::modules::hyprland From f5bb086460fe8bd1995c05f9fc1b530d236a1bcb Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 24 May 2024 14:41:52 -0500 Subject: [PATCH 553/842] hyprland/workspaces: sort methods --- include/modules/hyprland/workspaces.hpp | 2 + src/modules/hyprland/workspaces.cpp | 810 ++++++++++++------------ 2 files changed, 408 insertions(+), 404 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 23e3e27f..e99bf40c 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -54,6 +54,8 @@ class Workspaces : public AModule, public EventHandler { void sortWorkspaces(); void createWorkspace(Json::Value const& workspaceData, Json::Value const& clientsData = Json::Value::nullRef); + + Json::Value createMonitorWorkspaceData(std::string const& name, std::string const& monitor); void removeWorkspace(std::string const& name); void setUrgentWorkspace(std::string const& windowaddress); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 2074ad54..eca30134 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -13,26 +13,6 @@ namespace waybar::modules::hyprland { -int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) { - // Rules that match against title are prioritized - // Rules that don't specify if they're matching against either title or class are deprioritized - bool const hasTitle = window_rule.find("title") != std::string::npos; - bool const hasClass = window_rule.find("class") != std::string::npos; - - if (hasTitle && hasClass) { - m_anyWindowRewriteRuleUsesTitle = true; - return 3; - } - if (hasTitle) { - m_anyWindowRewriteRuleUsesTitle = true; - return 2; - } - if (hasClass) { - return 1; - } - return 0; -} - Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, false), m_bar(bar), m_box(bar.orientation, 0) { modulesReady = true; @@ -54,132 +34,87 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value registerIpc(); } -auto Workspaces::parseConfig(const Json::Value &config) -> void { - const auto &configFormat = config["format"]; - m_format = configFormat.isString() ? configFormat.asString() : "{name}"; - m_withIcon = m_format.find("{icon}") != std::string::npos; - - if (m_withIcon && m_iconsMap.empty()) { - populateIconsMap(config["format-icons"]); - } - - populateBoolConfig(config, "all-outputs", m_allOutputs); - populateBoolConfig(config, "show-special", m_showSpecial); - populateBoolConfig(config, "active-only", m_activeOnly); - populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); - - populateSortByConfig(config); - populateIgnoreWorkspacesConfig(config); - populatePersistentWorkspacesConfig(config); - populateFormatWindowSeparatorConfig(config); - populateWindowRewriteConfig(config); +Workspaces::~Workspaces() { + gIPC->unregisterForIPC(this); + // wait for possible event handler to finish + std::lock_guard lg(m_mutex); } -auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { - for (const auto &name : formatIcons.getMemberNames()) { - m_iconsMap.emplace(name, formatIcons[name].asString()); - } - m_iconsMap.emplace("", ""); +void Workspaces::init() { + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + + initializeWorkspaces(); + dp.emit(); } -auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) - -> void { - auto configValue = config[key]; - if (configValue.isBool()) { - member = configValue.asBool(); +Json::Value Workspaces::createMonitorWorkspaceData(std::string const &name, + std::string const &monitor) { + spdlog::trace("Creating persistent workspace: {} on monitor {}", name, monitor); + Json::Value workspaceData; + try { + // numbered persistent workspaces get the name as ID + workspaceData["id"] = name == "special" ? -99 : std::stoi(name); + } catch (const std::exception &e) { + // named persistent workspaces start with ID=0 + workspaceData["id"] = 0; } + workspaceData["name"] = name; + workspaceData["monitor"] = monitor; + workspaceData["windows"] = 0; + return workspaceData; } -auto Workspaces::populateSortByConfig(const Json::Value &config) -> void { - auto configSortBy = config["sort-by"]; - if (configSortBy.isString()) { - auto sortByStr = configSortBy.asString(); - try { - m_sortBy = m_enumParser.parseStringToEnum(sortByStr, m_sortMap); - } catch (const std::invalid_argument &e) { - m_sortBy = SortMethod::DEFAULT; - spdlog::warn( - "Invalid string representation for sort-by. Falling back to default sort method."); +void Workspaces::createWorkspace(Json::Value const &workspace_data, + Json::Value const &clients_data) { + auto workspaceName = workspace_data["name"].asString(); + spdlog::debug("Creating workspace {}", workspaceName); + + // avoid recreating existing workspaces + auto workspace = std::find_if( + m_workspaces.begin(), m_workspaces.end(), + [workspaceName](std::unique_ptr const &w) { + return (workspaceName.starts_with("special:") && workspaceName.substr(8) == w->name()) || + workspaceName == w->name(); + }); + + if (workspace != m_workspaces.end()) { + // don't recreate workspace, but update persistency if necessary + 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()); } - } -} -auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> void { - auto ignoreWorkspaces = config["ignore-workspaces"]; - if (ignoreWorkspaces.isArray()) { - for (const auto &workspaceRegex : ignoreWorkspaces) { - if (workspaceRegex.isString()) { - std::string ruleString = workspaceRegex.asString(); - try { - const std::regex rule{ruleString, std::regex_constants::icase}; - m_ignoreWorkspaces.emplace_back(rule); - } catch (const std::regex_error &e) { - spdlog::error("Invalid rule {}: {}", ruleString, e.what()); - } - } else { - spdlog::error("Not a string: '{}'", workspaceRegex); - } + k = "persistent-config"; + if (std::find(keys.begin(), keys.end(), k) != keys.end()) { + spdlog::debug("Set config persistency of workspace {} to: {}", workspaceName, + workspace_data[k].asBool() ? "true" : "false"); + (*workspace)->setPersistentConfig(workspace_data[k].asBool()); } - } -} -auto Workspaces::populatePersistentWorkspacesConfig(const Json::Value &config) -> void { - if (config.isMember("persistent-workspaces") || config.isMember("persistent_workspaces")) { - spdlog::warn( - "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); - m_persistentWorkspaceConfig = - config.get("persistent-workspaces", config.get("persistent_workspaces", Json::Value())); - } -} - -auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void { - auto formatWindowSeparator = config["format-window-separator"]; - m_formatWindowSeparator = - formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " "; -} - -auto Workspaces::populateWindowRewriteConfig(const Json::Value &config) -> void { - const auto &windowRewrite = config["window-rewrite"]; - if (!windowRewrite.isObject()) { - spdlog::debug("window-rewrite is not defined or is not an object, using default rules."); return; } - const auto &windowRewriteDefaultConfig = config["window-rewrite-default"]; - std::string windowRewriteDefault = - windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; - - m_windowRewriteRules = util::RegexCollection( - windowRewrite, windowRewriteDefault, - [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); + // create new workspace + m_workspaces.emplace_back(std::make_unique(workspace_data, *this, clients_data)); + Gtk::Button &newWorkspaceButton = m_workspaces.back()->button(); + m_box.pack_start(newWorkspaceButton, false, false); + sortWorkspaces(); + newWorkspaceButton.show_all(); } -void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) { - if (!create_window_payload.isEmpty(*this)) { - m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this); +void Workspaces::createWorkspacesToCreate() { + for (const auto &[workspaceData, clientsData] : m_workspacesToCreate) { + createWorkspace(workspaceData, clientsData); } -} - -auto Workspaces::registerIpc() -> void { - gIPC->registerForIPC("workspace", this); - gIPC->registerForIPC("activespecial", this); - gIPC->registerForIPC("createworkspace", this); - gIPC->registerForIPC("destroyworkspace", this); - gIPC->registerForIPC("focusedmon", this); - gIPC->registerForIPC("moveworkspace", this); - gIPC->registerForIPC("renameworkspace", this); - gIPC->registerForIPC("openwindow", this); - gIPC->registerForIPC("closewindow", this); - gIPC->registerForIPC("movewindow", this); - gIPC->registerForIPC("urgent", this); - gIPC->registerForIPC("configreloaded", this); - - if (windowRewriteConfigUsesTitle()) { - spdlog::info( - "Registering for Hyprland's 'windowtitle' events because a user-defined window " - "rewrite rule uses the 'title' field."); - gIPC->registerForIPC("windowtitle", this); + if (!m_workspacesToCreate.empty()) { + updateWindowCount(); + sortWorkspaces(); } + m_workspacesToCreate.clear(); } /** @@ -207,22 +142,25 @@ void Workspaces::doUpdate() { } } -void Workspaces::removeWorkspacesToRemove() { - for (const auto &workspaceName : m_workspacesToRemove) { - removeWorkspace(workspaceName); +void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) { + spdlog::trace("Extending orphans with workspace {}", workspaceId); + for (const auto &client : clientsJson) { + if (client["workspace"]["id"].asInt() == workspaceId) { + registerOrphanWindow({client}); + } } - m_workspacesToRemove.clear(); } -void Workspaces::createWorkspacesToCreate() { - for (const auto &[workspaceData, clientsData] : m_workspacesToCreate) { - createWorkspace(workspaceData, clientsData); +std::string Workspaces::getRewrite(std::string window_class, std::string window_title) { + std::string windowReprKey; + if (windowRewriteConfigUsesTitle()) { + windowReprKey = fmt::format("class<{}> title<{}>", window_class, window_title); + } else { + windowReprKey = fmt::format("class<{}>", window_class); } - if (!m_workspacesToCreate.empty()) { - updateWindowCount(); - sortWorkspaces(); - } - m_workspacesToCreate.clear(); + auto const rewriteRule = m_windowRewriteRules.get(windowReprKey); + return fmt::format(fmt::runtime(rewriteRule), fmt::arg("class", window_class), + fmt::arg("title", window_title)); } std::vector Workspaces::getVisibleWorkspaces() { @@ -242,62 +180,36 @@ std::vector Workspaces::getVisibleWorkspaces() { return visibleWorkspaces; } -void Workspaces::updateWorkspaceStates(const std::vector &visibleWorkspaces) { - auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces"); +void Workspaces::initializeWorkspaces() { + spdlog::debug("Initializing workspaces"); + + // if the workspace rules changed since last initialization, make sure we reset everything: for (auto &workspace : m_workspaces) { - workspace->setActive(workspace->name() == m_activeWorkspaceName || - workspace->name() == m_activeSpecialWorkspaceName); - if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { - workspace->setUrgent(false); - } - workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), - workspace->name()) != visibleWorkspaces.end()); - std::string &workspaceIcon = m_iconsMap[""]; - if (m_withIcon) { - workspaceIcon = workspace->selectIcon(m_iconsMap); - } - auto updatedWorkspace = std::find_if( - updatedWorkspaces.begin(), updatedWorkspaces.end(), [&workspace](const auto &w) { - auto wNameRaw = w["name"].asString(); - auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; - return wName == workspace->name(); - }); - if (updatedWorkspace != updatedWorkspaces.end()) { - workspace->setOutput((*updatedWorkspace)["monitor"].asString()); - } - workspace->update(m_format, workspaceIcon); + m_workspacesToRemove.push_back(workspace->name()); } -} -bool Workspaces::updateWindowsToCreate() { - bool anyWindowCreated = false; - std::vector notCreated; - for (auto &windowPayload : m_windowsToCreate) { - bool created = false; - for (auto &workspace : m_workspaces) { - if (workspace->onWindowOpened(windowPayload)) { - created = true; - anyWindowCreated = true; - break; - } - } - if (!created) { - static auto const WINDOW_CREATION_TIMEOUT = 2; - if (windowPayload.incrementTimeSpentUncreated() < WINDOW_CREATION_TIMEOUT) { - notCreated.push_back(windowPayload); - } else { - registerOrphanWindow(windowPayload); - } + // get all current workspaces + auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + auto const clientsJson = gIPC->getSocket1JsonReply("clients"); + + for (Json::Value workspaceJson : workspacesJson) { + std::string workspaceName = workspaceJson["name"].asString(); + if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && + (!workspaceName.starts_with("special") || showSpecial()) && + !isWorkspaceIgnored(workspaceName)) { + m_workspacesToCreate.emplace_back(workspaceJson, clientsJson); + } else { + extendOrphans(workspaceJson["id"].asInt(), clientsJson); } } - m_windowsToCreate.clear(); - m_windowsToCreate = notCreated; - return anyWindowCreated; -} -auto Workspaces::update() -> void { - doUpdate(); - AModule::update(); + spdlog::debug("Initializing persistent workspaces"); + if (m_persistentWorkspaceConfig.isObject()) { + // a persistent workspace config is defined, so use that instead of workspace rules + loadPersistentWorkspacesFromConfig(clientsJson); + } + // load Hyprland's workspace rules + loadPersistentWorkspacesFromWorkspaceRules(clientsJson); } bool isDoubleSpecial(std::string const &workspace_name) { @@ -319,6 +231,90 @@ bool Workspaces::isWorkspaceIgnored(std::string const &name) { return false; } +void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJson) { + spdlog::info("Loading persistent workspaces from Waybar config"); + const std::vector keys = m_persistentWorkspaceConfig.getMemberNames(); + std::vector persistentWorkspacesToCreate; + + const std::string currentMonitor = m_bar.output->name; + const bool monitorInConfig = std::find(keys.begin(), keys.end(), currentMonitor) != keys.end(); + for (const std::string &key : keys) { + // only add if either: + // 1. key is the current monitor name + // 2. key is "*" and this monitor is not already defined in the config + bool canCreate = key == currentMonitor || (key == "*" && !monitorInConfig); + const Json::Value &value = m_persistentWorkspaceConfig[key]; + spdlog::trace("Parsing persistent workspace config: {} => {}", key, value.toStyledString()); + + if (value.isInt()) { + // value is a number => create that many workspaces for this monitor + if (canCreate) { + int amount = value.asInt(); + spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, currentMonitor); + for (int i = 0; i < amount; i++) { + persistentWorkspacesToCreate.emplace_back(std::to_string(m_monitorId * amount + i + 1)); + } + } + } else if (value.isArray() && !value.empty()) { + // value is an array => create defined workspaces for this monitor + if (canCreate) { + for (const Json::Value &workspace : value) { + if (workspace.isInt()) { + spdlog::debug("Creating workspace {} on monitor {}", workspace, currentMonitor); + persistentWorkspacesToCreate.emplace_back(std::to_string(workspace.asInt())); + } + } + } else { + // key is the workspace and value is array of monitors to create on + for (const Json::Value &monitor : value) { + if (monitor.isString() && monitor.asString() == currentMonitor) { + persistentWorkspacesToCreate.emplace_back(currentMonitor); + break; + } + } + } + } else { + // this workspace should be displayed on all monitors + persistentWorkspacesToCreate.emplace_back(key); + } + } + + for (auto const &workspace : persistentWorkspacesToCreate) { + auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name); + workspaceData["persistent-config"] = true; + m_workspacesToCreate.emplace_back(workspaceData, clientsJson); + } +} + +void Workspaces::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 = createMonitorWorkspaceData(workspace, m_bar.output->name); + workspaceData["persistent-rule"] = true; + m_workspacesToCreate.emplace_back(workspaceData, clientsJson); + } else { + m_workspacesToRemove.emplace_back(workspace); + } + } +} + void Workspaces::onEvent(const std::string &ev) { std::lock_guard lock(m_mutex); std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>')); @@ -569,66 +565,139 @@ void Workspaces::onConfigReloaded() { init(); } -void Workspaces::updateWindowCount() { - const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - for (auto &workspace : m_workspaces) { - auto workspaceJson = - std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) { - return x["name"].asString() == workspace->name() || - (workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name()); - }); - uint32_t count = 0; - if (workspaceJson != workspacesJson.end()) { - try { - count = (*workspaceJson)["windows"].asUInt(); - } catch (const std::exception &e) { - spdlog::error("Failed to update window count: {}", e.what()); - } - } - workspace->setWindows(count); +auto Workspaces::parseConfig(const Json::Value &config) -> void { + const auto &configFormat = config["format"]; + m_format = configFormat.isString() ? configFormat.asString() : "{name}"; + m_withIcon = m_format.find("{icon}") != std::string::npos; + + if (m_withIcon && m_iconsMap.empty()) { + populateIconsMap(config["format-icons"]); + } + + populateBoolConfig(config, "all-outputs", m_allOutputs); + populateBoolConfig(config, "show-special", m_showSpecial); + populateBoolConfig(config, "active-only", m_activeOnly); + populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); + + populateSortByConfig(config); + populateIgnoreWorkspacesConfig(config); + populatePersistentWorkspacesConfig(config); + populateFormatWindowSeparatorConfig(config); + populateWindowRewriteConfig(config); +} + +auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { + for (const auto &name : formatIcons.getMemberNames()) { + m_iconsMap.emplace(name, formatIcons[name].asString()); + } + m_iconsMap.emplace("", ""); +} + +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) + -> void { + auto configValue = config[key]; + if (configValue.isBool()) { + member = configValue.asBool(); } } -void Workspaces::createWorkspace(Json::Value const &workspace_data, - Json::Value const &clients_data) { - auto workspaceName = workspace_data["name"].asString(); - spdlog::debug("Creating workspace {}", workspaceName); - - // avoid recreating existing workspaces - auto workspace = std::find_if( - m_workspaces.begin(), m_workspaces.end(), - [workspaceName](std::unique_ptr const &w) { - return (workspaceName.starts_with("special:") && workspaceName.substr(8) == w->name()) || - workspaceName == w->name(); - }); - - if (workspace != m_workspaces.end()) { - // don't recreate workspace, but update persistency if necessary - 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()); +auto Workspaces::populateSortByConfig(const Json::Value &config) -> void { + auto configSortBy = config["sort-by"]; + if (configSortBy.isString()) { + auto sortByStr = configSortBy.asString(); + try { + m_sortBy = m_enumParser.parseStringToEnum(sortByStr, m_sortMap); + } catch (const std::invalid_argument &e) { + m_sortBy = SortMethod::DEFAULT; + spdlog::warn( + "Invalid string representation for sort-by. Falling back to default sort method."); } + } +} - k = "persistent-config"; - if (std::find(keys.begin(), keys.end(), k) != keys.end()) { - spdlog::debug("Set config persistency of workspace {} to: {}", workspaceName, - workspace_data[k].asBool() ? "true" : "false"); - (*workspace)->setPersistentConfig(workspace_data[k].asBool()); +auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> void { + auto ignoreWorkspaces = config["ignore-workspaces"]; + if (ignoreWorkspaces.isArray()) { + for (const auto &workspaceRegex : ignoreWorkspaces) { + if (workspaceRegex.isString()) { + std::string ruleString = workspaceRegex.asString(); + try { + const std::regex rule{ruleString, std::regex_constants::icase}; + m_ignoreWorkspaces.emplace_back(rule); + } catch (const std::regex_error &e) { + spdlog::error("Invalid rule {}: {}", ruleString, e.what()); + } + } else { + spdlog::error("Not a string: '{}'", workspaceRegex); + } } + } +} +auto Workspaces::populatePersistentWorkspacesConfig(const Json::Value &config) -> void { + if (config.isMember("persistent-workspaces") || config.isMember("persistent_workspaces")) { + spdlog::warn( + "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); + m_persistentWorkspaceConfig = + config.get("persistent-workspaces", config.get("persistent_workspaces", Json::Value())); + } +} + +auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void { + auto formatWindowSeparator = config["format-window-separator"]; + m_formatWindowSeparator = + formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " "; +} + +auto Workspaces::populateWindowRewriteConfig(const Json::Value &config) -> void { + const auto &windowRewrite = config["window-rewrite"]; + if (!windowRewrite.isObject()) { + spdlog::debug("window-rewrite is not defined or is not an object, using default rules."); return; } - // create new workspace - m_workspaces.emplace_back(std::make_unique(workspace_data, *this, clients_data)); - Gtk::Button &newWorkspaceButton = m_workspaces.back()->button(); - m_box.pack_start(newWorkspaceButton, false, false); - sortWorkspaces(); - newWorkspaceButton.show_all(); + const auto &windowRewriteDefaultConfig = config["window-rewrite-default"]; + std::string windowRewriteDefault = + windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; + + m_windowRewriteRules = util::RegexCollection( + windowRewrite, windowRewriteDefault, + [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); +} + +void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) { + if (!create_window_payload.isEmpty(*this)) { + m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this); + } +} + +auto Workspaces::registerIpc() -> void { + gIPC->registerForIPC("workspace", this); + gIPC->registerForIPC("activespecial", this); + gIPC->registerForIPC("createworkspace", this); + gIPC->registerForIPC("destroyworkspace", this); + gIPC->registerForIPC("focusedmon", this); + gIPC->registerForIPC("moveworkspace", this); + gIPC->registerForIPC("renameworkspace", this); + gIPC->registerForIPC("openwindow", this); + gIPC->registerForIPC("closewindow", this); + gIPC->registerForIPC("movewindow", this); + gIPC->registerForIPC("urgent", this); + gIPC->registerForIPC("configreloaded", this); + + if (windowRewriteConfigUsesTitle()) { + spdlog::info( + "Registering for Hyprland's 'windowtitle' events because a user-defined window " + "rewrite rule uses the 'title' field."); + gIPC->registerForIPC("windowtitle", this); + } +} + +void Workspaces::removeWorkspacesToRemove() { + for (const auto &workspaceName : m_workspacesToRemove) { + removeWorkspace(workspaceName); + } + m_workspacesToRemove.clear(); } void Workspaces::removeWorkspace(std::string const &name) { @@ -652,106 +721,6 @@ void Workspaces::removeWorkspace(std::string const &name) { m_workspaces.erase(workspace); } -Json::Value createMonitorWorkspaceData(std::string const &name, std::string const &monitor) { - spdlog::trace("Creating persistent workspace: {} on monitor {}", name, monitor); - Json::Value workspaceData; - try { - // numbered persistent workspaces get the name as ID - workspaceData["id"] = name == "special" ? -99 : std::stoi(name); - } catch (const std::exception &e) { - // named persistent workspaces start with ID=0 - workspaceData["id"] = 0; - } - workspaceData["name"] = name; - workspaceData["monitor"] = monitor; - workspaceData["windows"] = 0; - return workspaceData; -} - -void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJson) { - spdlog::info("Loading persistent workspaces from Waybar config"); - const std::vector keys = m_persistentWorkspaceConfig.getMemberNames(); - std::vector persistentWorkspacesToCreate; - - const std::string currentMonitor = m_bar.output->name; - const bool monitorInConfig = std::find(keys.begin(), keys.end(), currentMonitor) != keys.end(); - for (const std::string &key : keys) { - // only add if either: - // 1. key is the current monitor name - // 2. key is "*" and this monitor is not already defined in the config - bool canCreate = key == currentMonitor || (key == "*" && !monitorInConfig); - const Json::Value &value = m_persistentWorkspaceConfig[key]; - spdlog::trace("Parsing persistent workspace config: {} => {}", key, value.toStyledString()); - - if (value.isInt()) { - // value is a number => create that many workspaces for this monitor - if (canCreate) { - int amount = value.asInt(); - spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, currentMonitor); - for (int i = 0; i < amount; i++) { - persistentWorkspacesToCreate.emplace_back(std::to_string(m_monitorId * amount + i + 1)); - } - } - } else if (value.isArray() && !value.empty()) { - // value is an array => create defined workspaces for this monitor - if (canCreate) { - for (const Json::Value &workspace : value) { - if (workspace.isInt()) { - spdlog::debug("Creating workspace {} on monitor {}", workspace, currentMonitor); - persistentWorkspacesToCreate.emplace_back(std::to_string(workspace.asInt())); - } - } - } else { - // key is the workspace and value is array of monitors to create on - for (const Json::Value &monitor : value) { - if (monitor.isString() && monitor.asString() == currentMonitor) { - persistentWorkspacesToCreate.emplace_back(currentMonitor); - break; - } - } - } - } else { - // this workspace should be displayed on all monitors - persistentWorkspacesToCreate.emplace_back(key); - } - } - - for (auto const &workspace : persistentWorkspacesToCreate) { - auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name); - workspaceData["persistent-config"] = true; - m_workspacesToCreate.emplace_back(workspaceData, clientsJson); - } -} - -void Workspaces::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 = createMonitorWorkspaceData(workspace, m_bar.output->name); - workspaceData["persistent-rule"] = true; - m_workspacesToCreate.emplace_back(workspaceData, clientsJson); - } else { - m_workspacesToRemove.emplace_back(workspace); - } - } -} - void Workspaces::setCurrentMonitorId() { // get monitor ID from name (used by persistent workspaces) m_monitorId = 0; @@ -767,60 +736,6 @@ void Workspaces::setCurrentMonitorId() { } } -void Workspaces::initializeWorkspaces() { - spdlog::debug("Initializing workspaces"); - - // if the workspace rules changed since last initialization, make sure we reset everything: - for (auto &workspace : m_workspaces) { - m_workspacesToRemove.push_back(workspace->name()); - } - - // get all current workspaces - auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - auto const clientsJson = gIPC->getSocket1JsonReply("clients"); - - for (Json::Value workspaceJson : workspacesJson) { - std::string workspaceName = workspaceJson["name"].asString(); - if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && - (!workspaceName.starts_with("special") || showSpecial()) && - !isWorkspaceIgnored(workspaceName)) { - m_workspacesToCreate.emplace_back(workspaceJson, clientsJson); - } else { - extendOrphans(workspaceJson["id"].asInt(), clientsJson); - } - } - - spdlog::debug("Initializing persistent workspaces"); - if (m_persistentWorkspaceConfig.isObject()) { - // a persistent workspace config is defined, so use that instead of workspace rules - loadPersistentWorkspacesFromConfig(clientsJson); - } - // load Hyprland's workspace rules - loadPersistentWorkspacesFromWorkspaceRules(clientsJson); -} - -void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) { - spdlog::trace("Extending orphans with workspace {}", workspaceId); - for (const auto &client : clientsJson) { - if (client["workspace"]["id"].asInt() == workspaceId) { - registerOrphanWindow({client}); - } - } -} - -void Workspaces::init() { - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); - - initializeWorkspaces(); - dp.emit(); -} - -Workspaces::~Workspaces() { - gIPC->unregisterForIPC(this); - // wait for possible event handler to finish - std::lock_guard lg(m_mutex); -} - void Workspaces::sortWorkspaces() { std::sort(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr &a, std::unique_ptr &b) { @@ -903,15 +818,102 @@ void Workspaces::setUrgentWorkspace(std::string const &windowaddress) { } } -std::string Workspaces::getRewrite(std::string window_class, std::string window_title) { - std::string windowReprKey; - if (windowRewriteConfigUsesTitle()) { - windowReprKey = fmt::format("class<{}> title<{}>", window_class, window_title); - } else { - windowReprKey = fmt::format("class<{}>", window_class); - } - auto const rewriteRule = m_windowRewriteRules.get(windowReprKey); - return fmt::format(fmt::runtime(rewriteRule), fmt::arg("class", window_class), - fmt::arg("title", window_title)); +auto Workspaces::update() -> void { + doUpdate(); + AModule::update(); } + +void Workspaces::updateWindowCount() { + const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + for (auto &workspace : m_workspaces) { + auto workspaceJson = + std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) { + return x["name"].asString() == workspace->name() || + (workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name()); + }); + uint32_t count = 0; + if (workspaceJson != workspacesJson.end()) { + try { + count = (*workspaceJson)["windows"].asUInt(); + } catch (const std::exception &e) { + spdlog::error("Failed to update window count: {}", e.what()); + } + } + workspace->setWindows(count); + } +} + +bool Workspaces::updateWindowsToCreate() { + bool anyWindowCreated = false; + std::vector notCreated; + for (auto &windowPayload : m_windowsToCreate) { + bool created = false; + for (auto &workspace : m_workspaces) { + if (workspace->onWindowOpened(windowPayload)) { + created = true; + anyWindowCreated = true; + break; + } + } + if (!created) { + static auto const WINDOW_CREATION_TIMEOUT = 2; + if (windowPayload.incrementTimeSpentUncreated() < WINDOW_CREATION_TIMEOUT) { + notCreated.push_back(windowPayload); + } else { + registerOrphanWindow(windowPayload); + } + } + } + m_windowsToCreate.clear(); + m_windowsToCreate = notCreated; + return anyWindowCreated; +} + +void Workspaces::updateWorkspaceStates(const std::vector &visibleWorkspaces) { + auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces"); + for (auto &workspace : m_workspaces) { + workspace->setActive(workspace->name() == m_activeWorkspaceName || + workspace->name() == m_activeSpecialWorkspaceName); + if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { + workspace->setUrgent(false); + } + workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), + workspace->name()) != visibleWorkspaces.end()); + std::string &workspaceIcon = m_iconsMap[""]; + if (m_withIcon) { + workspaceIcon = workspace->selectIcon(m_iconsMap); + } + auto updatedWorkspace = std::find_if( + updatedWorkspaces.begin(), updatedWorkspaces.end(), [&workspace](const auto &w) { + auto wNameRaw = w["name"].asString(); + auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; + return wName == workspace->name(); + }); + if (updatedWorkspace != updatedWorkspaces.end()) { + workspace->setOutput((*updatedWorkspace)["monitor"].asString()); + } + workspace->update(m_format, workspaceIcon); + } +} + +int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) { + // Rules that match against title are prioritized + // Rules that don't specify if they're matching against either title or class are deprioritized + bool const hasTitle = window_rule.find("title") != std::string::npos; + bool const hasClass = window_rule.find("class") != std::string::npos; + + if (hasTitle && hasClass) { + m_anyWindowRewriteRuleUsesTitle = true; + return 3; + } + if (hasTitle) { + m_anyWindowRewriteRuleUsesTitle = true; + return 2; + } + if (hasClass) { + return 1; + } + return 0; +} + } // namespace waybar::modules::hyprland From af87388eb43ff7ce0c01ef3b1630db37325fc082 Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Tue, 28 May 2024 09:13:11 +0200 Subject: [PATCH 554/842] Update docker.yml --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 12927fb0..37042b16 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -28,4 +28,4 @@ jobs: context: . file: Dockerfiles/${{ matrix.os }} push: true - tags: alexays/${{ matrix.os }}:latest \ No newline at end of file + tags: alexays/waybar:${{ matrix.os }} From a4a4be3381faa7ddae690bd1b9851c558e72ba3e Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 28 May 2024 09:19:21 +0200 Subject: [PATCH 555/842] fix: lint --- include/bar.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bar.hpp b/include/bar.hpp index 2f225de6..6900da47 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -8,8 +8,8 @@ #include #include -#include #include +#include #include #include "AModule.hpp" From 1a9952d0c06eb5df35dc24662d055c7c30026f0d Mon Sep 17 00:00:00 2001 From: John Titor <50095635+JohnRTitor@users.noreply.github.com> Date: Tue, 28 May 2024 13:08:45 +0530 Subject: [PATCH 556/842] workflows: add nix-test workflow Checks the flake Builds and tests the package --- .github/workflows/nix-tests.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/nix-tests.yml diff --git a/.github/workflows/nix-tests.yml b/.github/workflows/nix-tests.yml new file mode 100644 index 00000000..8859ecb5 --- /dev/null +++ b/.github/workflows/nix-tests.yml @@ -0,0 +1,17 @@ +name: "Nix-Tests" +on: + pull_request: + push: +jobs: + nix-flake-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v27 + with: + extra_nix_config: | + experimental-features = nix-command flakes + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - run: nix flake show + - run: nix flake check --print-build-logs + - run: nix build --print-build-logs From b6ca3ea4d9d57aa1bfca2e2600e4dcabea36ec62 Mon Sep 17 00:00:00 2001 From: John Titor <50095635+JohnRTitor@users.noreply.github.com> Date: Tue, 28 May 2024 13:25:40 +0530 Subject: [PATCH 557/842] worflows: add update-flake-lock action automatically updates the nix flake lock file runs once a month --- .github/workflows/nix-update-flake-lock.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/nix-update-flake-lock.yml diff --git a/.github/workflows/nix-update-flake-lock.yml b/.github/workflows/nix-update-flake-lock.yml new file mode 100644 index 00000000..2b65c329 --- /dev/null +++ b/.github/workflows/nix-update-flake-lock.yml @@ -0,0 +1,21 @@ +name: update-flake-lock +on: + workflow_dispatch: # allows manual triggering + schedule: + - cron: '0 0 1 * *' # Run monthly + push: + paths: + - 'flake.nix' +jobs: + lockfile: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v27 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - name: Update flake.lock + uses: DeterminateSystems/update-flake-lock@v21 From c3581fb66ba23f8466358ce920f9038ce7607ab2 Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 17:20:31 +0200 Subject: [PATCH 558/842] =?UTF-8?q?=F0=9F=A5=85=20only=20check=20menu=20if?= =?UTF-8?q?=20speciifed=20in=20the=20conf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/AModule.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 77b82cee..9948edab 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -134,11 +134,14 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) { format = rec->second; } - // Check if the event is the one specified for the "menu" option - if (rec->second == config_["menu"].asString()) { - // Popup the menu - gtk_widget_show_all(GTK_WIDGET(menu_)); - gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast(e)); + // Check that a menu has been configured + if (config_["menu"].isString()) { + // Check if the event is the one specified for the "menu" option + if (rec->second == config_["menu"].asString()) { + // Popup the menu + gtk_widget_show_all(GTK_WIDGET(menu_)); + gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast(e)); + } } // Second call user scripts if (!format.empty()) { From 29e3d8c371d6dee3979ac3b2b2c365605e1b5057 Mon Sep 17 00:00:00 2001 From: alttabber Date: Tue, 28 May 2024 16:57:47 +0200 Subject: [PATCH 559/842] Hide non-visible special workspaces --- include/modules/hyprland/workspaces.hpp | 2 ++ man/waybar-hyprland-workspaces.5.scd | 5 +++++ src/modules/hyprland/workspace.cpp | 4 ++++ src/modules/hyprland/workspaces.cpp | 1 + 4 files changed, 12 insertions(+) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index e99bf40c..0432b870 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -38,6 +38,7 @@ class Workspaces : public AModule, public EventHandler { auto allOutputs() const -> bool { return m_allOutputs; } auto showSpecial() const -> bool { return m_showSpecial; } auto activeOnly() const -> bool { return m_activeOnly; } + auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; } auto moveToMonitor() const -> bool { return m_moveToMonitor; } auto getBarOutput() const -> std::string { return m_bar.output->name; } @@ -113,6 +114,7 @@ class Workspaces : public AModule, public EventHandler { bool m_allOutputs = false; bool m_showSpecial = false; bool m_activeOnly = false; + bool m_specialVisibleOnly = false; bool m_moveToMonitor = false; Json::Value m_persistentWorkspaceConfig; diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 2d0641b4..406ada7a 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -42,6 +42,11 @@ Addressed by *hyprland/workspaces* default: false ++ If set to true, special workspaces will be shown. +*special-visible-only*: ++ + typeof: bool ++ + default: false ++ + If this and show-special are to true, special workspaces will be shown only if visible. + *all-outputs*: ++ typeof: bool ++ default: false ++ diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index 694d3b0d..bf0a3368 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -182,6 +182,10 @@ void Workspace::update(const std::string &format, const std::string &icon) { m_button.hide(); return; } + if (this->m_workspaceManager.specialVisibleOnly() && this->isSpecial() && !this->isVisible()) { + m_button.hide(); + return; + } m_button.show(); auto styleContext = m_button.get_style_context(); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index eca30134..3b129375 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -576,6 +576,7 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { populateBoolConfig(config, "all-outputs", m_allOutputs); populateBoolConfig(config, "show-special", m_showSpecial); + populateBoolConfig(config, "special-visible-only", m_specialVisibleOnly); populateBoolConfig(config, "active-only", m_activeOnly); populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); From 24e8766aaaf5b6f8862d819790c7aa53c8fcb02f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 10:57:57 -0500 Subject: [PATCH 560/842] hyprland/backend: use /tmp Was hardcoded to /tmp in previous versions --- src/modules/hyprland/backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 1b47ff7d..29c65633 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -27,7 +27,7 @@ std::filesystem::path getSocketFolder(const char* instanceSig) { socketFolder = xdgRuntimeDir / "hypr"; } else { spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr"); - socketFolder = std::filesystem::temp_directory_path() / "hypr"; + socketFolder = std::filesystem::path("/tmp") / "hypr"; } socketFolder = socketFolder / instanceSig; return socketFolder; From f3ed5ca5afe3966f1af429eb5171526388dc18a6 Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 18:08:31 +0200 Subject: [PATCH 561/842] =?UTF-8?q?=F0=9F=8C=B1=20update=20default=20confi?= =?UTF-8?q?g=20with=20a=20menu=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/config.jsonc | 15 ++++++++++++- resources/custom_modules/power_menu.xml | 28 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 resources/custom_modules/power_menu.xml diff --git a/resources/config.jsonc b/resources/config.jsonc index 329275b1..7e0771f5 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -30,7 +30,8 @@ "battery", "battery#bat2", "clock", - "tray" + "tray", + "custom/power" ], // Modules configuration // "sway/workspaces": { @@ -198,5 +199,17 @@ "escape": true, "exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name + }, + "custom/power": { + "format" : "⏻ ", + "tooltip": false, + "menu": "on-click", + "menu-file": "$HOME/.config/waybar/power_menu.xml", // Menu file in resources folder + "menu-actions": { + "shutdown": "shutdown", + "reboot": "reboot", + "suspend": "systemctl suspend", + "hibernate": "systemctl hibernate" + } } } diff --git a/resources/custom_modules/power_menu.xml b/resources/custom_modules/power_menu.xml new file mode 100644 index 00000000..aa2a42ca --- /dev/null +++ b/resources/custom_modules/power_menu.xml @@ -0,0 +1,28 @@ + + + + + + Suspend + + + + + Hibernate + + + + + Shutdown + + + + + + + + Reboot + + + + From 161c8c4c47e219a3c457e01a355e7b6fde029228 Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 19:30:06 +0200 Subject: [PATCH 562/842] =?UTF-8?q?=F0=9F=A5=85=20do=20not=20crash=20when?= =?UTF-8?q?=20unable=20to=20make=20the=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the menu cannot be built (file not existing, or wrongly formatted), the menu is not created and a warning with an explanaition is displayed. --- src/ALabel.cpp | 49 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 56850617..5497c62a 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -2,6 +2,8 @@ #include +#include +#include #include namespace waybar { @@ -56,18 +58,41 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st // If a GTKMenu is requested in the config if (config_["menu"].isString()) { // Create the GTKMenu widget - GtkBuilder* builder = gtk_builder_new_from_file(config_["menu-file"].asString().c_str()); - menu_ = gtk_builder_get_object(builder, "menu"); - submenus_ = std::map(); - menuActionsMap_ = std::map(); - // Linking actions to the GTKMenu based on - for (Json::Value::const_iterator it = config_["menu-actions"].begin(); - it != config_["menu-actions"].end(); ++it) { - std::string key = it.key().asString(); - submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); - menuActionsMap_[key] = it->asString(); - g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), - (gpointer)menuActionsMap_[key].c_str()); + try { + // Check that the file exists + std::string menuFile = config_["menu-file"].asString(); + // Read the menu descriptor file + std::ifstream file(menuFile); + if (!file.is_open()) { + throw std::runtime_error("Failed to open file: " + menuFile); + } + std::stringstream fileContent; + fileContent << file.rdbuf(); + GtkBuilder* builder = gtk_builder_new(); + + // Make the GtkBuilder and check for errors in his parsing + if (!gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr)) { + throw std::runtime_error("Error found in the file " + menuFile); + } + + menu_ = gtk_builder_get_object(builder, "menu"); + if (!menu_) { + throw std::runtime_error("Failed to get 'menu' object from GtkBuilder"); + } + submenus_ = std::map(); + menuActionsMap_ = std::map(); + + // Linking actions to the GTKMenu based on + for (Json::Value::const_iterator it = config_["menu-actions"].begin(); + it != config_["menu-actions"].end(); ++it) { + std::string key = it.key().asString(); + submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); + menuActionsMap_[key] = it->asString(); + g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), + (gpointer)menuActionsMap_[key].c_str()); + } + } catch (std::runtime_error& e) { + spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what()); } } From d9f2e0f7d28c2107e266aeffd0dcd929a8d8bf4d Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 20:42:12 +0200 Subject: [PATCH 563/842] =?UTF-8?q?=F0=9F=93=9D=20add=20menu=20config=20in?= =?UTF-8?q?formations=20in=20manpages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- man/waybar-backlight.5.scd | 13 +++++++++++++ man/waybar-battery.5.scd | 13 +++++++++++++ man/waybar-bluetooth.5.scd | 13 +++++++++++++ man/waybar-cava.5.scd | 12 ++++++++++++ man/waybar-clock.5.scd | 12 ++++++++++++ man/waybar-custom.5.scd | 13 +++++++++++++ man/waybar-disk.5.scd | 13 +++++++++++++ man/waybar-hyprland-language.5.scd | 13 +++++++++++++ man/waybar-hyprland-submap.5.scd | 13 +++++++++++++ man/waybar-idle-inhibitor.5.scd | 13 +++++++++++++ man/waybar-inhibitor.5.scd | 13 +++++++++++++ man/waybar-jack.5.scd | 13 +++++++++++++ man/waybar-memory.5.scd | 13 +++++++++++++ man/waybar-mpd.5.scd | 13 +++++++++++++ man/waybar-network.5.scd | 13 +++++++++++++ man/waybar-pulseaudio.5.scd | 13 +++++++++++++ man/waybar-river-layout.5.scd | 13 +++++++++++++ man/waybar-river-mode.5.scd | 13 +++++++++++++ man/waybar-river-window.5.scd | 13 +++++++++++++ man/waybar-sndio.5.scd | 13 +++++++++++++ man/waybar-sway-language.5.scd | 13 +++++++++++++ man/waybar-sway-mode.5.scd | 13 +++++++++++++ man/waybar-sway-scratchpad.5.scd | 13 +++++++++++++ man/waybar-systemd-failed-units.5.scd | 13 +++++++++++++ man/waybar-temperature.5.scd | 13 +++++++++++++ man/waybar-upower.5.scd | 13 +++++++++++++ man/waybar-wireplumber.5.scd | 13 +++++++++++++ 27 files changed, 349 insertions(+) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index b92abd12..1f674fc0 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -81,6 +81,19 @@ The *backlight* module displays the current backlight level. default: 1.0 ++ The speed at which to change the brightness when scrolling. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLE: ``` diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 25c7caca..4fe9650a 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -109,6 +109,19 @@ The *battery* module displays the current capacity and state (eg. charging) of y default: false ++ Option to enable battery compatibility if not detected. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{capacity}*: Capacity in percentage diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 3808e855..1783dab3 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -129,6 +129,19 @@ Addressed by *bluetooth* typeof: string ++ This format is used to define how each connected device should be displayed within the *device_enumerate* format replacement in the tooltip menu. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{status}*: Status of the bluetooth device. diff --git a/man/waybar-cava.5.scd b/man/waybar-cava.5.scd index cf75441b..2a7e8f67 100644 --- a/man/waybar-cava.5.scd +++ b/man/waybar-cava.5.scd @@ -120,6 +120,18 @@ libcava lives in: :[ string :[ /dev/stdout :[ It's impossible to set it. Waybar sets it to = /dev/stdout for internal needs +|[ *menu* +:[ string +:[ +:[ Action that popups the menu. +|[ *menu-file* +:[ string +:[ +:[ Location of the menu descriptor file. There need to be an element of type GtkMenu with id *menu* +|[ *menu-actions* +:[ array +:[ +:[ The actions corresponding to the buttons of the menu. Configuration can be provided as: - The only cava configuration file which is provided through *cava_config*. The rest configuration can be skipped diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index e8ef7bed..40aedd15 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -84,6 +84,18 @@ $XDG_CONFIG_HOME/waybar/config ++ :[ string :[ same as format :[ Tooltip on hover +|[ *menu* +:[ string +:[ +:[ Action that popups the menu. +|[ *menu-file* +:[ string +:[ +:[ Location of the menu descriptor file. There need to be an element of type GtkMenu with id *menu* +|[ *menu-actions* +:[ array +:[ +:[ The actions corresponding to the buttons of the menu. View all valid format options in *strftime(3)* or have a look https://en.cppreference.com/w/cpp/chrono/duration/formatter diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index a62c9312..df866ae1 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -121,6 +121,19 @@ Addressed by *custom/* default: false ++ Option to enable escaping of script output. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # RETURN-TYPE When *return-type* is set to *json*, Waybar expects the *exec*-script to output its data in JSON format. diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index a279718b..df9ca4e5 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -93,6 +93,19 @@ Addressed by *disk* typeof: string ++ Use with specific_free, specific_used, and specific_total to force calculation to always be in a certain unit. Accepts kB, kiB, MB, Mib, GB, GiB, TB, TiB. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{percentage_used}*: Percentage of disk in use. diff --git a/man/waybar-hyprland-language.5.scd b/man/waybar-hyprland-language.5.scd index dba7dbca..33b28ae4 100644 --- a/man/waybar-hyprland-language.5.scd +++ b/man/waybar-hyprland-language.5.scd @@ -25,6 +25,19 @@ Addressed by *hyprland/language* typeof: string ++ Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index 3f23784d..64398e61 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -80,6 +80,19 @@ Addressed by *hyprland/submap* default: Default ++ Option to set the submap name to display when not in an active submap. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLES diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index 71b3b30c..f7677634 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -89,6 +89,19 @@ screensaver, also known as "presentation mode". typeof: string ++ This format is used when the inhibit is deactivated. +*menu*: ++ + typeof: string ++ + Action that popups the menu. Cannot be "on-click". + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{status}*: status (*activated* or *deactivated*) diff --git a/man/waybar-inhibitor.5.scd b/man/waybar-inhibitor.5.scd index 47b6ffce..679a5c4b 100644 --- a/man/waybar-inhibitor.5.scd +++ b/man/waybar-inhibitor.5.scd @@ -76,6 +76,19 @@ See *systemd-inhibit*(1) for more information. default: true ++ Option to disable tooltip on hover. +*menu*: ++ + typeof: string ++ + Action that popups the menu. Cannot be "on-click". + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{status}*: status (*activated* or *deactivated*) diff --git a/man/waybar-jack.5.scd b/man/waybar-jack.5.scd index 87a38354..573b36c2 100644 --- a/man/waybar-jack.5.scd +++ b/man/waybar-jack.5.scd @@ -85,6 +85,19 @@ Addressed by *jack* typeof: string ++ Command to execute when the module is updated. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{load}*: The current CPU load estimated by JACK. diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index e0252caf..7738c576 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -84,6 +84,19 @@ Addressed by *memory* default: true ++ Option to disable tooltip on hover. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{percentage}*: Percentage of memory in use. diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index fe6ee5a1..2f1bdf20 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -162,6 +162,19 @@ Addressed by *mpd* default: {} ++ Icon to show depending on the "single" option (*{ "on": "...", "off": "..." }*) +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS ## WHEN PLAYING/PAUSED diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index b5580c52..cc0b470b 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -129,6 +129,19 @@ Addressed by *network* typeof: string ++ This format is used when the displayed interface is disabled. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{ifname}*: Name of the network interface. diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index 4bc75258..8d3761c4 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -113,6 +113,19 @@ Additionally, you can control the volume by scrolling *up* or *down* while the c typeof: array ++ Sinks in this list will not be shown as active sink by Waybar. Entries should be the sink's description field. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name. diff --git a/man/waybar-river-layout.5.scd b/man/waybar-river-layout.5.scd index 1c09d6f6..4fb23085 100644 --- a/man/waybar-river-layout.5.scd +++ b/man/waybar-river-layout.5.scd @@ -51,6 +51,19 @@ Addressed by *river/layout* typeof: string ++ Command to execute when you right-click on the module. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLE ``` diff --git a/man/waybar-river-mode.5.scd b/man/waybar-river-mode.5.scd index 2d63b5e1..5769a9a2 100644 --- a/man/waybar-river-mode.5.scd +++ b/man/waybar-river-mode.5.scd @@ -65,6 +65,19 @@ Addressed by *river/mode* typeof: double ++ Threshold to be used when scrolling. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLES ``` diff --git a/man/waybar-river-window.5.scd b/man/waybar-river-window.5.scd index dbd9f130..7e661f43 100644 --- a/man/waybar-river-window.5.scd +++ b/man/waybar-river-window.5.scd @@ -49,6 +49,19 @@ Addressed by *river/window* typeof: string ++ Command to execute when you right-click on the module. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLES ``` diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd index 197aaba0..f8d1615d 100644 --- a/man/waybar-sndio.5.scd +++ b/man/waybar-sndio.5.scd @@ -74,6 +74,19 @@ cursor is over the module, and clicking on the module toggles mute. typeof: double ++ Threshold to be used when scrolling. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{volume}*: Volume in percentage. diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index 1c62fd95..a1fc5d08 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -32,6 +32,19 @@ Addressed by *sway/language* default: true ++ Option to disable tooltip on hover. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{short}*: Short name of layout (e.g. "us"). Equals to {}. diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index 44c8b81a..1fcf3cf8 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -70,6 +70,19 @@ Addressed by *sway/mode* default: true ++ Option to disable tooltip on hover. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # EXAMPLES ``` diff --git a/man/waybar-sway-scratchpad.5.scd b/man/waybar-sway-scratchpad.5.scd index 64c43db6..5ae104bc 100644 --- a/man/waybar-sway-scratchpad.5.scd +++ b/man/waybar-sway-scratchpad.5.scd @@ -36,6 +36,19 @@ Addressed by *sway/scratchpad* default: {app}: {title} ++ The format, how information in the tooltip should be displayed. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{icon}*: Icon, as defined in *format-icons*. diff --git a/man/waybar-systemd-failed-units.5.scd b/man/waybar-systemd-failed-units.5.scd index ac92c533..ada3ab8b 100644 --- a/man/waybar-systemd-failed-units.5.scd +++ b/man/waybar-systemd-failed-units.5.scd @@ -36,6 +36,19 @@ Addressed by *systemd-failed-units* default: *true* ++ Option to hide this module when there is no failing units. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{nr_failed_system}*: Number of failed units from systemwide (PID=1) systemd. diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index ff2168ea..8b84ef6c 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -110,6 +110,19 @@ Addressed by *temperature* default: true ++ Option to disable tooltip on hover. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{temperatureC}*: Temperature in Celsius. diff --git a/man/waybar-upower.5.scd b/man/waybar-upower.5.scd index 5e2a8eb8..5ec0222d 100644 --- a/man/waybar-upower.5.scd +++ b/man/waybar-upower.5.scd @@ -62,6 +62,19 @@ compatible devices in the tooltip. default: true ++ Option to disable battery icon. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{percentage}*: The battery capacity in percentage diff --git a/man/waybar-wireplumber.5.scd b/man/waybar-wireplumber.5.scd index b08fd90f..770ff0d5 100644 --- a/man/waybar-wireplumber.5.scd +++ b/man/waybar-wireplumber.5.scd @@ -87,6 +87,19 @@ The *wireplumber* module displays the current volume reported by WirePlumber. default: 100 ++ The maximum volume that can be set, in percentage. +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + # FORMAT REPLACEMENTS *{volume}*: Volume in percentage. From 8220dbb513fe2cf9051bb685eb19784f8e3f4f2f Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 20:42:47 +0200 Subject: [PATCH 564/842] =?UTF-8?q?=F0=9F=93=9D=20add=20a=20wayba-menu=20e?= =?UTF-8?q?ntry=20for=20documenting=20popup=20menus.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- man/waybar-menu.5.scd | 111 ++++++++++++++++++++++++++++++++++++++++++ meson.build | 1 + 2 files changed, 112 insertions(+) create mode 100644 man/waybar-menu.5.scd diff --git a/man/waybar-menu.5.scd b/man/waybar-menu.5.scd new file mode 100644 index 00000000..10484e41 --- /dev/null +++ b/man/waybar-menu.5.scd @@ -0,0 +1,111 @@ +waybar-menu(5) + +# NAME + +waybar - menu property + +# OVERVIEW + + +Some modules support a 'menu', which allows to have a popup menu whan a defined +click is done over the module. + +# PROPERTIES + +A module that implements a 'menu' needs 3 properties defined in its config : + +*menu*: ++ + typeof: string ++ + Action that popups the menu. The possibles actions are : + +[- *Option* +:- *Description* +|[ *on-click* +:< When you left-click on the module +|[ *on-click-release* +:< When you release left button on the module +|[ *on-double-click* +:< When you double left click on the module +|[ *on-triple-click* +:< When you triple left click on the module +|[ *on-click-middle* +:< When you middle click on the module using mousewheel +|[ *on-click-middle-release* +:< When you release mousewheel button on the module +|[ *on-double-click-middle* +:< When you double middle click on the module +|[ *on-triple-click-middle* +:< When you triple middle click on the module +|[ *on-click-right* +:< When you right click on the module using +|[ *on-click-right-release* +:< When you release right button on the module +|[ *on-double-click-right* +:< When you double right click on the module +|[ *on-triple-click-right* +:< When you triple middle click on the module +|[ *on-click-backward* +:< When you click on the module using mouse backward button +|[ *on-click-backward-release* +:< When you release mouse backward button on the module +|[ *on-double-click-backward* +:< When you double click on the module using mouse backward button +|[ *on-triple-click-backward* +:< When you triple click on the module using mouse backawrd button +|[ *on-click-forward* +:< When you click on the module using mouse forward button +|[ *on-click-forward-release* +:< When you release mouse forward button on the module +|[ *on-double-click-forward* +:< When you double click on the module using mouse forward button +|[ *on-triple-click-forward* +:< When you triple click on the module using mouse forward button + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type + GtkMenu with id *menu*. + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. The identifiers of + each actions needs to exists as an id in the 'menu-file' for it to be linked + properly. + +# EXAMPLE + +``` +"custom/power": { + "format" : "⏻ ", + "tooltip": false, + "menu": "on-click", + "menu-file": "~/.config/waybar/power_menu.xml", + "menu-actions": { + "shutdown": "shutdown", + "reboot": "reboot", + "suspend": "systemctl suspend", + "hibernate": "systemctl hibernate", + }, +}, +``` + +# STYLING MENUS + +- *menu* + Style for the menu + +- *menuitem* + Style for items in the menu + +# EXAMPLE: + +``` +menu { + border-radius: 15px; + background: #161320; + color: #B5E8E0; +} +menuitem { + border-radius: 15px; +} +``` diff --git a/meson.build b/meson.build index a57b17f8..56b772a5 100644 --- a/meson.build +++ b/meson.build @@ -192,6 +192,7 @@ man_files = files( 'man/waybar-idle-inhibitor.5.scd', 'man/waybar-image.5.scd', 'man/waybar-states.5.scd', + 'man/waybar-menu.5.scd', 'man/waybar-temperature.5.scd', ) From 885290d907ac49b4d6289b0e35daf01faedc698a Mon Sep 17 00:00:00 2001 From: Benjamin Voisin Date: Tue, 28 May 2024 21:02:07 +0200 Subject: [PATCH 565/842] =?UTF-8?q?=F0=9F=93=9D=20improve=20waybar-menu=20?= =?UTF-8?q?file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- man/waybar-menu.5.scd | 62 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/man/waybar-menu.5.scd b/man/waybar-menu.5.scd index 10484e41..19790ed4 100644 --- a/man/waybar-menu.5.scd +++ b/man/waybar-menu.5.scd @@ -72,23 +72,65 @@ A module that implements a 'menu' needs 3 properties defined in its config : each actions needs to exists as an id in the 'menu-file' for it to be linked properly. +# MENU-FILE + +The menu-file is an `.xml` file representing a GtkBuilder. Documentation for it +can be found here : https://docs.gtk.org/gtk4/class.Builder.html + +Here, it needs to have an element of type GtkMenu with id "menu". Eeach actions +in *menu-actions* are linked to elements in the *menu-file* file by the id of +the elements. + # EXAMPLE +Module config : ``` "custom/power": { - "format" : "⏻ ", - "tooltip": false, - "menu": "on-click", - "menu-file": "~/.config/waybar/power_menu.xml", - "menu-actions": { - "shutdown": "shutdown", - "reboot": "reboot", - "suspend": "systemctl suspend", - "hibernate": "systemctl hibernate", - }, + "format" : "⏻ ", + "tooltip": false, + "menu": "on-click", + "menu-file": "~/.config/waybar/power_menu.xml", + "menu-actions": { + "shutdown": "shutdown", + "reboot": "reboot", + "suspend": "systemctl suspend", + "hibernate": "systemctl hibernate", + }, }, ``` +~/.config/waybar/power_menu.xml : +``` + + + + + + Suspend + + + + + Hibernate + + + + + Shutdown + + + + + + + + Reboot + + + + +``` + # STYLING MENUS - *menu* From 8adb0a5644db9e204277ceba8c548e516f0139c5 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 15:03:22 -0500 Subject: [PATCH 566/842] .github/workflows: fix meson deprecations --- .github/workflows/freebsd.yml | 2 +- .github/workflows/linux.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 0b628d19..7effb484 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -31,6 +31,6 @@ jobs: libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ pkgconf pipewire pulseaudio scdoc sndio spdlog wayland-protocols upower \ libinotify - meson build -Dman-pages=enabled + meson setup build -Dman-pages=enabled ninja -C build meson test -C build --no-rebuild --print-errorlogs --suite waybar diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index dc6b7ede..ae0f1f7f 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: configure - run: meson -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build + run: meson setup -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build - name: build run: ninja -C build - name: test From 381fe830081c4ce8fa7236b7f1e4e744f12487fe Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 15:14:24 -0500 Subject: [PATCH 567/842] Makefile: fix meson deprecations --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b1dbfc6e..3bb11199 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,11 @@ default: build build: - meson build + meson setup build ninja -C build build-debug: - meson build --buildtype=debug + meson setup build --buildtype=debug ninja -C build install: build From c5b5b64dfa45bd30ff2b3cc7d158728dea5619cd Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 15:33:16 -0500 Subject: [PATCH 568/842] modules/temperature: remove unused import --- src/modules/temperature.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 922fa639..88910982 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -1,7 +1,5 @@ #include "modules/temperature.hpp" -#include - #include #include From cf66604f85562ccee510b5e8a0ef402d31f94075 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 30 May 2024 19:35:32 +0200 Subject: [PATCH 569/842] fix fedora image --- Dockerfiles/fedora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index 5892159c..9dc0337b 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -29,6 +29,6 @@ RUN dnf install -y @c-development \ 'pkgconfig(wayland-client)' \ 'pkgconfig(wayland-cursor)' \ 'pkgconfig(wayland-protocols)' \ - 'pkgconfig(wireplumber-0.4)' \ + 'pkgconfig(wireplumber-0.5)' \ 'pkgconfig(xkbregistry)' && \ dnf clean all -y From 532a90259b68b08385648aa9332de78f1df709bf Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 30 May 2024 20:18:33 +0200 Subject: [PATCH 570/842] Dont fail docker image builds when another build fails --- .github/workflows/docker.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 37042b16..d9fc5d3e 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -9,6 +9,7 @@ jobs: build-and-push: runs-on: ubuntu-latest strategy: + fail-fast: false # don't fail the other jobs if one of the images fails to build matrix: os: [ 'alpine', 'archlinux', 'debian', 'fedora', 'gentoo', 'opensuse' ] From e9350cf25ff61a6e4525ca88b39ab6174ef04cf1 Mon Sep 17 00:00:00 2001 From: Jack Wilsdon Date: Fri, 31 May 2024 14:31:29 +0000 Subject: [PATCH 571/842] Fix format replacement names --- man/waybar-mpris.5.scd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 186d73c6..455fcb17 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -142,11 +142,11 @@ The *mpris* module displays currently playing media via libplayerctl. *player-icons*: ++ typeof: map[string]string ++ - Allows setting _{player-icon}_ based on player-name property. + Allows setting _{player_icon}_ based on player-name property. *status-icons*: ++ typeof: map[string]string ++ - Allows setting _{status-icon}_ based on player status (playing, paused, stopped). + Allows setting _{status_icon}_ based on player status (playing, paused, stopped). # FORMAT REPLACEMENTS From 1474cc626d0bc353bd27fb682cf372150ee94523 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 1 Jun 2024 00:09:05 +0000 Subject: [PATCH 572/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/44d0940ea560dee511026a53f0e2e2cde489b4d4?narHash=sha256-YN/Ciidm%2BA0fmJPWlHBGvVkcarYWSC%2Bs3NTPk/P%2Bq3c%3D' (2024-03-23) → 'github:NixOS/nixpkgs/ad57eef4ef0659193044870c731987a6df5cf56b?narHash=sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs%3D' (2024-05-29) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 7647478b..f35322ec 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1711163522, - "narHash": "sha256-YN/Ciidm+A0fmJPWlHBGvVkcarYWSC+s3NTPk/P+q3c=", + "lastModified": 1716948383, + "narHash": "sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4", + "rev": "ad57eef4ef0659193044870c731987a6df5cf56b", "type": "github" }, "original": { From 4fbd4f212a80009e92d179f9e96a50562891c2e8 Mon Sep 17 00:00:00 2001 From: giskard Date: Thu, 9 May 2024 01:45:21 +0800 Subject: [PATCH 573/842] privacy: consider only configured modules along with the local clang-tidy warning fixes --- src/modules/privacy/privacy.cpp | 81 +++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index d3e3d4b2..97996c33 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -53,23 +53,22 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st modules.append(obj); } } - for (const auto& module_config : modules) { - if (!module_config.isObject() || !module_config["type"].isString()) continue; - const std::string type = module_config["type"].asString(); - if (type == "screenshare") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_VIDEO_INPUT, - &nodes_screenshare, pos, iconSize, transition_duration); - box_.add(*item); - } else if (type == "audio-in") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_INPUT, - &nodes_audio_in, pos, iconSize, transition_duration); - box_.add(*item); - } else if (type == "audio-out") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_OUTPUT, - &nodes_audio_out, pos, iconSize, transition_duration); + + std::map > typeMap = { + {"screenshare", {&nodes_screenshare, PRIVACY_NODE_TYPE_VIDEO_INPUT}}, + {"audio-in", {&nodes_audio_in, PRIVACY_NODE_TYPE_AUDIO_INPUT}}, + {"audio-out", {&nodes_audio_out, PRIVACY_NODE_TYPE_AUDIO_OUTPUT}}, + }; + + for (const auto& module : modules) { + if (!module.isObject() || !module["type"].isString()) continue; + const std::string type = module["type"].asString(); + + auto iter = typeMap.find(type); + if (iter != typeMap.end()) { + auto& [nodePtr, nodeType] = iter->second; + auto* item = Gtk::make_managed(module, nodeType, nodePtr, pos, iconSize, + transition_duration); box_.add(*item); } } @@ -114,26 +113,35 @@ void Privacy::onPrivacyNodesChanged() { } auto Privacy::update() -> void { - mutex_.lock(); - bool screenshare = false; - bool audio_in = false; - bool audio_out = false; + // set in modules or not + bool setScreenshare = false; + bool setAudioIn = false; + bool setAudioOut = false; + // used or not + bool useScreenshare = false; + bool useAudioIn = false; + bool useAudioOut = false; + + mutex_.lock(); for (Gtk::Widget* widget : box_.get_children()) { auto* module = dynamic_cast(widget); if (module == nullptr) continue; switch (module->privacy_type) { case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: - screenshare = !nodes_screenshare.empty(); - module->set_in_use(screenshare); + setScreenshare = true; + useScreenshare = !nodes_screenshare.empty(); + module->set_in_use(useScreenshare); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT: - audio_in = !nodes_audio_in.empty(); - module->set_in_use(audio_in); + setAudioIn = true; + useAudioIn = !nodes_audio_in.empty(); + module->set_in_use(useAudioIn); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT: - audio_out = !nodes_audio_out.empty(); - module->set_in_use(audio_out); + setAudioOut = true; + useAudioOut = !nodes_audio_out.empty(); + module->set_in_use(useAudioOut); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_NONE: break; @@ -142,25 +150,28 @@ auto Privacy::update() -> void { mutex_.unlock(); // Hide the whole widget if none are in use - bool is_visible = screenshare || audio_in || audio_out; - if (is_visible != event_box_.get_visible()) { + bool isVisible = (setScreenshare && useScreenshare) || (setAudioIn && useAudioIn) || + (setAudioOut && useAudioOut); + + if (isVisible != event_box_.get_visible()) { // Disconnect any previous connection so that it doesn't get activated in // the future, hiding the module when it should be visible visibility_conn.disconnect(); - if (is_visible) { + if (isVisible) { event_box_.set_visible(true); } else { // Hides the widget when all of the privacy_item revealers animations // have finished animating visibility_conn = Glib::signal_timeout().connect( sigc::track_obj( - [this] { + [this, setScreenshare, setAudioOut, setAudioIn]() { mutex_.lock(); - bool screenshare = !nodes_screenshare.empty(); - bool audio_in = !nodes_audio_in.empty(); - bool audio_out = !nodes_audio_out.empty(); + bool visible = false; + visible |= setScreenshare && !nodes_screenshare.empty(); + visible |= setAudioIn && !nodes_audio_in.empty(); + visible |= setAudioOut && !nodes_audio_out.empty(); mutex_.unlock(); - event_box_.set_visible(screenshare || audio_in || audio_out); + event_box_.set_visible(visible); return false; }, *this), From 02eaa8b46e791ec9a47780ebb0cc36cea430e040 Mon Sep 17 00:00:00 2001 From: williammmm <91826947+williamwith4ms@users.noreply.github.com> Date: Mon, 3 Jun 2024 19:46:55 +0100 Subject: [PATCH 574/842] escape & in mediaplayer --- resources/custom_modules/mediaplayer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index acc47496..d1bb72b4 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -113,6 +113,7 @@ class PlayerManager: player_name = player.props.player_name artist = player.get_artist() title = player.get_title() + title = title.replace("&", "&") track_info = "" if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]: From 76c2f3166ef6003fe5f206925ff5dd7eb0dde6a4 Mon Sep 17 00:00:00 2001 From: Nicolas Lenz Date: Wed, 5 Jun 2024 19:58:27 +0200 Subject: [PATCH 575/842] format RegexCollection output using match results --- include/util/regex_collection.hpp | 4 ++-- src/util/regex_collection.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/util/regex_collection.hpp b/include/util/regex_collection.hpp index 5ea2882e..fe958461 100644 --- a/include/util/regex_collection.hpp +++ b/include/util/regex_collection.hpp @@ -36,7 +36,7 @@ class RegexCollection { std::map regex_cache; std::string default_repr; - std::string& find_match(std::string& value, bool& matched_any); + std::string find_match(std::string& value, bool& matched_any); public: RegexCollection() = default; @@ -48,4 +48,4 @@ class RegexCollection { std::string& get(std::string& value); }; -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util diff --git a/src/util/regex_collection.cpp b/src/util/regex_collection.cpp index 704d6545..db2f30ea 100644 --- a/src/util/regex_collection.cpp +++ b/src/util/regex_collection.cpp @@ -31,11 +31,12 @@ RegexCollection::RegexCollection(const Json::Value& map, std::string default_rep std::sort(rules.begin(), rules.end(), [](Rule& a, Rule& b) { return a.priority > b.priority; }); } -std::string& RegexCollection::find_match(std::string& value, bool& matched_any) { +std::string RegexCollection::find_match(std::string& value, bool& matched_any) { for (auto& rule : rules) { - if (std::regex_search(value, rule.rule)) { + std::smatch match; + if (std::regex_search(value, match, rule.rule)) { matched_any = true; - return rule.repr; + return match.format(rule.repr.data()); } } From d0a8c1d90dace8cccdb23e0da6d1e288770cc151 Mon Sep 17 00:00:00 2001 From: Nicolas Lenz Date: Wed, 5 Jun 2024 20:16:30 +0200 Subject: [PATCH 576/842] document capturing in window-rewrite --- man/waybar-hyprland-workspaces.5.scd | 1 + man/waybar-sway-workspaces.5.scd | 1 + 2 files changed, 2 insertions(+) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 406ada7a..686f8aa7 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -147,6 +147,7 @@ Additional to workspace name matching, the following *format-icons* can be set. "class title<.*github.*>": "", // Windows whose class is "firefox" and title contains "github". Note that "class" always comes first. "foot": "", // Windows that contain "foot" in either class or title. For optimization reasons, it will only match against a title if at least one other window explicitly matches against a title. "code": "󰨞", + "title<.* - (.*) - VSCodium>": "codium $1" // captures part of the window title and formats it into output } } ``` diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 8f0ac858..a65a999b 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -171,6 +171,7 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge "window-rewrite": { "class": "", "class": "k", + "title<.* - (.*) - VSCodium>": "codium $1" // captures part of the window title and formats it into output } } ``` From 1b1442e3ba8393edcd7f3a4b10ab212d35c3f523 Mon Sep 17 00:00:00 2001 From: zspher <66728045+zspher@users.noreply.github.com> Date: Thu, 6 Jun 2024 03:23:47 +0800 Subject: [PATCH 577/842] fix: taskbar not applying empty class on empty --- src/modules/wlr/taskbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 6e3e4e08..41d46748 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -894,7 +894,7 @@ void Taskbar::move_button(Gtk::Button &bt, int pos) { box_.reorder_child(bt, pos void Taskbar::remove_button(Gtk::Button &bt) { box_.remove(bt); - if (tasks_.empty()) { + if (box_.get_children().empty()) { box_.get_style_context()->add_class("empty"); } } From 637b220f820424f644e5785117695683b0888ddd Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 6 Jun 2024 15:24:01 -0700 Subject: [PATCH 578/842] sway/workspaces: Correct behavior when "current-only" is set The `current-only` workspace setting should display only the active workspace name as determined by its `focused` attribute. However, according to the `get_tree` output, workspaces that contain a focused window will report `"focused": false` and the window will report `"focused": true.` In this case, Waybar will not display a workspace name at all. This change updates the logic for determining if a workspace is focused by also looking for a focused window. --- src/modules/sway/workspaces.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 7517dc26..086ed5d1 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -501,7 +501,16 @@ std::string Workspaces::trimWorkspaceName(std::string name) { void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) { if (config_["current-only"].asBool()) { - if (node["focused"].asBool()) { + // If a workspace has a focused container then get_tree will say + // that the workspace itself isn't focused. Therefore we need to + // check if any of its nodes are focused as well. + bool focused = node["focused"].asBool() || + std::any_of(node["nodes"].begin(), node["nodes"].end(), + [](const auto &child) { + return child["focused"].asBool(); + }); + + if (focused) { button.show(); } else { button.hide(); From e1a6d513cca4ffa29ea60d38ab5d8e060965a1cd Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 13:01:00 -0500 Subject: [PATCH 579/842] test/config: add hyprland-workspaces config --- test/config.cpp | 39 ++++++++++++++++++++++++++++ test/config/hyprland-workspaces.json | 37 ++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 test/config/hyprland-workspaces.json diff --git a/test/config.cpp b/test/config.cpp index ad3df065..c60519ce 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -117,3 +117,42 @@ TEST_CASE("Load multiple bar config with include", "[config]") { REQUIRE(data.size() == 4); REQUIRE(data[0]["output"].asString() == "OUT-0"); } + +TEST_CASE("Load Hyprland Workspaces bar config", "[config]") { + waybar::Config conf; + conf.load("test/config/hyprland-workspaces.json"); + + auto& data = conf.getConfig(); + auto hyprland = data[0]["hyprland/workspaces"]; + auto hyprland_window_rewrite = data[0]["hyprland/workspaces"]["window-rewrite"]; + auto hyprland_format_icons = data[0]["hyprland/workspaces"]["format-icons"]; + auto hyprland_persistent_workspaces = data[0]["hyprland/workspaces"]["persistent-workspaces"]; + + REQUIRE(data.isArray()); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["height"].asInt() == 20); + REQUIRE(data[0]["layer"].asString() == "bottom"); + REQUIRE(data[0]["output"].isArray()); + REQUIRE(data[0]["output"][0].asString() == "HDMI-0"); + REQUIRE(data[0]["output"][1].asString() == "DP-0"); + + REQUIRE(hyprland["active-only"].asBool() == true); + REQUIRE(hyprland["all-outputs"].asBool() == false); + REQUIRE(hyprland["move-to-monitor"].asBool() == true); + REQUIRE(hyprland["format"].asString() == "{icon} {windows}"); + REQUIRE(hyprland["format-window-separator"].asString() == " "); + REQUIRE(hyprland["on-scroll-down"].asString() == "hyprctl dispatch workspace e-1"); + REQUIRE(hyprland["on-scroll-up"].asString() == "hyprctl dispatch workspace e+1"); + REQUIRE(hyprland["show-special"].asBool() == true); + REQUIRE(hyprland["window-rewrite-default"].asString() == ""); + REQUIRE(hyprland["window-rewrite-separator"].asString() == " "); + REQUIRE(hyprland_format_icons["1"].asString() == "󰎤"); + REQUIRE(hyprland_format_icons["2"].asString() == "󰎧"); + REQUIRE(hyprland_format_icons["3"].asString() == "󰎪"); + REQUIRE(hyprland_format_icons["default"].asString() == ""); + REQUIRE(hyprland_format_icons["empty"].asString() == "󱓼"); + REQUIRE(hyprland_format_icons["urgent"].asString() == "󱨇"); + REQUIRE(hyprland_persistent_workspaces["1"].asString() == "HDMI-0"); + REQUIRE(hyprland_window_rewrite["title"].asString() == ""); + REQUIRE(hyprland["sort-by"].asString() == "number"); +} diff --git a/test/config/hyprland-workspaces.json b/test/config/hyprland-workspaces.json new file mode 100644 index 00000000..dd733897 --- /dev/null +++ b/test/config/hyprland-workspaces.json @@ -0,0 +1,37 @@ +[ + { + "height": 20, + "layer": "bottom", + "output": [ + "HDMI-0", + "DP-0" + ], + "hyprland/workspaces": { + "active-only": true, + "all-outputs": false, + "show-special": true, + "move-to-monitor": true, + "format": "{icon} {windows}", + "format-window-separator": " ", + "format-icons": { + "1": "󰎤", + "2": "󰎧", + "3": "󰎪", + "default": "", + "empty": "󱓼", + "urgent": "󱨇" + }, + "persistent-workspaces": { + "1": "HDMI-0" + }, + "on-scroll-down": "hyprctl dispatch workspace e-1", + "on-scroll-up": "hyprctl dispatch workspace e+1", + "window-rewrite": { + "title": "" + }, + "window-rewrite-default": "", + "window-rewrite-separator": " ", + "sort-by": "number" + } + } +] From 1b3b45779aa308aa05067c98c33e173073890711 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 18:52:19 -0500 Subject: [PATCH 580/842] modules/hyprland/backend: add getSocketFolder to header --- include/modules/hyprland/backend.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index 9ce0ec33..b327482c 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -37,4 +38,5 @@ class IPC { inline std::unique_ptr gIPC; inline bool modulesReady = false; +std::filesystem::path getSocketFolder(const char* instanceSig); }; // namespace waybar::modules::hyprland From 0055ee69102441558bc8e72348c689540221dc57 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 18:52:42 -0500 Subject: [PATCH 581/842] modules/hyprland/workspaces: remove unneccesary visibleWorkspaces variable --- include/modules/hyprland/workspaces.hpp | 2 +- src/modules/hyprland/workspaces.cpp | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 0432b870..6c6e0932 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -100,7 +100,7 @@ class Workspaces : public AModule, public EventHandler { void removeWorkspacesToRemove(); void createWorkspacesToCreate(); std::vector getVisibleWorkspaces(); - void updateWorkspaceStates(const std::vector& visibleWorkspaces); + void updateWorkspaceStates(); bool updateWindowsToCreate(); void extendOrphans(int workspaceId, Json::Value const& clientsJson); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3b129375..3209243c 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -128,10 +128,7 @@ void Workspaces::doUpdate() { removeWorkspacesToRemove(); createWorkspacesToCreate(); - - std::vector visibleWorkspaces = getVisibleWorkspaces(); - - updateWorkspaceStates(visibleWorkspaces); + updateWorkspaceStates(); updateWindowCount(); sortWorkspaces(); @@ -870,7 +867,8 @@ bool Workspaces::updateWindowsToCreate() { return anyWindowCreated; } -void Workspaces::updateWorkspaceStates(const std::vector &visibleWorkspaces) { +void Workspaces::updateWorkspaceStates() { + const std::vector visibleWorkspaces = getVisibleWorkspaces(); auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { workspace->setActive(workspace->name() == m_activeWorkspaceName || From 749f46f86f1aa0d44227a2888d50d2551080beb5 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 21:50:52 -0500 Subject: [PATCH 582/842] test/fixtures: Add GlibTestsFixture --- test/SafeSignal.cpp | 2 +- test/{ => fixtures}/GlibTestsFixture.hpp | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename test/{ => fixtures}/GlibTestsFixture.hpp (100%) diff --git a/test/SafeSignal.cpp b/test/SafeSignal.cpp index f496d7ab..341e8e2e 100644 --- a/test/SafeSignal.cpp +++ b/test/SafeSignal.cpp @@ -10,7 +10,7 @@ #include #include -#include "GlibTestsFixture.hpp" +#include "fixtures/GlibTestsFixture.hpp" using namespace waybar; diff --git a/test/GlibTestsFixture.hpp b/test/fixtures/GlibTestsFixture.hpp similarity index 100% rename from test/GlibTestsFixture.hpp rename to test/fixtures/GlibTestsFixture.hpp From 87eaa75b8a8f53ea1cc877e31bdb1839956e83c9 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 28 May 2024 21:52:54 -0500 Subject: [PATCH 583/842] test/hyprland/backend: init --- test/hyprland/backend.cpp | 110 ++++++++++++++++++++++++++++++++++++++ test/hyprland/meson.build | 34 ++++++++++++ test/meson.build | 4 ++ 3 files changed, 148 insertions(+) create mode 100644 test/hyprland/backend.cpp create mode 100644 test/hyprland/meson.build diff --git a/test/hyprland/backend.cpp b/test/hyprland/backend.cpp new file mode 100644 index 00000000..edb51c55 --- /dev/null +++ b/test/hyprland/backend.cpp @@ -0,0 +1,110 @@ +#include +#if __has_include() +#include +#else +#include +#endif +#include +#include + +#include "modules/hyprland/backend.hpp" + +namespace fs = std::filesystem; +namespace hyprland = waybar::modules::hyprland; + +class testRunListener : public Catch::EventListenerBase { + public: + using Catch::EventListenerBase::EventListenerBase; + + void testCaseStarting(Catch::TestCaseInfo const&) override { + // TODO: reset state of module here + } +}; + +CATCH_REGISTER_LISTENER(testRunListener) + +TEST_CASE("GetSocketFolderTest", "[getSocketFolder]") { + SECTION("XDGRuntimeDirExists") { + // Test case: XDG_RUNTIME_DIR exists and contains "hypr" directory + // Arrange + std::cout << "Starting XDGRuntimeDirExists " << '\n'; + const char* instanceSig = "instance_sig"; + + fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; + std::cout << "Temp dir: " << tempDir << '\n'; + + fs::path expectedPath = tempDir / "hypr" / instanceSig; + std::cout << "Expected path: " << expectedPath << '\n'; + + fs::create_directories(tempDir / "hypr" / instanceSig); + + setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); + + // Act/* + std::cout << "Getting socket folder" << '\n'; + fs::path actualPath = hyprland::getSocketFolder(instanceSig); + + // Assert expected result + REQUIRE(actualPath == expectedPath); + + // Cleanup + fs::remove_all(tempDir); + + std::cout << "Finishing XDGRuntimeDirExists " << '\n'; + } + + // TODO: properly clear state so we can actually test these.... + /* SECTION("XDGRuntimeDirDoesNotExist") { */ + /* // Test case: XDG_RUNTIME_DIR does not exist */ + /* // Arrange */ + /* std::cout << "Starting XDGRuntimeDirDoesNotExist " << '\n'; */ + /* const char* instanceSig = "instance_sig"; */ + /**/ + /* std::cout << "Current XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ + /**/ + /* unsetenv("XDG_RUNTIME_DIR"); */ + /**/ + /* std::cout << "New XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ + /**/ + /* // Act */ + /* fs::path actualPath = hyprland::getSocketFolder(instanceSig); */ + /**/ + /* // Assert expected result */ + /* fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; */ + /* REQUIRE(actualPath == expectedPath); */ + /**/ + /* // Cleanup */ + /* std::cout << "Finishing XDGRuntimeDirDoesNotExist " << '\n'; */ + /* } */ + /**/ + /* SECTION("XDGRuntimeDirExistsNoHyprDir") { */ + /* // Test case: XDG_RUNTIME_DIR exists but does not contain "hypr" directory */ + /* // Arrange */ + /* std::cout << "Starting XDGRuntimeDirExistsNoHyprDir " << '\n'; */ + /**/ + /* const char* instanceSig = "instance_sig"; */ + /**/ + /* fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; */ + /* std::cout << "Temp dir: " << tempDir << '\n'; */ + /**/ + /* fs::create_directories(tempDir); */ + /**/ + /* setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); */ + /**/ + /* std::cout << "Current XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ + /**/ + /* // Act */ + /* fs::path actualPath = hyprland::getSocketFolder(instanceSig); */ + /**/ + /* // Assert expected result */ + /* fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; */ + /* std::cout << "Expected path: " << expectedPath << '\n'; */ + /**/ + /* REQUIRE(actualPath == expectedPath); */ + /**/ + /* // Cleanup */ + /* fs::remove_all(tempDir); */ + /**/ + /* std::cout << "Finishing XDGRuntimeDirExistsNoHyprDir " << '\n'; */ + /* } */ +} diff --git a/test/hyprland/meson.build b/test/hyprland/meson.build new file mode 100644 index 00000000..1f8e3a8b --- /dev/null +++ b/test/hyprland/meson.build @@ -0,0 +1,34 @@ +test_inc = include_directories('../../include') + +test_dep = [ + catch2, + fmt, + gtkmm, + jsoncpp, + spdlog, +] + +test_src = files( + '../main.cpp', + '../JsonParser.cpp', + '../SafeSignal.cpp', + '../config.cpp', + '../css_reload_helper.cpp', + '../../src/config.cpp', + '../../src/util/css_reload_helper.cpp', + 'backend.cpp', + '../../src/modules/hyprland/backend.cpp' +) + +hyprland_test = executable( + 'hyprland_test', + test_src, + dependencies: test_dep, + include_directories: test_inc, +) + +test( + 'hyprland', + hyprland_test, + workdir: meson.project_source_root(), +) diff --git a/test/meson.build b/test/meson.build index 7c922671..2c5f9c76 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,4 +1,5 @@ test_inc = include_directories('../include') + test_dep = [ catch2, fmt, @@ -6,6 +7,7 @@ test_dep = [ jsoncpp, spdlog, ] + test_src = files( 'main.cpp', 'JsonParser.cpp', @@ -33,3 +35,5 @@ test( waybar_test, workdir: meson.project_source_root(), ) + +subdir('hyprland') From 58e7abba2c2c7a8d5f3b258b7c79919030cbc70f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 8 Jun 2024 22:30:01 -0500 Subject: [PATCH 584/842] tests: split into separate binaries --- test/hyprland/meson.build | 6 ---- test/meson.build | 10 +----- test/{ => utils}/JsonParser.cpp | 0 test/{ => utils}/SafeSignal.cpp | 0 test/{ => utils}/css_reload_helper.cpp | 0 test/{ => utils}/date.cpp | 0 .../{ => utils}/fixtures/GlibTestsFixture.hpp | 0 test/utils/meson.build | 36 +++++++++++++++++++ 8 files changed, 37 insertions(+), 15 deletions(-) rename test/{ => utils}/JsonParser.cpp (100%) rename test/{ => utils}/SafeSignal.cpp (100%) rename test/{ => utils}/css_reload_helper.cpp (100%) rename test/{ => utils}/date.cpp (100%) rename test/{ => utils}/fixtures/GlibTestsFixture.hpp (100%) create mode 100644 test/utils/meson.build diff --git a/test/hyprland/meson.build b/test/hyprland/meson.build index 1f8e3a8b..533022fc 100644 --- a/test/hyprland/meson.build +++ b/test/hyprland/meson.build @@ -10,12 +10,6 @@ test_dep = [ test_src = files( '../main.cpp', - '../JsonParser.cpp', - '../SafeSignal.cpp', - '../config.cpp', - '../css_reload_helper.cpp', - '../../src/config.cpp', - '../../src/util/css_reload_helper.cpp', 'backend.cpp', '../../src/modules/hyprland/backend.cpp' ) diff --git a/test/meson.build b/test/meson.build index 2c5f9c76..ea430c50 100644 --- a/test/meson.build +++ b/test/meson.build @@ -10,19 +10,10 @@ test_dep = [ test_src = files( 'main.cpp', - 'JsonParser.cpp', - 'SafeSignal.cpp', 'config.cpp', - 'css_reload_helper.cpp', '../src/config.cpp', - '../src/util/css_reload_helper.cpp', ) -if tz_dep.found() - test_dep += tz_dep - test_src += files('date.cpp') -endif - waybar_test = executable( 'waybar_test', test_src, @@ -36,4 +27,5 @@ test( workdir: meson.project_source_root(), ) +subdir('utils') subdir('hyprland') diff --git a/test/JsonParser.cpp b/test/utils/JsonParser.cpp similarity index 100% rename from test/JsonParser.cpp rename to test/utils/JsonParser.cpp diff --git a/test/SafeSignal.cpp b/test/utils/SafeSignal.cpp similarity index 100% rename from test/SafeSignal.cpp rename to test/utils/SafeSignal.cpp diff --git a/test/css_reload_helper.cpp b/test/utils/css_reload_helper.cpp similarity index 100% rename from test/css_reload_helper.cpp rename to test/utils/css_reload_helper.cpp diff --git a/test/date.cpp b/test/utils/date.cpp similarity index 100% rename from test/date.cpp rename to test/utils/date.cpp diff --git a/test/fixtures/GlibTestsFixture.hpp b/test/utils/fixtures/GlibTestsFixture.hpp similarity index 100% rename from test/fixtures/GlibTestsFixture.hpp rename to test/utils/fixtures/GlibTestsFixture.hpp diff --git a/test/utils/meson.build b/test/utils/meson.build new file mode 100644 index 00000000..b7b3665a --- /dev/null +++ b/test/utils/meson.build @@ -0,0 +1,36 @@ +test_inc = include_directories('../../include') + +test_dep = [ + catch2, + fmt, + gtkmm, + jsoncpp, + spdlog, +] +test_src = files( + '../main.cpp', + '../config.cpp', + '../../src/config.cpp', + 'JsonParser.cpp', + 'SafeSignal.cpp', + 'css_reload_helper.cpp', + '../../src/util/css_reload_helper.cpp', +) + +if tz_dep.found() + test_dep += tz_dep + test_src += files('date.cpp') +endif + +utils_test = executable( + 'utils_test', + test_src, + dependencies: test_dep, + include_directories: test_inc, +) + +test( + 'utils', + utils_test, + workdir: meson.project_source_root(), +) From fa2e21dfd5f4870043c9ec7843e2e609e9a21521 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 8 Jun 2024 23:52:58 -0500 Subject: [PATCH 585/842] modules/hyprland/backend: move getSocketFolder to class --- include/modules/hyprland/backend.hpp | 5 ++++- src/modules/hyprland/backend.cpp | 23 +++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index b327482c..11e73d8f 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -26,6 +26,10 @@ class IPC { static std::string getSocket1Reply(const std::string& rq); Json::Value getSocket1JsonReply(const std::string& rq); + static std::filesystem::path getSocketFolder(const char* instanceSig); + + protected: + static std::filesystem::path socketFolder_; private: void startIPC(); @@ -38,5 +42,4 @@ class IPC { inline std::unique_ptr gIPC; inline bool modulesReady = false; -std::filesystem::path getSocketFolder(const char* instanceSig); }; // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 29c65633..05d16d05 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -15,22 +15,25 @@ namespace waybar::modules::hyprland { -std::filesystem::path getSocketFolder(const char* instanceSig) { +std::filesystem::path IPC::socketFolder_; + +std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { // socket path, specified by EventManager of Hyprland - static std::filesystem::path socketFolder; - if (!socketFolder.empty()) { - return socketFolder; + if (!socketFolder_.empty()) { + spdlog::warn("socketFolder already set, using {}", socketFolder_.c_str()); + return socketFolder_; + } } std::filesystem::path xdgRuntimeDir = std::filesystem::path(getenv("XDG_RUNTIME_DIR")); if (!xdgRuntimeDir.empty() && std::filesystem::exists(xdgRuntimeDir / "hypr")) { - socketFolder = xdgRuntimeDir / "hypr"; + socketFolder_ = xdgRuntimeDir / "hypr"; } else { spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr"); - socketFolder = std::filesystem::path("/tmp") / "hypr"; + socketFolder_ = std::filesystem::path("/tmp") / "hypr"; } - socketFolder = socketFolder / instanceSig; - return socketFolder; + socketFolder_ = socketFolder_ / instanceSig; + return socketFolder_; } void IPC::startIPC() { @@ -59,7 +62,7 @@ void IPC::startIPC() { addr.sun_family = AF_UNIX; - auto socketPath = getSocketFolder(his) / ".socket2.sock"; + auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock"; strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); addr.sun_path[sizeof(addr.sun_path) - 1] = 0; @@ -169,7 +172,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) { sockaddr_un serverAddress = {0}; serverAddress.sun_family = AF_UNIX; - std::string socketPath = getSocketFolder(instanceSig) / ".socket.sock"; + std::string socketPath = IPC::getSocketFolder(instanceSig) / ".socket.sock"; // Use snprintf to copy the socketPath string into serverAddress.sun_path if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < From 959422f143b37729dda3ae5ab14f5c4c0d71734b Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 8 Jun 2024 23:53:19 -0500 Subject: [PATCH 586/842] modules/hyprland/backend: protect against crash when XDG_RUNTIME_DIR not set --- src/modules/hyprland/backend.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 05d16d05..a39fcd69 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -23,15 +23,21 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { spdlog::warn("socketFolder already set, using {}", socketFolder_.c_str()); return socketFolder_; } + + const char* xdgRuntimeDirEnv = std::getenv("XDG_RUNTIME_DIR"); + std::filesystem::path xdgRuntimeDir; + // Only set path if env variable is set + if (xdgRuntimeDirEnv) { + xdgRuntimeDir = std::filesystem::path(xdgRuntimeDirEnv); } - std::filesystem::path xdgRuntimeDir = std::filesystem::path(getenv("XDG_RUNTIME_DIR")); if (!xdgRuntimeDir.empty() && std::filesystem::exists(xdgRuntimeDir / "hypr")) { socketFolder_ = xdgRuntimeDir / "hypr"; } else { spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr"); socketFolder_ = std::filesystem::path("/tmp") / "hypr"; } + socketFolder_ = socketFolder_ / instanceSig; return socketFolder_; } From b3658318391664c83844335ad2a07cdcb3279b82 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 9 Jun 2024 10:18:42 -0500 Subject: [PATCH 587/842] test/hyprland/backend: fix --- test/hyprland/backend.cpp | 135 +++++++--------------- test/hyprland/fixtures/IPCTestFixture.hpp | 16 +++ 2 files changed, 56 insertions(+), 95 deletions(-) create mode 100644 test/hyprland/fixtures/IPCTestFixture.hpp diff --git a/test/hyprland/backend.cpp b/test/hyprland/backend.cpp index edb51c55..e6b209da 100644 --- a/test/hyprland/backend.cpp +++ b/test/hyprland/backend.cpp @@ -4,107 +4,52 @@ #else #include #endif -#include -#include +#include "fixtures/IPCTestFixture.hpp" #include "modules/hyprland/backend.hpp" namespace fs = std::filesystem; namespace hyprland = waybar::modules::hyprland; -class testRunListener : public Catch::EventListenerBase { - public: - using Catch::EventListenerBase::EventListenerBase; +TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirExists", "[getSocketFolder]") { + // Test case: XDG_RUNTIME_DIR exists and contains "hypr" directory + // Arrange + tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; + fs::path expectedPath = tempDir / "hypr" / instanceSig; + fs::create_directories(tempDir / "hypr" / instanceSig); + setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); - void testCaseStarting(Catch::TestCaseInfo const&) override { - // TODO: reset state of module here - } -}; + // Act + fs::path actualPath = getSocketFolder(instanceSig); -CATCH_REGISTER_LISTENER(testRunListener) - -TEST_CASE("GetSocketFolderTest", "[getSocketFolder]") { - SECTION("XDGRuntimeDirExists") { - // Test case: XDG_RUNTIME_DIR exists and contains "hypr" directory - // Arrange - std::cout << "Starting XDGRuntimeDirExists " << '\n'; - const char* instanceSig = "instance_sig"; - - fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; - std::cout << "Temp dir: " << tempDir << '\n'; - - fs::path expectedPath = tempDir / "hypr" / instanceSig; - std::cout << "Expected path: " << expectedPath << '\n'; - - fs::create_directories(tempDir / "hypr" / instanceSig); - - setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); - - // Act/* - std::cout << "Getting socket folder" << '\n'; - fs::path actualPath = hyprland::getSocketFolder(instanceSig); - - // Assert expected result - REQUIRE(actualPath == expectedPath); - - // Cleanup - fs::remove_all(tempDir); - - std::cout << "Finishing XDGRuntimeDirExists " << '\n'; - } - - // TODO: properly clear state so we can actually test these.... - /* SECTION("XDGRuntimeDirDoesNotExist") { */ - /* // Test case: XDG_RUNTIME_DIR does not exist */ - /* // Arrange */ - /* std::cout << "Starting XDGRuntimeDirDoesNotExist " << '\n'; */ - /* const char* instanceSig = "instance_sig"; */ - /**/ - /* std::cout << "Current XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ - /**/ - /* unsetenv("XDG_RUNTIME_DIR"); */ - /**/ - /* std::cout << "New XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ - /**/ - /* // Act */ - /* fs::path actualPath = hyprland::getSocketFolder(instanceSig); */ - /**/ - /* // Assert expected result */ - /* fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; */ - /* REQUIRE(actualPath == expectedPath); */ - /**/ - /* // Cleanup */ - /* std::cout << "Finishing XDGRuntimeDirDoesNotExist " << '\n'; */ - /* } */ - /**/ - /* SECTION("XDGRuntimeDirExistsNoHyprDir") { */ - /* // Test case: XDG_RUNTIME_DIR exists but does not contain "hypr" directory */ - /* // Arrange */ - /* std::cout << "Starting XDGRuntimeDirExistsNoHyprDir " << '\n'; */ - /**/ - /* const char* instanceSig = "instance_sig"; */ - /**/ - /* fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; */ - /* std::cout << "Temp dir: " << tempDir << '\n'; */ - /**/ - /* fs::create_directories(tempDir); */ - /**/ - /* setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); */ - /**/ - /* std::cout << "Current XDG_RUNTIME_DIR: " << getenv("XDG_RUNTIME_DIR") << '\n'; */ - /**/ - /* // Act */ - /* fs::path actualPath = hyprland::getSocketFolder(instanceSig); */ - /**/ - /* // Assert expected result */ - /* fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; */ - /* std::cout << "Expected path: " << expectedPath << '\n'; */ - /**/ - /* REQUIRE(actualPath == expectedPath); */ - /**/ - /* // Cleanup */ - /* fs::remove_all(tempDir); */ - /**/ - /* std::cout << "Finishing XDGRuntimeDirExistsNoHyprDir " << '\n'; */ - /* } */ + // Assert expected result + REQUIRE(actualPath == expectedPath); +} + +TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirDoesNotExist", "[getSocketFolder]") { + // Test case: XDG_RUNTIME_DIR does not exist + // Arrange + unsetenv("XDG_RUNTIME_DIR"); + fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; + + // Act + fs::path actualPath = getSocketFolder(instanceSig); + + // Assert expected result + REQUIRE(actualPath == expectedPath); +} + +TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirExistsNoHyprDir", "[getSocketFolder]") { + // Test case: XDG_RUNTIME_DIR exists but does not contain "hypr" directory + // Arrange + fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000"; + fs::create_directories(tempDir); + setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1); + fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig; + + // Act + fs::path actualPath = getSocketFolder(instanceSig); + + // Assert expected result + REQUIRE(actualPath == expectedPath); } diff --git a/test/hyprland/fixtures/IPCTestFixture.hpp b/test/hyprland/fixtures/IPCTestFixture.hpp new file mode 100644 index 00000000..3b04b3b0 --- /dev/null +++ b/test/hyprland/fixtures/IPCTestFixture.hpp @@ -0,0 +1,16 @@ +#include "modules/hyprland/backend.hpp" + +namespace fs = std::filesystem; +namespace hyprland = waybar::modules::hyprland; + +class IPCTestFixture : public hyprland::IPC { + public: + IPCTestFixture() : IPC() { IPC::socketFolder_ = ""; } + ~IPCTestFixture() { fs::remove_all(tempDir); } + + protected: + const char* instanceSig = "instance_sig"; + fs::path tempDir = fs::temp_directory_path() / "hypr_test"; + + private: +}; From 08c5df3633e728864283d45e7f7f32970bb93311 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 9 Jun 2024 13:05:47 -0500 Subject: [PATCH 588/842] modules/sway/workspaces: clang-format fix --- src/modules/sway/workspaces.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 086ed5d1..2adde69c 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -505,10 +505,8 @@ void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) { // that the workspace itself isn't focused. Therefore we need to // check if any of its nodes are focused as well. bool focused = node["focused"].asBool() || - std::any_of(node["nodes"].begin(), node["nodes"].end(), - [](const auto &child) { - return child["focused"].asBool(); - }); + std::any_of(node["nodes"].begin(), node["nodes"].end(), + [](const auto &child) { return child["focused"].asBool(); }); if (focused) { button.show(); From 16ff5ee99b7c9be3ff6e7a22f5569dc59f1be5c7 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 9 Jun 2024 13:31:46 -0500 Subject: [PATCH 589/842] .github/workflows/linux: fail-fast --- .github/workflows/linux.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ae0f1f7f..c36f68e2 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -9,6 +9,7 @@ concurrency: jobs: build: strategy: + fail-fast: false matrix: distro: - alpine From 06fa931de9ea1e27b500c0f6d7ee77a29b233299 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 9 Jun 2024 13:38:45 -0500 Subject: [PATCH 590/842] Dockerfiles/opensuse: add python3-packaging dependency --- Dockerfiles/opensuse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index bdb42fbf..6ac3e058 100644 --- a/Dockerfiles/opensuse +++ b/Dockerfiles/opensuse @@ -6,4 +6,4 @@ RUN zypper -n up && \ zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \ zypper -n refresh && \ zypper -n install -t pattern devel_C_C++ && \ - zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc playerctl-devel + zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc playerctl-devel python3-packaging From 71bb2b64bfe906c10773195d79339200c1930745 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sun, 9 Jun 2024 15:08:43 -0500 Subject: [PATCH 591/842] subprojects/spdlog: bump spdlog Fixes alpine build and is a commonly distributed version --- subprojects/spdlog.wrap | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/subprojects/spdlog.wrap b/subprojects/spdlog.wrap index 69ef566f..08004c90 100644 --- a/subprojects/spdlog.wrap +++ b/subprojects/spdlog.wrap @@ -1,12 +1,13 @@ [wrap-file] -directory = spdlog-1.11.0 -source_url = https://github.com/gabime/spdlog/archive/v1.11.0.tar.gz -source_filename = v1.11.0.tar.gz -source_hash = ca5cae8d6cac15dae0ec63b21d6ad3530070650f68076f3a4a862ca293a858bb -patch_filename = spdlog_1.11.0-2_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.11.0-2/get_patch -patch_hash = db1364fe89502ac67f245a6c8c51290a52afd74a51eed26fa9ecb5b3443df57a -wrapdb_version = 1.11.0-2 +directory = spdlog-1.12.0 +source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.12.0.tar.gz +source_filename = spdlog-1.12.0.tar.gz +source_hash = 4dccf2d10f410c1e2feaff89966bfc49a1abb29ef6f08246335b110e001e09a9 +patch_filename = spdlog_1.12.0-2_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.12.0-2/get_patch +patch_hash = 9596972d1eb2e0a69cea4a53273ca7bbbcb9b2fa872cd734864fc7232dc2d573 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.12.0-2/spdlog-1.12.0.tar.gz +wrapdb_version = 1.12.0-2 [provide] spdlog = spdlog_dep From 07737867660a37c4507c1e0db6b2fbd52d92bace Mon Sep 17 00:00:00 2001 From: giskard Date: Thu, 9 May 2024 01:45:21 +0800 Subject: [PATCH 592/842] privacy: consider only configured modules along with the local clang-tidy warning fixes --- src/modules/privacy/privacy.cpp | 81 +++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index d3e3d4b2..97996c33 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -53,23 +53,22 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st modules.append(obj); } } - for (const auto& module_config : modules) { - if (!module_config.isObject() || !module_config["type"].isString()) continue; - const std::string type = module_config["type"].asString(); - if (type == "screenshare") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_VIDEO_INPUT, - &nodes_screenshare, pos, iconSize, transition_duration); - box_.add(*item); - } else if (type == "audio-in") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_INPUT, - &nodes_audio_in, pos, iconSize, transition_duration); - box_.add(*item); - } else if (type == "audio-out") { - auto* item = - Gtk::make_managed(module_config, PRIVACY_NODE_TYPE_AUDIO_OUTPUT, - &nodes_audio_out, pos, iconSize, transition_duration); + + std::map > typeMap = { + {"screenshare", {&nodes_screenshare, PRIVACY_NODE_TYPE_VIDEO_INPUT}}, + {"audio-in", {&nodes_audio_in, PRIVACY_NODE_TYPE_AUDIO_INPUT}}, + {"audio-out", {&nodes_audio_out, PRIVACY_NODE_TYPE_AUDIO_OUTPUT}}, + }; + + for (const auto& module : modules) { + if (!module.isObject() || !module["type"].isString()) continue; + const std::string type = module["type"].asString(); + + auto iter = typeMap.find(type); + if (iter != typeMap.end()) { + auto& [nodePtr, nodeType] = iter->second; + auto* item = Gtk::make_managed(module, nodeType, nodePtr, pos, iconSize, + transition_duration); box_.add(*item); } } @@ -114,26 +113,35 @@ void Privacy::onPrivacyNodesChanged() { } auto Privacy::update() -> void { - mutex_.lock(); - bool screenshare = false; - bool audio_in = false; - bool audio_out = false; + // set in modules or not + bool setScreenshare = false; + bool setAudioIn = false; + bool setAudioOut = false; + // used or not + bool useScreenshare = false; + bool useAudioIn = false; + bool useAudioOut = false; + + mutex_.lock(); for (Gtk::Widget* widget : box_.get_children()) { auto* module = dynamic_cast(widget); if (module == nullptr) continue; switch (module->privacy_type) { case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT: - screenshare = !nodes_screenshare.empty(); - module->set_in_use(screenshare); + setScreenshare = true; + useScreenshare = !nodes_screenshare.empty(); + module->set_in_use(useScreenshare); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT: - audio_in = !nodes_audio_in.empty(); - module->set_in_use(audio_in); + setAudioIn = true; + useAudioIn = !nodes_audio_in.empty(); + module->set_in_use(useAudioIn); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT: - audio_out = !nodes_audio_out.empty(); - module->set_in_use(audio_out); + setAudioOut = true; + useAudioOut = !nodes_audio_out.empty(); + module->set_in_use(useAudioOut); break; case util::PipewireBackend::PRIVACY_NODE_TYPE_NONE: break; @@ -142,25 +150,28 @@ auto Privacy::update() -> void { mutex_.unlock(); // Hide the whole widget if none are in use - bool is_visible = screenshare || audio_in || audio_out; - if (is_visible != event_box_.get_visible()) { + bool isVisible = (setScreenshare && useScreenshare) || (setAudioIn && useAudioIn) || + (setAudioOut && useAudioOut); + + if (isVisible != event_box_.get_visible()) { // Disconnect any previous connection so that it doesn't get activated in // the future, hiding the module when it should be visible visibility_conn.disconnect(); - if (is_visible) { + if (isVisible) { event_box_.set_visible(true); } else { // Hides the widget when all of the privacy_item revealers animations // have finished animating visibility_conn = Glib::signal_timeout().connect( sigc::track_obj( - [this] { + [this, setScreenshare, setAudioOut, setAudioIn]() { mutex_.lock(); - bool screenshare = !nodes_screenshare.empty(); - bool audio_in = !nodes_audio_in.empty(); - bool audio_out = !nodes_audio_out.empty(); + bool visible = false; + visible |= setScreenshare && !nodes_screenshare.empty(); + visible |= setAudioIn && !nodes_audio_in.empty(); + visible |= setAudioOut && !nodes_audio_out.empty(); mutex_.unlock(); - event_box_.set_visible(screenshare || audio_in || audio_out); + event_box_.set_visible(visible); return false; }, *this), From e8d91eb14bdc70759c8b1d34a677489911eebed8 Mon Sep 17 00:00:00 2001 From: giskard Date: Tue, 14 May 2024 14:39:30 +0800 Subject: [PATCH 593/842] mpris: hide on current player vanished --- src/modules/mpris/mpris.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index eea9a82b..1e8741f8 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -433,6 +433,7 @@ auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlaye if (std::string(player_name->name) == mpris->player_) { mpris->player = nullptr; + mpris->event_box_.set_visible(false); mpris->dp.emit(); } } From 7721dcdae80553584cfaf53f857335dfd2b07bc1 Mon Sep 17 00:00:00 2001 From: giskard Date: Tue, 14 May 2024 15:00:05 +0800 Subject: [PATCH 594/842] mpris: some clang-tidy fix --- src/modules/mpris/mpris.cpp | 72 ++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 1e8741f8..ed383b0c 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -96,9 +96,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } if (config_["dynamic-order"].isArray()) { dynamic_order_.clear(); - for (auto it = config_["dynamic-order"].begin(); it != config_["dynamic-order"].end(); ++it) { - if (it->isString()) { - dynamic_order_.push_back(it->asString()); + for (const auto& item : config_["dynamic-order"]) { + if (item.isString()) { + dynamic_order_.push_back(item.asString()); } } } @@ -110,10 +110,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) player_ = config_["player"].asString(); } if (config_["ignored-players"].isArray()) { - for (auto it = config_["ignored-players"].begin(); it != config_["ignored-players"].end(); - ++it) { - if (it->isString()) { - ignored_players_.push_back(it->asString()); + for (const auto& item : config_["ignored-players"]) { + if (item.isString()) { + ignored_players_.push_back(item.asString()); } } } @@ -146,8 +145,8 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) throw std::runtime_error(fmt::format("unable to list players: {}", error->message)); } - for (auto p = players; p != NULL; p = p->next) { - auto pn = static_cast(p->data); + for (auto* p = players; p != nullptr; p = p->next) { + auto* pn = static_cast(p->data); if (strcmp(pn->name, player_.c_str()) == 0) { player = playerctl_player_new_from_name(pn, &error); break; @@ -180,17 +179,14 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } Mpris::~Mpris() { - if (manager != NULL) g_object_unref(manager); - if (player != NULL) g_object_unref(player); + if (manager != nullptr) g_object_unref(manager); + if (player != nullptr) g_object_unref(player); } auto Mpris::getIconFromJson(const Json::Value& icons, const std::string& key) -> std::string { if (icons.isObject()) { - if (icons[key].isString()) { - return icons[key].asString(); - } else if (icons["default"].isString()) { - return icons["default"].asString(); - } + if (icons[key].isString()) return icons[key].asString(); + if (icons["default"].isString()) return icons["default"].asString(); } return ""; } @@ -205,7 +201,7 @@ size_t utf8_truncate(std::string& str, size_t width = std::string::npos) { size_t total_width = 0; - for (gchar *data = str.data(), *end = data + str.size(); data;) { + for (gchar *data = str.data(), *end = data + str.size(); data != nullptr;) { gunichar c = g_utf8_get_char_validated(data, end - data); if (c == -1U || c == -2U) { // invalid unicode, treat string as ascii @@ -269,7 +265,7 @@ auto Mpris::getLengthStr(const PlayerInfo& info, bool truncated) -> std::string auto length = info.length.value(); return (truncated && length.substr(0, 3) == "00:") ? length.substr(3) : length; } - return std::string(); + return {}; } auto Mpris::getPositionStr(const PlayerInfo& info, bool truncated) -> std::string { @@ -277,7 +273,7 @@ auto Mpris::getPositionStr(const PlayerInfo& info, bool truncated) -> std::strin auto position = info.position.value(); return (truncated && position.substr(0, 3) == "00:") ? position.substr(3) : position; } - return std::string(); + return {}; } auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> std::string { @@ -319,33 +315,33 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> size_t totalLen = 0; - for (auto it = dynamic_prio_.begin(); it != dynamic_prio_.end(); ++it) { - if (*it == "artist") { + for (const auto& item : dynamic_prio_) { + if (item == "artist") { if (totalLen + artistLen > dynamicLen) { showArtist = false; } else if (showArtist) { totalLen += artistLen; } - } else if (*it == "album") { + } else if (item == "album") { if (totalLen + albumLen > dynamicLen) { showAlbum = false; } else if (showAlbum) { totalLen += albumLen; } - } else if (*it == "title") { + } else if (item == "title") { if (totalLen + titleLen > dynamicLen) { showTitle = false; } else if (showTitle) { totalLen += titleLen; } - } else if (*it == "length") { + } else if (item == "length") { if (totalLen + lengthLen > dynamicLen) { showLength = false; } else if (showLength) { totalLen += lengthLen; posLen = std::max((size_t)2, posLen) - 2; } - } else if (*it == "position") { + } else if (item == "position") { if (totalLen + posLen > dynamicLen) { showPos = false; } else if (showPos) { @@ -406,7 +402,7 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: name-appeared callback: {}", player_name->name); @@ -415,7 +411,7 @@ auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlaye return; } - mpris->player = playerctl_player_new_from_name(player_name, NULL); + mpris->player = playerctl_player_new_from_name(player_name, nullptr); g_object_connect(mpris->player, "signal::play", G_CALLBACK(onPlayerPlay), mpris, "signal::pause", G_CALLBACK(onPlayerPause), mpris, "signal::stop", G_CALLBACK(onPlayerStop), mpris, "signal::stop", G_CALLBACK(onPlayerStop), mpris, "signal::metadata", @@ -426,7 +422,7 @@ auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlaye auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: player-vanished callback: {}", player_name->name); @@ -439,7 +435,7 @@ auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlaye } auto Mpris::onPlayerPlay(PlayerctlPlayer* player, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: player-play callback"); @@ -448,7 +444,7 @@ auto Mpris::onPlayerPlay(PlayerctlPlayer* player, gpointer data) -> void { } auto Mpris::onPlayerPause(PlayerctlPlayer* player, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: player-pause callback"); @@ -457,7 +453,7 @@ auto Mpris::onPlayerPause(PlayerctlPlayer* player, gpointer data) -> void { } auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: player-stop callback"); @@ -469,7 +465,7 @@ auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void { } auto Mpris::onPlayerMetadata(PlayerctlPlayer* player, GVariant* metadata, gpointer data) -> void { - Mpris* mpris = static_cast(data); + auto* mpris = static_cast(data); if (!mpris) return; spdlog::debug("mpris: player-metadata callback"); @@ -524,30 +520,30 @@ auto Mpris::getPlayerInfo() -> std::optional { .length = std::nullopt, }; - if (auto artist_ = playerctl_player_get_artist(player, &error)) { + if (auto* artist_ = playerctl_player_get_artist(player, &error)) { spdlog::debug("mpris[{}]: artist = {}", info.name, artist_); info.artist = artist_; g_free(artist_); } if (error) goto errorexit; - if (auto album_ = playerctl_player_get_album(player, &error)) { + if (auto* album_ = playerctl_player_get_album(player, &error)) { spdlog::debug("mpris[{}]: album = {}", info.name, album_); info.album = album_; g_free(album_); } if (error) goto errorexit; - if (auto title_ = playerctl_player_get_title(player, &error)) { + if (auto* title_ = playerctl_player_get_title(player, &error)) { spdlog::debug("mpris[{}]: title = {}", info.name, title_); info.title = title_; g_free(title_); } if (error) goto errorexit; - if (auto length_ = playerctl_player_print_metadata_prop(player, "mpris:length", &error)) { + if (auto* length_ = playerctl_player_print_metadata_prop(player, "mpris:length", &error)) { spdlog::debug("mpris[{}]: mpris:length = {}", info.name, length_); - std::chrono::microseconds len = std::chrono::microseconds(std::strtol(length_, nullptr, 10)); + auto len = std::chrono::microseconds(std::strtol(length_, nullptr, 10)); auto len_h = std::chrono::duration_cast(len); auto len_m = std::chrono::duration_cast(len - len_h); auto len_s = std::chrono::duration_cast(len - len_h - len_m); @@ -564,7 +560,7 @@ auto Mpris::getPlayerInfo() -> std::optional { error = nullptr; } else { spdlog::debug("mpris[{}]: position = {}", info.name, position_); - std::chrono::microseconds len = std::chrono::microseconds(position_); + auto len = std::chrono::microseconds(position_); auto len_h = std::chrono::duration_cast(len); auto len_m = std::chrono::duration_cast(len - len_h); auto len_s = std::chrono::duration_cast(len - len_h - len_m); From 1cd013a09b1034733056d3775c4a73a870ab6d91 Mon Sep 17 00:00:00 2001 From: giskard Date: Mon, 10 Jun 2024 17:30:33 +0800 Subject: [PATCH 595/842] clock: respect tooltip option --- src/modules/clock.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 9f279711..55d3395b 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -12,7 +12,8 @@ #ifdef HAVE_LANGINFO_1STDAY #include -#include + +#include #endif namespace fmt_lib = waybar::util::date::format; @@ -126,8 +127,10 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } } - label_.set_has_tooltip(true); - label_.signal_query_tooltip().connect(sigc::mem_fun(*this, &Clock::query_tlp_cb)); + if (tooltipEnabled()) { + label_.set_has_tooltip(true); + label_.signal_query_tooltip().connect(sigc::mem_fun(*this, &Clock::query_tlp_cb)); + } thread_ = [this] { dp.emit(); @@ -194,8 +197,8 @@ const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) { return 2u + ceil((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count(); } -auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line) - -> const year_month_weekday { +auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, + const unsigned line) -> const year_month_weekday { unsigned index{line - 2}; if (weekday{ym / 1} == firstdow) ++index; return ym / firstdow[index]; From 892042eb92f9b95736930298bf07ff02ebfaded9 Mon Sep 17 00:00:00 2001 From: Oliver Locke Date: Wed, 12 Jun 2024 17:03:39 +1000 Subject: [PATCH 596/842] Support muted icons for pulseaudio devices/ports --- src/modules/pulseaudio.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index d7dc80d3..3efd9d23 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -42,15 +42,27 @@ static const std::array ports = { }; const std::vector waybar::modules::Pulseaudio::getPulseIcon() const { - std::vector res = {backend->getCurrentSinkName(), backend->getDefaultSourceName()}; + std::vector res; + auto sink_muted = backend->getSinkMuted(); + if (sink_muted) { + res.emplace_back(backend->getCurrentSinkName() + "-muted"); + } + res.push_back(backend->getCurrentSinkName()); + res.push_back(backend->getDefaultSourceName()); std::string nameLC = backend->getSinkPortName() + backend->getFormFactor(); std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { if (nameLC.find(port) != std::string::npos) { + if (sink_muted) { + res.emplace_back(port + "-muted"); + } res.push_back(port); - return res; + break; } } + if (sink_muted) { + res.emplace_back("default-muted"); + } return res; } From 0bc43c1aa74cebb688e16f8d00d15d71ac68067a Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 12 Jun 2024 23:08:27 +0200 Subject: [PATCH 597/842] fix: lint --- include/AModule.hpp | 2 +- include/modules/clock.hpp | 4 ++-- include/util/clara.hpp | 12 ++++++------ include/util/format.hpp | 2 +- src/modules/bluetooth.cpp | 17 ++++++++--------- src/modules/cpu_usage/linux.cpp | 3 +-- src/modules/dwl/tags.cpp | 4 ++-- src/modules/hyprland/workspaces.cpp | 4 ++-- src/modules/memory/bsd.cpp | 8 ++++---- src/modules/wlr/workspace_manager.cpp | 4 ++-- test/utils/SafeSignal.cpp | 2 +- 11 files changed, 30 insertions(+), 32 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 58076655..8a5a665b 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -15,7 +15,7 @@ class AModule : public IModule { virtual ~AModule(); auto update() -> void override; - virtual auto refresh(int) -> void{}; + virtual auto refresh(int) -> void {}; operator Gtk::Widget &() override; auto doAction(const std::string &name) -> void override; diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index c212ec8b..0c62b676 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -51,8 +51,8 @@ class Clock final : public ALabel { day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight) std::string cldText_{""}; // calendar text to print CldMode cldMode_{CldMode::MONTH}; - auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) - -> const std::string; + auto get_calendar(const year_month_day& today, const year_month_day& ymd, + const time_zone* tz) -> const std::string; // get local time zone auto local_zone() -> const time_zone*; diff --git a/include/util/clara.hpp b/include/util/clara.hpp index 73fa5415..da7151fe 100644 --- a/include/util/clara.hpp +++ b/include/util/clara.hpp @@ -622,8 +622,8 @@ inline auto convertInto(std::string const &source, bool &target) -> ParserResult } #ifdef CLARA_CONFIG_OPTIONAL_TYPE template -inline auto convertInto(std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE &target) - -> ParserResult { +inline auto convertInto(std::string const &source, + CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult { T temp; auto result = convertInto(source, temp); if (result) target = std::move(temp); @@ -751,8 +751,8 @@ class ParserBase { public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse(std::string const &exeName, TokenStream const &tokens) const - -> InternalParseResult = 0; + virtual auto parse(std::string const &exeName, + TokenStream const &tokens) const -> InternalParseResult = 0; virtual auto cardinality() const -> size_t { return 1; } auto parse(Args const &args) const -> InternalParseResult { @@ -1098,8 +1098,8 @@ struct Parser : ParserBase { using ParserBase::parse; - auto parse(std::string const &exeName, TokenStream const &tokens) const - -> InternalParseResult override { + auto parse(std::string const &exeName, + TokenStream const &tokens) const -> InternalParseResult override { struct ParserInfo { ParserBase const *parser = nullptr; size_t count = 0; diff --git a/include/util/format.hpp b/include/util/format.hpp index 069d8897..df39bb1b 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -6,7 +6,7 @@ class pow_format { public: pow_format(long long val, std::string&& unit, bool binary = false) - : val_(val), unit_(unit), binary_(binary){}; + : val_(val), unit_(unit), binary_(binary) {}; long long val_; std::string unit_; diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 80e4731b..3436ae29 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -49,8 +49,8 @@ auto getBoolProperty(GDBusProxy* proxy, const char* property_name) -> bool { return false; } -auto getOptionalStringProperty(GDBusProxy* proxy, const char* property_name) - -> std::optional { +auto getOptionalStringProperty(GDBusProxy* proxy, + const char* property_name) -> std::optional { auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name); if (gvar) { std::string property_value = g_variant_get_string(gvar, NULL); @@ -303,8 +303,8 @@ auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* m auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged( GDBusObjectManagerClient* manager, GDBusObjectProxy* object_proxy, GDBusProxy* interface_proxy, - GVariant* changed_properties, const gchar* const* invalidated_properties, gpointer user_data) - -> void { + GVariant* changed_properties, const gchar* const* invalidated_properties, + gpointer user_data) -> void { std::string interface_name = g_dbus_proxy_get_interface_name(interface_proxy); std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy)); @@ -348,8 +348,8 @@ auto waybar::modules::Bluetooth::getDeviceBatteryPercentage(GDBusObject* object) return std::nullopt; } -auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, DeviceInfo& device_info) - -> bool { +auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, + DeviceInfo& device_info) -> bool { GDBusProxy* proxy_device = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Device1")); if (proxy_device != NULL) { @@ -415,9 +415,8 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional& connected_devices) - -> void { +auto waybar::modules::Bluetooth::findConnectedDevices( + const std::string& cur_controller_path, std::vector& connected_devices) -> void { GList* objects = g_dbus_object_manager_get_objects(manager_.get()); for (GList* l = objects; l != NULL; l = l->next) { GDBusObject* object = G_DBUS_OBJECT(l->data); diff --git a/src/modules/cpu_usage/linux.cpp b/src/modules/cpu_usage/linux.cpp index bcd9594e..a4712350 100644 --- a/src/modules/cpu_usage/linux.cpp +++ b/src/modules/cpu_usage/linux.cpp @@ -16,8 +16,7 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( } std::stringstream sline(line.substr(5)); std::vector times; - for (size_t time = 0; sline >> time; times.push_back(time)) - ; + for (size_t time = 0; sline >> time; times.push_back(time)); size_t idle_time = 0; size_t total_time = 0; diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index 085b8224..f8b250c8 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -53,8 +53,8 @@ static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t // Intentionally empty } -static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid){ - // Intentionally empty +static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid) { + // Intentionally empty }; static const zdwl_ipc_output_v2_listener output_status_listener_impl{ diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3209243c..e5bee553 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -591,8 +591,8 @@ auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { m_iconsMap.emplace("", ""); } -auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) - -> void { +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, + bool &member) -> void { auto configValue = config[key]; if (configValue.isBool()) { member = configValue.asBool(); diff --git a/src/modules/memory/bsd.cpp b/src/modules/memory/bsd.cpp index 67f9fed7..1d970e8a 100644 --- a/src/modules/memory/bsd.cpp +++ b/src/modules/memory/bsd.cpp @@ -21,13 +21,13 @@ static uint64_t get_total_memory() { u_long physmem; #endif int mib[] = { - CTL_HW, + CTL_HW, #if defined(HW_MEMSIZE) - HW_MEMSIZE, + HW_MEMSIZE, #elif defined(HW_PHYSMEM64) - HW_PHYSMEM64, + HW_PHYSMEM64, #else - HW_PHYSMEM, + HW_PHYSMEM, #endif }; u_int miblen = sizeof(mib) / sizeof(mib[0]); diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index f556a161..3c630d81 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -118,8 +118,8 @@ auto WorkspaceManager::sort_workspaces() -> void { } } -auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) - -> void { +auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, + uint32_t version) -> void { if (workspace_manager_) { spdlog::warn("Register workspace manager again although already registered!"); return; diff --git a/test/utils/SafeSignal.cpp b/test/utils/SafeSignal.cpp index 341e8e2e..e7e096b0 100644 --- a/test/utils/SafeSignal.cpp +++ b/test/utils/SafeSignal.cpp @@ -71,7 +71,7 @@ struct TestObject { unsigned copied = 0; unsigned moved = 0; - TestObject(const T& v) : value(v){}; + TestObject(const T& v) : value(v) {}; ~TestObject() = default; TestObject(const TestObject& other) From 01438f71a489f5a755f215c203cd73c0c7fe20d3 Mon Sep 17 00:00:00 2001 From: Oliver Locke Date: Thu, 13 Jun 2024 15:59:42 +1000 Subject: [PATCH 598/842] Added muted icons usage to waybar-pulseaudio man page --- man/waybar-pulseaudio.5.scd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index 4bc75258..6a29e8fc 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -142,6 +142,8 @@ If they are found in the current PulseAudio port name, the corresponding icons w - *hifi* - *phone* +Additionally, suffixing a device name or port with *-muted* will cause the icon +to be selected when the corresponding audio device is muted. This applies to *default* as well. # EXAMPLES @@ -152,10 +154,12 @@ If they are found in the current PulseAudio port name, the corresponding icons w "format-muted": "", "format-icons": { "alsa_output.pci-0000_00_1f.3.analog-stereo": "", + "alsa_output.pci-0000_00_1f.3.analog-stereo-muted": "", "headphones": "", "handsfree": "", "headset": "", "phone": "", + "phone-muted": "", "portable": "", "car": "", "default": ["", ""] From ab91d0bac3b16948536b40a233acea446591be99 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Fri, 14 Jun 2024 02:24:24 +0200 Subject: [PATCH 599/842] Add hotplug detection of bluetooth controllers --- include/modules/bluetooth.hpp | 3 ++ src/modules/bluetooth.cpp | 79 +++++++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 18 deletions(-) diff --git a/include/modules/bluetooth.hpp b/include/modules/bluetooth.hpp index 89658dcf..b89383a0 100644 --- a/include/modules/bluetooth.hpp +++ b/include/modules/bluetooth.hpp @@ -49,6 +49,9 @@ class Bluetooth : public ALabel { auto update() -> void override; private: + static auto onObjectAdded(GDBusObjectManager*, GDBusObject*, gpointer) -> void; + static auto onObjectRemoved(GDBusObjectManager*, GDBusObject*, gpointer) -> void; + static auto onInterfaceAddedOrRemoved(GDBusObjectManager*, GDBusObject*, GDBusInterface*, gpointer) -> void; static auto onInterfaceProxyPropertiesChanged(GDBusObjectManagerClient*, GDBusObjectProxy*, diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 3436ae29..478d0073 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -98,30 +98,31 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& std::back_inserter(device_preference_), [](auto x) { return x.asString(); }); } - // NOTE: assumption made that the controller that is selected stays unchanged - // for duration of the module if (cur_controller_ = findCurController(); !cur_controller_) { if (config_["controller-alias"].isString()) { - spdlog::error("findCurController() failed: no bluetooth controller found with alias '{}'", + spdlog::warn("no bluetooth controller found with alias '{}'", config_["controller-alias"].asString()); } else { - spdlog::error("findCurController() failed: no bluetooth controller found"); + spdlog::warn("no bluetooth controller found"); } update(); } else { - // These calls only make sense if a controller could be found + // This call only make sense if a controller could be found findConnectedDevices(cur_controller_->path, connected_devices_); - g_signal_connect(manager_.get(), "interface-proxy-properties-changed", - G_CALLBACK(onInterfaceProxyPropertiesChanged), this); - g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), - this); - g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved), - this); + } + + g_signal_connect(manager_.get(), "object-added", G_CALLBACK(onObjectAdded), this); + g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this); + g_signal_connect(manager_.get(), "interface-proxy-properties-changed", + G_CALLBACK(onInterfaceProxyPropertiesChanged), this); + g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), + this); + g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved), + this); #ifdef WANT_RFKILL - rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update))); + rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update))); #endif - } dp.emit(); } @@ -282,6 +283,41 @@ auto waybar::modules::Bluetooth::update() -> void { ALabel::update(); } +auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, + GDBusObject* object, + gpointer user_data) -> void { + ControllerInfo info; + Bluetooth* bt = static_cast(user_data); + + if (!bt->cur_controller_.has_value() && + bt->getControllerProperties(object, info) && + (!bt->config_["controller-alias"].isString() || + bt->config_["controller-alias"].asString() == info.alias)) { + bt->cur_controller_ = std::move(info); + bt->dp.emit(); + } +} + +auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, + GDBusObject* object, + gpointer user_data) -> void { + GDBusProxy* proxy_controller = + G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1")); + + if (proxy_controller != NULL) { + + std::string object_path = g_dbus_object_get_object_path(object); + Bluetooth* bt = static_cast(user_data); + + if (object_path == bt->cur_controller_->path) { + bt->cur_controller_ = bt->findCurController(); + bt->dp.emit(); + } + + g_object_unref(proxy_controller); + } +} + // NOTE: only for when the org.bluez.Battery1 interface is added/removed after/before a device is // connected/disconnected auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* manager, @@ -292,11 +328,13 @@ auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* m std::string object_path = g_dbus_proxy_get_object_path(G_DBUS_PROXY(interface)); if (interface_name == "org.bluez.Battery1") { Bluetooth* bt = static_cast(user_data); - auto device = std::find_if(bt->connected_devices_.begin(), bt->connected_devices_.end(), - [object_path](auto d) { return d.path == object_path; }); - if (device != bt->connected_devices_.end()) { - device->battery_percentage = bt->getDeviceBatteryPercentage(object); - bt->dp.emit(); + if (bt->cur_controller_.has_value()) { + auto device = std::find_if(bt->connected_devices_.begin(), bt->connected_devices_.end(), + [object_path](auto d) { return d.path == object_path; }); + if (device != bt->connected_devices_.end()) { + device->battery_percentage = bt->getDeviceBatteryPercentage(object); + bt->dp.emit(); + } } } } @@ -309,6 +347,11 @@ auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged( std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy)); Bluetooth* bt = static_cast(user_data); + + if (!bt->cur_controller_.has_value()) { + return; + } + if (interface_name == "org.bluez.Adapter1") { if (object_path == bt->cur_controller_->path) { bt->getControllerProperties(G_DBUS_OBJECT(object_proxy), *bt->cur_controller_); From 0df3c84c0f66e9ca156859fe2b4ad1a832e9b6a6 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Fri, 14 Jun 2024 14:00:55 +0200 Subject: [PATCH 600/842] Fix device list not being updated on selecting new bluetooth controller --- src/modules/bluetooth.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 478d0073..c3c8c2c3 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -301,16 +301,25 @@ auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, GDBusObject* object, gpointer user_data) -> void { - GDBusProxy* proxy_controller = - G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1")); + Bluetooth* bt = static_cast(user_data); + GDBusProxy* proxy_controller; + + if (!bt->cur_controller_.has_value()) { + return; + } + + proxy_controller = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1")); if (proxy_controller != NULL) { std::string object_path = g_dbus_object_get_object_path(object); - Bluetooth* bt = static_cast(user_data); if (object_path == bt->cur_controller_->path) { bt->cur_controller_ = bt->findCurController(); + if (bt->cur_controller_.has_value()) { + bt->connected_devices_.clear(); + bt->findConnectedDevices(bt->cur_controller_->path, bt->connected_devices_); + } bt->dp.emit(); } From bac4d038131ed9ab5a4f4440f102a455cdbe7cc2 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 15 Jun 2024 18:34:45 -0500 Subject: [PATCH 601/842] modules/hyprland/workspaces: remove deprecated property --- include/modules/hyprland/workspaces.hpp | 1 - src/modules/hyprland/workspaces.cpp | 15 +++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 6c6e0932..df7292b1 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -66,7 +66,6 @@ class Workspaces : public AModule, public EventHandler { auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) -> void; auto populateSortByConfig(const Json::Value& config) -> void; auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; - auto populatePersistentWorkspacesConfig(const Json::Value& config) -> void; auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; auto populateWindowRewriteConfig(const Json::Value& config) -> void; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index e5bee553..52239b7b 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -577,9 +577,9 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { populateBoolConfig(config, "active-only", m_activeOnly); populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); + m_persistentWorkspaceConfig = config.get("persistent-workspaces", Json::Value()); populateSortByConfig(config); populateIgnoreWorkspacesConfig(config); - populatePersistentWorkspacesConfig(config); populateFormatWindowSeparatorConfig(config); populateWindowRewriteConfig(config); } @@ -591,8 +591,8 @@ auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { m_iconsMap.emplace("", ""); } -auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, - bool &member) -> void { +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) + -> void { auto configValue = config[key]; if (configValue.isBool()) { member = configValue.asBool(); @@ -632,15 +632,6 @@ auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> vo } } -auto Workspaces::populatePersistentWorkspacesConfig(const Json::Value &config) -> void { - if (config.isMember("persistent-workspaces") || config.isMember("persistent_workspaces")) { - spdlog::warn( - "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); - m_persistentWorkspaceConfig = - config.get("persistent-workspaces", config.get("persistent_workspaces", Json::Value())); - } -} - auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void { auto formatWindowSeparator = config["format-window-separator"]; m_formatWindowSeparator = From f9e693b2a2f5c627c64725487c6c085803a98d93 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 15 Jun 2024 18:36:59 -0500 Subject: [PATCH 602/842] modules/hyprland/backend: remove testing log warn --- src/modules/hyprland/backend.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index a39fcd69..6b36b04e 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -20,7 +20,6 @@ std::filesystem::path IPC::socketFolder_; std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { // socket path, specified by EventManager of Hyprland if (!socketFolder_.empty()) { - spdlog::warn("socketFolder already set, using {}", socketFolder_.c_str()); return socketFolder_; } From b114b1155c2a9fe4c888087533cacac1d0c3438e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 15 Jun 2024 18:44:46 -0500 Subject: [PATCH 603/842] treewide: clang-format --- include/AModule.hpp | 2 +- include/modules/clock.hpp | 4 ++-- include/util/clara.hpp | 12 +++++----- include/util/format.hpp | 2 +- src/modules/bluetooth.cpp | 32 ++++++++++++--------------- src/modules/clock.cpp | 4 ++-- src/modules/cpu_usage/linux.cpp | 3 ++- src/modules/dwl/tags.cpp | 4 ++-- src/modules/memory/bsd.cpp | 8 +++---- src/modules/wlr/workspace_manager.cpp | 4 ++-- test/utils/SafeSignal.cpp | 2 +- 11 files changed, 37 insertions(+), 40 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 8a5a665b..58076655 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -15,7 +15,7 @@ class AModule : public IModule { virtual ~AModule(); auto update() -> void override; - virtual auto refresh(int) -> void {}; + virtual auto refresh(int) -> void{}; operator Gtk::Widget &() override; auto doAction(const std::string &name) -> void override; diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 0c62b676..c212ec8b 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -51,8 +51,8 @@ class Clock final : public ALabel { day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight) std::string cldText_{""}; // calendar text to print CldMode cldMode_{CldMode::MONTH}; - auto get_calendar(const year_month_day& today, const year_month_day& ymd, - const time_zone* tz) -> const std::string; + auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) + -> const std::string; // get local time zone auto local_zone() -> const time_zone*; diff --git a/include/util/clara.hpp b/include/util/clara.hpp index da7151fe..73fa5415 100644 --- a/include/util/clara.hpp +++ b/include/util/clara.hpp @@ -622,8 +622,8 @@ inline auto convertInto(std::string const &source, bool &target) -> ParserResult } #ifdef CLARA_CONFIG_OPTIONAL_TYPE template -inline auto convertInto(std::string const &source, - CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult { +inline auto convertInto(std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE &target) + -> ParserResult { T temp; auto result = convertInto(source, temp); if (result) target = std::move(temp); @@ -751,8 +751,8 @@ class ParserBase { public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse(std::string const &exeName, - TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto parse(std::string const &exeName, TokenStream const &tokens) const + -> InternalParseResult = 0; virtual auto cardinality() const -> size_t { return 1; } auto parse(Args const &args) const -> InternalParseResult { @@ -1098,8 +1098,8 @@ struct Parser : ParserBase { using ParserBase::parse; - auto parse(std::string const &exeName, - TokenStream const &tokens) const -> InternalParseResult override { + auto parse(std::string const &exeName, TokenStream const &tokens) const + -> InternalParseResult override { struct ParserInfo { ParserBase const *parser = nullptr; size_t count = 0; diff --git a/include/util/format.hpp b/include/util/format.hpp index df39bb1b..069d8897 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -6,7 +6,7 @@ class pow_format { public: pow_format(long long val, std::string&& unit, bool binary = false) - : val_(val), unit_(unit), binary_(binary) {}; + : val_(val), unit_(unit), binary_(binary){}; long long val_; std::string unit_; diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index c3c8c2c3..06475a2e 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -49,8 +49,8 @@ auto getBoolProperty(GDBusProxy* proxy, const char* property_name) -> bool { return false; } -auto getOptionalStringProperty(GDBusProxy* proxy, - const char* property_name) -> std::optional { +auto getOptionalStringProperty(GDBusProxy* proxy, const char* property_name) + -> std::optional { auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name); if (gvar) { std::string property_value = g_variant_get_string(gvar, NULL); @@ -101,7 +101,7 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& if (cur_controller_ = findCurController(); !cur_controller_) { if (config_["controller-alias"].isString()) { spdlog::warn("no bluetooth controller found with alias '{}'", - config_["controller-alias"].asString()); + config_["controller-alias"].asString()); } else { spdlog::warn("no bluetooth controller found"); } @@ -115,8 +115,7 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this); g_signal_connect(manager_.get(), "interface-proxy-properties-changed", G_CALLBACK(onInterfaceProxyPropertiesChanged), this); - g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), - this); + g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), this); g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved), this); @@ -283,14 +282,12 @@ auto waybar::modules::Bluetooth::update() -> void { ALabel::update(); } -auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, - GDBusObject* object, +auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, GDBusObject* object, gpointer user_data) -> void { ControllerInfo info; Bluetooth* bt = static_cast(user_data); - if (!bt->cur_controller_.has_value() && - bt->getControllerProperties(object, info) && + if (!bt->cur_controller_.has_value() && bt->getControllerProperties(object, info) && (!bt->config_["controller-alias"].isString() || bt->config_["controller-alias"].asString() == info.alias)) { bt->cur_controller_ = std::move(info); @@ -298,8 +295,7 @@ auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, } } -auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, - GDBusObject* object, +auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, GDBusObject* object, gpointer user_data) -> void { Bluetooth* bt = static_cast(user_data); GDBusProxy* proxy_controller; @@ -311,7 +307,6 @@ auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, proxy_controller = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1")); if (proxy_controller != NULL) { - std::string object_path = g_dbus_object_get_object_path(object); if (object_path == bt->cur_controller_->path) { @@ -350,8 +345,8 @@ auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* m auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged( GDBusObjectManagerClient* manager, GDBusObjectProxy* object_proxy, GDBusProxy* interface_proxy, - GVariant* changed_properties, const gchar* const* invalidated_properties, - gpointer user_data) -> void { + GVariant* changed_properties, const gchar* const* invalidated_properties, gpointer user_data) + -> void { std::string interface_name = g_dbus_proxy_get_interface_name(interface_proxy); std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy)); @@ -400,8 +395,8 @@ auto waybar::modules::Bluetooth::getDeviceBatteryPercentage(GDBusObject* object) return std::nullopt; } -auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, - DeviceInfo& device_info) -> bool { +auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, DeviceInfo& device_info) + -> bool { GDBusProxy* proxy_device = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Device1")); if (proxy_device != NULL) { @@ -467,8 +462,9 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional& connected_devices) -> void { +auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path, + std::vector& connected_devices) + -> void { GList* objects = g_dbus_object_manager_get_objects(manager_.get()); for (GList* l = objects; l != NULL; l = l->next) { GDBusObject* object = G_DBUS_OBJECT(l->data); diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 55d3395b..fe2c4c8f 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -197,8 +197,8 @@ const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) { return 2u + ceil((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count(); } -auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, - const unsigned line) -> const year_month_weekday { +auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line) + -> const year_month_weekday { unsigned index{line - 2}; if (weekday{ym / 1} == firstdow) ++index; return ym / firstdow[index]; diff --git a/src/modules/cpu_usage/linux.cpp b/src/modules/cpu_usage/linux.cpp index a4712350..bcd9594e 100644 --- a/src/modules/cpu_usage/linux.cpp +++ b/src/modules/cpu_usage/linux.cpp @@ -16,7 +16,8 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( } std::stringstream sline(line.substr(5)); std::vector times; - for (size_t time = 0; sline >> time; times.push_back(time)); + for (size_t time = 0; sline >> time; times.push_back(time)) + ; size_t idle_time = 0; size_t total_time = 0; diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index f8b250c8..085b8224 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -53,8 +53,8 @@ static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t // Intentionally empty } -static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid) { - // Intentionally empty +static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid){ + // Intentionally empty }; static const zdwl_ipc_output_v2_listener output_status_listener_impl{ diff --git a/src/modules/memory/bsd.cpp b/src/modules/memory/bsd.cpp index 1d970e8a..67f9fed7 100644 --- a/src/modules/memory/bsd.cpp +++ b/src/modules/memory/bsd.cpp @@ -21,13 +21,13 @@ static uint64_t get_total_memory() { u_long physmem; #endif int mib[] = { - CTL_HW, + CTL_HW, #if defined(HW_MEMSIZE) - HW_MEMSIZE, + HW_MEMSIZE, #elif defined(HW_PHYSMEM64) - HW_PHYSMEM64, + HW_PHYSMEM64, #else - HW_PHYSMEM, + HW_PHYSMEM, #endif }; u_int miblen = sizeof(mib) / sizeof(mib[0]); diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 3c630d81..f556a161 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -118,8 +118,8 @@ auto WorkspaceManager::sort_workspaces() -> void { } } -auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, - uint32_t version) -> void { +auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) + -> void { if (workspace_manager_) { spdlog::warn("Register workspace manager again although already registered!"); return; diff --git a/test/utils/SafeSignal.cpp b/test/utils/SafeSignal.cpp index e7e096b0..341e8e2e 100644 --- a/test/utils/SafeSignal.cpp +++ b/test/utils/SafeSignal.cpp @@ -71,7 +71,7 @@ struct TestObject { unsigned copied = 0; unsigned moved = 0; - TestObject(const T& v) : value(v) {}; + TestObject(const T& v) : value(v){}; ~TestObject() = default; TestObject(const TestObject& other) From c4d769a586d9f72ff4d209c51c44e23591965df9 Mon Sep 17 00:00:00 2001 From: Felix Glinka Date: Fri, 21 Jun 2024 15:30:12 +0200 Subject: [PATCH 604/842] Add explicit constructor to struct Profile Not adding the constructor causes a compilation error on Ubuntu 22.04 with both clang 14 and gcc 11: /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/alloc_traits.h:518:4: error: no matching function for call to 'construct_at' std::construct_at(__p, std::forward<_Args>(__args)...); ^~~~~~~~~~~~~~~~~ /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/vector.tcc:117:21: note: in instantiation of function template specialization 'std::allocator_traits>::construct' requested here _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, ^ ../src/modules/power_profiles_daemon.cpp:106:26: note: in instantiation of function template specialization 'std::vector::emplace_back' requested here availableProfiles_.emplace_back(std::move(name), std::move(driver)); ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_construct.h:94:5: note: candidate template ignored: substitution failure [with _Tp = waybar::modules::Profile, _Args = ]: no matching constructor for initialization of 'waybar::modules::Profile' construct_at(_Tp* __location, _Args&&... __args) ^ --- include/modules/power_profiles_daemon.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index edd9fe00..8bc250b8 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -10,6 +10,10 @@ namespace waybar::modules { struct Profile { std::string name; std::string driver; + + Profile(std::string n, std::string d) + : name(std::move(n)), driver(std::move(d)){ + } }; class PowerProfilesDaemon : public ALabel { From 136b207a12b8c4de9666150f31b7c9d688fc9c55 Mon Sep 17 00:00:00 2001 From: Felix Glinka Date: Fri, 21 Jun 2024 16:43:21 +0200 Subject: [PATCH 605/842] Add suggestion by clang-format --- include/modules/power_profiles_daemon.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index 8bc250b8..a2bd3858 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -11,9 +11,7 @@ struct Profile { std::string name; std::string driver; - Profile(std::string n, std::string d) - : name(std::move(n)), driver(std::move(d)){ - } + Profile(std::string n, std::string d) : name(std::move(n)), driver(std::move(d)) {} }; class PowerProfilesDaemon : public ALabel { From 4126502fe86a17ba3b68bdfed87055c947c65ee0 Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Sat, 22 Jun 2024 17:58:22 +0300 Subject: [PATCH 606/842] Add debug information for keyboard layout selection --- src/modules/hyprland/language.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 549faf73..db7435aa 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -36,6 +36,11 @@ Language::~Language() { auto Language::update() -> void { std::lock_guard lg(mutex_); + spdlog::debug("hyprland language update with full name {}", layout_.full_name); + spdlog::debug("hyprland language update with short name {}", layout_.short_name); + spdlog::debug("hyprland language update with short description {}", layout_.short_description); + spdlog::debug("hyprland language update with variant {}", layout_.variant); + std::string layoutName = std::string{}; if (config_.isMember("format-" + layout_.short_description + "-" + layout_.variant)) { const auto propName = "format-" + layout_.short_description + "-" + layout_.variant; @@ -50,6 +55,8 @@ auto Language::update() -> void { fmt::arg("variant", layout_.variant))); } + spdlog::debug("hyprland language formatted layout name {}", layoutName); + if (!format_.empty()) { label_.show(); label_.set_markup(layoutName); From d68bcbd292e63fd0f7d1a968cf640c4f601b1259 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Sun, 23 Jun 2024 14:35:48 +0100 Subject: [PATCH 607/842] modules/battery: Deprioritize `capacity` /sys value --- src/modules/battery.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 327420ae..d87cc612 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -272,13 +272,6 @@ waybar::modules::Battery::getInfos() { // Some battery will report current and charge in μA/μAh. // Scale these by the voltage to get μW/μWh. - uint32_t capacity = 0; - bool capacity_exists = false; - if (fs::exists(bat / "capacity")) { - capacity_exists = true; - std::ifstream(bat / "capacity") >> capacity; - } - uint32_t current_now = 0; bool current_now_exists = false; if (fs::exists(bat / "current_now")) { @@ -382,6 +375,19 @@ waybar::modules::Battery::getInfos() { } } + uint32_t capacity = 0; + bool capacity_exists = false; + if (charge_now_exists && charge_full_exists && charge_full != 0) { + capacity_exists = true; + capacity = 100 * (uint64_t)charge_now / (uint64_t)charge_full; + } else if (energy_now_exists && energy_full_exists && energy_full != 0) { + capacity_exists = true; + capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full; + } else if (fs::exists(bat / "capacity")) { + capacity_exists = true; + std::ifstream(bat / "capacity") >> capacity; + } + if (!voltage_now_exists) { if (power_now_exists && current_now_exists && current_now != 0) { voltage_now_exists = true; @@ -422,13 +428,7 @@ waybar::modules::Battery::getInfos() { } if (!capacity_exists) { - if (charge_now_exists && charge_full_exists && charge_full != 0) { - capacity_exists = true; - capacity = 100 * (uint64_t)charge_now / (uint64_t)charge_full; - } else if (energy_now_exists && energy_full_exists && energy_full != 0) { - capacity_exists = true; - capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full; - } else if (charge_now_exists && energy_full_exists && voltage_now_exists) { + if (charge_now_exists && energy_full_exists && voltage_now_exists) { if (!charge_full_exists && voltage_now != 0) { charge_full_exists = true; charge_full = 1000000 * (uint64_t)energy_full / (uint64_t)voltage_now; From ccc3c132124623bde5127937fe4fc9aa45a9d35d Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Mon, 24 Jun 2024 08:58:29 +0200 Subject: [PATCH 608/842] Update archlinux --- Dockerfiles/archlinux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index 73f19067..d4274a46 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,5 +3,5 @@ FROM archlinux:base-devel RUN pacman -Syu --noconfirm && \ - pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw && \ + pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols glib2-devel pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw && \ sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen From f6482c36dc1f92f3230761ae65f43eb43ce8177f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 22 Jun 2024 23:19:48 -0500 Subject: [PATCH 609/842] hyprland: clangd cleanup --- include/modules/hyprland/workspaces.hpp | 15 +++++++-------- src/modules/hyprland/backend.cpp | 2 +- src/modules/hyprland/submap.cpp | 4 ++-- src/modules/hyprland/windowcreationpayload.cpp | 3 --- src/modules/hyprland/workspace.cpp | 3 --- src/modules/hyprland/workspaces.cpp | 7 +++---- 6 files changed, 13 insertions(+), 21 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index df7292b1..f5c20f69 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -4,14 +4,11 @@ #include #include -#include #include #include #include -#include #include #include -#include #include #include "AModule.hpp" @@ -53,17 +50,19 @@ class Workspaces : public AModule, public EventHandler { void onEvent(const std::string& e) override; void updateWindowCount(); void sortWorkspaces(); - void createWorkspace(Json::Value const& workspaceData, - Json::Value const& clientsData = Json::Value::nullRef); + void createWorkspace(Json::Value const& workspace_data, + Json::Value const& clients_data = Json::Value::nullRef); - Json::Value createMonitorWorkspaceData(std::string const& name, std::string const& monitor); + static Json::Value createMonitorWorkspaceData(std::string const& name, + std::string const& monitor); void removeWorkspace(std::string const& name); void setUrgentWorkspace(std::string const& windowaddress); // Config void parseConfig(const Json::Value& config); auto populateIconsMap(const Json::Value& formatIcons) -> void; - auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) -> void; + static auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) + -> void; auto populateSortByConfig(const Json::Value& config) -> void; auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; @@ -98,7 +97,7 @@ class Workspaces : public AModule, public EventHandler { void doUpdate(); void removeWorkspacesToRemove(); void createWorkspacesToCreate(); - std::vector getVisibleWorkspaces(); + static std::vector getVisibleWorkspaces(); void updateWorkspaceStates(); bool updateWindowsToCreate(); diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 6b36b04e..b3fb36d4 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -26,7 +26,7 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { const char* xdgRuntimeDirEnv = std::getenv("XDG_RUNTIME_DIR"); std::filesystem::path xdgRuntimeDir; // Only set path if env variable is set - if (xdgRuntimeDirEnv) { + if (xdgRuntimeDirEnv != nullptr) { xdgRuntimeDir = std::filesystem::path(xdgRuntimeDirEnv); } diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 3d8ed699..96677d12 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -38,12 +38,12 @@ Submap::~Submap() { } auto Submap::parseConfig(const Json::Value& config) -> void { - auto const alwaysOn = config["always-on"]; + auto const& alwaysOn = config["always-on"]; if (alwaysOn.isBool()) { always_on_ = alwaysOn.asBool(); } - auto const defaultSubmap = config["default-submap"]; + auto const& defaultSubmap = config["default-submap"]; if (defaultSubmap.isString()) { default_submap_ = defaultSubmap.asString(); } diff --git a/src/modules/hyprland/windowcreationpayload.cpp b/src/modules/hyprland/windowcreationpayload.cpp index 22febc59..df7fe784 100644 --- a/src/modules/hyprland/windowcreationpayload.cpp +++ b/src/modules/hyprland/windowcreationpayload.cpp @@ -3,14 +3,11 @@ #include #include -#include -#include #include #include #include #include "modules/hyprland/workspaces.hpp" -#include "util/regex_collection.hpp" namespace waybar::modules::hyprland { diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index bf0a3368..eac21b7e 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -1,14 +1,11 @@ #include #include -#include #include #include #include -#include #include "modules/hyprland/workspaces.hpp" -#include "util/regex_collection.hpp" namespace waybar::modules::hyprland { diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 52239b7b..047703cc 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "util/regex_collection.hpp" @@ -593,14 +592,14 @@ auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) -> void { - auto configValue = config[key]; + const auto &configValue = config[key]; if (configValue.isBool()) { member = configValue.asBool(); } } auto Workspaces::populateSortByConfig(const Json::Value &config) -> void { - auto configSortBy = config["sort-by"]; + const auto &configSortBy = config["sort-by"]; if (configSortBy.isString()) { auto sortByStr = configSortBy.asString(); try { @@ -633,7 +632,7 @@ auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> vo } auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void { - auto formatWindowSeparator = config["format-window-separator"]; + const auto &formatWindowSeparator = config["format-window-separator"]; m_formatWindowSeparator = formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " "; } From c08660d837f9896a538e1b6dce541eccc2b1feb7 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 28 Jun 2024 13:11:50 -0500 Subject: [PATCH 610/842] modules/hyprland/backend: handle empty json responses Fixes https://github.com/Alexays/Waybar/issues/3388 --- src/modules/hyprland/backend.cpp | 8 +++++++- test/hyprland/backend.cpp | 10 ++++++++-- test/hyprland/fixtures/IPCTestFixture.hpp | 6 ++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index b3fb36d4..8ec6edda 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -218,7 +218,13 @@ std::string IPC::getSocket1Reply(const std::string& rq) { } Json::Value IPC::getSocket1JsonReply(const std::string& rq) { - return parser_.parse(getSocket1Reply("j/" + rq)); + std::string reply = getSocket1Reply("j/" + rq); + + if (reply.empty()) { + return {}; + } + + return parser_.parse(reply); } } // namespace waybar::modules::hyprland diff --git a/test/hyprland/backend.cpp b/test/hyprland/backend.cpp index e6b209da..dcae0509 100644 --- a/test/hyprland/backend.cpp +++ b/test/hyprland/backend.cpp @@ -1,4 +1,3 @@ -#include #if __has_include() #include #else @@ -6,7 +5,6 @@ #endif #include "fixtures/IPCTestFixture.hpp" -#include "modules/hyprland/backend.hpp" namespace fs = std::filesystem; namespace hyprland = waybar::modules::hyprland; @@ -53,3 +51,11 @@ TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirExistsNoHyprDir", "[getSocketFold // Assert expected result REQUIRE(actualPath == expectedPath); } + +TEST_CASE_METHOD(IPCMock, "getSocket1JsonReply handles empty response", "[getSocket1JsonReply]") { + std::string request = "test_request"; + + Json::Value jsonResponse = getSocket1JsonReply(request); + + REQUIRE(jsonResponse.isNull()); +} diff --git a/test/hyprland/fixtures/IPCTestFixture.hpp b/test/hyprland/fixtures/IPCTestFixture.hpp index 3b04b3b0..f6fa335f 100644 --- a/test/hyprland/fixtures/IPCTestFixture.hpp +++ b/test/hyprland/fixtures/IPCTestFixture.hpp @@ -14,3 +14,9 @@ class IPCTestFixture : public hyprland::IPC { private: }; + +class IPCMock : public IPCTestFixture { + public: + // Mock getSocket1Reply to return an empty string + static std::string getSocket1Reply(const std::string& rq) { return ""; } +}; From 64a31330833d008e568adaaead65458c17226cb7 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 30 Jun 2024 14:52:49 +0200 Subject: [PATCH 611/842] workaround for icons not rendered for apps existing before waybar launch --- src/modules/wlr/taskbar.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 41d46748..e6c8e536 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -788,6 +788,10 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu } icon_themes_.push_back(Gtk::IconTheme::get_default()); + + for (auto &t : tasks_) { + t->handle_app_id(t->app_id().c_str()); + } } Taskbar::~Taskbar() { From fb24e8cb1f9cd7ece865b9f150f2d23101be624d Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 30 Jun 2024 21:16:52 +0200 Subject: [PATCH 612/842] add hide-empty-text option to hide module whenever output is empty but format is not --- src/modules/custom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 45e849cc..20d8d934 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -162,7 +162,7 @@ auto waybar::modules::Custom::update() -> void { auto str = fmt::format(fmt::runtime(format_), text_, fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); - if (str.empty()) { + if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { event_box_.hide(); } else { label_.set_markup(str); From 8eee5687316af4a6c32752e79210f58fee52c499 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 30 Jun 2024 21:23:54 +0200 Subject: [PATCH 613/842] manpage for PR #3395 --- man/waybar-custom.5.scd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index a62c9312..1c09eac9 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -21,6 +21,10 @@ Addressed by *custom/* The path to a script, which determines if the script in *exec* should be executed. ++ *exec* will be executed if the exit code of *exec-if* equals 0. +*hide-empty-text*: ++ + typeof: bool ++ + Disables the module when output is empty, but format might contain additional static content. + *exec-on-event*: ++ typeof: bool ++ default: true ++ From f609042ecebbf8911cdc6598d61807778c5b758f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 1 Jul 2024 00:09:58 +0000 Subject: [PATCH 614/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/ad57eef4ef0659193044870c731987a6df5cf56b?narHash=sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs%3D' (2024-05-29) → 'github:NixOS/nixpkgs/b2852eb9365c6de48ffb0dc2c9562591f652242a?narHash=sha256-C8e9S7RzshSdHB7L%2Bv9I51af1gDM5unhJ2xO1ywxNH8%3D' (2024-06-27) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index f35322ec..3f0deffe 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1716948383, - "narHash": "sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs=", + "lastModified": 1719506693, + "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ad57eef4ef0659193044870c731987a6df5cf56b", + "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a", "type": "github" }, "original": { From 8f64caceb5cf3cfe08de82b07af0fb4da97dd6d9 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Mon, 1 Jul 2024 18:30:58 +0200 Subject: [PATCH 615/842] fix example in manpage for pulseaudio/slider --- man/waybar-pulseaudio-slider.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-pulseaudio-slider.5.scd b/man/waybar-pulseaudio-slider.5.scd index fc1da1c4..cf07fed1 100644 --- a/man/waybar-pulseaudio-slider.5.scd +++ b/man/waybar-pulseaudio-slider.5.scd @@ -31,7 +31,7 @@ The volume can be controlled by dragging the slider across the bar or clicking o ``` "modules-right": [ - "pulseaudio-slider", + "pulseaudio/slider", ], "pulseaudio/slider": { "min": 0, From 14c3235c12eae0615dda9b1e71c6b8fd6cb20cd3 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 2 Jul 2024 10:12:41 -0500 Subject: [PATCH 616/842] src: clang-tidy --- include/AModule.hpp | 8 ++++---- src/AAppIconLabel.cpp | 8 ++++---- src/ALabel.cpp | 8 ++++---- src/AModule.cpp | 14 +++++++------- src/bar.cpp | 27 +++++++++++++-------------- src/client.cpp | 33 +++++++++++++++++---------------- src/config.cpp | 12 +++++++----- src/group.cpp | 4 ++-- src/main.cpp | 7 ++++--- 9 files changed, 62 insertions(+), 59 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 90f34187..facb3130 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -14,9 +14,9 @@ class AModule : public IModule { public: static constexpr const char *MODULE_CLASS = "module"; - virtual ~AModule(); + ~AModule() override; auto update() -> void override; - virtual auto refresh(int) -> void{}; + virtual auto refresh(int shouldRefresh) -> void{}; operator Gtk::Widget &() override; auto doAction(const std::string &name) -> void override; @@ -32,13 +32,13 @@ class AModule : public IModule { enum SCROLL_DIR { NONE, UP, DOWN, LEFT, RIGHT }; SCROLL_DIR getScrollDir(GdkEventScroll *e); - bool tooltipEnabled(); + bool tooltipEnabled() const; const std::string name_; const Json::Value &config_; Gtk::EventBox event_box_; - virtual void setCursor(Gdk::CursorType const c); + virtual void setCursor(Gdk::CursorType const &c); virtual bool handleToggle(GdkEventButton *const &ev); virtual bool handleMouseEnter(GdkEventCrossing *const &ev); diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index e64e6daa..fda5f9fd 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -96,14 +96,14 @@ std::optional getIconName(const std::string& app_identifier, return app_identifier; } - const auto app_identifier_desktop = app_identifier + "-desktop"; + auto app_identifier_desktop = app_identifier + "-desktop"; if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) { return app_identifier_desktop; } - const auto first_space = app_identifier.find_first_of(' '); + auto first_space = app_identifier.find_first_of(' '); if (first_space != std::string::npos) { - const auto first_word = toLowerCase(app_identifier.substr(0, first_space)); + auto first_word = toLowerCase(app_identifier.substr(0, first_space)); if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { return first_word; } @@ -111,7 +111,7 @@ std::optional getIconName(const std::string& app_identifier, const auto first_dash = app_identifier.find_first_of('-'); if (first_dash != std::string::npos) { - const auto first_word = toLowerCase(app_identifier.substr(0, first_dash)); + auto first_word = toLowerCase(app_identifier.substr(0, first_dash)); if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { return first_word; } diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 5497c62a..da2991a3 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -71,12 +71,12 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st GtkBuilder* builder = gtk_builder_new(); // Make the GtkBuilder and check for errors in his parsing - if (!gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr)) { + if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) { throw std::runtime_error("Error found in the file " + menuFile); } menu_ = gtk_builder_get_object(builder, "menu"); - if (!menu_) { + if (menu_ == nullptr) { throw std::runtime_error("Failed to get 'menu' object from GtkBuilder"); } submenus_ = std::map(); @@ -121,7 +121,7 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_ } if (format_icons.isArray()) { auto size = format_icons.size(); - if (size) { + if (size != 0U) { auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); format_icons = format_icons[idx]; } @@ -147,7 +147,7 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector& } if (format_icons.isArray()) { auto size = format_icons.size(); - if (size) { + if (size != 0U) { auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); format_icons = format_icons[idx]; } diff --git a/src/AModule.cpp b/src/AModule.cpp index 9948edab..c40e3a56 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -8,8 +8,8 @@ namespace waybar { AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id, bool enable_click, bool enable_scroll) - : name_(std::move(name)), - config_(std::move(config)), + : name_(name), + config_(config), isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true}, distance_scrolled_y_(0.0), distance_scrolled_x_(0.0) { @@ -18,12 +18,12 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: for (Json::Value::const_iterator it = actions.begin(); it != actions.end(); ++it) { if (it.key().isString() && it->isString()) - if (eventActionMap_.count(it.key().asString()) == 0) { + if (!eventActionMap_.contains(it.key().asString())) { eventActionMap_.insert({it.key().asString(), it->asString()}); enable_click = true; enable_scroll = true; } else - spdlog::warn("Dublicate action is ignored: {0}", it.key().asString()); + spdlog::warn("Duplicate action is ignored: {0}", it.key().asString()); else spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); } @@ -90,7 +90,7 @@ auto AModule::doAction(const std::string& name) -> void { } } -void AModule::setCursor(Gdk::CursorType c) { +void AModule::setCursor(Gdk::CursorType const& c) { auto cursor = Gdk::Cursor::create(c); auto gdk_window = event_box_.get_window(); gdk_window->set_cursor(cursor); @@ -164,7 +164,7 @@ AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { // ignore reverse-scrolling if event comes from a mouse wheel GdkDevice* device = gdk_event_get_source_device((GdkEvent*)e); - if (device != NULL && gdk_device_get_source(device) == GDK_SOURCE_MOUSE) { + if (device != nullptr && gdk_device_get_source(device) == GDK_SOURCE_MOUSE) { reverse = reverse_mouse; } @@ -242,7 +242,7 @@ bool AModule::handleScroll(GdkEventScroll* e) { return true; } -bool AModule::tooltipEnabled() { return isTooltip; } +bool AModule::tooltipEnabled() const { return isTooltip; } AModule::operator Gtk::Widget&() { return event_box_; } diff --git a/src/bar.cpp b/src/bar.cpp index 5efe5889..8c75c2c2 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -72,16 +72,16 @@ void from_json(const Json::Value& j, std::optional& l) { /* Deserializer for struct bar_mode */ void from_json(const Json::Value& j, bar_mode& m) { if (j.isObject()) { - if (auto v = j["layer"]; v.isString()) { + if (const auto& v = j["layer"]; v.isString()) { from_json(v, m.layer); } - if (auto v = j["exclusive"]; v.isBool()) { + if (const auto& v = j["exclusive"]; v.isBool()) { m.exclusive = v.asBool(); } - if (auto v = j["passthrough"]; v.isBool()) { + if (const auto& v = j["passthrough"]; v.isBool()) { m.passthrough = v.asBool(); } - if (auto v = j["visible"]; v.isBool()) { + if (const auto& v = j["visible"]; v.isBool()) { m.visible = v.asBool(); } } @@ -118,7 +118,7 @@ Glib::ustring to_string(Gtk::PositionType pos) { * Assumes that all the values in the object are deserializable to the same type. */ template ::value>> + typename = std::enable_if_t>> void from_json(const Json::Value& j, std::map& m) { if (j.isObject()) { for (auto it = j.begin(); it != j.end(); ++it) { @@ -393,19 +393,18 @@ void waybar::Bar::setPosition(Gtk::PositionType position) { } } -void waybar::Bar::onMap(GdkEventAny*) { +void waybar::Bar::onMap(GdkEventAny* /*unused*/) { /* * Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor). */ - auto gdk_window = window.get_window()->gobj(); + auto* gdk_window = window.get_window()->gobj(); surface = gdk_wayland_window_get_wl_surface(gdk_window); configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window)); setPassThrough(passthrough_); } -void waybar::Bar::setVisible(bool value) { - visible = value; +void waybar::Bar::setVisible(bool visible) { if (auto mode = config.get("mode", {}); mode.isString()) { setMode(visible ? config["mode"].asString() : MODE_INVISIBLE); } else { @@ -473,7 +472,7 @@ void waybar::Bar::handleSignal(int signal) { void waybar::Bar::getModules(const Factory& factory, const std::string& pos, waybar::Group* group = nullptr) { - auto module_list = group ? config[pos]["modules"] : config[pos]; + auto module_list = group != nullptr ? config[pos]["modules"] : config[pos]; if (module_list.isArray()) { for (const auto& name : module_list) { try { @@ -485,10 +484,10 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, auto id_name = ref.substr(6, hash_pos - 6); auto class_name = hash_pos != std::string::npos ? ref.substr(hash_pos + 1) : ""; - auto vertical = (group ? group->getBox().get_orientation() : box_.get_orientation()) == - Gtk::ORIENTATION_VERTICAL; + auto vertical = (group != nullptr ? group->getBox().get_orientation() + : box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL; - auto group_module = new waybar::Group(id_name, class_name, config[ref], vertical); + auto* group_module = new waybar::Group(id_name, class_name, config[ref], vertical); getModules(factory, ref, group_module); module = group_module; } else { @@ -497,7 +496,7 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, std::shared_ptr module_sp(module); modules_all_.emplace_back(module_sp); - if (group) { + if (group != nullptr) { group->addWidget(*module); } else { if (pos == "modules-left") { diff --git a/src/client.cpp b/src/client.cpp index 7c59dd5e..5768b1b0 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "gtkmm/icontheme.h" #include "idle-inhibit-unstable-v1-client-protocol.h" @@ -11,13 +12,13 @@ #include "util/format.hpp" waybar::Client *waybar::Client::inst() { - static auto c = new Client(); + static auto *c = new Client(); return c; } void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - auto client = static_cast(data); + auto *client = static_cast(data); if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { client->xdg_output_manager = static_cast(wl_registry_bind( @@ -42,7 +43,7 @@ void waybar::Client::handleOutput(struct waybar_output &output) { .description = &handleOutputDescription, }; // owned by output->monitor; no need to destroy - auto wl_output = gdk_wayland_monitor_get_wl_output(output.monitor->gobj()); + auto *wl_output = gdk_wayland_monitor_get_wl_output(output.monitor->gobj()); output.xdg_output.reset(zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, wl_output)); zxdg_output_v1_add_listener(output.xdg_output.get(), &xdgOutputListener, &output); } @@ -61,7 +62,7 @@ std::vector waybar::Client::getOutputConfigs(struct waybar_output & } void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_output*/) { - auto client = waybar::Client::inst(); + auto *client = waybar::Client::inst(); try { auto &output = client->getOutput(data); /** @@ -85,24 +86,24 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_ } } } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; + std::cerr << e.what() << '\n'; } } void waybar::Client::handleOutputName(void *data, struct zxdg_output_v1 * /*xdg_output*/, const char *name) { - auto client = waybar::Client::inst(); + auto *client = waybar::Client::inst(); try { auto &output = client->getOutput(data); output.name = name; } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; + std::cerr << e.what() << '\n'; } } void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * /*xdg_output*/, const char *description) { - auto client = waybar::Client::inst(); + auto *client = waybar::Client::inst(); try { auto &output = client->getOutput(data); const char *open_paren = strrchr(description, '('); @@ -111,13 +112,13 @@ void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * size_t identifier_length = open_paren - description; output.identifier = std::string(description, identifier_length - 1); } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; + std::cerr << e.what() << '\n'; } } void waybar::Client::handleMonitorAdded(Glib::RefPtr monitor) { auto &output = outputs_.emplace_back(); - output.monitor = monitor; + output.monitor = std::move(monitor); handleOutput(output); } @@ -154,15 +155,15 @@ const std::string waybar::Client::getStyle(const std::string &style, std::vector search_files; switch (appearance.value_or(portal->getAppearance())) { case waybar::Appearance::LIGHT: - search_files.push_back("style-light.css"); + search_files.emplace_back("style-light.css"); break; case waybar::Appearance::DARK: - search_files.push_back("style-dark.css"); + search_files.emplace_back("style-dark.css"); break; case waybar::Appearance::UNKNOWN: break; } - search_files.push_back("style.css"); + search_files.emplace_back("style.css"); css_file = Config::findConfigPath(search_files); } else { css_file = style; @@ -196,7 +197,7 @@ void waybar::Client::bindInterfaces() { wl_registry_add_listener(registry, ®istry_listener, this); wl_display_roundtrip(wl_display); - if (!gtk_layer_is_supported()) { + if (gtk_layer_is_supported() == 0) { throw std::runtime_error("The Wayland compositor does not support wlr-layer-shell protocol"); } @@ -233,11 +234,11 @@ int waybar::Client::main(int argc, char *argv[]) { return 1; } if (show_help) { - std::cout << cli << std::endl; + std::cout << cli << '\n'; return 0; } if (show_version) { - std::cout << "Waybar v" << VERSION << std::endl; + std::cout << "Waybar v" << VERSION << '\n'; return 0; } if (!log_level.empty()) { diff --git a/src/config.cpp b/src/config.cpp index c43e5a63..b78af56c 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -21,10 +21,10 @@ const std::vector Config::CONFIG_DIRS = { const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR"; -std::optional tryExpandPath(const std::string base, const std::string filename) { +std::optional tryExpandPath(const std::string &base, const std::string &filename) { fs::path path; - if (filename != "") { + if (!filename.empty()) { path = fs::path(base) / fs::path(filename); } else { path = fs::path(base); @@ -129,9 +129,9 @@ bool isValidOutput(const Json::Value &config, const std::string &name, if (config_output.substr(0, 1) == "!") { if (config_output.substr(1) == name || config_output.substr(1) == identifier) { return false; - } else { - continue; } + + continue; } if (config_output == name || config_output == identifier) { return true; @@ -142,7 +142,9 @@ bool isValidOutput(const Json::Value &config, const std::string &name, } } return false; - } else if (config["output"].isString()) { + } + + if (config["output"].isString()) { auto config_output = config["output"].asString(); if (!config_output.empty()) { if (config_output.substr(0, 1) == "!") { diff --git a/src/group.cpp b/src/group.cpp index 9f707dc9..c77f2d31 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -19,9 +19,9 @@ const Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) { if (is_vertical) { return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_UP; - } else { - return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_LEFT; } + + return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_LEFT; } Group::Group(const std::string& name, const std::string& id, const Json::Value& config, diff --git a/src/main.cpp b/src/main.cpp index ff446ffc..679c66d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,8 @@ std::list reap; volatile bool reload; void* signalThread(void* args) { - int err, signum; + int err; + int signum; sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); @@ -46,7 +47,7 @@ void* signalThread(void* args) { } } -void startSignalThread(void) { +void startSignalThread() { int err; sigset_t mask; sigemptyset(&mask); @@ -71,7 +72,7 @@ void startSignalThread(void) { int main(int argc, char* argv[]) { try { - auto client = waybar::Client::inst(); + auto* client = waybar::Client::inst(); std::signal(SIGUSR1, [](int /*signal*/) { for (auto& bar : waybar::Client::inst()->bars) { From d66685a3aa85d02f1a9498cf9d59afef94c820de Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 2 Jul 2024 10:30:04 -0500 Subject: [PATCH 617/842] util: clang-tidy --- include/util/backlight_backend.hpp | 8 +++---- include/util/regex_collection.hpp | 8 ++++--- src/util/audio_backend.cpp | 37 +++++++++++++++--------------- src/util/backlight_backend.cpp | 18 ++++++++------- src/util/portal.cpp | 4 +++- src/util/prepare_for_sleep.cpp | 12 +++++----- src/util/regex_collection.cpp | 6 +++-- src/util/sanitize_str.cpp | 3 +-- src/util/ustring_clen.cpp | 6 ++--- 9 files changed, 54 insertions(+), 48 deletions(-) diff --git a/include/util/backlight_backend.hpp b/include/util/backlight_backend.hpp index 20925b52..eb42d3cc 100644 --- a/include/util/backlight_backend.hpp +++ b/include/util/backlight_backend.hpp @@ -56,10 +56,10 @@ class BacklightBackend { void set_previous_best_device(const BacklightDevice *device); - void set_brightness(std::string preferred_device, ChangeType change_type, double step); + void set_brightness(const std::string &preferred_device, ChangeType change_type, double step); - void set_scaled_brightness(std::string preferred_device, int brightness); - int get_scaled_brightness(std::string preferred_device); + void set_scaled_brightness(const std::string &preferred_device, int brightness); + int get_scaled_brightness(const std::string &preferred_device); bool is_login_proxy_initialized() const { return static_cast(login_proxy_); } @@ -70,7 +70,7 @@ class BacklightBackend { std::mutex udev_thread_mutex_; private: - void set_brightness_internal(std::string device_name, int brightness, int max_brightness); + void set_brightness_internal(const std::string &device_name, int brightness, int max_brightness); std::function on_updated_cb_; std::chrono::milliseconds polling_interval_; diff --git a/include/util/regex_collection.hpp b/include/util/regex_collection.hpp index fe958461..30d26d4a 100644 --- a/include/util/regex_collection.hpp +++ b/include/util/regex_collection.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace waybar::util { @@ -17,7 +18,7 @@ struct Rule { // See https://en.cppreference.com/w/cpp/compiler_support/20 "Parenthesized initialization of // aggregates" Rule(std::regex rule, std::string repr, int priority) - : rule(rule), repr(repr), priority(priority) {} + : rule(std::move(rule)), repr(std::move(repr)), priority(priority) {} }; int default_priority_function(std::string& key); @@ -40,8 +41,9 @@ class RegexCollection { public: RegexCollection() = default; - RegexCollection(const Json::Value& map, std::string default_repr = "", - std::function priority_function = default_priority_function); + RegexCollection( + const Json::Value& map, std::string default_repr = "", + const std::function& priority_function = default_priority_function); ~RegexCollection() = default; std::string& get(std::string& value, bool& matched_any); diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index f4dd72c4..e634784b 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace waybar::util { @@ -15,13 +16,11 @@ AudioBackend::AudioBackend(std::function on_updated_cb, private_construc : mainloop_(nullptr), mainloop_api_(nullptr), context_(nullptr), - sink_idx_(0), volume_(0), muted_(false), - source_idx_(0), source_volume_(0), source_muted_(false), - on_updated_cb_(on_updated_cb) { + on_updated_cb_(std::move(on_updated_cb)) { mainloop_ = pa_threaded_mainloop_new(); if (mainloop_ == nullptr) { throw std::runtime_error("pa_mainloop_new() failed."); @@ -66,7 +65,7 @@ void AudioBackend::connectContext() { } void AudioBackend::contextStateCb(pa_context *c, void *data) { - auto backend = static_cast(data); + auto *backend = static_cast(data); switch (pa_context_get_state(c)) { case PA_CONTEXT_TERMINATED: backend->mainloop_api_->quit(backend->mainloop_api_, 0); @@ -127,7 +126,7 @@ void AudioBackend::subscribeCb(pa_context *context, pa_subscription_event_type_t * Called in response to a volume change request */ void AudioBackend::volumeModifyCb(pa_context *c, int success, void *data) { - auto backend = static_cast(data); + auto *backend = static_cast(data); if (success != 0) { pa_context_get_sink_info_by_index(backend->context_, backend->sink_idx_, sinkInfoCb, data); } @@ -140,7 +139,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i void *data) { if (i == nullptr) return; - auto backend = static_cast(data); + auto *backend = static_cast(data); if (!backend->ignored_sinks_.empty()) { for (const auto &ignored_sink : backend->ignored_sinks_) { @@ -151,11 +150,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i } if (backend->current_sink_name_ == i->name) { - if (i->state != PA_SINK_RUNNING) { - backend->current_sink_running_ = false; - } else { - backend->current_sink_running_ = true; - } + backend->current_sink_running_ = i->state == PA_SINK_RUNNING; } if (!backend->current_sink_running_ && i->state == PA_SINK_RUNNING) { @@ -173,7 +168,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i backend->desc_ = i->description; backend->monitor_ = i->monitor_source_name; backend->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown"; - if (auto ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) { + if (const auto *ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) { backend->form_factor_ = ff; } else { backend->form_factor_ = ""; @@ -187,7 +182,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i */ void AudioBackend::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i, int /*eol*/, void *data) { - auto backend = static_cast(data); + auto *backend = static_cast(data); if (i != nullptr && backend->default_source_name_ == i->name) { auto source_volume = static_cast(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM}; backend->source_volume_ = std::round(source_volume * 100.0F); @@ -204,7 +199,7 @@ void AudioBackend::sourceInfoCb(pa_context * /*context*/, const pa_source_info * * used to find the default PulseAudio sink. */ void AudioBackend::serverInfoCb(pa_context *context, const pa_server_info *i, void *data) { - auto backend = static_cast(data); + auto *backend = static_cast(data); backend->current_sink_name_ = i->default_sink_name; backend->default_source_name_ = i->default_source_name; @@ -253,22 +248,26 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma void AudioBackend::toggleSinkMute() { muted_ = !muted_; - pa_context_set_sink_mute_by_index(context_, sink_idx_, muted_, nullptr, nullptr); + pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast(muted_), nullptr, + nullptr); } void AudioBackend::toggleSinkMute(bool mute) { muted_ = mute; - pa_context_set_sink_mute_by_index(context_, sink_idx_, muted_, nullptr, nullptr); + pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast(muted_), nullptr, + nullptr); } void AudioBackend::toggleSourceMute() { source_muted_ = !muted_; - pa_context_set_source_mute_by_index(context_, source_idx_, source_muted_, nullptr, nullptr); + pa_context_set_source_mute_by_index(context_, source_idx_, static_cast(source_muted_), + nullptr, nullptr); } void AudioBackend::toggleSourceMute(bool mute) { source_muted_ = mute; - pa_context_set_source_mute_by_index(context_, source_idx_, source_muted_, nullptr, nullptr); + pa_context_set_source_mute_by_index(context_, source_idx_, static_cast(source_muted_), + nullptr, nullptr); } bool AudioBackend::isBluetooth() { @@ -287,4 +286,4 @@ void AudioBackend::setIgnoredSinks(const Json::Value &config) { } } -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index 60d5ca3a..bb102cd9 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -4,7 +4,9 @@ #include #include +#include #include +#include namespace { class FileDescriptor { @@ -122,7 +124,7 @@ static void enumerate_devices(std::vector &devices, udev *udev) } BacklightDevice::BacklightDevice(std::string name, int actual, int max, bool powered) - : name_(name), actual_(actual), max_(max), powered_(powered) {} + : name_(std::move(name)), actual_(actual), max_(max), powered_(powered) {} std::string BacklightDevice::name() const { return name_; } @@ -140,7 +142,7 @@ void BacklightDevice::set_powered(bool powered) { powered_ = powered; } BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, std::function on_updated_cb) - : on_updated_cb_(on_updated_cb), polling_interval_(interval), previous_best_({}) { + : on_updated_cb_(std::move(on_updated_cb)), polling_interval_(interval), previous_best_({}) { std::unique_ptr udev_check{udev_new()}; check_nn(udev_check.get(), "Udev check new failed"); enumerate_devices(devices_, udev_check.get()); @@ -236,24 +238,24 @@ void BacklightBackend::set_previous_best_device(const BacklightDevice *device) { } } -void BacklightBackend::set_scaled_brightness(std::string preferred_device, int brightness) { +void BacklightBackend::set_scaled_brightness(const std::string &preferred_device, int brightness) { GET_BEST_DEVICE(best, (*this), preferred_device); if (best != nullptr) { const auto max = best->get_max(); - const auto abs_val = static_cast(round(brightness * max / 100.0f)); + const auto abs_val = static_cast(std::round(brightness * max / 100.0F)); set_brightness_internal(best->name(), abs_val, best->get_max()); } } -void BacklightBackend::set_brightness(std::string preferred_device, ChangeType change_type, +void BacklightBackend::set_brightness(const std::string &preferred_device, ChangeType change_type, double step) { GET_BEST_DEVICE(best, (*this), preferred_device); if (best != nullptr) { const auto max = best->get_max(); - const auto abs_step = static_cast(round(step * max / 100.0f)); + const auto abs_step = static_cast(round(step * max / 100.0F)); const int new_brightness = change_type == ChangeType::Increase ? best->get_actual() + abs_step : best->get_actual() - abs_step; @@ -261,7 +263,7 @@ void BacklightBackend::set_brightness(std::string preferred_device, ChangeType c } } -void BacklightBackend::set_brightness_internal(std::string device_name, int brightness, +void BacklightBackend::set_brightness_internal(const std::string &device_name, int brightness, int max_brightness) { brightness = std::clamp(brightness, 0, max_brightness); @@ -271,7 +273,7 @@ void BacklightBackend::set_brightness_internal(std::string device_name, int brig login_proxy_->call_sync("SetBrightness", call_args); } -int BacklightBackend::get_scaled_brightness(std::string preferred_device) { +int BacklightBackend::get_scaled_brightness(const std::string &preferred_device) { GET_BEST_DEVICE(best, (*this), preferred_device); if (best != nullptr) { diff --git a/src/util/portal.cpp b/src/util/portal.cpp index 50c646c5..5874871b 100644 --- a/src/util/portal.cpp +++ b/src/util/portal.cpp @@ -85,7 +85,9 @@ void waybar::Portal::on_signal(const Glib::ustring& sender_name, const Glib::ust if (signal_name != "SettingChanged" || parameters.get_n_children() != 3) { return; } - Glib::VariantBase nspcv, keyv, valuev; + Glib::VariantBase nspcv; + Glib::VariantBase keyv; + Glib::VariantBase valuev; parameters.get_child(nspcv, 0); parameters.get_child(keyv, 1); parameters.get_child(valuev, 2); diff --git a/src/util/prepare_for_sleep.cpp b/src/util/prepare_for_sleep.cpp index 661285a2..3adcdf67 100644 --- a/src/util/prepare_for_sleep.cpp +++ b/src/util/prepare_for_sleep.cpp @@ -7,14 +7,14 @@ namespace { class PrepareForSleep { private: PrepareForSleep() { - login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); - if (!login1_connection) { + login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr); + if (login1_connection == nullptr) { spdlog::warn("Unable to connect to the SYSTEM Bus!..."); } else { login1_id = g_dbus_connection_signal_subscribe( login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager", - "PrepareForSleep", "/org/freedesktop/login1", NULL, G_DBUS_SIGNAL_FLAGS_NONE, - prepareForSleep_cb, this, NULL); + "PrepareForSleep", "/org/freedesktop/login1", nullptr, G_DBUS_SIGNAL_FLAGS_NONE, + prepareForSleep_cb, this, nullptr); } } @@ -22,11 +22,11 @@ class PrepareForSleep { const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { - if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)"))) { + if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)")) != 0) { gboolean sleeping; g_variant_get(parameters, "(b)", &sleeping); - PrepareForSleep *self = static_cast(user_data); + auto *self = static_cast(user_data); self->signal.emit(sleeping); } } diff --git a/src/util/regex_collection.cpp b/src/util/regex_collection.cpp index db2f30ea..929e67cd 100644 --- a/src/util/regex_collection.cpp +++ b/src/util/regex_collection.cpp @@ -3,13 +3,15 @@ #include #include +#include + namespace waybar::util { int default_priority_function(std::string& key) { return 0; } RegexCollection::RegexCollection(const Json::Value& map, std::string default_repr, - std::function priority_function) - : default_repr(default_repr) { + const std::function& priority_function) + : default_repr(std::move(default_repr)) { if (!map.isObject()) { spdlog::warn("Mapping is not an object"); return; diff --git a/src/util/sanitize_str.cpp b/src/util/sanitize_str.cpp index 131b9f28..ae9a9e37 100644 --- a/src/util/sanitize_str.cpp +++ b/src/util/sanitize_str.cpp @@ -10,9 +10,8 @@ std::string sanitize_string(std::string str) { const std::pair replacement_table[] = { {'&', "&"}, {'<', "<"}, {'>', ">"}, {'"', """}, {'\'', "'"}}; size_t startpoint; - for (size_t i = 0; i < (sizeof(replacement_table) / sizeof(replacement_table[0])); ++i) { + for (const auto& pair : replacement_table) { startpoint = 0; - std::pair pair = replacement_table[i]; while ((startpoint = str.find(pair.first, startpoint)) != std::string::npos) { str.replace(startpoint, 1, pair.second); startpoint += pair.second.length(); diff --git a/src/util/ustring_clen.cpp b/src/util/ustring_clen.cpp index 374df0d6..a8b9c9af 100644 --- a/src/util/ustring_clen.cpp +++ b/src/util/ustring_clen.cpp @@ -2,8 +2,8 @@ int ustring_clen(const Glib::ustring &str) { int total = 0; - for (auto i = str.begin(); i != str.end(); ++i) { - total += g_unichar_iswide(*i) + 1; + for (unsigned int i : str) { + total += g_unichar_iswide(i) + 1; } return total; -} \ No newline at end of file +} From 702e10649e9f6ddcb1c9972c034775b63b724bb8 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 3 Jul 2024 00:19:54 -0500 Subject: [PATCH 618/842] modules/hyprland/workspace: ignore empty window-rewrite I'd like to ignore some windows from having icons or empty space taken on the bar. By filtering out empty repr we can supply rewrite rules that will ignore them from being processed and showing an empty space or default icon. --- man/waybar-hyprland-workspaces.5.scd | 1 + src/modules/hyprland/workspace.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 686f8aa7..c71168d4 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -26,6 +26,7 @@ Addressed by *hyprland/workspaces* Regex rules to map window class to an icon or preferred method of representation for a workspace's window. Keys are the rules, while the values are the methods of representation. Values may use the placeholders {class} and {title} to use the window's original class and/or title respectively. Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching. + You may assign an empty value to a rule to have it ignored from generating any representation in workspaces. *window-rewrite-default*: typeof: string ++ diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index eac21b7e..e575d1c4 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -92,7 +92,11 @@ void Workspace::initializeWindowMap(const Json::Value &clients_data) { void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { if (!create_window_paylod.isEmpty(m_workspaceManager)) { - m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager); + auto repr = create_window_paylod.repr(m_workspaceManager); + + if (!repr.empty()) { + m_windowMap[create_window_paylod.getAddress()] = repr; + } } }; From 7e2d8ab2a37302d09727b29d600f7768d1a2efb4 Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" Date: Wed, 15 May 2024 20:07:28 +0200 Subject: [PATCH 619/842] fix(#3239): hide cursor type change behind config flag also, statically configure the cursor type --- man/waybar-styles.5.scd.in | 32 ++++++++++++++++++++++++++++++++ src/AModule.cpp | 37 +++++++++++++++++++++++++++---------- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/man/waybar-styles.5.scd.in b/man/waybar-styles.5.scd.in index 0af393ef..c1bc25e3 100644 --- a/man/waybar-styles.5.scd.in +++ b/man/waybar-styles.5.scd.in @@ -39,6 +39,38 @@ You can apply special styling to any module for when the cursor hovers it. } ``` +## Setting cursor style + +Most, if not all, module types support setting the `cursor` option. This is +configured in your `config.jsonc`. If set to `true`, when hovering the module a +"pointer"(as commonly known from web CSS styling `cursor: pointer`) style cursor +will be shown. +There are more cursor types to choose from by setting the `cursor` option to +a number, see Gdk3 official docs for all possible cursor types: +https://docs.gtk.org/gdk3/enum.CursorType.html. +However, note that not all cursor options listed may be available on +your system. If you attempt to use a cursor which is not available, the +application will crash. + +Example of enabling pointer(`Gdk::Hand2`) cursor type on a custom module: + +``` +"custom/my-custom-module": { + ... + "cursor": true, +} +``` + +Example of setting cursor type to `Gdk::Boat`(according to +https://docs.gtk.org/gdk3/enum.CursorType.html#boat): + +``` +"custom/my-custom-module": { + ... + "cursor": 8, +} +``` + # SEE ALSO - *waybar(5)* diff --git a/src/AModule.cpp b/src/AModule.cpp index c40e3a56..887b0de0 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -1,9 +1,13 @@ #include "AModule.hpp" #include +#include #include +#include "gdk/gdk.h" +#include "gdkmm/cursor.h" + namespace waybar { AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id, @@ -64,6 +68,16 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll)); } + + if (config_.isMember("cursor")) { + if (config_["cursor"].isBool() && config_["cursor"].asBool()) { + setCursor(Gdk::HAND2); + } else if (config_["cursor"].isInt()) { + setCursor(Gdk::CursorType(config_["cursor"].asInt())); + } else { + spdlog::warn("unknown cursor option configured on module {}", name_); + } + } } AModule::~AModule() { @@ -91,19 +105,26 @@ auto AModule::doAction(const std::string& name) -> void { } void AModule::setCursor(Gdk::CursorType const& c) { - auto cursor = Gdk::Cursor::create(c); auto gdk_window = event_box_.get_window(); - gdk_window->set_cursor(cursor); + if (gdk_window) { + auto cursor = Gdk::Cursor::create(c); + gdk_window->set_cursor(cursor); + } else { + // window may not be accessible yet, in this case, + // schedule another call for setting the cursor in 1 sec + Glib::signal_timeout().connect_seconds( + [this, c]() { + setCursor(c); + return false; + }, + 1); + } } bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - - if (hasUserEvents_) { - setCursor(Gdk::HAND2); - } return false; } @@ -111,10 +132,6 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } - - if (hasUserEvents_) { - setCursor(Gdk::ARROW); - } return false; } From f78f29ee66f5d67579791380098759768e3682e8 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 2 Jul 2024 18:13:53 -0500 Subject: [PATCH 620/842] AModule: retain existing default behavior when unconfigured --- man/waybar-styles.5.scd.in | 10 ++++++---- src/AModule.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/man/waybar-styles.5.scd.in b/man/waybar-styles.5.scd.in index c1bc25e3..b11e15bd 100644 --- a/man/waybar-styles.5.scd.in +++ b/man/waybar-styles.5.scd.in @@ -42,9 +42,11 @@ You can apply special styling to any module for when the cursor hovers it. ## Setting cursor style Most, if not all, module types support setting the `cursor` option. This is -configured in your `config.jsonc`. If set to `true`, when hovering the module a +configured in your `config.jsonc`. If set to `false`, when hovering the module a "pointer"(as commonly known from web CSS styling `cursor: pointer`) style cursor -will be shown. +will not be shown. Default behavior is to indicate an interaction event is +available. + There are more cursor types to choose from by setting the `cursor` option to a number, see Gdk3 official docs for all possible cursor types: https://docs.gtk.org/gdk3/enum.CursorType.html. @@ -52,12 +54,12 @@ However, note that not all cursor options listed may be available on your system. If you attempt to use a cursor which is not available, the application will crash. -Example of enabling pointer(`Gdk::Hand2`) cursor type on a custom module: +Example of disabling pointer(`Gdk::Hand2`) cursor type on a custom module: ``` "custom/my-custom-module": { ... - "cursor": true, + "cursor": false, } ``` diff --git a/src/AModule.cpp b/src/AModule.cpp index 887b0de0..c180b480 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -69,6 +69,7 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll)); } + // Respect user configuration of cursor if (config_.isMember("cursor")) { if (config_["cursor"].isBool() && config_["cursor"].asBool()) { setCursor(Gdk::HAND2); @@ -125,6 +126,12 @@ bool AModule::handleMouseEnter(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } + + // Default behavior indicating event availability + if (hasUserEvents_ && !config_.isMember("cursor")) { + setCursor(Gdk::HAND2); + } + return false; } @@ -132,6 +139,12 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) { if (auto* module = event_box_.get_child(); module != nullptr) { module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); } + + // Default behavior indicating event availability + if (hasUserEvents_ && !config_.isMember("cursor")) { + setCursor(Gdk::ARROW); + } + return false; } From 23274a9d570f3821c20dcc004da5e6e0afa122bd Mon Sep 17 00:00:00 2001 From: Lauri Niskanen Date: Sat, 6 Jul 2024 01:15:16 +0300 Subject: [PATCH 621/842] pulseaudio: Consider ignored sinks never running If the current sink happens to be ignored it is never considered running so it will be replaced with another sink. --- src/util/audio_backend.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index e634784b..3d90b6d5 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -144,6 +144,12 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i if (!backend->ignored_sinks_.empty()) { for (const auto &ignored_sink : backend->ignored_sinks_) { if (ignored_sink == i->description) { + if (i->name == backend->current_sink_name_) { + // If the current sink happens to be ignored it is never considered running + // so it will be replaced with another sink. + backend->current_sink_running_ = false; + } + return; } } From 21d42baa8ef13d98f29ac74e28565dfefe1e2217 Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Sat, 6 Jul 2024 08:16:45 +0800 Subject: [PATCH 622/842] (temperature) fix clang-tidy lint . --- src/modules/temperature.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 88910982..30287763 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -80,10 +80,10 @@ auto waybar::modules::Temperature::update() -> void { if (format.empty()) { event_box_.hide(); return; - } else { - event_box_.show(); } + event_box_.show(); + auto max_temp = config_["critical-threshold"].isInt() ? config_["critical-threshold"].asInt() : 0; label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("temperatureC", temperature_c), fmt::arg("temperatureF", temperature_f), From 7725f6ed5aca20eff825666937e76ae80ab7ea7d Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Mon, 8 Jul 2024 20:28:26 +0000 Subject: [PATCH 623/842] Fix build with fmt11 Since fmt 11.0.0, formatter:format() is required to be const.Mark affected functions as const to stay compatible with fmt 11. Signed-off-by: Yao Zi --- include/util/date.hpp | 2 +- include/util/format.hpp | 2 +- src/client.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/util/date.hpp b/include/util/date.hpp index 2431b766..a467cc56 100644 --- a/include/util/date.hpp +++ b/include/util/date.hpp @@ -64,7 +64,7 @@ struct fmt::formatter> { } template - auto format(const date::zoned_time& ztime, FormatContext& ctx) { + auto format(const date::zoned_time& ztime, FormatContext& ctx) const { if (ctx.locale()) { const auto loc = ctx.locale().template get(); return fmt::format_to(ctx.out(), "{}", date::format(loc, fmt::to_string(specs), ztime)); diff --git a/include/util/format.hpp b/include/util/format.hpp index 069d8897..a5630cf4 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -45,7 +45,7 @@ struct formatter { } template - auto format(const pow_format& s, FormatContext& ctx) -> decltype(ctx.out()) { + auto format(const pow_format& s, FormatContext& ctx) const -> decltype(ctx.out()) { const char* units[] = {"", "k", "M", "G", "T", "P", nullptr}; auto base = s.binary_ ? 1024ull : 1000ll; diff --git a/src/client.cpp b/src/client.cpp index 5768b1b0..cac1ffe8 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -123,7 +123,7 @@ void waybar::Client::handleMonitorAdded(Glib::RefPtr monitor) { } void waybar::Client::handleMonitorRemoved(Glib::RefPtr monitor) { - spdlog::debug("Output removed: {} {}", monitor->get_manufacturer(), monitor->get_model()); + spdlog::debug("Output removed: {} {}", monitor->get_manufacturer().c_str(), monitor->get_model().c_str()); /* This event can be triggered from wl_display_roundtrip called by GTK or our code. * Defer destruction of bars for the output to the next iteration of the event loop to avoid * deleting objects referenced by currently executed code. From e2e5d4d447fceca6d3185ddd4b7171a751412920 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 7 Jul 2024 22:08:45 +0200 Subject: [PATCH 624/842] feat/issue 3256: Toggle drawer state --- include/group.hpp | 4 ++++ man/waybar.5.scd.in | 5 +++++ src/group.cpp | 31 ++++++++++++++++++++++++++++--- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index 564d2eb5..b10402c6 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -25,9 +25,13 @@ class Group : public AModule { Gtk::Revealer revealer; bool is_first_widget = true; bool is_drawer = false; + bool click_to_reveal = false; std::string add_class_to_drawer_children; bool handleMouseEnter(GdkEventCrossing *const &ev) override; bool handleMouseLeave(GdkEventCrossing *const &ev) override; + bool handleToggle(GdkEventButton *const &ev) override; + void show_group(); + void hide_group(); }; } // namespace waybar diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 53613e4a..db546e17 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -278,6 +278,11 @@ A group may hide all but one element, showing them only on mouse hover. In order default: "hidden" ++ Defines the CSS class to be applied to the hidden elements. +*click-to-reveal*: ++ + typeof: bool ++ + default: false ++ + Whether left click should reveal the content rather than mouse over. Note that grouped modules may still process their own on-click events. + *transition-left-to-right*: ++ typeof: bool ++ default: true ++ diff --git a/src/group.cpp b/src/group.cpp index c77f2d31..deeecc75 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -62,6 +62,7 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& const bool left_to_right = (drawer_config["transition-left-to-right"].isBool() ? drawer_config["transition-left-to-right"].asBool() : true); + click_to_reveal = drawer_config["click-to-reveal"].asBool(); auto transition_type = getPreferredTransitionType(vertical); @@ -83,18 +84,42 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& event_box_.add(box); } -bool Group::handleMouseEnter(GdkEventCrossing* const& e) { +void Group::show_group() { box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); revealer.set_reveal_child(true); +} + +void Group::hide_group() { + box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); + revealer.set_reveal_child(false); +} + +bool Group::handleMouseEnter(GdkEventCrossing* const& e) { + if (!click_to_reveal) { + show_group(); + } return false; } bool Group::handleMouseLeave(GdkEventCrossing* const& e) { - box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); - revealer.set_reveal_child(false); + if (!click_to_reveal) { + hide_group(); + } return false; } +bool Group::handleToggle(GdkEventButton* const& e) { + if (!click_to_reveal || e->button != 1) { + return false; + } + if (box.get_state_flags() & Gtk::StateFlags::STATE_FLAG_PRELIGHT) { + hide_group(); + } else { + show_group(); + } + return true; +} + auto Group::update() -> void { // noop } From 0dd6af5a7e374161e2831a6f1cd052aeb0edbe25 Mon Sep 17 00:00:00 2001 From: Matt Provost Date: Thu, 11 Jul 2024 08:01:54 -0500 Subject: [PATCH 625/842] chore: update meson build command Signed-off-by: Matt Provost --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5781ea3a..a019eb6f 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ An Ubuntu PPA with more recent versions is available ```bash $ git clone https://github.com/Alexays/Waybar $ cd Waybar -$ meson build +$ meson setup build $ ninja -C build $ ./build/waybar # If you want to install it From e117bd7cb6da994fbe5bd3cb69ccecdd86664d09 Mon Sep 17 00:00:00 2001 From: Siddhant Kameswar <115331356+grimsteel@users.noreply.github.com> Date: Fri, 12 Jul 2024 20:46:26 -0500 Subject: [PATCH 626/842] network: add bssid format replacement --- include/modules/network.hpp | 2 ++ man/waybar-network.5.scd | 2 ++ src/modules/network.cpp | 23 ++++++++++++++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 47701b4e..4a84b02f 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -40,6 +40,7 @@ class Network : public ALabel { void parseEssid(struct nlattr**); void parseSignal(struct nlattr**); void parseFreq(struct nlattr**); + void parseBssid(struct nlattr**); bool associatedOrJoined(struct nlattr**); bool checkInterface(std::string name); auto getInfo() -> void; @@ -69,6 +70,7 @@ class Network : public ALabel { std::string state_; std::string essid_; + std::string bssid_; bool carrier_; std::string ifname_; std::string ipaddr_; diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index cc0b470b..bd546916 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -156,6 +156,8 @@ Addressed by *network* *{essid}*: Name (SSID) of the wireless network. +*{bssid}*: MAC address (BSSID) of the wireless access point. + *{signalStrength}*: Signal strength of the wireless network. *{signaldBm}*: Signal strength of the wireless network in dBm. diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 654afbe8..0e49177a 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -332,8 +332,8 @@ auto waybar::modules::Network::update() -> void { getState(signal_strength_); auto text = fmt::format( - fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), - fmt::arg("signalStrength", signal_strength_), + fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_), + fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), @@ -364,7 +364,7 @@ auto waybar::modules::Network::update() -> void { } if (!tooltip_format.empty()) { auto tooltip_text = fmt::format( - fmt::runtime(tooltip_format), fmt::arg("essid", essid_), + fmt::runtime(tooltip_format), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_), @@ -407,6 +407,7 @@ void waybar::modules::Network::clearIface() { ifid_ = -1; ifname_.clear(); essid_.clear(); + bssid_.clear(); ipaddr_.clear(); gwaddr_.clear(); netmask_.clear(); @@ -481,6 +482,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } else { // clear state related to WiFi connection net->essid_.clear(); + net->bssid_.clear(); net->signal_strength_dbm_ = 0; net->signal_strength_ = 0; net->signal_strength_app_.clear(); @@ -772,6 +774,7 @@ int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) { net->parseEssid(bss); net->parseSignal(bss); net->parseFreq(bss); + net->parseBssid(bss); return NL_OK; } @@ -837,6 +840,20 @@ void waybar::modules::Network::parseFreq(struct nlattr **bss) { } } +void waybar::modules::Network::parseBssid(struct nlattr **bss) { + if (bss[NL80211_BSS_BSSID] != nullptr) { + auto bssid = static_cast(nla_data(bss[NL80211_BSS_BSSID])); + auto bssid_len = nla_len(bss[NL80211_BSS_BSSID]); + if (bssid_len == 6) { + bssid_ = std::format( + "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", + bssid[0], bssid[1], bssid[2], + bssid[3], bssid[4], bssid[5] + ); + } + } +} + bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) { if (bss[NL80211_BSS_STATUS] == nullptr) { return false; From 0a78da0315a96e31365f49076021356cd278e5fa Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 15 Jul 2024 08:55:30 -0500 Subject: [PATCH 627/842] flake.lock: update --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 3f0deffe..0d945dbe 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1719506693, - "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=", + "lastModified": 1720957393, + "narHash": "sha256-oedh2RwpjEa+TNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a", + "rev": "693bc46d169f5af9c992095736e82c3488bf7dbb", "type": "github" }, "original": { From b41fcdedff884f25e96011278dcdb38788a291fe Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 15 Jul 2024 08:44:26 -0500 Subject: [PATCH 628/842] hyprland/window: fix crash when no return from socket Gracefully handle lack of response from the IPC. If socket isn't available, we already log the IPC isn't running. We dont need to crash program just because we couldn't get responses. We can just return an empty object. --- src/modules/hyprland/window.cpp | 132 +++++++++++++++++--------------- 1 file changed, 71 insertions(+), 61 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index ec151a7b..b5ed8f02 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -92,30 +92,39 @@ auto Window::update() -> void { auto Window::getActiveWorkspace() -> Workspace { const auto workspace = gIPC->getSocket1JsonReply("activeworkspace"); - assert(workspace.isObject()); - return Workspace::parse(workspace); + + if (workspace.isObject()) { + return Workspace::parse(workspace); + } + + return {}; } auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { const auto monitors = gIPC->getSocket1JsonReply("monitors"); - assert(monitors.isArray()); - auto monitor = std::find_if(monitors.begin(), monitors.end(), - [&](Json::Value monitor) { return monitor["name"] == monitorName; }); - if (monitor == std::end(monitors)) { - spdlog::warn("Monitor not found: {}", monitorName); - return Workspace{-1, 0, "", ""}; - } - const int id = (*monitor)["activeWorkspace"]["id"].asInt(); + if (monitors.isArray()) { + auto monitor = std::find_if(monitors.begin(), monitors.end(), [&](Json::Value monitor) { + return monitor["name"] == monitorName; + }); + if (monitor == std::end(monitors)) { + spdlog::warn("Monitor not found: {}", monitorName); + return Workspace{-1, 0, "", ""}; + } + const int id = (*monitor)["activeWorkspace"]["id"].asInt(); - const auto workspaces = gIPC->getSocket1JsonReply("workspaces"); - assert(workspaces.isArray()); - auto workspace = std::find_if(workspaces.begin(), workspaces.end(), - [&](Json::Value workspace) { return workspace["id"] == id; }); - if (workspace == std::end(workspaces)) { - spdlog::warn("No workspace with id {}", id); - return Workspace{-1, 0, "", ""}; - } - return Workspace::parse(*workspace); + const auto workspaces = gIPC->getSocket1JsonReply("workspaces"); + if (workspaces.isArray()) { + auto workspace = std::find_if(workspaces.begin(), workspaces.end(), + [&](Json::Value workspace) { return workspace["id"] == id; }); + if (workspace == std::end(workspaces)) { + spdlog::warn("No workspace with id {}", id); + return Workspace{-1, 0, "", ""}; + } + return Workspace::parse(*workspace); + }; + }; + + return {}; } auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { @@ -146,53 +155,54 @@ void Window::queryActiveWorkspace() { focused_ = true; if (workspace_.windows > 0) { const auto clients = gIPC->getSocket1JsonReply("clients"); - assert(clients.isArray()); - auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { - return window["address"] == workspace_.last_window; - }); + if (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; - } + if (activeWindow == std::end(clients)) { + focused_ = false; + return; + } - windowData_ = WindowData::parse(*activeWindow); - updateAppIconName(windowData_.class_name, windowData_.initial_class_name); - std::vector workspaceWindows; - std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows), - [&](Json::Value window) { - return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); - }); - swallowing_ = - std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) { - return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; - }); - std::vector visibleWindows; - std::copy_if(workspaceWindows.begin(), workspaceWindows.end(), - std::back_inserter(visibleWindows), - [&](Json::Value window) { return !window["hidden"].asBool(); }); - solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), - [&](Json::Value window) { return !window["floating"].asBool(); }); - allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(), - [&](Json::Value window) { return window["floating"].asBool(); }); - fullscreen_ = windowData_.fullscreen; + windowData_ = WindowData::parse(*activeWindow); + updateAppIconName(windowData_.class_name, windowData_.initial_class_name); + std::vector workspaceWindows; + std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows), + [&](Json::Value window) { + return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); + }); + swallowing_ = + std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) { + return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; + }); + std::vector visibleWindows; + std::copy_if(workspaceWindows.begin(), workspaceWindows.end(), + std::back_inserter(visibleWindows), + [&](Json::Value window) { return !window["hidden"].asBool(); }); + solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), + [&](Json::Value window) { return !window["floating"].asBool(); }); + allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(), + [&](Json::Value window) { return window["floating"].asBool(); }); + fullscreen_ = windowData_.fullscreen; - // Fullscreen windows look like they are solo - if (fullscreen_) { - solo_ = true; - } + // Fullscreen windows look like they are solo + if (fullscreen_) { + solo_ = true; + } - // Grouped windows have a tab bar and therefore don't look fullscreen or solo - if (windowData_.grouped) { - fullscreen_ = false; - solo_ = false; - } + // Grouped windows have a tab bar and therefore don't look fullscreen or solo + if (windowData_.grouped) { + fullscreen_ = false; + solo_ = false; + } - if (solo_) { - soloClass_ = windowData_.class_name; - } else { - soloClass_ = ""; - } + if (solo_) { + soloClass_ = windowData_.class_name; + } else { + soloClass_ = ""; + } + }; } else { focused_ = false; windowData_ = WindowData{}; From b19890c0b1e9de87bb5ada10e2d6fa3bf5ad518b Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 15 Jul 2024 08:48:01 -0500 Subject: [PATCH 629/842] network: clang-format --- src/modules/network.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 0e49177a..e84b0d90 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -845,11 +845,8 @@ void waybar::modules::Network::parseBssid(struct nlattr **bss) { auto bssid = static_cast(nla_data(bss[NL80211_BSS_BSSID])); auto bssid_len = nla_len(bss[NL80211_BSS_BSSID]); if (bssid_len == 6) { - bssid_ = std::format( - "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", - bssid[0], bssid[1], bssid[2], - bssid[3], bssid[4], bssid[5] - ); + bssid_ = std::format("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", bssid[0], bssid[1], bssid[2], bssid[3], + bssid[4], bssid[5]); } } } From 47d7324a19647996dda96aaae1a7ee3956900440 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 15 Jul 2024 08:48:08 -0500 Subject: [PATCH 630/842] client: clang-format --- src/client.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client.cpp b/src/client.cpp index cac1ffe8..63a9276a 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -123,7 +123,8 @@ void waybar::Client::handleMonitorAdded(Glib::RefPtr monitor) { } void waybar::Client::handleMonitorRemoved(Glib::RefPtr monitor) { - spdlog::debug("Output removed: {} {}", monitor->get_manufacturer().c_str(), monitor->get_model().c_str()); + spdlog::debug("Output removed: {} {}", monitor->get_manufacturer().c_str(), + monitor->get_model().c_str()); /* This event can be triggered from wl_display_roundtrip called by GTK or our code. * Defer destruction of bars for the output to the next iteration of the event loop to avoid * deleting objects referenced by currently executed code. From 895c870d02758b8698cdce859fc4c359d255c5a9 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 15 Jul 2024 09:44:39 -0500 Subject: [PATCH 631/842] network: use fmt for format Fixes the gentoo build --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index e84b0d90..0bbea631 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -845,7 +845,7 @@ void waybar::modules::Network::parseBssid(struct nlattr **bss) { auto bssid = static_cast(nla_data(bss[NL80211_BSS_BSSID])); auto bssid_len = nla_len(bss[NL80211_BSS_BSSID]); if (bssid_len == 6) { - bssid_ = std::format("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", bssid[0], bssid[1], bssid[2], bssid[3], + bssid_ = fmt::format("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); } } From b71dfce1f7eefcd6c0dc99f162899b053d7fe082 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Tue, 16 Jul 2024 06:39:45 +0800 Subject: [PATCH 632/842] Fix build with fmt11 Since fmt 11.0.0, formatter:format() is required to be const. Mark all of the specializations as const to be compatible with fmt 11. This change is implemented in the same spirit of 7725f6ed5a. Signed-off-by: Kefu Chai --- include/util/format.hpp | 2 +- src/modules/sni/item.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/util/format.hpp b/include/util/format.hpp index a5630cf4..cf8d706b 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -92,7 +92,7 @@ struct formatter { template <> struct formatter : formatter { template - auto format(const Glib::ustring& value, FormatContext& ctx) { + auto format(const Glib::ustring& value, FormatContext& ctx) const { return formatter::format(static_cast(value), ctx); } }; diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index b5c0dd85..6c4ec8c0 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -14,14 +14,14 @@ template <> struct fmt::formatter : formatter { - bool is_printable(const Glib::VariantBase& value) { + bool is_printable(const Glib::VariantBase& value) const { auto type = value.get_type_string(); /* Print only primitive (single character excluding 'v') and short complex types */ return (type.length() == 1 && islower(type[0]) && type[0] != 'v') || value.get_size() <= 32; } template - auto format(const Glib::VariantBase& value, FormatContext& ctx) { + auto format(const Glib::VariantBase& value, FormatContext& ctx) const { if (is_printable(value)) { return formatter::format(static_cast(value.print()), ctx); } else { From b65ca334a8c9218a9ee1f42c411ef81be5014f38 Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Tue, 16 Jul 2024 09:07:39 +0800 Subject: [PATCH 633/842] fix #3442 --- src/modules/sni/watcher.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/modules/sni/watcher.cpp b/src/modules/sni/watcher.cpp index 8c035ae1..324bd9f5 100644 --- a/src/modules/sni/watcher.cpp +++ b/src/modules/sni/watcher.cpp @@ -67,10 +67,9 @@ gboolean Watcher::handleRegisterHost(Watcher* obj, GDBusMethodInvocation* invoca } auto watch = gfWatchFind(obj->hosts_, bus_name, object_path); if (watch != nullptr) { - g_dbus_method_invocation_return_error( - invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, - "Status Notifier Host with bus name '%s' and object path '%s' is already registered", - bus_name, object_path); + g_warning("Status Notifier Host with bus name '%s' and object path '%s' is already registered", + bus_name, object_path); + sn_watcher_complete_register_item(obj->watcher_, invocation); return TRUE; } watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj); From 17132b250d4a61e0366742fb9d86210762d5a5a2 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 16 Jul 2024 18:24:40 -0500 Subject: [PATCH 634/842] sway/workspaces: remove deprecated field Was deprecated a long time ago, we removed the Hyprland version. Removing this, as well. --- src/modules/sway/workspaces.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 2adde69c..f5851737 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -125,18 +125,10 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { std::copy(output["floating_nodes"].begin(), output["floating_nodes"].end(), std::back_inserter(workspaces_)); } - if (config_["persistent_workspaces"].isObject()) { - spdlog::warn( - "persistent_workspaces is deprecated. Please change config to use " - "persistent-workspaces."); - } // adding persistent workspaces (as per the config file) - if (config_["persistent-workspaces"].isObject() || - config_["persistent_workspaces"].isObject()) { - const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject() - ? config_["persistent-workspaces"] - : config_["persistent_workspaces"]; + if (config_["persistent-workspaces"].isObject()) { + const Json::Value &p_workspaces = config_["persistent-workspaces"]; const std::vector p_workspaces_names = p_workspaces.getMemberNames(); for (const std::string &p_w_name : p_workspaces_names) { From 9c40137d05cfac4f41a49e354dd77d008a96386e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 16 Jul 2024 18:26:28 -0500 Subject: [PATCH 635/842] sway/workspaces: clang-tidy --- include/modules/sway/workspaces.hpp | 8 ++++---- src/modules/sway/workspaces.cpp | 16 +++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 4258252a..97f4e950 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -19,7 +19,7 @@ namespace waybar::modules::sway { class Workspaces : public AModule, public sigc::trackable { public: Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); - virtual ~Workspaces() = default; + ~Workspaces() override = default; auto update() -> void override; private: @@ -38,10 +38,10 @@ class Workspaces : public AModule, public sigc::trackable { Gtk::Button& addButton(const Json::Value&); void onButtonReady(const Json::Value&, Gtk::Button&); std::string getIcon(const std::string&, const Json::Value&); - const std::string getCycleWorkspace(std::vector::iterator, bool prev) const; + std::string getCycleWorkspace(std::vector::iterator, bool prev) const; uint16_t getWorkspaceIndex(const std::string& name) const; - std::string trimWorkspaceName(std::string); - bool handleScroll(GdkEventScroll*) override; + static std::string trimWorkspaceName(std::string); + bool handleScroll(GdkEventScroll* /*unused*/) override; const Bar& bar_; std::vector workspaces_; diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index f5851737..0ca41d1c 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -11,15 +11,14 @@ namespace waybar::modules::sway { // Helper function to assign a number to a workspace, just like sway. In fact // this is taken quite verbatim from `sway/ipc-json.c`. int Workspaces::convertWorkspaceNameToNum(std::string name) { - if (isdigit(name[0])) { + if (isdigit(name[0]) != 0) { errno = 0; - char *endptr = NULL; + char *endptr = nullptr; long long parsed_num = strtoll(name.c_str(), &endptr, 10); if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == name.c_str()) { return -1; - } else { - return (int)parsed_num; } + return (int)parsed_num; } return -1; } @@ -47,7 +46,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value bar_(bar), box_(bar.orientation, 0) { if (config["format-icons"]["high-priority-named"].isArray()) { - for (auto &it : config["format-icons"]["high-priority-named"]) { + for (const auto &it : config["format-icons"]["high-priority-named"]) { high_priority_named_.push_back(it.asString()); } } @@ -70,7 +69,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value m_windowRewriteRules = waybar::util::RegexCollection( windowRewrite, m_windowRewriteDefault, - [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); + [](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); ipc_.subscribe(R"(["workspace"])"); ipc_.subscribe(R"(["window"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); @@ -414,7 +413,7 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node } bool Workspaces::handleScroll(GdkEventScroll *e) { - if (gdk_event_get_pointer_emulated((GdkEvent *)e)) { + if (gdk_event_get_pointer_emulated((GdkEvent *)e) != 0) { /** * Ignore emulated scroll events on window */ @@ -464,8 +463,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { return true; } -const std::string Workspaces::getCycleWorkspace(std::vector::iterator it, - bool prev) const { +std::string Workspaces::getCycleWorkspace(std::vector::iterator it, bool prev) const { if (prev && it == workspaces_.begin() && !config_["disable-scroll-wraparound"].asBool()) { return (*(--workspaces_.end()))["name"].asString(); } From 4295faa7c4b23b7f6e86669d1fe8c93562e10241 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 16 Jul 2024 14:53:54 -0500 Subject: [PATCH 636/842] hyprland/backend: throw runtime_error instead of log Allows us to disable modules entirely when socket connection isn't working. This is similar to how sway handles their socket connections disabling modules. This supports a single waybar config for multiple IPCs. --- src/modules/hyprland/backend.cpp | 16 ++++++---------- test/hyprland/backend.cpp | 6 ++---- test/hyprland/fixtures/IPCTestFixture.hpp | 3 +++ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 8ec6edda..60453dcb 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -153,8 +153,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) { const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0); if (serverSocket < 0) { - spdlog::error("Hyprland IPC: Couldn't open a socket (1)"); - return ""; + throw std::runtime_error("Hyprland IPC: Couldn't open a socket (1)"); } memset(&aiHints, 0, sizeof(struct addrinfo)); @@ -162,16 +161,15 @@ std::string IPC::getSocket1Reply(const std::string& rq) { aiHints.ai_socktype = SOCK_STREAM; if (getaddrinfo("localhost", nullptr, &aiHints, &aiRes) != 0) { - spdlog::error("Hyprland IPC: Couldn't get host (2)"); - return ""; + throw std::runtime_error("Hyprland IPC: Couldn't get host (2)"); } // get the instance signature auto* instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE"); if (instanceSig == nullptr) { - spdlog::error("Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"); - return ""; + throw std::runtime_error( + "Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"); } sockaddr_un serverAddress = {0}; @@ -182,14 +180,12 @@ std::string IPC::getSocket1Reply(const std::string& rq) { // Use snprintf to copy the socketPath string into serverAddress.sun_path if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < 0) { - spdlog::error("Hyprland IPC: Couldn't copy socket path (6)"); - return ""; + throw std::runtime_error("Hyprland IPC: Couldn't copy socket path (6)"); } if (connect(serverSocket, reinterpret_cast(&serverAddress), sizeof(serverAddress)) < 0) { - spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); - return ""; + throw std::runtime_error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); } auto sizeWritten = write(serverSocket, rq.c_str(), rq.length()); diff --git a/test/hyprland/backend.cpp b/test/hyprland/backend.cpp index dcae0509..b83b839c 100644 --- a/test/hyprland/backend.cpp +++ b/test/hyprland/backend.cpp @@ -52,10 +52,8 @@ TEST_CASE_METHOD(IPCTestFixture, "XDGRuntimeDirExistsNoHyprDir", "[getSocketFold REQUIRE(actualPath == expectedPath); } -TEST_CASE_METHOD(IPCMock, "getSocket1JsonReply handles empty response", "[getSocket1JsonReply]") { +TEST_CASE_METHOD(IPCTestFixture, "getSocket1Reply throws on no socket", "[getSocket1Reply]") { std::string request = "test_request"; - Json::Value jsonResponse = getSocket1JsonReply(request); - - REQUIRE(jsonResponse.isNull()); + CHECK_THROWS(getSocket1Reply(request)); } diff --git a/test/hyprland/fixtures/IPCTestFixture.hpp b/test/hyprland/fixtures/IPCTestFixture.hpp index f6fa335f..caa92975 100644 --- a/test/hyprland/fixtures/IPCTestFixture.hpp +++ b/test/hyprland/fixtures/IPCTestFixture.hpp @@ -19,4 +19,7 @@ class IPCMock : public IPCTestFixture { public: // Mock getSocket1Reply to return an empty string static std::string getSocket1Reply(const std::string& rq) { return ""; } + + protected: + const char* instanceSig = "instance_sig"; }; From 90ac7d5d2c0fd5728647dd63fe0069170bfabe16 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 16 Jul 2024 22:48:25 -0500 Subject: [PATCH 637/842] sway/workspaces: support ignore window-rewrite Similar to hyprland implementation to ignore "" empty rules --- man/waybar-sway-workspaces.5.scd | 1 + src/modules/sway/workspaces.cpp | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index a65a999b..fc73a85a 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -87,6 +87,7 @@ warp-on-scroll: ++ Regex rules to map window class to an icon or preferred method of representation for a workspace's window. Keys are the rules, while the values are the methods of representation. Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching. + You may assign an empty value to a rule to have it ignored from generating any representation in workspaces. *window-rewrite-default*: typeof: string ++ diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 0ca41d1c..8f273300 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -261,13 +261,17 @@ void Workspaces::updateWindows(const Json::Value &node, std::string &windows) { node["name"].isString()) { std::string title = g_markup_escape_text(node["name"].asString().c_str(), -1); std::string windowClass = node["app_id"].asString(); - std::string windowReprKey = fmt::format("class<{}> title<{}>", windowClass, title); - std::string window = m_windowRewriteRules.get(windowReprKey); - // allow result to have formatting - window = - fmt::format(fmt::runtime(window), fmt::arg("name", title), fmt::arg("class", windowClass)); - windows.append(window); - windows.append(m_formatWindowSeperator); + + // Only add window rewrites that can be looked up + if (!windowClass.empty()) { + std::string windowReprKey = fmt::format("class<{}> title<{}>", windowClass, title); + std::string window = m_windowRewriteRules.get(windowReprKey); + // allow result to have formatting + window = fmt::format(fmt::runtime(window), fmt::arg("name", title), + fmt::arg("class", windowClass)); + windows.append(window); + windows.append(m_formatWindowSeperator); + } } for (const Json::Value &child : node["nodes"]) { updateWindows(child, windows); From ed0ed398b74cdd3bbf6bfcc751255507c56062e2 Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Wed, 17 Jul 2024 22:46:58 +0200 Subject: [PATCH 638/842] Update freebsd.yml --- .github/workflows/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 7effb484..242633f5 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Test in FreeBSD VM - uses: cross-platform-actions/action@v0.23.0 + uses: cross-platform-actions/action@v0.25.0 timeout-minutes: 180 env: CPPFLAGS: '-isystem/usr/local/include' From dcbcf90aef9665b179cf8c021dfd1c008e06ee10 Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Wed, 17 Jul 2024 22:52:39 +0200 Subject: [PATCH 639/842] Update freebsd.yml --- .github/workflows/freebsd.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 242633f5..bbb97198 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -21,11 +21,10 @@ jobs: LDFLAGS: '-L/usr/local/lib' with: operating_system: freebsd - version: "13.2" + version: "14.1" environment_variables: CPPFLAGS LDFLAGS sync_files: runner-to-vm run: | - sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf sudo pkg install -y git # subprojects/date sudo pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \ libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ From 15e1547661bfc5fe9b3d45bb0d9cea11cf07db7f Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 17 Jul 2024 23:04:05 +0200 Subject: [PATCH 640/842] chore: 0.10.4 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a154a51b..8daa6c9c 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.10.3', + version: '0.10.4', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From ee0912a254326ecbc0686fdd6c571f63bac73d95 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sat, 20 Jul 2024 09:00:59 +0200 Subject: [PATCH 641/842] Issue #3414/clock: Shift ONLY calendar Right now, for the tooltip, all times are shifted if shift-down/shift-up actions are used. But it really only makes sense for this to apply to the {calendar} replacement, so use shiftedNow there and now for all the rest. --- src/modules/clock.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index fe2c4c8f..7a4cb9c2 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -163,15 +163,16 @@ auto waybar::modules::Clock::update() -> void { // std::vformat doesn't support named arguments. m_tlpText_ = std::regex_replace(m_tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_); - m_tlpText_ = - std::regex_replace(m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_); + m_tlpText_ = std::regex_replace( + m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), + fmt_lib::vformat(m_locale_, cldText_, fmt_lib::make_format_args(shiftedNow))); m_tlpText_ = std::regex_replace(m_tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_); } else { m_tlpText_ = m_tlpFmt_; } - m_tlpText_ = fmt_lib::vformat(m_locale_, m_tlpText_, fmt_lib::make_format_args(shiftedNow)); + m_tlpText_ = fmt_lib::vformat(m_locale_, m_tlpText_, fmt_lib::make_format_args(now)); m_tooltip_->set_markup(m_tlpText_); label_.trigger_tooltip_query(); } From a544f4b2cdcf632f1a4424b89f6e3d85ef5aaa85 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 20 Jul 2024 09:33:13 -0500 Subject: [PATCH 642/842] bar: fix setVisible Accidentally removed updating the visible variable --- include/bar.hpp | 2 +- src/bar.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 6900da47..43756bfd 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -66,7 +66,7 @@ class Bar { ~Bar(); void setMode(const std::string &mode); - void setVisible(bool visible); + void setVisible(bool value); void toggle(); void handleSignal(int); diff --git a/src/bar.cpp b/src/bar.cpp index 8c75c2c2..8a245ad1 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -404,7 +404,8 @@ void waybar::Bar::onMap(GdkEventAny* /*unused*/) { setPassThrough(passthrough_); } -void waybar::Bar::setVisible(bool visible) { +void waybar::Bar::setVisible(bool value) { + visible = value; if (auto mode = config.get("mode", {}); mode.isString()) { setMode(visible ? config["mode"].asString() : MODE_INVISIBLE); } else { From 58e21e876e3b4184f197ed8c8f48a081130ab3a4 Mon Sep 17 00:00:00 2001 From: DomCristaldi Date: Sat, 20 Jul 2024 22:58:03 -0400 Subject: [PATCH 643/842] walk up symlink tree "reload_style_on_change" would check if the target file is a symlink, but only resolves the first link. If the symlink is acutally a chain of symlink, such as what happens with NixOS's mkOutOfStoreSymlink, we will not find the actual file style file. Update the symlink resolution logic to walk down the symlink chain until it finds a non-symlink. Also check against a the original filename (which may be a symlink) to guard against infinitely looping on a circular symlink chain. --- src/util/css_reload_helper.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/util/css_reload_helper.cpp b/src/util/css_reload_helper.cpp index 45fd801a..e440c3c1 100644 --- a/src/util/css_reload_helper.cpp +++ b/src/util/css_reload_helper.cpp @@ -43,8 +43,14 @@ std::string waybar::CssReloadHelper::findPath(const std::string& filename) { } // File monitor does not work with symlinks, so resolve them - if (std::filesystem::is_symlink(result)) { + std::string original = result; + while(std::filesystem::is_symlink(result)) { result = std::filesystem::read_symlink(result); + + // prevent infinite cycle + if (result == original) { + break; + } } return result; From f4608b3e312448b37a8f9d6351154026e67c680a Mon Sep 17 00:00:00 2001 From: schmop Date: Thu, 25 Jul 2024 01:40:49 +0200 Subject: [PATCH 644/842] Fix battery status changes not being detected Historically we listened to /sys/class/poewr_supply inotify events, which does not seem to work anymore. We switched now to udev netlink kernel events. --- include/modules/battery.hpp | 8 +++++-- include/util/udev_deleter.hpp | 21 ++++++++++++++++++ src/modules/battery.cpp | 40 +++++++++++++++++++--------------- src/util/backlight_backend.cpp | 17 +-------------- 4 files changed, 51 insertions(+), 35 deletions(-) create mode 100644 include/util/udev_deleter.hpp diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 8e1a2ad2..fc403be0 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -5,8 +5,11 @@ #include #if defined(__linux__) #include +#include "util/udev_deleter.hpp" #endif +#include + #include #include #include @@ -36,11 +39,12 @@ class Battery : public ALabel { const std::string formatTimeRemaining(float hoursRemaining); void setBarClass(std::string&); - int global_watch; std::map batteries_; + std::unique_ptr udev_; + std::array poll_fds_; + std::unique_ptr mon_; fs::path adapter_; int battery_watch_fd_; - int global_watch_fd_; std::mutex battery_list_mutex_; std::string old_status_; bool warnFirstTime_{true}; diff --git a/include/util/udev_deleter.hpp b/include/util/udev_deleter.hpp new file mode 100644 index 00000000..b2f1e538 --- /dev/null +++ b/include/util/udev_deleter.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace waybar::util { +struct UdevDeleter { + void operator()(udev *ptr) const { udev_unref(ptr); } +}; + +struct UdevDeviceDeleter { + void operator()(udev_device *ptr) const { udev_device_unref(ptr); } +}; + +struct UdevEnumerateDeleter { + void operator()(udev_enumerate *ptr) const { udev_enumerate_unref(ptr); } +}; + +struct UdevMonitorDeleter { + void operator()(udev_monitor *ptr) const { udev_monitor_unref(ptr); } +}; +} // namespace waybar::util \ No newline at end of file diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index d87cc612..bad72e6b 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -5,6 +5,9 @@ #include #endif #include +#include +#include +#include #include waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const Json::Value& config) @@ -14,17 +17,18 @@ waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const J if (battery_watch_fd_ == -1) { throw std::runtime_error("Unable to listen batteries."); } - - global_watch_fd_ = inotify_init1(IN_CLOEXEC); - if (global_watch_fd_ == -1) { - throw std::runtime_error("Unable to listen batteries."); + udev_ = std::unique_ptr(udev_new()); + if (udev_ == nullptr) { + throw std::runtime_error("udev_new failed"); } - - // Watch the directory for any added or removed batteries - global_watch = inotify_add_watch(global_watch_fd_, data_dir_.c_str(), IN_CREATE | IN_DELETE); - if (global_watch < 0) { - throw std::runtime_error("Could not watch for battery plug/unplug"); + mon_ = std::unique_ptr(udev_monitor_new_from_netlink(udev_.get(), "kernel")); + if (mon_ == nullptr) { + throw std::runtime_error("udev monitor new failed"); } + if (udev_monitor_filter_add_match_subsystem_devtype(mon_.get(), "power_supply", nullptr) < 0) { + throw std::runtime_error("udev failed to add monitor filter"); + } + udev_monitor_enable_receiving(mon_.get()); #endif worker(); } @@ -33,11 +37,6 @@ waybar::modules::Battery::~Battery() { #if defined(__linux__) std::lock_guard guard(battery_list_mutex_); - if (global_watch >= 0) { - inotify_rm_watch(global_watch_fd_, global_watch); - } - close(global_watch_fd_); - for (auto it = batteries_.cbegin(), next_it = it; it != batteries_.cend(); it = next_it) { ++next_it; auto watch_id = (*it).second; @@ -74,12 +73,18 @@ void waybar::modules::Battery::worker() { dp.emit(); }; thread_battery_update_ = [this] { - struct inotify_event event = {0}; - int nbytes = read(global_watch_fd_, &event, sizeof(event)); - if (nbytes != sizeof(event) || event.mask & IN_IGNORED) { + poll_fds_[0].revents = 0; + poll_fds_[0].events = POLLIN; + poll_fds_[0].fd = udev_monitor_get_fd(mon_.get()); + int ret = poll(poll_fds_.data(), poll_fds_.size(), -1); + if (ret < 0) { thread_.stop(); return; } + if ((poll_fds_[0].revents & POLLIN) != 0) { + signalfd_siginfo signal_info; + read(poll_fds_[0].fd, &signal_info, sizeof(signal_info)); + } refreshBatteries(); dp.emit(); }; @@ -668,6 +673,7 @@ auto waybar::modules::Battery::update() -> void { status = getAdapterStatus(capacity); } auto status_pretty = status; + puts(status.c_str()); // Transform to lowercase and replace space with dash std::transform(status.begin(), status.end(), status.begin(), [](char ch) { return ch == ' ' ? '-' : std::tolower(ch); }); diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index bb102cd9..df6afd56 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -1,4 +1,5 @@ #include "util/backlight_backend.hpp" +#include "util/udev_deleter.hpp" #include #include @@ -29,22 +30,6 @@ class FileDescriptor { 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)); From 7e1fffc455cb9930982e65d29e1b8bfd7d2c90d9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 Aug 2024 00:09:59 +0000 Subject: [PATCH 645/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/693bc46d169f5af9c992095736e82c3488bf7dbb?narHash=sha256-oedh2RwpjEa%2BTNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA%3D' (2024-07-14) → 'github:NixOS/nixpkgs/52ec9ac3b12395ad677e8b62106f0b98c1f8569d?narHash=sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k%3D' (2024-07-28) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 0d945dbe..b8f68f4b 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1720957393, - "narHash": "sha256-oedh2RwpjEa+TNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA=", + "lastModified": 1722185531, + "narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "693bc46d169f5af9c992095736e82c3488bf7dbb", + "rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d", "type": "github" }, "original": { From 7ec1343ad5012e5ada25e76aefc227d35e4ce7f7 Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Thu, 1 Aug 2024 17:47:10 +0800 Subject: [PATCH 646/842] fix #3490: expand menu file before opening it --- include/config.hpp | 3 +++ src/ALabel.cpp | 12 +++++++++++- src/config.cpp | 3 ++- src/util/css_reload_helper.cpp | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/config.hpp b/include/config.hpp index 66945542..18a1daed 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -20,6 +20,9 @@ class Config { static std::optional findConfigPath( const std::vector &names, const std::vector &dirs = CONFIG_DIRS); + static std::optional tryExpandPath(const std::string &base, + const std::string &filename); + Config() = default; void load(const std::string &config); diff --git a/src/ALabel.cpp b/src/ALabel.cpp index da2991a3..ecb1b7ce 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -6,6 +6,8 @@ #include #include +#include "config.hpp" + namespace waybar { ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id, @@ -61,6 +63,14 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st try { // Check that the file exists std::string menuFile = config_["menu-file"].asString(); + + // there might be "~" or "$HOME" in original path, try to expand it. + auto result = Config::tryExpandPath(menuFile, ""); + if (!result.has_value()) { + throw std::runtime_error("Failed to expand file: " + menuFile); + } + + menuFile = result.value(); // Read the menu descriptor file std::ifstream file(menuFile); if (!file.is_open()) { @@ -170,7 +180,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) { return AModule::handleToggle(e); } -void ALabel::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) { +void ALabel::handleGtkMenuEvent(GtkMenuItem* /*menuitem*/, gpointer data) { waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu"); } diff --git a/src/config.cpp b/src/config.cpp index b78af56c..375dc4cb 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -21,7 +21,8 @@ const std::vector Config::CONFIG_DIRS = { const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR"; -std::optional tryExpandPath(const std::string &base, const std::string &filename) { +std::optional Config::tryExpandPath(const std::string &base, + const std::string &filename) { fs::path path; if (!filename.empty()) { diff --git a/src/util/css_reload_helper.cpp b/src/util/css_reload_helper.cpp index e440c3c1..274bdeed 100644 --- a/src/util/css_reload_helper.cpp +++ b/src/util/css_reload_helper.cpp @@ -44,7 +44,7 @@ std::string waybar::CssReloadHelper::findPath(const std::string& filename) { // File monitor does not work with symlinks, so resolve them std::string original = result; - while(std::filesystem::is_symlink(result)) { + while (std::filesystem::is_symlink(result)) { result = std::filesystem::read_symlink(result); // prevent infinite cycle From 24a9886952297a3be27c26195b924c5bf975f260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20S=C3=A1lyi?= Date: Fri, 2 Aug 2024 15:21:01 +0200 Subject: [PATCH 647/842] Handle offline CPUs and CPU hotplug First of all in case when the number CPUs change prevent out-of-bound index access in waybar::modules::CpuUsage::getCpuUsage() Secondly on Linux when updating CPU usage read /sys/devices/system/cpu/present and use it to detect the offline CPUs missing from /proc/stat For offline CPUs report 0 usage and "offline" in the tooltip Fixes issue #3498 On Linux one can test this functionality with: echo 0 > /sys/devices/system/cpu/cpu1/online echo 1 > /sys/devices/system/cpu/cpu1/online On non-Linux OSes I'm not sure how to detect offline CPUs, so I didn't add the offline CPU detection there but at least CPU number change should not cause a crash there anymore or cause memory safety issues after this fix --- src/modules/cpu_usage/common.cpp | 27 +++++++++++++++++++++++ src/modules/cpu_usage/linux.cpp | 38 ++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/modules/cpu_usage/common.cpp b/src/modules/cpu_usage/common.cpp index 4e36f48e..e3947967 100644 --- a/src/modules/cpu_usage/common.cpp +++ b/src/modules/cpu_usage/common.cpp @@ -61,9 +61,36 @@ std::tuple, std::string> waybar::modules::CpuUsage::getCpu std::vector> curr_times = CpuUsage::parseCpuinfo(); std::string tooltip; std::vector usage; + + if (curr_times.size() != prev_times.size()) { + // The number of CPUs has changed, eg. due to CPU hotplug + // We don't know which CPU came up or went down + // so only give total usage (if we can) + if (!curr_times.empty() && !prev_times.empty()) { + auto [curr_idle, curr_total] = curr_times[0]; + auto [prev_idle, prev_total] = prev_times[0]; + const float delta_idle = curr_idle - prev_idle; + const float delta_total = curr_total - prev_total; + uint16_t tmp = 100 * (1 - delta_idle / delta_total); + tooltip = fmt::format("Total: {}%\nCores: (pending)", tmp); + usage.push_back(tmp); + } else { + tooltip = "(pending)"; + usage.push_back(0); + } + prev_times = curr_times; + return {usage, tooltip}; + } + for (size_t i = 0; i < curr_times.size(); ++i) { auto [curr_idle, curr_total] = curr_times[i]; auto [prev_idle, prev_total] = prev_times[i]; + if (i > 0 && (curr_total == 0 || prev_total == 0)) { + // This CPU is offline + tooltip = tooltip + fmt::format("\nCore{}: offline", i - 1); + usage.push_back(0); + continue; + } const float delta_idle = curr_idle - prev_idle; const float delta_total = curr_total - prev_total; uint16_t tmp = 100 * (1 - delta_idle / delta_total); diff --git a/src/modules/cpu_usage/linux.cpp b/src/modules/cpu_usage/linux.cpp index bcd9594e..6fbd659b 100644 --- a/src/modules/cpu_usage/linux.cpp +++ b/src/modules/cpu_usage/linux.cpp @@ -3,6 +3,23 @@ #include "modules/cpu_usage.hpp" std::vector> waybar::modules::CpuUsage::parseCpuinfo() { + // Get the "existing CPU count" from /sys/devices/system/cpu/present + // Probably this is what the user wants the offline CPUs accounted from + // For further details see: + // https://www.kernel.org/doc/html/latest/core-api/cpu_hotplug.html + const std::string sys_cpu_present_path = "/sys/devices/system/cpu/present"; + size_t cpu_present_last = 0; + std::ifstream cpu_present_file(sys_cpu_present_path); + std::string cpu_present_text; + if (cpu_present_file.is_open()) { + getline(cpu_present_file, cpu_present_text); + // This is a comma-separated list of ranges, eg. 0,2-4,7 + size_t last_separator = cpu_present_text.find_last_of("-,"); + if (last_separator < cpu_present_text.size()) { + std::stringstream(cpu_present_text.substr(last_separator + 1)) >> cpu_present_last; + } + } + const std::string data_dir_ = "/proc/stat"; std::ifstream info(data_dir_); if (!info.is_open()) { @@ -10,14 +27,23 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( } std::vector> cpuinfo; std::string line; + size_t current_cpu_number = -1; // First line is total, second line is cpu 0 while (getline(info, line)) { if (line.substr(0, 3).compare("cpu") != 0) { break; } + size_t line_cpu_number; + if (current_cpu_number >= 0) { + std::stringstream(line.substr(3)) >> line_cpu_number; + while (line_cpu_number > current_cpu_number) { + // Fill in 0 for offline CPUs missing inside the lines of /proc/stat + cpuinfo.emplace_back(0, 0); + current_cpu_number++; + } + } std::stringstream sline(line.substr(5)); std::vector times; - for (size_t time = 0; sline >> time; times.push_back(time)) - ; + for (size_t time = 0; sline >> time; times.push_back(time)); size_t idle_time = 0; size_t total_time = 0; @@ -27,6 +53,14 @@ std::vector> waybar::modules::CpuUsage::parseCpuinfo( total_time = std::accumulate(times.begin(), times.end(), 0); } cpuinfo.emplace_back(idle_time, total_time); + current_cpu_number++; } + + while (cpu_present_last >= current_cpu_number) { + // Fill in 0 for offline CPUs missing after the lines of /proc/stat + cpuinfo.emplace_back(0, 0); + current_cpu_number++; + } + return cpuinfo; } From 4efa1231835f87b55852cdf9e27b96d0cdb3d60c Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 2 Aug 2024 22:30:56 -0500 Subject: [PATCH 648/842] group: clang-tidy --- include/group.hpp | 2 +- src/group.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index b10402c6..5ce331a8 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -12,7 +12,7 @@ namespace waybar { class Group : public AModule { public: Group(const std::string &, const std::string &, const Json::Value &, bool); - virtual ~Group() = default; + ~Group() override = default; auto update() -> void override; operator Gtk::Widget &() override; diff --git a/src/group.cpp b/src/group.cpp index deeecc75..2660868a 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -9,7 +9,7 @@ namespace waybar { -const Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) { +Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) { /* The transition direction of a drawer is not actually determined by the transition type, * but rather by the order of 'box' and 'revealer_box': * 'REVEALER_TRANSITION_TYPE_SLIDE_LEFT' and 'REVEALER_TRANSITION_TYPE_SLIDE_RIGHT' @@ -112,7 +112,7 @@ bool Group::handleToggle(GdkEventButton* const& e) { if (!click_to_reveal || e->button != 1) { return false; } - if (box.get_state_flags() & Gtk::StateFlags::STATE_FLAG_PRELIGHT) { + if ((box.get_state_flags() & Gtk::StateFlags::STATE_FLAG_PRELIGHT) != 0U) { hide_group(); } else { show_group(); From 3ae81d62bc300ece26c23e4f01c44180d6cc3edc Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 2 Aug 2024 22:32:22 -0500 Subject: [PATCH 649/842] group: fix hover regression We aren't including the hover detection on the revealer, so when the animation fires we fire the leave event which starts an infinite loop of enter/leave while we watch boxes move back and forth. --- include/group.hpp | 1 + src/group.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/group.hpp b/include/group.hpp index 5ce331a8..f5c6864b 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -30,6 +30,7 @@ class Group : public AModule { bool handleMouseEnter(GdkEventCrossing *const &ev) override; bool handleMouseLeave(GdkEventCrossing *const &ev) override; bool handleToggle(GdkEventButton *const &ev) override; + void addHoverHandlerTo(Gtk::Widget &widget); void show_group(); void hide_group(); }; diff --git a/src/group.cpp b/src/group.cpp index 2660868a..9b7ac2d5 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -81,9 +81,16 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& } } + addHoverHandlerTo(revealer); event_box_.add(box); } +void Group::addHoverHandlerTo(Gtk::Widget& widget) { + widget.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK); + widget.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseEnter)); + widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseLeave)); +} + void Group::show_group() { box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); revealer.set_reveal_child(true); From 05d69ae82244cb28d4ce22009dc2bc486d278574 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 2 Aug 2024 22:37:06 -0500 Subject: [PATCH 650/842] src/util/css_reload_helper: clang-format --- src/util/css_reload_helper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/css_reload_helper.cpp b/src/util/css_reload_helper.cpp index e440c3c1..274bdeed 100644 --- a/src/util/css_reload_helper.cpp +++ b/src/util/css_reload_helper.cpp @@ -44,7 +44,7 @@ std::string waybar::CssReloadHelper::findPath(const std::string& filename) { // File monitor does not work with symlinks, so resolve them std::string original = result; - while(std::filesystem::is_symlink(result)) { + while (std::filesystem::is_symlink(result)) { result = std::filesystem::read_symlink(result); // prevent infinite cycle From 17f07b24522da93da28f3e9083d25cd7126489cf Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 2 Aug 2024 23:37:52 -0500 Subject: [PATCH 651/842] group: proper fix of enter/leave Ignore mouse leave event when we are still within the parent element --- include/group.hpp | 1 - src/group.cpp | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index f5c6864b..5ce331a8 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -30,7 +30,6 @@ class Group : public AModule { bool handleMouseEnter(GdkEventCrossing *const &ev) override; bool handleMouseLeave(GdkEventCrossing *const &ev) override; bool handleToggle(GdkEventButton *const &ev) override; - void addHoverHandlerTo(Gtk::Widget &widget); void show_group(); void hide_group(); }; diff --git a/src/group.cpp b/src/group.cpp index 9b7ac2d5..50841efd 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -81,16 +81,9 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value& } } - addHoverHandlerTo(revealer); event_box_.add(box); } -void Group::addHoverHandlerTo(Gtk::Widget& widget) { - widget.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK); - widget.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseEnter)); - widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseLeave)); -} - void Group::show_group() { box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); revealer.set_reveal_child(true); @@ -109,7 +102,7 @@ bool Group::handleMouseEnter(GdkEventCrossing* const& e) { } bool Group::handleMouseLeave(GdkEventCrossing* const& e) { - if (!click_to_reveal) { + if (!click_to_reveal && e->detail != GDK_NOTIFY_INFERIOR) { hide_group(); } return false; From fdc8431709447bbfaf124115e2cffa1b54b391cb Mon Sep 17 00:00:00 2001 From: Scott Moreau Date: Sun, 4 Aug 2024 22:49:51 -0600 Subject: [PATCH 652/842] taskbar: Send minimize geometry hints This allows compositors to know the minimize widget geometry so that minimize animations work properly. --- include/modules/wlr/taskbar.hpp | 7 +++++++ src/modules/wlr/taskbar.cpp | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 4465dd06..026f364a 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -24,6 +24,10 @@ namespace waybar::modules::wlr { +struct widget_geometry { + int x, y, w, h; +}; + class Taskbar; class Task { @@ -42,6 +46,7 @@ class Task { }; // made public so TaskBar can reorder based on configuration. Gtk::Button button; + struct widget_geometry minimize_hint; private: static uint32_t global_id; @@ -82,6 +87,8 @@ class Task { private: std::string repr() const; std::string state_string(bool = false) const; + void set_minimize_hint(); + void on_button_size_allocated(Gtk::Allocation &alloc); void set_app_info_from_app_id_list(const std::string &app_id_list); bool image_load_icon(Gtk::Image &image, const Glib::RefPtr &icon_theme, Glib::RefPtr app_info, int size); diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index e6c8e536..7ff11baf 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -387,6 +387,10 @@ void Task::handle_title(const char *title) { hide_if_ignored(); } +void Task::set_minimize_hint() { + zwlr_foreign_toplevel_handle_v1_set_rectangle(handle_, bar_.surface, minimize_hint.x, minimize_hint.y, minimize_hint.w, minimize_hint.h); +} + void Task::hide_if_ignored() { if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) { ignored_ = true; @@ -447,6 +451,12 @@ void Task::handle_app_id(const char *app_id) { spdlog::debug("Couldn't find icon for {}", app_id_); } +void Task::on_button_size_allocated(Gtk::Allocation &alloc) { + gtk_widget_translate_coordinates(GTK_WIDGET(button.gobj()), GTK_WIDGET(bar_.window.gobj()), 0, 0, &minimize_hint.x, &minimize_hint.y); + minimize_hint.w = button.get_width(); + minimize_hint.h = button.get_height(); +} + void Task::handle_output_enter(struct wl_output *output) { if (ignored_) { spdlog::debug("{} is ignored", repr()); @@ -457,6 +467,7 @@ void Task::handle_output_enter(struct wl_output *output) { if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) { /* The task entered the output of the current bar make the button visible */ + button.signal_size_allocate().connect_notify(sigc::mem_fun(this, &Task::on_button_size_allocated)); tbar_->add_button(button); button.show(); button_visible_ = true; @@ -553,9 +564,11 @@ bool Task::handle_clicked(GdkEventButton *bt) { return true; else if (action == "activate") activate(); - else if (action == "minimize") + else if (action == "minimize") { + set_minimize_hint(); minimize(!minimized()); - else if (action == "minimize-raise") { + } else if (action == "minimize-raise") { + set_minimize_hint(); if (minimized()) minimize(false); else if (active()) From c468119f5220b61045f6fcc4588f2bba7528b3a1 Mon Sep 17 00:00:00 2001 From: hacrvlq Date: Tue, 6 Aug 2024 18:01:37 +0200 Subject: [PATCH 653/842] fix(wireplumber): Handle changes to the default node ID --- src/modules/wireplumber.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index bd019b62..eddc3e6b 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -163,7 +163,8 @@ void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wir "[{}]: (onDefaultNodesApiChanged) - got the following default node: Node(name: {}, id: {})", self->name_, defaultNodeName, defaultNodeId); - if (g_strcmp0(self->default_node_name_, defaultNodeName) == 0) { + if (g_strcmp0(self->default_node_name_, defaultNodeName) == 0 && + self->node_id_ == defaultNodeId) { spdlog::debug( "[{}]: (onDefaultNodesApiChanged) - Default node has not changed. Node(name: {}, id: {}). " "Ignoring.", From 62cb61c670876f192976a366b3fc4175cbe72406 Mon Sep 17 00:00:00 2001 From: Sonter Date: Wed, 14 Aug 2024 11:34:28 +0300 Subject: [PATCH 654/842] Add format_silent to cava module --- include/modules/cava.hpp | 1 + src/modules/cava.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/modules/cava.hpp b/include/modules/cava.hpp index 430c71b7..219d9302 100644 --- a/include/modules/cava.hpp +++ b/include/modules/cava.hpp @@ -39,6 +39,7 @@ class Cava final : public ALabel { std::chrono::seconds suspend_silence_delay_{0}; bool silence_{false}; bool hide_on_silence_{false}; + std::string format_silent_{""}; int sleep_counter_{0}; // Cava method void pause_resume(); diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 431ce5f1..1841e276 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -59,6 +59,7 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) if (config_["input_delay"].isInt()) fetch_input_delay_ = std::chrono::seconds(config_["input_delay"].asInt()); if (config_["hide_on_silence"].isBool()) hide_on_silence_ = config_["hide_on_silence"].asBool(); + if (config_["format_silent"].isString()) format_silent_ = config_["format_silent"].asString(); // Make cava parameters configuration plan_ = new cava::cava_plan{}; @@ -176,6 +177,7 @@ auto waybar::modules::Cava::update() -> void { } else { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); if (hide_on_silence_) label_.hide(); + else if (config_["format_silent"].isString()) label_.set_markup(format_silent_); } } From ed40168d89085d30538422252016018f152a2d06 Mon Sep 17 00:00:00 2001 From: Sonter <108224581+S0nter@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:43:17 +0000 Subject: [PATCH 655/842] Add cava.silence to css --- src/modules/cava.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 1841e276..26ad4fd1 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -174,10 +174,14 @@ auto waybar::modules::Cava::update() -> void { label_.show(); ALabel::update(); } + + label_.get_style_context()->remove_class("silence"); } else { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); if (hide_on_silence_) label_.hide(); else if (config_["format_silent"].isString()) label_.set_markup(format_silent_); + + label_.get_style_context()->add_class("silence"); } } From 367f156eb0f956b848739c94139ea0311e8f1890 Mon Sep 17 00:00:00 2001 From: Sonter Date: Wed, 14 Aug 2024 19:25:07 +0300 Subject: [PATCH 656/842] Add cava.update to css --- src/modules/cava.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 26ad4fd1..1e6a97f7 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -173,6 +173,7 @@ auto waybar::modules::Cava::update() -> void { label_.set_markup(text_); label_.show(); ALabel::update(); + label_.get_style_context()->add_class("update"); } label_.get_style_context()->remove_class("silence"); @@ -182,6 +183,7 @@ auto waybar::modules::Cava::update() -> void { else if (config_["format_silent"].isString()) label_.set_markup(format_silent_); label_.get_style_context()->add_class("silence"); + label_.get_style_context()->remove_class("update"); } } From 36497cd41a599d8c5f9da8f11a8dd4ea5efe169e Mon Sep 17 00:00:00 2001 From: Sonter Date: Wed, 14 Aug 2024 19:27:12 +0300 Subject: [PATCH 657/842] Rename cava css values --- src/modules/cava.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 1e6a97f7..f81bf799 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -173,17 +173,17 @@ auto waybar::modules::Cava::update() -> void { label_.set_markup(text_); label_.show(); ALabel::update(); - label_.get_style_context()->add_class("update"); + label_.get_style_context()->add_class("updated"); } - label_.get_style_context()->remove_class("silence"); + label_.get_style_context()->remove_class("silent"); } else { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); if (hide_on_silence_) label_.hide(); else if (config_["format_silent"].isString()) label_.set_markup(format_silent_); - label_.get_style_context()->add_class("silence"); - label_.get_style_context()->remove_class("update"); + label_.get_style_context()->add_class("silent"); + label_.get_style_context()->remove_class("updated"); } } From 1f23b30b560b4577bf65adc6f77bb5595abaccb0 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Aug 2024 22:24:15 -0700 Subject: [PATCH 658/842] hyprland/backend: drop unnecessary getaddrinfo call Hyprland hasn't been using TCP sockets for IPC since the first release, so this getaddrinfo call and its result was never needed. Additionally, it leaks the `aiRes`, causing test failure under ASan. --- src/modules/hyprland/backend.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 60453dcb..77f534e0 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -148,22 +148,12 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { std::string IPC::getSocket1Reply(const std::string& rq) { // basically hyprctl - struct addrinfo aiHints; - struct addrinfo* aiRes = nullptr; const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0); if (serverSocket < 0) { throw std::runtime_error("Hyprland IPC: Couldn't open a socket (1)"); } - memset(&aiHints, 0, sizeof(struct addrinfo)); - aiHints.ai_family = AF_UNSPEC; - aiHints.ai_socktype = SOCK_STREAM; - - if (getaddrinfo("localhost", nullptr, &aiHints, &aiRes) != 0) { - throw std::runtime_error("Hyprland IPC: Couldn't get host (2)"); - } - // get the instance signature auto* instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE"); From fd478bf2ab3d00be7889054b4c517463e48df7ca Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Mon, 19 Aug 2024 12:35:52 +0800 Subject: [PATCH 659/842] fix crash caused by use bar instance after it is freed (use-after-free) --- include/bar.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bar.hpp b/include/bar.hpp index 43756bfd..936bc749 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -54,7 +54,7 @@ class BarIpcClient; } #endif // HAVE_SWAY -class Bar { +class Bar : public sigc::trackable { public: using bar_mode_map = std::map; static const bar_mode_map PRESET_MODES; From 0fb1957daedee6316932be438fbbcf6140003849 Mon Sep 17 00:00:00 2001 From: Andrea Scarpino Date: Tue, 20 Aug 2024 13:57:29 +0200 Subject: [PATCH 660/842] fix: check format-source before use --- src/modules/pulseaudio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 3efd9d23..255ca571 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -106,7 +106,7 @@ auto waybar::modules::Pulseaudio::update() -> void { } } else { label_.get_style_context()->remove_class("source-muted"); - if (config_["format-source-muted"].isString()) { + if (config_["format-source"].isString()) { format_source = config_["format-source"].asString(); } } From 4d89c64bed8b5f5963615e65850238f1a4ee9cc6 Mon Sep 17 00:00:00 2001 From: Scott Moreau Date: Mon, 26 Aug 2024 04:44:22 -0600 Subject: [PATCH 661/842] taskbar: Fixup whitespace --- include/modules/wlr/taskbar.hpp | 2 +- src/modules/wlr/taskbar.cpp | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 026f364a..07110dde 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -25,7 +25,7 @@ namespace waybar::modules::wlr { struct widget_geometry { - int x, y, w, h; + int x, y, w, h; }; class Taskbar; diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 7ff11baf..30e4ee48 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -388,7 +388,8 @@ void Task::handle_title(const char *title) { } void Task::set_minimize_hint() { - zwlr_foreign_toplevel_handle_v1_set_rectangle(handle_, bar_.surface, minimize_hint.x, minimize_hint.y, minimize_hint.w, minimize_hint.h); + zwlr_foreign_toplevel_handle_v1_set_rectangle(handle_, bar_.surface, minimize_hint.x, + minimize_hint.y, minimize_hint.w, minimize_hint.h); } void Task::hide_if_ignored() { @@ -452,9 +453,10 @@ void Task::handle_app_id(const char *app_id) { } void Task::on_button_size_allocated(Gtk::Allocation &alloc) { - gtk_widget_translate_coordinates(GTK_WIDGET(button.gobj()), GTK_WIDGET(bar_.window.gobj()), 0, 0, &minimize_hint.x, &minimize_hint.y); - minimize_hint.w = button.get_width(); - minimize_hint.h = button.get_height(); + gtk_widget_translate_coordinates(GTK_WIDGET(button.gobj()), GTK_WIDGET(bar_.window.gobj()), 0, 0, + &minimize_hint.x, &minimize_hint.y); + minimize_hint.w = button.get_width(); + minimize_hint.h = button.get_height(); } void Task::handle_output_enter(struct wl_output *output) { @@ -467,7 +469,8 @@ void Task::handle_output_enter(struct wl_output *output) { if (!button_visible_ && (tbar_->all_outputs() || tbar_->show_output(output))) { /* The task entered the output of the current bar make the button visible */ - button.signal_size_allocate().connect_notify(sigc::mem_fun(this, &Task::on_button_size_allocated)); + button.signal_size_allocate().connect_notify( + sigc::mem_fun(this, &Task::on_button_size_allocated)); tbar_->add_button(button); button.show(); button_visible_ = true; From 0ee519753cf832c723623fbe8ba307acb1707503 Mon Sep 17 00:00:00 2001 From: Antoine Bolvy Date: Tue, 27 Aug 2024 15:43:19 +0200 Subject: [PATCH 662/842] feat: hidpi support for image module --- src/modules/image.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 8274d323..5e6c1493 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -42,7 +42,6 @@ void waybar::modules::Image::refresh(int sig) { } auto waybar::modules::Image::update() -> void { - Glib::RefPtr pixbuf; if (config_["path"].isString()) { path_ = config_["path"].asString(); } else if (config_["exec"].isString()) { @@ -51,19 +50,24 @@ auto waybar::modules::Image::update() -> void { } else { path_ = ""; } - if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) - pixbuf = Gdk::Pixbuf::create_from_file(path_, size_, size_); - else - pixbuf = {}; - if (pixbuf) { + if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) { + Glib::RefPtr pixbuf; + + int scaled_icon_size = size_ * image_.get_scale_factor(); + pixbuf = Gdk::Pixbuf::create_from_file(path_, scaled_icon_size, scaled_icon_size); + + auto surface = + Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), image_.get_window()); + image_.set(surface); + image_.show(); + if (tooltipEnabled() && !tooltip_.empty()) { if (box_.get_tooltip_markup() != tooltip_) { box_.set_tooltip_markup(tooltip_); } } - image_.set(pixbuf); - image_.show(); + box_.get_style_context()->remove_class("empty"); } else { image_.clear(); From 9b5c2dc7ed059ca5304fcbe8b23b9697cc853491 Mon Sep 17 00:00:00 2001 From: Leonard Cohnen Date: Sat, 31 Aug 2024 20:33:52 +0200 Subject: [PATCH 663/842] fix: upower module selection with multiple devices While looping over all the upower devices, the currently set device that will be rendered in the waybar, is overridden. Since the loop doesn't end when the device is found, the upDevice_ is overridden with NULL in the iteration for the next device. Now we only override upDevice_ if the current device matches the constraints. Fixes d2a719d67c5427dffc3e431c41b96b60399576e6 ("Redo to minimize code duplication.") Fixes #3267 --- src/modules/upower.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 552495f8..5ee6d64c 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -384,10 +384,11 @@ void UPower::setDisplayDevice() { displayDevice = upDevice; } } - // Unref current upDevice - if (displayDevice.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); - // Reassign new upDevice - thisPtr->upDevice_ = displayDevice; + // Unref current upDevice if it exists + if (displayDevice.upDevice != NULL) { + if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); + thisPtr->upDevice_ = displayDevice; + } }, this); } From 7a6960842aac3cabc8256489631fe36e219f2576 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 1 Sep 2024 00:11:30 +0000 Subject: [PATCH 664/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/52ec9ac3b12395ad677e8b62106f0b98c1f8569d?narHash=sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k%3D' (2024-07-28) → 'github:NixOS/nixpkgs/71e91c409d1e654808b2621f28a327acfdad8dc2?narHash=sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w%3D' (2024-08-28) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index b8f68f4b..9bd73acc 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1722185531, - "narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=", + "lastModified": 1724819573, + "narHash": "sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d", + "rev": "71e91c409d1e654808b2621f28a327acfdad8dc2", "type": "github" }, "original": { From 1fa8019ad5de59f7a9077a0d928618576581a9b8 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Sun, 8 Sep 2024 13:33:52 +0200 Subject: [PATCH 665/842] man: Clarify rotate option fixes: #3576 --- man/waybar-backlight.5.scd | 2 +- man/waybar-battery.5.scd | 2 +- man/waybar-bluetooth.5.scd | 2 +- man/waybar-clock.5.scd | 2 +- man/waybar-cpu.5.scd | 2 +- man/waybar-custom.5.scd | 2 +- man/waybar-disk.5.scd | 2 +- man/waybar-dwl-window.5.scd | 2 +- man/waybar-hyprland-submap.5.scd | 2 +- man/waybar-idle-inhibitor.5.scd | 2 +- man/waybar-inhibitor.5.scd | 2 +- man/waybar-jack.5.scd | 2 +- man/waybar-memory.5.scd | 2 +- man/waybar-mpd.5.scd | 2 +- man/waybar-mpris.5.scd | 2 +- man/waybar-network.5.scd | 2 +- man/waybar-pulseaudio.5.scd | 2 +- man/waybar-river-layout.5.scd | 2 +- man/waybar-river-mode.5.scd | 2 +- man/waybar-river-window.5.scd | 2 +- man/waybar-sndio.5.scd | 2 +- man/waybar-sway-mode.5.scd | 2 +- man/waybar-sway-window.5.scd | 2 +- man/waybar-temperature.5.scd | 2 +- man/waybar-wireplumber.5.scd | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index 1f674fc0..5286c2ed 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -38,7 +38,7 @@ The *backlight* module displays the current backlight level. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 4fe9650a..29cb7d6d 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -69,7 +69,7 @@ The *battery* module displays the current capacity and state (eg. charging) of y *rotate*: ++ typeof: integer++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *on-click*: ++ typeof: string ++ diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index 1783dab3..bd64f457 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -54,7 +54,7 @@ Addressed by *bluetooth* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 40aedd15..3947266d 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -51,7 +51,7 @@ $XDG_CONFIG_HOME/waybar/config ++ |[ *rotate* :[ integer :[ -:[ Positive value to rotate the text label +:[ Positive value to rotate the text label (in 90 degree increments) |[ *on-click* :[ string :[ diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index fcbd1265..6b13a563 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -43,7 +43,7 @@ The *cpu* module displays the current CPU utilization. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 4cf3c33d..aba1c18f 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -64,7 +64,7 @@ Addressed by *custom/* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index df9ca4e5..1699a511 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -29,7 +29,7 @@ Addressed by *disk* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ diff --git a/man/waybar-dwl-window.5.scd b/man/waybar-dwl-window.5.scd index c2f5b93e..f185c82c 100644 --- a/man/waybar-dwl-window.5.scd +++ b/man/waybar-dwl-window.5.scd @@ -19,7 +19,7 @@ Addressed by *dwl/window* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index 64398e61..e27138e7 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -19,7 +19,7 @@ Addressed by *hyprland/submap* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index f7677634..81a097a7 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -21,7 +21,7 @@ screensaver, also known as "presentation mode". *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-inhibitor.5.scd b/man/waybar-inhibitor.5.scd index 679a5c4b..5513cc49 100644 --- a/man/waybar-inhibitor.5.scd +++ b/man/waybar-inhibitor.5.scd @@ -25,7 +25,7 @@ See *systemd-inhibit*(1) for more information. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-jack.5.scd b/man/waybar-jack.5.scd index 573b36c2..180143b7 100644 --- a/man/waybar-jack.5.scd +++ b/man/waybar-jack.5.scd @@ -51,7 +51,7 @@ Addressed by *jack* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index 7738c576..5c368ae8 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -29,7 +29,7 @@ Addressed by *memory* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index 2f1bdf20..c576a5c0 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -91,7 +91,7 @@ Addressed by *mpd* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 455fcb17..380a1a19 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -107,7 +107,7 @@ The *mpris* module displays currently playing media via libplayerctl. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index bd546916..e1cf810d 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -58,7 +58,7 @@ Addressed by *network* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index 232e84a0..5b38e8b7 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -40,7 +40,7 @@ Additionally, you can control the volume by scrolling *up* or *down* while the c *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ diff --git a/man/waybar-river-layout.5.scd b/man/waybar-river-layout.5.scd index 4fb23085..1368bda9 100644 --- a/man/waybar-river-layout.5.scd +++ b/man/waybar-river-layout.5.scd @@ -21,7 +21,7 @@ Addressed by *river/layout* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-river-mode.5.scd b/man/waybar-river-mode.5.scd index 5769a9a2..b992fdaf 100644 --- a/man/waybar-river-mode.5.scd +++ b/man/waybar-river-mode.5.scd @@ -19,7 +19,7 @@ Addressed by *river/mode* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-river-window.5.scd b/man/waybar-river-window.5.scd index 7e661f43..6db9a2fa 100644 --- a/man/waybar-river-window.5.scd +++ b/man/waybar-river-window.5.scd @@ -19,7 +19,7 @@ Addressed by *river/window* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd index f8d1615d..03dfe0af 100644 --- a/man/waybar-sndio.5.scd +++ b/man/waybar-sndio.5.scd @@ -20,7 +20,7 @@ cursor is over the module, and clicking on the module toggles mute. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index 1fcf3cf8..8d5d7c2c 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -19,7 +19,7 @@ Addressed by *sway/mode* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 037e6b55..6d1e3196 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -19,7 +19,7 @@ Addressed by *sway/window* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index eab4cbb3..541bf3af 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -60,7 +60,7 @@ Addressed by *temperature* *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *max-length*: ++ typeof: integer ++ diff --git a/man/waybar-wireplumber.5.scd b/man/waybar-wireplumber.5.scd index 770ff0d5..9c26ebaf 100644 --- a/man/waybar-wireplumber.5.scd +++ b/man/waybar-wireplumber.5.scd @@ -31,7 +31,7 @@ The *wireplumber* module displays the current volume reported by WirePlumber. *rotate*: ++ typeof: integer ++ - Positive value to rotate the text label. + Positive value to rotate the text label (in 90 degree increments). *states*: ++ typeof: object ++ From 5b1826d2f66b836e73643998cc1d82af125c218f Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Sun, 8 Sep 2024 13:54:50 +0200 Subject: [PATCH 666/842] label: Add warning for invalid rotate property --- src/ALabel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index ecb1b7ce..3cb2c590 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -45,6 +45,8 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st if (config_["rotate"].isUInt()) { rotate = config["rotate"].asUInt(); + if (not (rotate == 0 || rotate == 90 || rotate == 180 || rotate == 270)) + spdlog::warn("'rotate' is only supported in 90 degree increments {} is not valid.", rotate); label_.set_angle(rotate); } From 70f3c1d9e95e1886181604298e71993888844ff3 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 10 Sep 2024 01:16:42 +0900 Subject: [PATCH 667/842] chore: update power_profiles_daemon.cpp minor fix --- src/modules/power_profiles_daemon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index eaa47023..3ae3ae83 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -204,7 +204,7 @@ void PowerProfilesDaemon::setPropCb(Glib::RefPtr& r) { auto _ = powerProfilesProxy_->call_finish(r); dp.emit(); } catch (const std::exception& e) { - spdlog::error("Failed to set the the active power profile: {}", e.what()); + spdlog::error("Failed to set the active power profile: {}", e.what()); } catch (const Glib::Error& e) { spdlog::error("Failed to set the active power profile: {}", std::string(e.what())); } From c2f1a7894b0b99779a482fcc85d30861f461e21a Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 08:40:19 +0200 Subject: [PATCH 668/842] chore: update deps --- subprojects/catch2.wrap | 12 ++++++------ subprojects/fmt.wrap | 18 +++++++++--------- subprojects/spdlog.wrap | 18 +++++++++--------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap index f2dfd57c..489db6c6 100644 --- a/subprojects/catch2.wrap +++ b/subprojects/catch2.wrap @@ -1,10 +1,10 @@ [wrap-file] -directory = Catch2-3.5.1 -source_url = https://github.com/catchorg/Catch2/archive/v3.5.1.tar.gz -source_filename = Catch2-3.5.1.tar.gz -source_hash = 49c3ca7a68f1c8ec71307736bc6ed14fec21631707e1be9af45daf4037e75a08 -# source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.4.0-1/Catch2-3.4.0.tar.gz -# wrapdb_version = 3.4.0-1 +directory = Catch2-3.7.0 +source_url = https://github.com/catchorg/Catch2/archive/v3.7.0.tar.gz +source_filename = Catch2-3.7.0.tar.gz +source_hash = 5b10cd536fa3818112a82820ce0787bd9f2a906c618429e7c4dea639983c8e88 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.7.0-1/Catch2-3.7.0.tar.gz +wrapdb_version = 3.7.0-1 [provide] catch2 = catch2_dep diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap index 9efe101e..42b61596 100644 --- a/subprojects/fmt.wrap +++ b/subprojects/fmt.wrap @@ -1,13 +1,13 @@ [wrap-file] -directory = fmt-9.1.0 -source_url = https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz -source_filename = fmt-9.1.0.tar.gz -source_hash = 5dea48d1fcddc3ec571ce2058e13910a0d4a6bab4cc09a809d8b1dd1c88ae6f2 -patch_filename = fmt_9.1.0-2_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/fmt_9.1.0-2/get_patch -patch_hash = 23e8c4829f3e63f509b5643fe6bb87cbed39eae9594c451b338475d14d051967 -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_9.1.0-2/fmt-9.1.0.tar.gz -wrapdb_version = 9.1.0-2 +directory = fmt-11.0.1 +source_url = https://github.com/fmtlib/fmt/archive/11.0.1.tar.gz +source_filename = fmt-11.0.1.tar.gz +source_hash = 7d009f7f89ac84c0a83f79ed602463d092fbf66763766a907c97fd02b100f5e9 +patch_filename = fmt_11.0.1-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/fmt_11.0.1-1/get_patch +patch_hash = 0a8b93d1ee6d84a82d3872a9bfb4c3977d8a53f7f484d42d1f7ed63ed496d549 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_11.0.1-1/fmt-11.0.1.tar.gz +wrapdb_version = 11.0.1-1 [provide] fmt = fmt_dep diff --git a/subprojects/spdlog.wrap b/subprojects/spdlog.wrap index 08004c90..af00d5a7 100644 --- a/subprojects/spdlog.wrap +++ b/subprojects/spdlog.wrap @@ -1,13 +1,13 @@ [wrap-file] -directory = spdlog-1.12.0 -source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.12.0.tar.gz -source_filename = spdlog-1.12.0.tar.gz -source_hash = 4dccf2d10f410c1e2feaff89966bfc49a1abb29ef6f08246335b110e001e09a9 -patch_filename = spdlog_1.12.0-2_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.12.0-2/get_patch -patch_hash = 9596972d1eb2e0a69cea4a53273ca7bbbcb9b2fa872cd734864fc7232dc2d573 -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.12.0-2/spdlog-1.12.0.tar.gz -wrapdb_version = 1.12.0-2 +directory = spdlog-1.14.1 +source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.14.1.tar.gz +source_filename = spdlog-1.14.1.tar.gz +source_hash = 1586508029a7d0670dfcb2d97575dcdc242d3868a259742b69f100801ab4e16b +patch_filename = spdlog_1.14.1-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.14.1-1/get_patch +patch_hash = ae878e732330ea1048f90d7e117c40c0cd2a6fb8ae5492c7955818ce3aaade6c +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.14.1-1/spdlog-1.14.1.tar.gz +wrapdb_version = 1.14.1-1 [provide] spdlog = spdlog_dep From 64d99a588406e5bba88472f25f814e58406fc836 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 08:50:07 +0200 Subject: [PATCH 669/842] chore(fmt): std format --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 8daa6c9c..834ceb75 100644 --- a/meson.build +++ b/meson.build @@ -69,7 +69,7 @@ is_openbsd = host_machine.system() == 'openbsd' thread_dep = dependency('threads') fmt = dependency('fmt', version : ['>=8.1.1'], fallback : ['fmt', 'fmt_dep']) -spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled']) +spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled', 'std_format=enabled']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') From 6417782af6a88a7d2dd8f355d10d755f32e1d402 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 08:55:14 +0200 Subject: [PATCH 670/842] chore: lint --- .github/workflows/clang-format.yml | 1 + include/AModule.hpp | 2 +- include/modules/clock.hpp | 4 ++-- include/modules/hyprland/workspaces.hpp | 4 ++-- include/util/clara.hpp | 12 ++++++------ include/util/format.hpp | 2 +- src/modules/bluetooth.cpp | 17 ++++++++--------- src/modules/clock.cpp | 4 ++-- src/modules/dwl/tags.cpp | 4 ++-- src/modules/hyprland/workspaces.cpp | 4 ++-- src/modules/memory/bsd.cpp | 8 ++++---- src/modules/wlr/workspace_manager.cpp | 4 ++-- test/utils/SafeSignal.cpp | 2 +- 13 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 40fd3126..a7b5d896 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -16,4 +16,5 @@ jobs: with: source: "." extensions: "hpp,h,cpp,c" + style: "file:.clang-format" clangFormatVersion: 16 diff --git a/include/AModule.hpp b/include/AModule.hpp index facb3130..94a88371 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -16,7 +16,7 @@ class AModule : public IModule { ~AModule() override; auto update() -> void override; - virtual auto refresh(int shouldRefresh) -> void{}; + virtual auto refresh(int shouldRefresh) -> void {}; operator Gtk::Widget &() override; auto doAction(const std::string &name) -> void override; diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index c212ec8b..0c62b676 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -51,8 +51,8 @@ class Clock final : public ALabel { day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight) std::string cldText_{""}; // calendar text to print CldMode cldMode_{CldMode::MONTH}; - auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) - -> const std::string; + auto get_calendar(const year_month_day& today, const year_month_day& ymd, + const time_zone* tz) -> const std::string; // get local time zone auto local_zone() -> const time_zone*; diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index f5c20f69..a9d56b79 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -61,8 +61,8 @@ class Workspaces : public AModule, public EventHandler { // Config void parseConfig(const Json::Value& config); auto populateIconsMap(const Json::Value& formatIcons) -> void; - static auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) - -> void; + static auto populateBoolConfig(const Json::Value& config, const std::string& key, + bool& member) -> void; auto populateSortByConfig(const Json::Value& config) -> void; auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; diff --git a/include/util/clara.hpp b/include/util/clara.hpp index 73fa5415..da7151fe 100644 --- a/include/util/clara.hpp +++ b/include/util/clara.hpp @@ -622,8 +622,8 @@ inline auto convertInto(std::string const &source, bool &target) -> ParserResult } #ifdef CLARA_CONFIG_OPTIONAL_TYPE template -inline auto convertInto(std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE &target) - -> ParserResult { +inline auto convertInto(std::string const &source, + CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult { T temp; auto result = convertInto(source, temp); if (result) target = std::move(temp); @@ -751,8 +751,8 @@ class ParserBase { public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse(std::string const &exeName, TokenStream const &tokens) const - -> InternalParseResult = 0; + virtual auto parse(std::string const &exeName, + TokenStream const &tokens) const -> InternalParseResult = 0; virtual auto cardinality() const -> size_t { return 1; } auto parse(Args const &args) const -> InternalParseResult { @@ -1098,8 +1098,8 @@ struct Parser : ParserBase { using ParserBase::parse; - auto parse(std::string const &exeName, TokenStream const &tokens) const - -> InternalParseResult override { + auto parse(std::string const &exeName, + TokenStream const &tokens) const -> InternalParseResult override { struct ParserInfo { ParserBase const *parser = nullptr; size_t count = 0; diff --git a/include/util/format.hpp b/include/util/format.hpp index cf8d706b..c8ed837a 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -6,7 +6,7 @@ class pow_format { public: pow_format(long long val, std::string&& unit, bool binary = false) - : val_(val), unit_(unit), binary_(binary){}; + : val_(val), unit_(unit), binary_(binary) {}; long long val_; std::string unit_; diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index 06475a2e..c8f1f996 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -49,8 +49,8 @@ auto getBoolProperty(GDBusProxy* proxy, const char* property_name) -> bool { return false; } -auto getOptionalStringProperty(GDBusProxy* proxy, const char* property_name) - -> std::optional { +auto getOptionalStringProperty(GDBusProxy* proxy, + const char* property_name) -> std::optional { auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name); if (gvar) { std::string property_value = g_variant_get_string(gvar, NULL); @@ -345,8 +345,8 @@ auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* m auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged( GDBusObjectManagerClient* manager, GDBusObjectProxy* object_proxy, GDBusProxy* interface_proxy, - GVariant* changed_properties, const gchar* const* invalidated_properties, gpointer user_data) - -> void { + GVariant* changed_properties, const gchar* const* invalidated_properties, + gpointer user_data) -> void { std::string interface_name = g_dbus_proxy_get_interface_name(interface_proxy); std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy)); @@ -395,8 +395,8 @@ auto waybar::modules::Bluetooth::getDeviceBatteryPercentage(GDBusObject* object) return std::nullopt; } -auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, DeviceInfo& device_info) - -> bool { +auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, + DeviceInfo& device_info) -> bool { GDBusProxy* proxy_device = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Device1")); if (proxy_device != NULL) { @@ -462,9 +462,8 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional& connected_devices) - -> void { +auto waybar::modules::Bluetooth::findConnectedDevices( + const std::string& cur_controller_path, std::vector& connected_devices) -> void { GList* objects = g_dbus_object_manager_get_objects(manager_.get()); for (GList* l = objects; l != NULL; l = l->next) { GDBusObject* object = G_DBUS_OBJECT(l->data); diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 7a4cb9c2..db2979eb 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -198,8 +198,8 @@ const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) { return 2u + ceil((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count(); } -auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line) - -> const year_month_weekday { +auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, + const unsigned line) -> const year_month_weekday { unsigned index{line - 2}; if (weekday{ym / 1} == firstdow) ++index; return ym / firstdow[index]; diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index 085b8224..f8b250c8 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -53,8 +53,8 @@ static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t // Intentionally empty } -static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid){ - // Intentionally empty +static void appid(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, const char *appid) { + // Intentionally empty }; static const zdwl_ipc_output_v2_listener output_status_listener_impl{ diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 047703cc..13364f3f 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -590,8 +590,8 @@ auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { m_iconsMap.emplace("", ""); } -auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) - -> void { +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, + bool &member) -> void { const auto &configValue = config[key]; if (configValue.isBool()) { member = configValue.asBool(); diff --git a/src/modules/memory/bsd.cpp b/src/modules/memory/bsd.cpp index 67f9fed7..1d970e8a 100644 --- a/src/modules/memory/bsd.cpp +++ b/src/modules/memory/bsd.cpp @@ -21,13 +21,13 @@ static uint64_t get_total_memory() { u_long physmem; #endif int mib[] = { - CTL_HW, + CTL_HW, #if defined(HW_MEMSIZE) - HW_MEMSIZE, + HW_MEMSIZE, #elif defined(HW_PHYSMEM64) - HW_PHYSMEM64, + HW_PHYSMEM64, #else - HW_PHYSMEM, + HW_PHYSMEM, #endif }; u_int miblen = sizeof(mib) / sizeof(mib[0]); diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index f556a161..3c630d81 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -118,8 +118,8 @@ auto WorkspaceManager::sort_workspaces() -> void { } } -auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) - -> void { +auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, + uint32_t version) -> void { if (workspace_manager_) { spdlog::warn("Register workspace manager again although already registered!"); return; diff --git a/test/utils/SafeSignal.cpp b/test/utils/SafeSignal.cpp index 341e8e2e..e7e096b0 100644 --- a/test/utils/SafeSignal.cpp +++ b/test/utils/SafeSignal.cpp @@ -71,7 +71,7 @@ struct TestObject { unsigned copied = 0; unsigned moved = 0; - TestObject(const T& v) : value(v){}; + TestObject(const T& v) : value(v) {}; ~TestObject() = default; TestObject(const TestObject& other) From 4354da284969097f5d7ab2382cb9fe60a7f1a016 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 08:59:36 +0200 Subject: [PATCH 671/842] chore: disable fmt tests --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 834ceb75..ed5ec7b8 100644 --- a/meson.build +++ b/meson.build @@ -69,7 +69,7 @@ is_openbsd = host_machine.system() == 'openbsd' thread_dep = dependency('threads') fmt = dependency('fmt', version : ['>=8.1.1'], fallback : ['fmt', 'fmt_dep']) -spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled', 'std_format=enabled']) +spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled', 'std_format=disabled', 'tests=disabled']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') From 3ade275d100a4e53e6ac75912ea299d2965df250 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:02:23 +0200 Subject: [PATCH 672/842] fix: version --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index a7b5d896..4a774dbd 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -17,4 +17,4 @@ jobs: source: "." extensions: "hpp,h,cpp,c" style: "file:.clang-format" - clangFormatVersion: 16 + clangFormatVersion: 18 From d623a89cd17b89d8e69d8185170c9a05413fac30 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:33:40 +0200 Subject: [PATCH 673/842] fix: sni item fmt --- src/modules/image.cpp | 4 ++-- src/modules/sni/item.cpp | 21 +++++++++++++-------- subprojects/gtk-layer-shell.wrap | 8 ++++---- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 5e6c1493..71e93b94 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -57,8 +57,8 @@ auto waybar::modules::Image::update() -> void { int scaled_icon_size = size_ * image_.get_scale_factor(); pixbuf = Gdk::Pixbuf::create_from_file(path_, scaled_icon_size, scaled_icon_size); - auto surface = - Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), image_.get_window()); + auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), + image_.get_window()); image_.set(surface); image_.show(); diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 6c4ec8c0..126545b1 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -104,9 +104,11 @@ void Item::proxyReady(Glib::RefPtr& result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, + std::string(err.what())); } catch (const std::exception& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, + std::string(err.what())); } } @@ -124,14 +126,15 @@ ToolTip get_variant(const Glib::VariantBase& value) { result.text = get_variant(container.get_child(2)); auto description = get_variant(container.get_child(3)); if (!description.empty()) { - result.text = fmt::format("{}\n{}", result.text, description); + result.text = fmt::format("{}\n{}", std::string(result.text), std::string(description)); } return result; } void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { try { - spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value); + spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, + std::string(name), get_variant(value)); if (name == "Category") { category = get_variant(value); @@ -176,10 +179,12 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } } catch (const Glib::Error& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, name, value, err.what()); + id.empty() ? bus_name : id, std::string(name), get_variant(value), + std::string(err.what())); } catch (const std::exception& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, name, value, err.what()); + id.empty() ? bus_name : id, std::string(name), get_variant(value), + std::string(err.what())); } } @@ -221,9 +226,9 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::warn("Failed to update properties: {}", err.what()); + spdlog::warn("Failed to update properties: {}", std::string(err.what())); } catch (const std::exception& err) { - spdlog::warn("Failed to update properties: {}", err.what()); + spdlog::warn("Failed to update properties: {}", std::string(err.what())); } update_pending_.clear(); } diff --git a/subprojects/gtk-layer-shell.wrap b/subprojects/gtk-layer-shell.wrap index cb730345..fc0ddf74 100644 --- a/subprojects/gtk-layer-shell.wrap +++ b/subprojects/gtk-layer-shell.wrap @@ -1,5 +1,5 @@ [wrap-file] -directory = gtk-layer-shell-0.8.2 -source_filename = gtk-layer-shell-0.8.2.tar.gz -source_hash = 254dd246303127c5d5236ea640f01a82e35d2d652a48d139dd669c832a0f0dce -source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.8.2/gtk-layer-shell-0.8.2.tar.gz +directory = gtk-layer-shell-0.9.0 +source_filename = gtk-layer-shell-0.9.0.tar.gz +source_hash = 3809e5565d9ed02e44bb73787ff218523e8760fef65830afe60ea7322e22da1c +source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.9.0/gtk-layer-shell-0.9.0.tar.gz From 46e7ed35de6d105d7f65b5172a69250a57af4462 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Thu, 29 Aug 2024 11:45:17 +0300 Subject: [PATCH 674/842] Add niri/workspaces, niri/window, niri/language --- README.md | 1 + include/modules/niri/backend.hpp | 52 ++++++ include/modules/niri/language.hpp | 38 +++++ include/modules/niri/window.hpp | 28 ++++ include/modules/niri/workspaces.hpp | 30 ++++ man/waybar-niri-language.5.scd | 58 +++++++ man/waybar-niri-window.5.scd | 81 +++++++++ man/waybar-niri-workspaces.5.scd | 97 +++++++++++ man/waybar.5.scd.in | 3 + meson.build | 15 ++ src/factory.cpp | 16 ++ src/modules/niri/backend.cpp | 249 ++++++++++++++++++++++++++++ src/modules/niri/language.cpp | 138 +++++++++++++++ src/modules/niri/window.cpp | 121 ++++++++++++++ src/modules/niri/workspaces.cpp | 204 +++++++++++++++++++++++ 15 files changed, 1131 insertions(+) create mode 100644 include/modules/niri/backend.hpp create mode 100644 include/modules/niri/language.hpp create mode 100644 include/modules/niri/window.hpp create mode 100644 include/modules/niri/workspaces.hpp create mode 100644 man/waybar-niri-language.5.scd create mode 100644 man/waybar-niri-window.5.scd create mode 100644 man/waybar-niri-workspaces.5.scd create mode 100644 src/modules/niri/backend.cpp create mode 100644 src/modules/niri/language.cpp create mode 100644 src/modules/niri/window.cpp create mode 100644 src/modules/niri/workspaces.cpp diff --git a/README.md b/README.md index a019eb6f..55a6c7d9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ - Sway (Workspaces, Binding mode, Focused window name) - River (Mapping mode, Tags, Focused window name) - Hyprland (Window Icons, Workspaces, Focused window name) +- Niri (Workspaces, Focused window name, Language) - DWL (Tags, Focused window name) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc) - Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Local time diff --git a/include/modules/niri/backend.hpp b/include/modules/niri/backend.hpp new file mode 100644 index 00000000..01af5017 --- /dev/null +++ b/include/modules/niri/backend.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +#include "util/json.hpp" + +namespace waybar::modules::niri { + +class EventHandler { + public: + virtual void onEvent(const Json::Value& ev) = 0; + virtual ~EventHandler() = default; +}; + +class IPC { + public: + IPC() { startIPC(); } + + void registerForIPC(const std::string& ev, EventHandler* ev_handler); + void unregisterForIPC(EventHandler* handler); + + static Json::Value send(const Json::Value& request); + + // The data members are only safe to access while dataMutex_ is locked. + std::lock_guard lockData() { return std::lock_guard(dataMutex_); } + const std::vector &workspaces() const { return workspaces_; } + const std::vector &windows() const { return windows_; } + const std::vector &keyboardLayoutNames() const { return keyboardLayoutNames_; } + unsigned keyboardLayoutCurrent() const { return keyboardLayoutCurrent_; } + + private: + void startIPC(); + static int connectToSocket(); + void parseIPC(const std::string&); + + std::mutex dataMutex_; + std::vector workspaces_; + std::vector windows_; + std::vector keyboardLayoutNames_; + unsigned keyboardLayoutCurrent_; + + util::JsonParser parser_; + std::mutex callbackMutex_; + std::list> callbacks_; +}; + +inline std::unique_ptr gIPC; + +}; // namespace waybar::modules::niri diff --git a/include/modules/niri/language.hpp b/include/modules/niri/language.hpp new file mode 100644 index 00000000..1cecd206 --- /dev/null +++ b/include/modules/niri/language.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "ALabel.hpp" +#include "bar.hpp" +#include "modules/niri/backend.hpp" + +namespace waybar::modules::niri { + +class Language : public ALabel, public EventHandler { + public: + Language(const std::string&, const Bar&, const Json::Value&); + ~Language() override; + void update() override; + + private: + void updateFromIPC(); + void onEvent(const Json::Value &ev) override; + void doUpdate(); + + struct Layout { + std::string full_name; + std::string short_name; + std::string variant; + std::string short_description; + }; + + static Layout getLayout(const std::string &fullName); + + std::mutex mutex_; + const Bar &bar_; + + std::vector layouts_; + unsigned current_idx_; +}; + +} // namespace waybar::modules::niri diff --git a/include/modules/niri/window.hpp b/include/modules/niri/window.hpp new file mode 100644 index 00000000..909ae6f0 --- /dev/null +++ b/include/modules/niri/window.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "AAppIconLabel.hpp" +#include "bar.hpp" +#include "modules/niri/backend.hpp" + +namespace waybar::modules::niri { + +class Window : public AAppIconLabel, public EventHandler { + public: + Window(const std::string &, const Bar &, const Json::Value &); + ~Window() override; + void update() override; + + private: + void onEvent(const Json::Value &ev) override; + void doUpdate(); + void setClass(const std::string &className, bool enable); + + const Bar &bar_; + + std::string oldAppId_; +}; + +} // namespace waybar::modules::niri diff --git a/include/modules/niri/workspaces.hpp b/include/modules/niri/workspaces.hpp new file mode 100644 index 00000000..a6850ed1 --- /dev/null +++ b/include/modules/niri/workspaces.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "modules/niri/backend.hpp" + +namespace waybar::modules::niri { + +class Workspaces : public AModule, public EventHandler { + public: + Workspaces(const std::string &, const Bar &, const Json::Value &); + ~Workspaces() override; + void update() override; + + private: + void onEvent(const Json::Value &ev) override; + void doUpdate(); + Gtk::Button &addButton(const Json::Value &ws); + std::string getIcon(const std::string &value, const Json::Value &ws); + + const Bar &bar_; + Gtk::Box box_; + // Map from niri workspace id to button. + std::unordered_map buttons_; +}; + +} // namespace waybar::modules::niri diff --git a/man/waybar-niri-language.5.scd b/man/waybar-niri-language.5.scd new file mode 100644 index 00000000..6895d25c --- /dev/null +++ b/man/waybar-niri-language.5.scd @@ -0,0 +1,58 @@ +waybar-niri-language(5) + +# NAME + +waybar - niri language module + +# DESCRIPTION + +The *language* module displays the currently selected language in niri. + +# CONFIGURATION + +Addressed by *niri/language* + +*format*: ++ + typeof: string ++ + default: {} ++ + The format, how information should be displayed. + +*format-* ++ + typeof: string++ + Provide an alternative name to display per language where is the language of your choosing. Can be passed multiple times with multiple languages as shown by the example below. + +*menu*: ++ + typeof: string ++ + Action that popups the menu. + +*menu-file*: ++ + typeof: string ++ + Location of the menu descriptor file. There need to be an element of type GtkMenu with id *menu* + +*menu-actions*: ++ + typeof: array ++ + The actions corresponding to the buttons of the menu. + +# FORMAT REPLACEMENTS + +*{short}*: Short name of layout (e.g. "us"). Equals to {}. + +*{shortDescription}*: Short description of layout (e.g. "en"). + +*{long}*: Long name of layout (e.g. "English (Dvorak)"). + +*{variant}*: Variant of layout (e.g. "dvorak"). + +# EXAMPLES + +``` +"niri/language": { + "format": "Lang: {long}" + "format-en": "AMERICA, HELL YEAH!" + "format-tr": "As bayrakları" +} +``` + +# STYLE + +- *#language* diff --git a/man/waybar-niri-window.5.scd b/man/waybar-niri-window.5.scd new file mode 100644 index 00000000..9e2e9f63 --- /dev/null +++ b/man/waybar-niri-window.5.scd @@ -0,0 +1,81 @@ +waybar-niri-window(5) + +# NAME + +waybar - niri window module + +# DESCRIPTION + +The *window* module displays the title of the currently focused window in niri. + +# CONFIGURATION + +Addressed by *niri/window* + +*format*: ++ + typeof: string ++ + default: {title} ++ + The format, how information should be displayed. On {} the current window title is displayed. + +*rewrite*: ++ + typeof: object ++ + Rules to rewrite window title. See *rewrite rules*. + +*separate-outputs*: ++ + typeof: bool ++ + Show the active window of the monitor the bar belongs to, instead of the focused window. + +*icon*: ++ + typeof: bool ++ + default: false ++ + Option to hide the application icon. + +*icon-size*: ++ + typeof: integer ++ + default: 24 ++ + Option to change the size of the application icon. + +# FORMAT REPLACEMENTS + +See the output of "niri msg windows" for examples + +*{title}*: The current title of the focused window. + +*{app_id}*: The current app ID of the focused window. + +# REWRITE RULES + +*rewrite* is an object where keys are regular expressions and values are +rewrite rules if the expression matches. Rules may contain references to +captures of the expression. + +Regular expression and replacement follow ECMA-script rules. + +If no expression matches, the title is left unchanged. + +Invalid expressions (e.g., mismatched parentheses) are skipped. + +# EXAMPLES + +``` +"niri/window": { + "format": "{}", + "rewrite": { + "(.*) - Mozilla Firefox": "🌎 $1", + "(.*) - zsh": "> [$1]" + } +} +``` + +# STYLE + +- *#window* +- *window#waybar.empty #window* When no windows are on the workspace + +The following classes are applied to the entire Waybar rather than just the +window widget: + +- *window#waybar.empty* When no windows are in the workspace +- *window#waybar.solo* When only one window is on the workspace +- *window#waybar.* Where *app-id* is the app ID of the only window on + the workspace diff --git a/man/waybar-niri-workspaces.5.scd b/man/waybar-niri-workspaces.5.scd new file mode 100644 index 00000000..50e497cd --- /dev/null +++ b/man/waybar-niri-workspaces.5.scd @@ -0,0 +1,97 @@ +waybar-niri-workspaces(5) + +# NAME + +waybar - niri workspaces module + +# DESCRIPTION + +The *workspaces* module displays the currently used workspaces in niri. + +# CONFIGURATION + +Addressed by *niri/workspaces* + +*all-outputs*: ++ + typeof: bool ++ + default: false ++ + If set to false, workspaces will only be shown on the output they are on. If set to true all workspaces will be shown on every output. + +*format*: ++ + typeof: string ++ + default: {value} ++ + The format, how information should be displayed. + +*format-icons*: ++ + typeof: array ++ + Based on the workspace name, index and state, the corresponding icon gets selected. See *icons*. + +*disable-click*: ++ + typeof: bool ++ + default: false ++ + If set to false, you can click to change workspace. If set to true this behaviour is disabled. + +*disable-markup*: ++ + typeof: bool ++ + default: false ++ + If set to true, button label will escape pango markup. + +*current-only*: ++ + typeof: bool ++ + default: false ++ + If set to true, only the active or focused workspace will be shown. + +*on-update*: ++ + typeof: string ++ + Command to execute when the module is updated. + +# FORMAT REPLACEMENTS + +*{value}*: Name of the workspace, or index for unnamed workspaces, +as defined by niri. + +*{name}*: Name of the workspace for named workspaces. + +*{icon}*: Icon, as defined in *format-icons*. + +*{index}*: Index of the workspace on its output. + +*{output}*: Output where the workspace is located. + +# ICONS + +Additional to workspace name matching, the following *format-icons* can be set. + +- *default*: Will be shown, when no string matches are found. +- *focused*: Will be shown, when workspace is focused. +- *active*: Will be shown, when workspace is active on its output. + +# EXAMPLES + +``` +"niri/workspaces": { + "format": "{icon}", + "format-icons": { + // Named workspaces + // (you need to configure them in niri) + "browser": "", + "discord": "", + "chat": "", + + // Icons by state + "active": "", + "default": "" + } +} +``` + +# Style + +- *#workspaces button* +- *#workspaces button.focused*: The single focused workspace. +- *#workspaces button.active*: The workspace is active (visible) on its output. +- *#workspaces button.empty*: The workspace is empty. +- *#workspaces button.current_output*: The workspace is from the same output as + the bar that it is displayed on. +- *#workspaces button#niri-workspace-*: Workspaces named this, or index + for unnamed workspaces. diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index db546e17..f3a89656 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -323,6 +323,9 @@ A group may hide all but one element, showing them only on mouse hover. In order - *waybar-hyprland-submap(5)* - *waybar-hyprland-window(5)* - *waybar-hyprland-workspaces(5)* +- *waybar-niri-language(5)* +- *waybar-niri-window(5)* +- *waybar-niri-workspaces(5)* - *waybar-idle-inhibitor(5)* - *waybar-image(5)* - *waybar-inhibitor(5)* diff --git a/meson.build b/meson.build index ed5ec7b8..7097a9fb 100644 --- a/meson.build +++ b/meson.build @@ -318,6 +318,21 @@ if true ) endif +if true + add_project_arguments('-DHAVE_NIRI', language: 'cpp') + src_files += files( + 'src/modules/niri/backend.cpp', + 'src/modules/niri/language.cpp', + 'src/modules/niri/window.cpp', + 'src/modules/niri/workspaces.cpp', + ) + man_files += files( + 'man/waybar-niri-language.5.scd', + 'man/waybar-niri-window.5.scd', + 'man/waybar-niri-workspaces.5.scd', + ) +endif + if libnl.found() and libnlgen.found() add_project_arguments('-DHAVE_LIBNL', language: 'cpp') src_files += files('src/modules/network.cpp') diff --git a/src/factory.cpp b/src/factory.cpp index ca10ef95..6c2313e3 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -36,6 +36,11 @@ #include "modules/hyprland/window.hpp" #include "modules/hyprland/workspaces.hpp" #endif +#ifdef HAVE_NIRI +#include "modules/niri/language.hpp" +#include "modules/niri/window.hpp" +#include "modules/niri/workspaces.hpp" +#endif #if defined(__FreeBSD__) || defined(__linux__) #include "modules/battery.hpp" #endif @@ -205,6 +210,17 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "hyprland/workspaces") { return new waybar::modules::hyprland::Workspaces(id, bar_, config_[name]); } +#endif +#ifdef HAVE_NIRI + if (ref == "niri/language") { + return new waybar::modules::niri::Language(id, bar_, config_[name]); + } + if (ref == "niri/window") { + return new waybar::modules::niri::Window(id, bar_, config_[name]); + } + if (ref == "niri/workspaces") { + return new waybar::modules::niri::Workspaces(id, bar_, config_[name]); + } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); diff --git a/src/modules/niri/backend.cpp b/src/modules/niri/backend.cpp new file mode 100644 index 00000000..ef23c881 --- /dev/null +++ b/src/modules/niri/backend.cpp @@ -0,0 +1,249 @@ +#include "modules/niri/backend.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace waybar::modules::niri { + +int IPC::connectToSocket() { + const char* socket_path = getenv("NIRI_SOCKET"); + + if (socket_path == nullptr) { + spdlog::warn("Niri is not running, niri IPC will not be available."); + return -1; + } + + struct sockaddr_un addr; + int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (socketfd == -1) { + throw std::runtime_error("socketfd failed"); + } + + addr.sun_family = AF_UNIX; + + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + + addr.sun_path[sizeof(addr.sun_path) - 1] = 0; + + int l = sizeof(struct sockaddr_un); + + if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) { + close(socketfd); + throw std::runtime_error("unable to connect"); + } + + return socketfd; +} + +void IPC::startIPC() { + // will start IPC and relay events to parseIPC + + std::thread([&]() { + int socketfd; + try { + socketfd = connectToSocket(); + } catch (std::exception &e) { + spdlog::error("Niri IPC: failed to start, reason: {}", e.what()); + return; + } + if (socketfd == -1) + return; + + spdlog::info("Niri IPC starting"); + + __gnu_cxx::stdio_filebuf filebuf(socketfd, std::ios::in | std::ios::out); + std::iostream fs(&filebuf); + fs << R"("EventStream")" << std::endl; + + std::string line; + std::getline(fs, line); + if (line != R"({"Ok":"Handled"})") { + spdlog::error("Niri IPC: failed to start event stream"); + return; + } + + while (std::getline(fs, line)) { + spdlog::debug("Niri IPC: received {}", line); + + try { + parseIPC(line); + } catch (std::exception& e) { + spdlog::warn("Failed to parse IPC message: {}, reason: {}", line, e.what()); + } catch (...) { + throw; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + }).detach(); +} + +void IPC::parseIPC(const std::string& line) { + const auto ev = parser_.parse(line); + const auto members = ev.getMemberNames(); + if (members.size() != 1) + throw std::runtime_error("Event must have a single member"); + + { + auto lock = lockData(); + + if (const auto &payload = ev["WorkspacesChanged"]) { + workspaces_.clear(); + const auto &values = payload["workspaces"]; + std::copy(values.begin(), values.end(), std::back_inserter(workspaces_)); + + std::sort(workspaces_.begin(), workspaces_.end(), + [](const auto &a, const auto &b) { + const auto &aOutput = a["output"].asString(); + const auto &bOutput = b["output"].asString(); + const auto aIdx = a["idx"].asUInt(); + const auto bIdx = b["idx"].asUInt(); + if (aOutput == bOutput) + return aIdx < bIdx; + return aOutput < bOutput; + }); + } else if (const auto& payload = ev["WorkspaceActivated"]) { + const auto id = payload["id"].asUInt64(); + const auto focused = payload["focused"].asBool(); + auto it = std::find_if(workspaces_.begin(), workspaces_.end(), + [id](const auto &ws) { return ws["id"].asUInt64() == id; }); + if (it != workspaces_.end()) { + const auto &ws = *it; + const auto &output = ws["output"].asString(); + for (auto &ws : workspaces_) { + const auto got_activated = (ws["id"].asUInt64() == id); + if (ws["output"] == output) + ws["is_active"] = got_activated; + + if (focused) + ws["is_focused"] = got_activated; + } + } else { + spdlog::error("Activated unknown workspace"); + } + } else if (const auto& payload = ev["WorkspaceActiveWindowChanged"]) { + const auto workspaceId = payload["workspace_id"].asUInt64(); + auto it = std::find_if(workspaces_.begin(), workspaces_.end(), + [workspaceId](const auto &ws) { return ws["id"].asUInt64() == workspaceId; }); + if (it != workspaces_.end()) { + auto &ws = *it; + ws["active_window_id"] = payload["active_window_id"]; + } else { + spdlog::error("Active window changed on unknown workspace"); + } + } else if (const auto &payload = ev["KeyboardLayoutsChanged"]) { + const auto &layouts = payload["keyboard_layouts"]; + const auto &names = layouts["names"]; + keyboardLayoutCurrent_ = layouts["current_idx"].asUInt(); + + keyboardLayoutNames_.clear(); + for (const auto &fullName : names) + keyboardLayoutNames_.push_back(fullName.asString()); + } else if (const auto& payload = ev["KeyboardLayoutSwitched"]) { + keyboardLayoutCurrent_ = payload["idx"].asUInt(); + } else if (const auto &payload = ev["WindowsChanged"]) { + windows_.clear(); + const auto &values = payload["windows"]; + std::copy(values.begin(), values.end(), std::back_inserter(windows_)); + } else if (const auto &payload = ev["WindowOpenedOrChanged"]) { + const auto &window = payload["window"]; + const auto id = window["id"].asUInt64(); + auto it = std::find_if(windows_.begin(), windows_.end(), + [id](const auto &win) { return win["id"].asUInt64() == id; }); + if (it == windows_.end()) { + windows_.push_back(window); + + if (window["is_focused"].asBool()) { + for (auto &win : windows_) { + win["is_focused"] = win["id"].asUInt64() == id; + } + } + } else { + *it = window; + } + } else if (const auto &payload = ev["WindowClosed"]) { + const auto id = payload["id"].asUInt64(); + auto it = std::find_if(windows_.begin(), windows_.end(), + [id](const auto &win) { return win["id"].asUInt64() == id; }); + if (it != windows_.end()) { + windows_.erase(it); + } else { + spdlog::error("Unknown window closed"); + } + } else if (const auto &payload = ev["WindowFocusChanged"]) { + const auto focused = !payload["id"].isNull(); + const auto id = payload["id"].asUInt64(); + for (auto &win : windows_) { + win["is_focused"] = focused && win["id"].asUInt64() == id; + } + } + } + + std::unique_lock lock(callbackMutex_); + + for (auto& [eventname, handler] : callbacks_) { + if (eventname == members[0]) { + handler->onEvent(ev); + } + } +} + +void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) { + if (ev_handler == nullptr) { + return; + } + + std::unique_lock lock(callbackMutex_); + callbacks_.emplace_back(ev, ev_handler); +} + +void IPC::unregisterForIPC(EventHandler* ev_handler) { + if (ev_handler == nullptr) { + return; + } + + std::unique_lock lock(callbackMutex_); + + for (auto it = callbacks_.begin(); it != callbacks_.end();) { + auto& [eventname, handler] = *it; + if (handler == ev_handler) { + it = callbacks_.erase(it); + } else { + ++it; + } + } +} + +Json::Value IPC::send(const Json::Value& request) { + int socketfd = connectToSocket(); + if (socketfd == -1) + throw std::runtime_error("Niri is not running"); + + __gnu_cxx::stdio_filebuf filebuf(socketfd, std::ios::in | std::ios::out); + std::iostream fs(&filebuf); + + // Niri needs the request on a single line. + Json::StreamWriterBuilder builder; + builder["indentation"] = ""; + std::unique_ptr writer(builder.newStreamWriter()); + writer->write(request, &fs); + fs << std::endl; + + Json::Value response; + fs >> response; + return response; +} + +} // namespace waybar::modules::hyprland diff --git a/src/modules/niri/language.cpp b/src/modules/niri/language.cpp new file mode 100644 index 00000000..f124d4dd --- /dev/null +++ b/src/modules/niri/language.cpp @@ -0,0 +1,138 @@ +#include "modules/niri/language.hpp" + +#include +#include +#include + +#include "util/string.hpp" + +namespace waybar::modules::niri { + +Language::Language(const std::string &id, const Bar &bar, const Json::Value &config) + : ALabel(config, "language", id, "{}", 0, true), bar_(bar) { + label_.hide(); + + if (!gIPC) + gIPC = std::make_unique(); + + gIPC->registerForIPC("KeyboardLayoutsChanged", this); + gIPC->registerForIPC("KeyboardLayoutSwitched", this); + + updateFromIPC(); + dp.emit(); +} + +Language::~Language() { + gIPC->unregisterForIPC(this); + // wait for possible event handler to finish + std::lock_guard lock(mutex_); +} + +void Language::updateFromIPC() { + std::lock_guard lock(mutex_); + auto ipcLock = gIPC->lockData(); + + layouts_.clear(); + for (const auto &fullName : gIPC->keyboardLayoutNames()) + layouts_.push_back(getLayout(fullName)); + + current_idx_ = gIPC->keyboardLayoutCurrent(); +} + +/** + * Language::doUpdate - update workspaces in UI thread. + * + * Note: some member fields are modified by both UI thread and event listener thread, use mutex_ to + * protect these member fields, and lock should released before calling ALabel::update(). + */ +void Language::doUpdate() { + std::lock_guard lock(mutex_); + + if (layouts_.size() <= current_idx_) { + spdlog::error("niri language layout index out of bounds"); + label_.hide(); + return; + } + const auto &layout = layouts_[current_idx_]; + + spdlog::debug("niri language update with full name {}", layout.full_name); + spdlog::debug("niri language update with short name {}", layout.short_name); + spdlog::debug("niri language update with short description {}", layout.short_description); + spdlog::debug("niri language update with variant {}", layout.variant); + + std::string layoutName = std::string{}; + if (config_.isMember("format-" + layout.short_description + "-" + layout.variant)) { + const auto propName = "format-" + layout.short_description + "-" + layout.variant; + layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); + } else if (config_.isMember("format-" + layout.short_description)) { + const auto propName = "format-" + layout.short_description; + layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); + } else { + layoutName = trim(fmt::format(fmt::runtime(format_), fmt::arg("long", layout.full_name), + fmt::arg("short", layout.short_name), + fmt::arg("shortDescription", layout.short_description), + fmt::arg("variant", layout.variant))); + } + + spdlog::debug("niri language formatted layout name {}", layoutName); + + if (!format_.empty()) { + label_.show(); + label_.set_markup(layoutName); + } else { + label_.hide(); + } +} + +void Language::update() { + doUpdate(); + ALabel::update(); +} + +void Language::onEvent(const Json::Value& ev) { + if (ev["KeyboardLayoutsChanged"]) { + updateFromIPC(); + } else if (ev["KeyboardLayoutSwitched"]) { + std::lock_guard lock(mutex_); + auto ipcLock = gIPC->lockData(); + current_idx_ = gIPC->keyboardLayoutCurrent(); + } + + dp.emit(); +} + +Language::Layout Language::getLayout(const std::string &fullName) { + auto* const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES); + rxkb_context_parse_default_ruleset(context); + + rxkb_layout* layout = rxkb_layout_first(context); + while (layout != nullptr) { + std::string nameOfLayout = rxkb_layout_get_description(layout); + + if (nameOfLayout != fullName) { + layout = rxkb_layout_next(layout); + continue; + } + + auto name = std::string(rxkb_layout_get_name(layout)); + const auto* variantPtr = rxkb_layout_get_variant(layout); + std::string variant = variantPtr == nullptr ? "" : std::string(variantPtr); + + const auto* descriptionPtr = rxkb_layout_get_brief(layout); + std::string description = descriptionPtr == nullptr ? "" : std::string(descriptionPtr); + + Layout info = Layout{nameOfLayout, name, variant, description}; + + rxkb_context_unref(context); + + return info; + } + + rxkb_context_unref(context); + + spdlog::debug("niri language didn't find matching layout for {}", fullName); + + return Layout{"", "", "", ""}; +} + +} // namespace waybar::modules::niri diff --git a/src/modules/niri/window.cpp b/src/modules/niri/window.cpp new file mode 100644 index 00000000..b2405435 --- /dev/null +++ b/src/modules/niri/window.cpp @@ -0,0 +1,121 @@ +#include "modules/niri/window.hpp" + +#include +#include +#include + +#include "util/rewrite_string.hpp" +#include "util/sanitize_str.hpp" + +namespace waybar::modules::niri { + +Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) + : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { + if (!gIPC) + gIPC = std::make_unique(); + + gIPC->registerForIPC("WindowsChanged", this); + gIPC->registerForIPC("WindowOpenedOrChanged", this); + gIPC->registerForIPC("WindowClosed", this); + gIPC->registerForIPC("WindowFocusChanged", this); + + dp.emit(); +} + +Window::~Window() { + gIPC->unregisterForIPC(this); +} + +void Window::onEvent(const Json::Value &ev) { + dp.emit(); +} + +void Window::doUpdate() { + auto ipcLock = gIPC->lockData(); + + const auto &windows = gIPC->windows(); + const auto &workspaces = gIPC->workspaces(); + + const auto separateOutputs = config_["separate-outputs"].asBool(); + const auto ws_it = std::find_if(workspaces.cbegin(), workspaces.cend(), + [&](const auto &ws) { + if (separateOutputs) { + return ws["is_active"].asBool() && ws["output"].asString() == bar_.output->name; + } + + return ws["is_focused"].asBool(); + }); + + std::vector::const_iterator it; + if (ws_it == workspaces.cend() || (*ws_it)["active_window_id"].isNull()) { + it = windows.cend(); + } else { + const auto id = (*ws_it)["active_window_id"].asUInt64(); + it = std::find_if(windows.cbegin(), windows.cend(), + [id](const auto &win) { return win["id"].asUInt64() == id; }); + } + + setClass("empty", ws_it == workspaces.cend() || (*ws_it)["active_window_id"].isNull()); + + if (it != windows.cend()) { + const auto &window = *it; + + const auto title = window["title"].asString(); + const auto appId = window["app_id"].asString(); + const auto sanitizedTitle = waybar::util::sanitize_string(title); + const auto sanitizedAppId = waybar::util::sanitize_string(appId); + + label_.show(); + label_.set_markup(waybar::util::rewriteString( + fmt::format(fmt::runtime(format_), + fmt::arg("title", sanitizedTitle), + fmt::arg("app_id", sanitizedAppId)), + config_["rewrite"])); + + updateAppIconName(appId, ""); + + if (tooltipEnabled()) + label_.set_tooltip_text(title); + + const auto id = window["id"].asUInt64(); + const auto workspaceId = window["workspace_id"].asUInt64(); + const auto isSolo = std::none_of(windows.cbegin(), windows.cend(), + [&](const auto &win) { + return win["id"].asUInt64() != id && win["workspace_id"].asUInt64() == workspaceId; + }); + setClass("solo", isSolo); + if (!appId.empty()) + setClass(appId, isSolo); + + if (oldAppId_ != appId) { + if (!oldAppId_.empty()) + setClass(oldAppId_, false); + oldAppId_ = appId; + } + } else { + label_.hide(); + updateAppIconName("", ""); + setClass("solo", false); + if (!oldAppId_.empty()) + setClass(oldAppId_, false); + oldAppId_.clear(); + } +} + +void Window::update() { + doUpdate(); + AAppIconLabel::update(); +} + +void Window::setClass(const std::string &className, bool enable) { + auto styleContext = bar_.window.get_style_context(); + if (enable) { + if (!styleContext->has_class(className)) { + styleContext->add_class(className); + } + } else { + styleContext->remove_class(className); + } +} + +} // namespace waybar::modules::niri diff --git a/src/modules/niri/workspaces.cpp b/src/modules/niri/workspaces.cpp new file mode 100644 index 00000000..2ecfa1ba --- /dev/null +++ b/src/modules/niri/workspaces.cpp @@ -0,0 +1,204 @@ +#include "modules/niri/workspaces.hpp" + +#include +#include +#include + +namespace waybar::modules::niri { + +Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) + : AModule(config, "workspaces", id, false, false), + bar_(bar), + box_(bar.orientation, 0) { + box_.set_name("workspaces"); + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + box_.get_style_context()->add_class(MODULE_CLASS); + event_box_.add(box_); + + if (!gIPC) + gIPC = std::make_unique(); + + gIPC->registerForIPC("WorkspacesChanged", this); + gIPC->registerForIPC("WorkspaceActivated", this); + gIPC->registerForIPC("WorkspaceActiveWindowChanged", this); + + dp.emit(); +} + +Workspaces::~Workspaces() { + gIPC->unregisterForIPC(this); +} + +void Workspaces::onEvent(const Json::Value &ev) { + dp.emit(); +} + +void Workspaces::doUpdate() { + auto ipcLock = gIPC->lockData(); + + const auto alloutputs = config_["all-outputs"].asBool(); + std::vector my_workspaces; + const auto &workspaces = gIPC->workspaces(); + std::copy_if(workspaces.cbegin(), workspaces.cend(), std::back_inserter(my_workspaces), + [&](const auto &ws) { + if (alloutputs) + return true; + return ws["output"].asString() == bar_.output->name; + }); + + // Remove buttons for removed workspaces. + for (auto it = buttons_.begin(); it != buttons_.end(); ) { + auto ws = std::find_if(my_workspaces.begin(), my_workspaces.end(), + [it](const auto &ws) { return ws["id"].asUInt64() == it->first; }); + if (ws == my_workspaces.end()) { + it = buttons_.erase(it); + } else { + ++it; + } + } + + // Add buttons for new workspaces, update existing ones. + for (const auto &ws : my_workspaces) { + auto bit = buttons_.find(ws["id"].asUInt64()); + auto &button = bit == buttons_.end() ? addButton(ws) : bit->second; + auto style_context = button.get_style_context(); + + if (ws["is_focused"].asBool()) + style_context->add_class("focused"); + else + style_context->remove_class("focused"); + + if (ws["is_active"].asBool()) + style_context->add_class("active"); + else + style_context->remove_class("active"); + + if (ws["output"]) { + if (ws["output"].asString() == bar_.output->name) + style_context->add_class("current_output"); + else + style_context->remove_class("current_output"); + } else { + style_context->remove_class("current_output"); + } + + if (ws["active_window_id"].isNull()) + style_context->add_class("empty"); + else + style_context->remove_class("empty"); + + std::string name; + if (ws["name"]) { + name = ws["name"].asString(); + } else { + name = std::to_string(ws["idx"].asUInt()); + } + button.set_name("niri-workspace-" + name); + + if (config_["format"].isString()) { + auto format = config_["format"].asString(); + name = fmt::format( + fmt::runtime(format), + fmt::arg("icon", getIcon(name, ws)), + fmt::arg("value", name), + fmt::arg("name", ws["name"].asString()), + fmt::arg("index", ws["idx"].asUInt()), + fmt::arg("output", ws["output"].asString())); + } + if (!config_["disable-markup"].asBool()) { + static_cast(button.get_children()[0])->set_markup(name); + } else { + button.set_label(name); + } + + if (config_["current-only"].asBool()) { + const auto *property = alloutputs ? "is_focused" : "is_active"; + if (ws[property].asBool()) + button.show(); + else + button.hide(); + } else { + button.show(); + } + } + + // Refresh the button order. + for (auto it = my_workspaces.cbegin(); it != my_workspaces.cend(); ++it) { + const auto &ws = *it; + + auto pos = ws["idx"].asUInt() - 1; + if (alloutputs) + pos = it - my_workspaces.cbegin(); + + auto &button = buttons_[ws["id"].asUInt64()]; + box_.reorder_child(button, pos); + } +} + +void Workspaces::update() { + doUpdate(); + AModule::update(); +} + +Gtk::Button &Workspaces::addButton(const Json::Value &ws) { + std::string name; + if (ws["name"]) { + name = ws["name"].asString(); + } else { + name = std::to_string(ws["idx"].asUInt()); + } + + auto pair = buttons_.emplace(ws["id"].asUInt64(), name); + auto &&button = pair.first->second; + box_.pack_start(button, false, false, 0); + button.set_relief(Gtk::RELIEF_NONE); + if (!config_["disable-click"].asBool()) { + const auto id = ws["id"].asUInt64(); + button.signal_pressed().connect([=] { + try { + // {"Action":{"FocusWorkspace":{"reference":{"Id":1}}}} + Json::Value request(Json::objectValue); + auto &action = (request["Action"] = Json::Value(Json::objectValue)); + auto &focusWorkspace = (action["FocusWorkspace"] = Json::Value(Json::objectValue)); + auto &reference = (focusWorkspace["reference"] = Json::Value(Json::objectValue)); + reference["Id"] = id; + + IPC::send(request); + } catch (const std::exception &e) { + spdlog::error("Error switching workspace: {}", e.what()); + } + }); + } + return button; +} + +std::string Workspaces::getIcon(const std::string &value, const Json::Value &ws) { + const auto &icons = config_["format-icons"]; + if (!icons) + return value; + + if (ws["is_focused"].asBool() && icons["focused"]) + return icons["focused"].asString(); + + if (ws["is_active"].asBool() && icons["active"]) + return icons["active"].asString(); + + if (ws["name"]) { + const auto &name = ws["name"].asString(); + if (icons[name]) + return icons[name].asString(); + } + + const auto idx = ws["idx"].asString(); + if (icons[idx]) + return icons[idx].asString(); + + if (icons["default"]) + return icons["default"].asString(); + + return value; +} + +} // namespace waybar::modules::niri From fef0bb995c0300f796856460173ce731c075a0ab Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Fri, 13 Sep 2024 10:33:08 +0300 Subject: [PATCH 675/842] niri: Replace gnu extension with GDataInputStream --- src/modules/niri/backend.cpp | 45 +++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/modules/niri/backend.cpp b/src/modules/niri/backend.cpp index ef23c881..6e251d31 100644 --- a/src/modules/niri/backend.cpp +++ b/src/modules/niri/backend.cpp @@ -8,8 +8,11 @@ #include #include #include +#include "giomm/datainputstream.h" +#include "giomm/dataoutputstream.h" +#include "giomm/unixinputstream.h" +#include "giomm/unixoutputstream.h" -#include #include #include #include @@ -63,18 +66,23 @@ void IPC::startIPC() { spdlog::info("Niri IPC starting"); - __gnu_cxx::stdio_filebuf filebuf(socketfd, std::ios::in | std::ios::out); - std::iostream fs(&filebuf); - fs << R"("EventStream")" << std::endl; + auto unix_istream = Gio::UnixInputStream::create(socketfd, true); + auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false); + auto istream = Gio::DataInputStream::create(unix_istream); + auto ostream = Gio::DataOutputStream::create(unix_ostream); - std::string line; - std::getline(fs, line); - if (line != R"({"Ok":"Handled"})") { + if (!ostream->put_string("\"EventStream\"\n") || !ostream->flush()) { spdlog::error("Niri IPC: failed to start event stream"); return; } - while (std::getline(fs, line)) { + std::string line; + if (!istream->read_line(line) || line != R"({"Ok":"Handled"})") { + spdlog::error("Niri IPC: failed to start event stream"); + return; + } + + while (istream->read_line(line)) { spdlog::debug("Niri IPC: received {}", line); try { @@ -231,18 +239,29 @@ Json::Value IPC::send(const Json::Value& request) { if (socketfd == -1) throw std::runtime_error("Niri is not running"); - __gnu_cxx::stdio_filebuf filebuf(socketfd, std::ios::in | std::ios::out); - std::iostream fs(&filebuf); + auto unix_istream = Gio::UnixInputStream::create(socketfd, true); + auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false); + auto istream = Gio::DataInputStream::create(unix_istream); + auto ostream = Gio::DataOutputStream::create(unix_ostream); // Niri needs the request on a single line. Json::StreamWriterBuilder builder; builder["indentation"] = ""; std::unique_ptr writer(builder.newStreamWriter()); - writer->write(request, &fs); - fs << std::endl; + std::ostringstream oss; + writer->write(request, &oss); + oss << '\n'; + if (!ostream->put_string(oss.str()) || !ostream->flush()) + throw std::runtime_error("error writing to niri socket"); + + std::string line; + if (!istream->read_line(line)) + throw std::runtime_error("error reading from niri socket"); + + std::istringstream iss(std::move(line)); Json::Value response; - fs >> response; + iss >> response; return response; } From 34bfefcd2e6f6916a1b6b6b29c5836b5aeaf70b4 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Fri, 13 Sep 2024 10:33:50 +0300 Subject: [PATCH 676/842] niri: Gate behind a meson option --- meson.build | 2 +- meson_options.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 7097a9fb..666c0dd0 100644 --- a/meson.build +++ b/meson.build @@ -318,7 +318,7 @@ if true ) endif -if true +if get_option('niri') add_project_arguments('-DHAVE_NIRI', language: 'cpp') src_files += files( 'src/modules/niri/backend.cpp', diff --git a/meson_options.txt b/meson_options.txt index fef50839..303ef038 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -19,3 +19,4 @@ option('experimental', type : 'boolean', value : false, description: 'Enable exp option('jack', type: 'feature', value: 'auto', description: 'Enable support for JACK') option('wireplumber', type: 'feature', value: 'auto', description: 'Enable support for WirePlumber') option('cava', type: 'feature', value: 'auto', description: 'Enable support for Cava') +option('niri', type: 'boolean', description: 'Enable support for niri') From a4d31ab10d1630cb9104c695d7b777ca12468904 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:40:35 +0200 Subject: [PATCH 677/842] fix: sni item fmt --- src/modules/image.cpp | 4 ++-- src/modules/sni/item.cpp | 23 ++++++++++++++--------- subprojects/gtk-layer-shell.wrap | 8 ++++---- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 5e6c1493..71e93b94 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -57,8 +57,8 @@ auto waybar::modules::Image::update() -> void { int scaled_icon_size = size_ * image_.get_scale_factor(); pixbuf = Gdk::Pixbuf::create_from_file(path_, scaled_icon_size, scaled_icon_size); - auto surface = - Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), image_.get_window()); + auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), + image_.get_window()); image_.set(surface); image_.show(); diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 6c4ec8c0..8afb39fb 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -104,9 +104,11 @@ void Item::proxyReady(Glib::RefPtr& result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, + std::string(err.what())); } catch (const std::exception& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, + std::string(err.what())); } } @@ -124,14 +126,15 @@ ToolTip get_variant(const Glib::VariantBase& value) { result.text = get_variant(container.get_child(2)); auto description = get_variant(container.get_child(3)); if (!description.empty()) { - result.text = fmt::format("{}\n{}", result.text, description); + result.text = fmt::format("{}\n{}", std::string(result.text), std::string(description)); } return result; } void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { try { - spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value); + spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, + std::string(name), get_variant(value)); if (name == "Category") { category = get_variant(value); @@ -176,10 +179,12 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } } catch (const Glib::Error& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, name, value, err.what()); + id.empty() ? bus_name : id, std::string(name), get_variant(value), + std::string(err.what())); } catch (const std::exception& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, name, value, err.what()); + id.empty() ? bus_name : id, std::string(name), get_variant(value), + std::string(err.what())); } } @@ -221,9 +226,9 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::warn("Failed to update properties: {}", err.what()); + spdlog::warn("Failed to update properties: {}", std::string(err.what())); } catch (const std::exception& err) { - spdlog::warn("Failed to update properties: {}", err.what()); + spdlog::warn("Failed to update properties: {}", std::string(err.what())); } update_pending_.clear(); } @@ -245,7 +250,7 @@ static const std::map> signal2props void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, const Glib::VariantContainerBase& arguments) { - spdlog::trace("Tray item '{}' got signal {}", id, signal_name); + spdlog::trace("Tray item '{}' got signal {}", id, std::string(signal_name)); auto changed = signal2props.find(signal_name.raw()); if (changed != signal2props.end()) { if (update_pending_.empty()) { diff --git a/subprojects/gtk-layer-shell.wrap b/subprojects/gtk-layer-shell.wrap index cb730345..fc0ddf74 100644 --- a/subprojects/gtk-layer-shell.wrap +++ b/subprojects/gtk-layer-shell.wrap @@ -1,5 +1,5 @@ [wrap-file] -directory = gtk-layer-shell-0.8.2 -source_filename = gtk-layer-shell-0.8.2.tar.gz -source_hash = 254dd246303127c5d5236ea640f01a82e35d2d652a48d139dd669c832a0f0dce -source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.8.2/gtk-layer-shell-0.8.2.tar.gz +directory = gtk-layer-shell-0.9.0 +source_filename = gtk-layer-shell-0.9.0.tar.gz +source_hash = 3809e5565d9ed02e44bb73787ff218523e8760fef65830afe60ea7322e22da1c +source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.9.0/gtk-layer-shell-0.9.0.tar.gz From 1142979581de1b6487e0ca5c65d887f4fb71eba0 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:43:19 +0200 Subject: [PATCH 678/842] chore: lint --- include/modules/niri/backend.hpp | 6 +- include/modules/niri/language.hpp | 2 +- src/modules/niri/backend.cpp | 113 ++++++++++++++---------------- src/modules/niri/language.cpp | 16 ++--- src/modules/niri/window.cpp | 46 +++++------- src/modules/niri/workspaces.cpp | 52 +++++--------- 6 files changed, 98 insertions(+), 137 deletions(-) diff --git a/include/modules/niri/backend.hpp b/include/modules/niri/backend.hpp index 01af5017..42b9ff7f 100644 --- a/include/modules/niri/backend.hpp +++ b/include/modules/niri/backend.hpp @@ -26,9 +26,9 @@ class IPC { // The data members are only safe to access while dataMutex_ is locked. std::lock_guard lockData() { return std::lock_guard(dataMutex_); } - const std::vector &workspaces() const { return workspaces_; } - const std::vector &windows() const { return windows_; } - const std::vector &keyboardLayoutNames() const { return keyboardLayoutNames_; } + const std::vector& workspaces() const { return workspaces_; } + const std::vector& windows() const { return windows_; } + const std::vector& keyboardLayoutNames() const { return keyboardLayoutNames_; } unsigned keyboardLayoutCurrent() const { return keyboardLayoutCurrent_; } private: diff --git a/include/modules/niri/language.hpp b/include/modules/niri/language.hpp index 1cecd206..42b90ac4 100644 --- a/include/modules/niri/language.hpp +++ b/include/modules/niri/language.hpp @@ -10,7 +10,7 @@ namespace waybar::modules::niri { class Language : public ALabel, public EventHandler { public: - Language(const std::string&, const Bar&, const Json::Value&); + Language(const std::string &, const Bar &, const Json::Value &); ~Language() override; void update() override; diff --git a/src/modules/niri/backend.cpp b/src/modules/niri/backend.cpp index 6e251d31..383bf113 100644 --- a/src/modules/niri/backend.cpp +++ b/src/modules/niri/backend.cpp @@ -8,46 +8,47 @@ #include #include #include -#include "giomm/datainputstream.h" -#include "giomm/dataoutputstream.h" -#include "giomm/unixinputstream.h" -#include "giomm/unixoutputstream.h" #include #include #include +#include "giomm/datainputstream.h" +#include "giomm/dataoutputstream.h" +#include "giomm/unixinputstream.h" +#include "giomm/unixoutputstream.h" + namespace waybar::modules::niri { int IPC::connectToSocket() { - const char* socket_path = getenv("NIRI_SOCKET"); + const char *socket_path = getenv("NIRI_SOCKET"); - if (socket_path == nullptr) { - spdlog::warn("Niri is not running, niri IPC will not be available."); - return -1; - } + if (socket_path == nullptr) { + spdlog::warn("Niri is not running, niri IPC will not be available."); + return -1; + } - struct sockaddr_un addr; - int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); + struct sockaddr_un addr; + int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (socketfd == -1) { - throw std::runtime_error("socketfd failed"); - } + if (socketfd == -1) { + throw std::runtime_error("socketfd failed"); + } - addr.sun_family = AF_UNIX; + addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); - addr.sun_path[sizeof(addr.sun_path) - 1] = 0; + addr.sun_path[sizeof(addr.sun_path) - 1] = 0; - int l = sizeof(struct sockaddr_un); + int l = sizeof(struct sockaddr_un); - if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) { - close(socketfd); - throw std::runtime_error("unable to connect"); - } + if (connect(socketfd, (struct sockaddr *)&addr, l) == -1) { + close(socketfd); + throw std::runtime_error("unable to connect"); + } - return socketfd; + return socketfd; } void IPC::startIPC() { @@ -61,8 +62,7 @@ void IPC::startIPC() { spdlog::error("Niri IPC: failed to start, reason: {}", e.what()); return; } - if (socketfd == -1) - return; + if (socketfd == -1) return; spdlog::info("Niri IPC starting"); @@ -87,7 +87,7 @@ void IPC::startIPC() { try { parseIPC(line); - } catch (std::exception& e) { + } catch (std::exception &e) { spdlog::warn("Failed to parse IPC message: {}, reason: {}", line, e.what()); } catch (...) { throw; @@ -98,11 +98,10 @@ void IPC::startIPC() { }).detach(); } -void IPC::parseIPC(const std::string& line) { +void IPC::parseIPC(const std::string &line) { const auto ev = parser_.parse(line); const auto members = ev.getMemberNames(); - if (members.size() != 1) - throw std::runtime_error("Event must have a single member"); + if (members.size() != 1) throw std::runtime_error("Event must have a single member"); { auto lock = lockData(); @@ -112,17 +111,15 @@ void IPC::parseIPC(const std::string& line) { const auto &values = payload["workspaces"]; std::copy(values.begin(), values.end(), std::back_inserter(workspaces_)); - std::sort(workspaces_.begin(), workspaces_.end(), - [](const auto &a, const auto &b) { - const auto &aOutput = a["output"].asString(); - const auto &bOutput = b["output"].asString(); - const auto aIdx = a["idx"].asUInt(); - const auto bIdx = b["idx"].asUInt(); - if (aOutput == bOutput) - return aIdx < bIdx; - return aOutput < bOutput; - }); - } else if (const auto& payload = ev["WorkspaceActivated"]) { + std::sort(workspaces_.begin(), workspaces_.end(), [](const auto &a, const auto &b) { + const auto &aOutput = a["output"].asString(); + const auto &bOutput = b["output"].asString(); + const auto aIdx = a["idx"].asUInt(); + const auto bIdx = b["idx"].asUInt(); + if (aOutput == bOutput) return aIdx < bIdx; + return aOutput < bOutput; + }); + } else if (const auto &payload = ev["WorkspaceActivated"]) { const auto id = payload["id"].asUInt64(); const auto focused = payload["focused"].asBool(); auto it = std::find_if(workspaces_.begin(), workspaces_.end(), @@ -132,19 +129,18 @@ void IPC::parseIPC(const std::string& line) { const auto &output = ws["output"].asString(); for (auto &ws : workspaces_) { const auto got_activated = (ws["id"].asUInt64() == id); - if (ws["output"] == output) - ws["is_active"] = got_activated; + if (ws["output"] == output) ws["is_active"] = got_activated; - if (focused) - ws["is_focused"] = got_activated; + if (focused) ws["is_focused"] = got_activated; } } else { spdlog::error("Activated unknown workspace"); } - } else if (const auto& payload = ev["WorkspaceActiveWindowChanged"]) { + } else if (const auto &payload = ev["WorkspaceActiveWindowChanged"]) { const auto workspaceId = payload["workspace_id"].asUInt64(); - auto it = std::find_if(workspaces_.begin(), workspaces_.end(), - [workspaceId](const auto &ws) { return ws["id"].asUInt64() == workspaceId; }); + auto it = std::find_if(workspaces_.begin(), workspaces_.end(), [workspaceId](const auto &ws) { + return ws["id"].asUInt64() == workspaceId; + }); if (it != workspaces_.end()) { auto &ws = *it; ws["active_window_id"] = payload["active_window_id"]; @@ -157,9 +153,8 @@ void IPC::parseIPC(const std::string& line) { keyboardLayoutCurrent_ = layouts["current_idx"].asUInt(); keyboardLayoutNames_.clear(); - for (const auto &fullName : names) - keyboardLayoutNames_.push_back(fullName.asString()); - } else if (const auto& payload = ev["KeyboardLayoutSwitched"]) { + for (const auto &fullName : names) keyboardLayoutNames_.push_back(fullName.asString()); + } else if (const auto &payload = ev["KeyboardLayoutSwitched"]) { keyboardLayoutCurrent_ = payload["idx"].asUInt(); } else if (const auto &payload = ev["WindowsChanged"]) { windows_.clear(); @@ -201,14 +196,14 @@ void IPC::parseIPC(const std::string& line) { std::unique_lock lock(callbackMutex_); - for (auto& [eventname, handler] : callbacks_) { + for (auto &[eventname, handler] : callbacks_) { if (eventname == members[0]) { handler->onEvent(ev); } } } -void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) { +void IPC::registerForIPC(const std::string &ev, EventHandler *ev_handler) { if (ev_handler == nullptr) { return; } @@ -217,7 +212,7 @@ void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) { callbacks_.emplace_back(ev, ev_handler); } -void IPC::unregisterForIPC(EventHandler* ev_handler) { +void IPC::unregisterForIPC(EventHandler *ev_handler) { if (ev_handler == nullptr) { return; } @@ -225,7 +220,7 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { std::unique_lock lock(callbackMutex_); for (auto it = callbacks_.begin(); it != callbacks_.end();) { - auto& [eventname, handler] = *it; + auto &[eventname, handler] = *it; if (handler == ev_handler) { it = callbacks_.erase(it); } else { @@ -234,10 +229,9 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { } } -Json::Value IPC::send(const Json::Value& request) { +Json::Value IPC::send(const Json::Value &request) { int socketfd = connectToSocket(); - if (socketfd == -1) - throw std::runtime_error("Niri is not running"); + if (socketfd == -1) throw std::runtime_error("Niri is not running"); auto unix_istream = Gio::UnixInputStream::create(socketfd, true); auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false); @@ -256,8 +250,7 @@ Json::Value IPC::send(const Json::Value& request) { throw std::runtime_error("error writing to niri socket"); std::string line; - if (!istream->read_line(line)) - throw std::runtime_error("error reading from niri socket"); + if (!istream->read_line(line)) throw std::runtime_error("error reading from niri socket"); std::istringstream iss(std::move(line)); Json::Value response; @@ -265,4 +258,4 @@ Json::Value IPC::send(const Json::Value& request) { return response; } -} // namespace waybar::modules::hyprland +} // namespace waybar::modules::niri diff --git a/src/modules/niri/language.cpp b/src/modules/niri/language.cpp index f124d4dd..1e4d6d10 100644 --- a/src/modules/niri/language.cpp +++ b/src/modules/niri/language.cpp @@ -12,8 +12,7 @@ Language::Language(const std::string &id, const Bar &bar, const Json::Value &con : ALabel(config, "language", id, "{}", 0, true), bar_(bar) { label_.hide(); - if (!gIPC) - gIPC = std::make_unique(); + if (!gIPC) gIPC = std::make_unique(); gIPC->registerForIPC("KeyboardLayoutsChanged", this); gIPC->registerForIPC("KeyboardLayoutSwitched", this); @@ -33,8 +32,7 @@ void Language::updateFromIPC() { auto ipcLock = gIPC->lockData(); layouts_.clear(); - for (const auto &fullName : gIPC->keyboardLayoutNames()) - layouts_.push_back(getLayout(fullName)); + for (const auto &fullName : gIPC->keyboardLayoutNames()) layouts_.push_back(getLayout(fullName)); current_idx_ = gIPC->keyboardLayoutCurrent(); } @@ -89,7 +87,7 @@ void Language::update() { ALabel::update(); } -void Language::onEvent(const Json::Value& ev) { +void Language::onEvent(const Json::Value &ev) { if (ev["KeyboardLayoutsChanged"]) { updateFromIPC(); } else if (ev["KeyboardLayoutSwitched"]) { @@ -102,10 +100,10 @@ void Language::onEvent(const Json::Value& ev) { } Language::Layout Language::getLayout(const std::string &fullName) { - auto* const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES); + auto *const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES); rxkb_context_parse_default_ruleset(context); - rxkb_layout* layout = rxkb_layout_first(context); + rxkb_layout *layout = rxkb_layout_first(context); while (layout != nullptr) { std::string nameOfLayout = rxkb_layout_get_description(layout); @@ -115,10 +113,10 @@ Language::Layout Language::getLayout(const std::string &fullName) { } auto name = std::string(rxkb_layout_get_name(layout)); - const auto* variantPtr = rxkb_layout_get_variant(layout); + const auto *variantPtr = rxkb_layout_get_variant(layout); std::string variant = variantPtr == nullptr ? "" : std::string(variantPtr); - const auto* descriptionPtr = rxkb_layout_get_brief(layout); + const auto *descriptionPtr = rxkb_layout_get_brief(layout); std::string description = descriptionPtr == nullptr ? "" : std::string(descriptionPtr); Layout info = Layout{nameOfLayout, name, variant, description}; diff --git a/src/modules/niri/window.cpp b/src/modules/niri/window.cpp index b2405435..6e6fd36f 100644 --- a/src/modules/niri/window.cpp +++ b/src/modules/niri/window.cpp @@ -11,8 +11,7 @@ namespace waybar::modules::niri { Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { - if (!gIPC) - gIPC = std::make_unique(); + if (!gIPC) gIPC = std::make_unique(); gIPC->registerForIPC("WindowsChanged", this); gIPC->registerForIPC("WindowOpenedOrChanged", this); @@ -22,13 +21,9 @@ Window::Window(const std::string &id, const Bar &bar, const Json::Value &config) dp.emit(); } -Window::~Window() { - gIPC->unregisterForIPC(this); -} +Window::~Window() { gIPC->unregisterForIPC(this); } -void Window::onEvent(const Json::Value &ev) { - dp.emit(); -} +void Window::onEvent(const Json::Value &ev) { dp.emit(); } void Window::doUpdate() { auto ipcLock = gIPC->lockData(); @@ -37,14 +32,13 @@ void Window::doUpdate() { const auto &workspaces = gIPC->workspaces(); const auto separateOutputs = config_["separate-outputs"].asBool(); - const auto ws_it = std::find_if(workspaces.cbegin(), workspaces.cend(), - [&](const auto &ws) { - if (separateOutputs) { - return ws["is_active"].asBool() && ws["output"].asString() == bar_.output->name; - } + const auto ws_it = std::find_if(workspaces.cbegin(), workspaces.cend(), [&](const auto &ws) { + if (separateOutputs) { + return ws["is_active"].asBool() && ws["output"].asString() == bar_.output->name; + } - return ws["is_focused"].asBool(); - }); + return ws["is_focused"].asBool(); + }); std::vector::const_iterator it; if (ws_it == workspaces.cend() || (*ws_it)["active_window_id"].isNull()) { @@ -67,37 +61,31 @@ void Window::doUpdate() { label_.show(); label_.set_markup(waybar::util::rewriteString( - fmt::format(fmt::runtime(format_), - fmt::arg("title", sanitizedTitle), + fmt::format(fmt::runtime(format_), fmt::arg("title", sanitizedTitle), fmt::arg("app_id", sanitizedAppId)), config_["rewrite"])); updateAppIconName(appId, ""); - if (tooltipEnabled()) - label_.set_tooltip_text(title); + if (tooltipEnabled()) label_.set_tooltip_text(title); const auto id = window["id"].asUInt64(); const auto workspaceId = window["workspace_id"].asUInt64(); - const auto isSolo = std::none_of(windows.cbegin(), windows.cend(), - [&](const auto &win) { - return win["id"].asUInt64() != id && win["workspace_id"].asUInt64() == workspaceId; - }); + const auto isSolo = std::none_of(windows.cbegin(), windows.cend(), [&](const auto &win) { + return win["id"].asUInt64() != id && win["workspace_id"].asUInt64() == workspaceId; + }); setClass("solo", isSolo); - if (!appId.empty()) - setClass(appId, isSolo); + if (!appId.empty()) setClass(appId, isSolo); if (oldAppId_ != appId) { - if (!oldAppId_.empty()) - setClass(oldAppId_, false); + if (!oldAppId_.empty()) setClass(oldAppId_, false); oldAppId_ = appId; } } else { label_.hide(); updateAppIconName("", ""); setClass("solo", false); - if (!oldAppId_.empty()) - setClass(oldAppId_, false); + if (!oldAppId_.empty()) setClass(oldAppId_, false); oldAppId_.clear(); } } diff --git a/src/modules/niri/workspaces.cpp b/src/modules/niri/workspaces.cpp index 2ecfa1ba..d2fcad5d 100644 --- a/src/modules/niri/workspaces.cpp +++ b/src/modules/niri/workspaces.cpp @@ -7,9 +7,7 @@ namespace waybar::modules::niri { Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) - : AModule(config, "workspaces", id, false, false), - bar_(bar), - box_(bar.orientation, 0) { + : AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.orientation, 0) { box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -17,8 +15,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); - if (!gIPC) - gIPC = std::make_unique(); + if (!gIPC) gIPC = std::make_unique(); gIPC->registerForIPC("WorkspacesChanged", this); gIPC->registerForIPC("WorkspaceActivated", this); @@ -27,13 +24,9 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value dp.emit(); } -Workspaces::~Workspaces() { - gIPC->unregisterForIPC(this); -} +Workspaces::~Workspaces() { gIPC->unregisterForIPC(this); } -void Workspaces::onEvent(const Json::Value &ev) { - dp.emit(); -} +void Workspaces::onEvent(const Json::Value &ev) { dp.emit(); } void Workspaces::doUpdate() { auto ipcLock = gIPC->lockData(); @@ -43,13 +36,12 @@ void Workspaces::doUpdate() { const auto &workspaces = gIPC->workspaces(); std::copy_if(workspaces.cbegin(), workspaces.cend(), std::back_inserter(my_workspaces), [&](const auto &ws) { - if (alloutputs) - return true; + if (alloutputs) return true; return ws["output"].asString() == bar_.output->name; }); // Remove buttons for removed workspaces. - for (auto it = buttons_.begin(); it != buttons_.end(); ) { + for (auto it = buttons_.begin(); it != buttons_.end();) { auto ws = std::find_if(my_workspaces.begin(), my_workspaces.end(), [it](const auto &ws) { return ws["id"].asUInt64() == it->first; }); if (ws == my_workspaces.end()) { @@ -99,13 +91,10 @@ void Workspaces::doUpdate() { if (config_["format"].isString()) { auto format = config_["format"].asString(); - name = fmt::format( - fmt::runtime(format), - fmt::arg("icon", getIcon(name, ws)), - fmt::arg("value", name), - fmt::arg("name", ws["name"].asString()), - fmt::arg("index", ws["idx"].asUInt()), - fmt::arg("output", ws["output"].asString())); + name = fmt::format(fmt::runtime(format), fmt::arg("icon", getIcon(name, ws)), + fmt::arg("value", name), fmt::arg("name", ws["name"].asString()), + fmt::arg("index", ws["idx"].asUInt()), + fmt::arg("output", ws["output"].asString())); } if (!config_["disable-markup"].asBool()) { static_cast(button.get_children()[0])->set_markup(name); @@ -129,8 +118,7 @@ void Workspaces::doUpdate() { const auto &ws = *it; auto pos = ws["idx"].asUInt() - 1; - if (alloutputs) - pos = it - my_workspaces.cbegin(); + if (alloutputs) pos = it - my_workspaces.cbegin(); auto &button = buttons_[ws["id"].asUInt64()]; box_.reorder_child(button, pos); @@ -176,27 +164,21 @@ Gtk::Button &Workspaces::addButton(const Json::Value &ws) { std::string Workspaces::getIcon(const std::string &value, const Json::Value &ws) { const auto &icons = config_["format-icons"]; - if (!icons) - return value; + if (!icons) return value; - if (ws["is_focused"].asBool() && icons["focused"]) - return icons["focused"].asString(); + if (ws["is_focused"].asBool() && icons["focused"]) return icons["focused"].asString(); - if (ws["is_active"].asBool() && icons["active"]) - return icons["active"].asString(); + if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString(); if (ws["name"]) { const auto &name = ws["name"].asString(); - if (icons[name]) - return icons[name].asString(); + if (icons[name]) return icons[name].asString(); } const auto idx = ws["idx"].asString(); - if (icons[idx]) - return icons[idx].asString(); + if (icons[idx]) return icons[idx].asString(); - if (icons["default"]) - return icons["default"].asString(); + if (icons["default"]) return icons["default"].asString(); return value; } From d56dd6ee7fdf8c5ba4e90790af62b7f7829d3a47 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:51:11 +0200 Subject: [PATCH 679/842] chore: v0.11.0 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 666c0dd0..42f9da92 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.10.4', + version: '0.11.0', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From d177969f51b3435308a520c9c0385ae80579b255 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Sep 2024 09:53:15 +0200 Subject: [PATCH 680/842] chore: lint --- src/ALabel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 3cb2c590..467572f1 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -45,7 +45,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st if (config_["rotate"].isUInt()) { rotate = config["rotate"].asUInt(); - if (not (rotate == 0 || rotate == 90 || rotate == 180 || rotate == 270)) + if (not(rotate == 0 || rotate == 90 || rotate == 180 || rotate == 270)) spdlog::warn("'rotate' is only supported in 90 degree increments {} is not valid.", rotate); label_.set_angle(rotate); } From 4a6af0da99306fdf155c5739cea9e7b9e54b6a34 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Aug 2024 15:30:24 -0700 Subject: [PATCH 681/842] fix(bar): use overlay layer for `hide` and `overlay` modes This fixes a major inconsistency with the swaybar implementation of these modes[^1]. `overlay` layer no longer has security implications due to a wide adoption of `ext-session-lock`, so it's safe to use. Following config will restore the previous behavior: ```json "modes": { "hide": { "layer": "top" }, "overlay": { "layer": "top" } }, ``` [^1]: https://github.com/swaywm/sway/commit/2f7247e08a16610228067c9ec34d2b6d897e15fa --- src/bar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index 8a245ad1..8bb214c0 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -37,7 +37,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // .visible = true}}, {"hide", {// - .layer = bar_layer::TOP, + .layer = bar_layer::OVERLAY, .exclusive = false, .passthrough = false, .visible = true}}, @@ -49,7 +49,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // .visible = false}}, {"overlay", {// - .layer = bar_layer::TOP, + .layer = bar_layer::OVERLAY, .exclusive = false, .passthrough = true, .visible = true}}}; From f60c291b82181b6e871b4807625fbcc7818d3c36 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 14 Sep 2024 07:36:23 -0700 Subject: [PATCH 682/842] chore: update fmt wrap to 11.0.2 --- subprojects/fmt.wrap | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap index 42b61596..fd508477 100644 --- a/subprojects/fmt.wrap +++ b/subprojects/fmt.wrap @@ -1,13 +1,13 @@ [wrap-file] -directory = fmt-11.0.1 -source_url = https://github.com/fmtlib/fmt/archive/11.0.1.tar.gz -source_filename = fmt-11.0.1.tar.gz -source_hash = 7d009f7f89ac84c0a83f79ed602463d092fbf66763766a907c97fd02b100f5e9 -patch_filename = fmt_11.0.1-1_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/fmt_11.0.1-1/get_patch -patch_hash = 0a8b93d1ee6d84a82d3872a9bfb4c3977d8a53f7f484d42d1f7ed63ed496d549 -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_11.0.1-1/fmt-11.0.1.tar.gz -wrapdb_version = 11.0.1-1 +directory = fmt-11.0.2 +source_url = https://github.com/fmtlib/fmt/archive/11.0.2.tar.gz +source_filename = fmt-11.0.2.tar.gz +source_hash = 6cb1e6d37bdcb756dbbe59be438790db409cdb4868c66e888d5df9f13f7c027f +patch_filename = fmt_11.0.2-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/fmt_11.0.2-1/get_patch +patch_hash = 90c9e3b8e8f29713d40ca949f6f93ad115d78d7fb921064112bc6179e6427c5e +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_11.0.2-1/fmt-11.0.2.tar.gz +wrapdb_version = 11.0.2-1 [provide] fmt = fmt_dep From 0006e4713ae19776528038b3242ded05db884ba5 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 14 Sep 2024 07:37:37 -0700 Subject: [PATCH 683/842] fix(tray): revert ustring formatting changes This reverts commit a4d31ab10d1630cb9104c695d7b777ca12468904. --- src/modules/sni/item.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 8afb39fb..6c4ec8c0 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -104,11 +104,9 @@ void Item::proxyReady(Glib::RefPtr& result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, - std::string(err.what())); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); } catch (const std::exception& err) { - spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, - std::string(err.what())); + spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); } } @@ -126,15 +124,14 @@ ToolTip get_variant(const Glib::VariantBase& value) { result.text = get_variant(container.get_child(2)); auto description = get_variant(container.get_child(3)); if (!description.empty()) { - result.text = fmt::format("{}\n{}", std::string(result.text), std::string(description)); + result.text = fmt::format("{}\n{}", result.text, description); } return result; } void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { try { - spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, - std::string(name), get_variant(value)); + spdlog::trace("Set tray item property: {}.{} = {}", id.empty() ? bus_name : id, name, value); if (name == "Category") { category = get_variant(value); @@ -179,12 +176,10 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { } } catch (const Glib::Error& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, std::string(name), get_variant(value), - std::string(err.what())); + id.empty() ? bus_name : id, name, value, err.what()); } catch (const std::exception& err) { spdlog::warn("Failed to set tray item property: {}.{}, value = {}, err = {}", - id.empty() ? bus_name : id, std::string(name), get_variant(value), - std::string(err.what())); + id.empty() ? bus_name : id, name, value, err.what()); } } @@ -226,9 +221,9 @@ void Item::processUpdatedProperties(Glib::RefPtr& _result) { this->updateImage(); } catch (const Glib::Error& err) { - spdlog::warn("Failed to update properties: {}", std::string(err.what())); + spdlog::warn("Failed to update properties: {}", err.what()); } catch (const std::exception& err) { - spdlog::warn("Failed to update properties: {}", std::string(err.what())); + spdlog::warn("Failed to update properties: {}", err.what()); } update_pending_.clear(); } @@ -250,7 +245,7 @@ static const std::map> signal2props void Item::onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, const Glib::VariantContainerBase& arguments) { - spdlog::trace("Tray item '{}' got signal {}", id, std::string(signal_name)); + spdlog::trace("Tray item '{}' got signal {}", id, signal_name); auto changed = signal2props.find(signal_name.raw()); if (changed != signal2props.end()) { if (update_pending_.empty()) { From 7b23d586846e9cdb10f203f25a472bf945f4ed68 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Aug 2024 21:55:28 -0700 Subject: [PATCH 684/842] fix(bar): force commit for occluded surfaces All the mode or visibility changes require `wl_surface_commit` to be applied. gtk-layer-shell will attempt to force GTK to commit, but may fail if the surface has stopped receiving frame callbacks[^1]. Thus, we could get stuck in a state where the bar is hidden and unable to regain visibility. To address this, a new API has been added to gtk-layer-shell, `gtk_layer_try_force_commit`, which does `wl_surface_commit` with the necessary safety checks to avoid corrupting GTK internal state. Note: this change bumps gtk-layer-shell requirement to 0.9.0. [^1]: https://github.com/wmww/gtk-layer-shell/issues/185 --- meson.build | 2 +- src/bar.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 42f9da92..f4941c8f 100644 --- a/meson.build +++ b/meson.build @@ -106,7 +106,7 @@ if libsndio.found() endif endif -gtk_layer_shell = dependency('gtk-layer-shell-0', version: ['>=0.6.0'], +gtk_layer_shell = dependency('gtk-layer-shell-0', version: ['>=0.9.0'], default_options: ['introspection=false', 'vapi=false'], fallback: ['gtk-layer-shell', 'gtk_layer_shell']) systemd = dependency('systemd', required: get_option('systemd')) diff --git a/src/bar.cpp b/src/bar.cpp index 8bb214c0..ebe23367 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -132,6 +132,7 @@ void from_json(const Json::Value& j, std::map& m) { waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) : output(w_output), config(w_config), + surface(nullptr), window{Gtk::WindowType::WINDOW_TOPLEVEL}, x_global(0), y_global(0), @@ -339,6 +340,13 @@ void waybar::Bar::setMode(const struct bar_mode& mode) { window.get_style_context()->add_class("hidden"); window.set_opacity(0); } + /* + * All the changes above require `wl_surface_commit`. + * gtk-layer-shell schedules a commit on the next frame event in GTK, but this could fail in + * certain scenarios, such as fully occluded bar. + */ + gtk_layer_try_force_commit(gtk_window); + wl_display_flush(Client::inst()->wl_display); } void waybar::Bar::setPassThrough(bool passthrough) { From 45fec7bcbbb475e6ab9afa32bc57b8409dc4ed86 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 17 Aug 2024 22:40:56 -0700 Subject: [PATCH 685/842] Revert "change layer for mode invisible to nullopt" Previous commit should have a better workaround for #3211. This reverts commit b61ea62732a51e564ded6e7d4d37cd4796b014f2. --- include/bar.hpp | 2 +- src/bar.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 936bc749..9b407abf 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -42,7 +42,7 @@ struct bar_margins { }; struct bar_mode { - std::optional layer; + bar_layer layer; bool exclusive; bool passthrough; bool visible; diff --git a/src/bar.cpp b/src/bar.cpp index ebe23367..5068e90d 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -43,7 +43,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { // .visible = true}}, {"invisible", {// - .layer = std::nullopt, + .layer = bar_layer::BOTTOM, .exclusive = false, .passthrough = true, .visible = false}}, @@ -59,7 +59,7 @@ const std::string Bar::MODE_INVISIBLE = "invisible"; const std::string_view DEFAULT_BAR_ID = "bar-0"; /* Deserializer for enum bar_layer */ -void from_json(const Json::Value& j, std::optional& l) { +void from_json(const Json::Value& j, bar_layer& l) { if (j == "bottom") { l = bar_layer::BOTTOM; } else if (j == "top") { @@ -317,13 +317,13 @@ void waybar::Bar::setMode(const std::string& mode) { void waybar::Bar::setMode(const struct bar_mode& mode) { auto* gtk_window = window.gobj(); - if (mode.layer == bar_layer::BOTTOM) { - gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_BOTTOM); - } else if (mode.layer == bar_layer::TOP) { - gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_TOP); + auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM; + if (mode.layer == bar_layer::TOP) { + layer = GTK_LAYER_SHELL_LAYER_TOP; } else if (mode.layer == bar_layer::OVERLAY) { - gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_OVERLAY); + layer = GTK_LAYER_SHELL_LAYER_OVERLAY; } + gtk_layer_set_layer(gtk_window, layer); if (mode.exclusive) { gtk_layer_auto_exclusive_zone_enable(gtk_window); From e0be3ac178300982f1507218026fe450f898abf6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 15 Sep 2024 05:59:33 +0000 Subject: [PATCH 686/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/71e91c409d1e654808b2621f28a327acfdad8dc2?narHash=sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w%3D' (2024-08-28) → 'github:NixOS/nixpkgs/4f807e8940284ad7925ebd0a0993d2a1791acb2f?narHash=sha256-IiA3jfbR7K/B5%2B9byVi9BZGWTD4VSbWe8VLpp9B/iYk%3D' (2024-09-11) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 9bd73acc..15bf9f09 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1724819573, - "narHash": "sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w=", + "lastModified": 1726062873, + "narHash": "sha256-IiA3jfbR7K/B5+9byVi9BZGWTD4VSbWe8VLpp9B/iYk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "71e91c409d1e654808b2621f28a327acfdad8dc2", + "rev": "4f807e8940284ad7925ebd0a0993d2a1791acb2f", "type": "github" }, "original": { From 085a1ede97bef27f08adb17788be901a8e29a60f Mon Sep 17 00:00:00 2001 From: Kainoa Kanter Date: Sun, 15 Sep 2024 21:28:15 -0300 Subject: [PATCH 687/842] fix: use app_identifier itself in AAppIconLabel if it's an absolute path --- src/AAppIconLabel.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index fda5f9fd..dc22f20e 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -154,6 +154,16 @@ void AAppIconLabel::updateAppIcon() { update_app_icon_ = false; if (app_icon_name_.empty()) { image_.set_visible(false); + } + else if (app_icon_name_.front() == '/') { + auto pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_); + int scaled_icon_size = app_icon_size_ * image_.get_scale_factor(); + pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size); + + auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), + image_.get_window()); + image_.set(surface); + image_.set_visible(true); } else { image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); image_.set_visible(true); From ff66b5dd57408158f2eea84eba64269d90b92c5b Mon Sep 17 00:00:00 2001 From: Sonter <108224581+S0nter@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:47:10 +0000 Subject: [PATCH 688/842] Update waybar-cava man page --- man/waybar-cava.5.scd | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/man/waybar-cava.5.scd b/man/waybar-cava.5.scd index 2a7e8f67..7825c38a 100644 --- a/man/waybar-cava.5.scd +++ b/man/waybar-cava.5.scd @@ -64,6 +64,10 @@ libcava lives in: :[ bool :[ false :[ Hides the widget if no input (after sleep_timer elapsed) +|[ *format_silent* +:[ string +:[ +:[ Widget's text after sleep_timer elapsed (hide_on_silence has to be false) |[ *method* :[ string :[ pulse @@ -196,3 +200,8 @@ In case when cava releases new version and you're wanna get it, it should be rai } }, ``` +# STYLE + +- *#cava* +- *#cava.silent* Applied after no sound has been detected for sleep_timer seconds +- *#cava.updated* Applied when a new frame is shown From 3bb3c2d23fdecebdaa2da80cbb17fcc3beec32da Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Tue, 17 Sep 2024 00:13:23 +0200 Subject: [PATCH 689/842] fix(custom): stop mixing manual and automatic arg indexing The current documentation for the custom module suggests mixing manual (`{icon}`) and automatic (`{}`) indexing of format args. Newer versions of the fmt library seem to not support this anymore (see issue #3605). This commit introduces a name for the `text` output of the script, so that `{text}` can now be used instead of `{}` in the configuration. --- src/modules/custom.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 20d8d934..bc5df76f 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -159,7 +159,9 @@ auto waybar::modules::Custom::update() -> void { parseOutputRaw(); } - auto str = fmt::format(fmt::runtime(format_), text_, fmt::arg("alt", alt_), + auto str = fmt::format(fmt::runtime(format_), + fmt::arg("text", text_), + fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { @@ -169,7 +171,9 @@ auto waybar::modules::Custom::update() -> void { if (tooltipEnabled()) { if (tooltip_format_enabled_) { auto tooltip = config_["tooltip-format"].asString(); - tooltip = fmt::format(fmt::runtime(tooltip), text_, fmt::arg("alt", alt_), + tooltip = fmt::format(fmt::runtime(tooltip), + fmt::arg("text", text_), + fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); label_.set_tooltip_markup(tooltip); From 83992d29a063361ed89e41f100d8ddbbd5456e27 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Tue, 17 Sep 2024 00:39:33 +0200 Subject: [PATCH 690/842] Fix formatting --- src/modules/custom.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index bc5df76f..90c54128 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -159,9 +159,7 @@ auto waybar::modules::Custom::update() -> void { parseOutputRaw(); } - auto str = fmt::format(fmt::runtime(format_), - fmt::arg("text", text_), - fmt::arg("alt", alt_), + auto str = fmt::format(fmt::runtime(format_), fmt::arg("text", text_), fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { @@ -171,10 +169,8 @@ auto waybar::modules::Custom::update() -> void { if (tooltipEnabled()) { if (tooltip_format_enabled_) { auto tooltip = config_["tooltip-format"].asString(); - tooltip = fmt::format(fmt::runtime(tooltip), - fmt::arg("text", text_), - fmt::arg("alt", alt_), - fmt::arg("icon", getIcon(percentage_, alt_)), + tooltip = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_), + fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); label_.set_tooltip_markup(tooltip); } else if (text_ == tooltip_) { From de170fa57989a11867b179cce222a3a01b9bbaa8 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Tue, 17 Sep 2024 02:56:38 +0200 Subject: [PATCH 691/842] Update documentation --- man/waybar-custom.5.scd | 16 ++++++++-------- resources/config.jsonc | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index aba1c18f..6b96d2a4 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -55,8 +55,8 @@ Addressed by *custom/* *format*: ++ typeof: string ++ - default: {} ++ - The format, how information should be displayed. On {} data gets inserted. + default: {text} ++ + The format, how information should be displayed. On {text} data gets inserted. *format-icons*: ++ typeof: array ++ @@ -160,7 +160,7 @@ $text\\n$tooltip\\n$class* # FORMAT REPLACEMENTS -*{}*: Output of the script. +*{text}*: Output of the script. *{percentage}* Percentage which can be set via a json return type. @@ -172,7 +172,7 @@ $text\\n$tooltip\\n$class* ``` "custom/spotify": { - "format": " {}", + "format": " {text}", "max-length": 40, "interval": 30, // Remove this if your script is endless and write in loop "exec": "$HOME/.config/waybar/mediaplayer.sh 2> /dev/null", // Script in resources folder @@ -185,7 +185,7 @@ $text\\n$tooltip\\n$class* ``` "custom/mpd": { - "format": "♪ {}", + "format": "♪ {text}", //"max-length": 15, "interval": 10, "exec": "mpc current", @@ -199,7 +199,7 @@ $text\\n$tooltip\\n$class* ``` "custom/cmus": { - "format": "♪ {}", + "format": "♪ {text}", //"max-length": 15, "interval": 10, "exec": "cmus-remote -C \"format_print '%a - %t'\"", // artist - title @@ -214,7 +214,7 @@ $text\\n$tooltip\\n$class* ``` "custom/pacman": { - "format": "{} ", + "format": "{text} ", "interval": "once", "exec": "pacman_packages", "on-click": "update-system", @@ -226,7 +226,7 @@ $text\\n$tooltip\\n$class* ``` "custom/pacman": { - "format": "{} ", + "format": "{text} ", "interval": 3600, // every hour "exec": "checkupdates | wc -l", // # of updates "exec-if": "exit 0", // always run; consider advanced run conditions diff --git a/resources/config.jsonc b/resources/config.jsonc index 7e0771f5..6ac1aa50 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -189,7 +189,7 @@ "on-click": "pavucontrol" }, "custom/media": { - "format": "{icon} {}", + "format": "{icon} {text}", "return-type": "json", "max-length": 40, "format-icons": { From 254111ff91c68816b35d426ab6977630e26a5a65 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Wed, 18 Sep 2024 17:28:58 +0200 Subject: [PATCH 692/842] Improve error message for mixed arg indexing in format string --- src/modules/custom.cpp | 76 +++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 90c54128..a4255521 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -159,43 +159,51 @@ auto waybar::modules::Custom::update() -> void { parseOutputRaw(); } - auto str = fmt::format(fmt::runtime(format_), fmt::arg("text", text_), fmt::arg("alt", alt_), - fmt::arg("icon", getIcon(percentage_, alt_)), - fmt::arg("percentage", percentage_)); - if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { - event_box_.hide(); - } else { - label_.set_markup(str); - if (tooltipEnabled()) { - if (tooltip_format_enabled_) { - auto tooltip = config_["tooltip-format"].asString(); - tooltip = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_), - fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), - fmt::arg("percentage", percentage_)); - label_.set_tooltip_markup(tooltip); - } else if (text_ == tooltip_) { - if (label_.get_tooltip_markup() != str) { - label_.set_tooltip_markup(str); - } - } else { - if (label_.get_tooltip_markup() != tooltip_) { - label_.set_tooltip_markup(tooltip_); + try { + auto str = fmt::format(fmt::runtime(format_), fmt::arg("text", text_), fmt::arg("alt", alt_), + fmt::arg("icon", getIcon(percentage_, alt_)), + fmt::arg("percentage", percentage_)); + if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) { + event_box_.hide(); + } else { + label_.set_markup(str); + if (tooltipEnabled()) { + if (tooltip_format_enabled_) { + auto tooltip = config_["tooltip-format"].asString(); + tooltip = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_), + fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), + fmt::arg("percentage", percentage_)); + label_.set_tooltip_markup(tooltip); + } else if (text_ == tooltip_) { + if (label_.get_tooltip_markup() != str) { + label_.set_tooltip_markup(str); + } + } else { + if (label_.get_tooltip_markup() != tooltip_) { + label_.set_tooltip_markup(tooltip_); + } } } + auto style = label_.get_style_context(); + auto classes = style->list_classes(); + for (auto const& c : classes) { + if (c == id_) continue; + style->remove_class(c); + } + for (auto const& c : class_) { + style->add_class(c); + } + style->add_class("flat"); + style->add_class("text-button"); + style->add_class(MODULE_CLASS); + event_box_.show(); } - auto style = label_.get_style_context(); - auto classes = style->list_classes(); - for (auto const& c : classes) { - if (c == id_) continue; - style->remove_class(c); - } - for (auto const& c : class_) { - style->add_class(c); - } - style->add_class("flat"); - style->add_class("text-button"); - style->add_class(MODULE_CLASS); - event_box_.show(); + } catch (const fmt::format_error& e) { + if (std::strcmp(e.what(), "cannot switch from manual to automatic argument indexing") != 0) + throw; + + throw fmt::format_error("mixing manual and automatic argument indexing is no longer supported; " + "try replacing \"{}\" with \"{text}\" in your format specifier"); } } // Call parent update From a3e7031fe25421c1f64d4b88ce2298bbd6dd2324 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Wed, 18 Sep 2024 17:30:55 +0200 Subject: [PATCH 693/842] Fix formatting --- src/modules/custom.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index a4255521..e023aaf6 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -170,9 +170,9 @@ auto waybar::modules::Custom::update() -> void { if (tooltipEnabled()) { if (tooltip_format_enabled_) { auto tooltip = config_["tooltip-format"].asString(); - tooltip = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_), - fmt::arg("alt", alt_), fmt::arg("icon", getIcon(percentage_, alt_)), - fmt::arg("percentage", percentage_)); + tooltip = fmt::format( + fmt::runtime(tooltip), fmt::arg("text", text_), fmt::arg("alt", alt_), + fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); label_.set_tooltip_markup(tooltip); } else if (text_ == tooltip_) { if (label_.get_tooltip_markup() != str) { @@ -202,8 +202,9 @@ auto waybar::modules::Custom::update() -> void { if (std::strcmp(e.what(), "cannot switch from manual to automatic argument indexing") != 0) throw; - throw fmt::format_error("mixing manual and automatic argument indexing is no longer supported; " - "try replacing \"{}\" with \"{text}\" in your format specifier"); + throw fmt::format_error( + "mixing manual and automatic argument indexing is no longer supported; " + "try replacing \"{}\" with \"{text}\" in your format specifier"); } } // Call parent update From 21af48fdc95b21ad067e627abb96655757c48b36 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 19 Sep 2024 17:31:07 +0200 Subject: [PATCH 694/842] chore: lint --- src/AAppIconLabel.cpp | 3 +-- src/modules/cava.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp index dc22f20e..3f47eff1 100644 --- a/src/AAppIconLabel.cpp +++ b/src/AAppIconLabel.cpp @@ -154,8 +154,7 @@ void AAppIconLabel::updateAppIcon() { update_app_icon_ = false; if (app_icon_name_.empty()) { image_.set_visible(false); - } - else if (app_icon_name_.front() == '/') { + } else if (app_icon_name_.front() == '/') { auto pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_); int scaled_icon_size = app_icon_size_ * image_.get_scale_factor(); pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size); diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index f81bf799..f16d3f63 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -175,12 +175,14 @@ auto waybar::modules::Cava::update() -> void { ALabel::update(); label_.get_style_context()->add_class("updated"); } - + label_.get_style_context()->remove_class("silent"); } else { upThreadDelay(frame_time_milsec_, suspend_silence_delay_); - if (hide_on_silence_) label_.hide(); - else if (config_["format_silent"].isString()) label_.set_markup(format_silent_); + if (hide_on_silence_) + label_.hide(); + else if (config_["format_silent"].isString()) + label_.set_markup(format_silent_); label_.get_style_context()->add_class("silent"); label_.get_style_context()->remove_class("updated"); From dedee8cd1456616e145d6ceb31788b28e4863918 Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Mon, 22 Jul 2024 12:17:21 +0200 Subject: [PATCH 695/842] pulseaudio: show correct sink volume on default output changes on sinkInfo callbacks, the default sink now has highest priority. That fixes an issue that the volume indicator is not updated when the changes the default output to another devices. added PA_SINK_IDLE as valid state. PA_SINK_RUNNING is only true if any sound output is happening on sink switch. Indicator should also update when no sound is being played. --- include/util/audio_backend.hpp | 2 ++ src/modules/pulseaudio.cpp | 7 +++++++ src/util/audio_backend.cpp | 28 +++++++++++++++++++++++++--- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/include/util/audio_backend.hpp b/include/util/audio_backend.hpp index 2f53103e..3737ae26 100644 --- a/include/util/audio_backend.hpp +++ b/include/util/audio_backend.hpp @@ -38,6 +38,8 @@ class AudioBackend { std::string desc_; std::string monitor_; std::string current_sink_name_; + std::string default_sink_name; + bool default_sink_running_; bool current_sink_running_; // SOURCE uint32_t source_idx_{0}; diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 255ca571..9d3f1862 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -1,4 +1,6 @@ #include "modules/pulseaudio.hpp" +#include +#include waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config) : ALabel(config, "pulseaudio", id, "{volume}%") { @@ -52,6 +54,7 @@ const std::vector waybar::modules::Pulseaudio::getPulseIcon() const std::string nameLC = backend->getSinkPortName() + backend->getFormFactor(); std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { + spdlog::trace("Port: {}", nameLC); if (nameLC.find(port) != std::string::npos) { if (sink_muted) { res.emplace_back(port + "-muted"); @@ -63,6 +66,10 @@ const std::vector waybar::modules::Pulseaudio::getPulseIcon() const if (sink_muted) { res.emplace_back("default-muted"); } + spdlog::trace("Ports:"); + for (auto const &item : res) { + spdlog::trace(" {}", item); + } return res; } diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 3d90b6d5..cfff1818 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -1,14 +1,18 @@ #include "util/audio_backend.hpp" #include +#include #include +#include #include #include #include #include +#include #include #include +#include namespace waybar::util { @@ -132,6 +136,7 @@ void AudioBackend::volumeModifyCb(pa_context *c, int success, void *data) { } } + /* * Called when the requested sink information is ready. */ @@ -139,6 +144,11 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i void *data) { if (i == nullptr) return; + spdlog::trace("Callback start"); + auto running = i->state == PA_SINK_RUNNING; + auto idle = i->state == PA_SINK_IDLE; + spdlog::trace("Sink name {} Running:[{}] Idle:[{}]", i->name, running,idle ); + auto *backend = static_cast(data); if (!backend->ignored_sinks_.empty()) { @@ -155,11 +165,22 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i } } - if (backend->current_sink_name_ == i->name) { - backend->current_sink_running_ = i->state == PA_SINK_RUNNING; + backend->default_sink_running_ = + backend->default_sink_name == i->name; + + if ( i->name != backend->default_sink_name) { + return; } - if (!backend->current_sink_running_ && i->state == PA_SINK_RUNNING) { + if (backend->current_sink_name_ == i->name) { + backend->current_sink_running_ = + (i->state == PA_SINK_RUNNING || + i->state == PA_SINK_IDLE); + } + + if (!backend->current_sink_running_ && ( + i->state == PA_SINK_RUNNING || + i->state == PA_SINK_IDLE)) { backend->current_sink_name_ = i->name; backend->current_sink_running_ = true; } @@ -207,6 +228,7 @@ void AudioBackend::sourceInfoCb(pa_context * /*context*/, const pa_source_info * void AudioBackend::serverInfoCb(pa_context *context, const pa_server_info *i, void *data) { auto *backend = static_cast(data); backend->current_sink_name_ = i->default_sink_name; + backend->default_sink_name = i->default_sink_name; backend->default_source_name_ = i->default_source_name; pa_context_get_sink_info_list(context, sinkInfoCb, data); From 9c47b2e9dddcb5fe58825cce69ff3fe785ad857f Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Mon, 22 Jul 2024 12:37:17 +0200 Subject: [PATCH 696/842] removed debug logging --- src/modules/pulseaudio.cpp | 7 ------- src/util/audio_backend.cpp | 3 --- 2 files changed, 10 deletions(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 9d3f1862..255ca571 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -1,6 +1,4 @@ #include "modules/pulseaudio.hpp" -#include -#include waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config) : ALabel(config, "pulseaudio", id, "{volume}%") { @@ -54,7 +52,6 @@ const std::vector waybar::modules::Pulseaudio::getPulseIcon() const std::string nameLC = backend->getSinkPortName() + backend->getFormFactor(); std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { - spdlog::trace("Port: {}", nameLC); if (nameLC.find(port) != std::string::npos) { if (sink_muted) { res.emplace_back(port + "-muted"); @@ -66,10 +63,6 @@ const std::vector waybar::modules::Pulseaudio::getPulseIcon() const if (sink_muted) { res.emplace_back("default-muted"); } - spdlog::trace("Ports:"); - for (auto const &item : res) { - spdlog::trace(" {}", item); - } return res; } diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index cfff1818..d084f94a 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -136,7 +135,6 @@ void AudioBackend::volumeModifyCb(pa_context *c, int success, void *data) { } } - /* * Called when the requested sink information is ready. */ @@ -144,7 +142,6 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i void *data) { if (i == nullptr) return; - spdlog::trace("Callback start"); auto running = i->state == PA_SINK_RUNNING; auto idle = i->state == PA_SINK_IDLE; spdlog::trace("Sink name {} Running:[{}] Idle:[{}]", i->name, running,idle ); From 8b1d73690d025379937bb63c063ecf088848f800 Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Mon, 22 Jul 2024 12:40:30 +0200 Subject: [PATCH 697/842] added running check to default sink return condition --- src/util/audio_backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index d084f94a..5f1cf2f2 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -165,7 +165,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i backend->default_sink_running_ = backend->default_sink_name == i->name; - if ( i->name != backend->default_sink_name) { + if ( i->name != backend->default_sink_name && !backend->default_sink_running_) { return; } From d6bfeb5a44996fe21fafbc93cf450e5a119a9644 Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Mon, 22 Jul 2024 13:05:25 +0200 Subject: [PATCH 698/842] added is running condition to default_sink_is_running check --- src/util/audio_backend.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 5f1cf2f2..d2c2928d 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -162,8 +162,9 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i } } - backend->default_sink_running_ = - backend->default_sink_name == i->name; + backend->default_sink_running_ = backend->default_sink_name == i->name + && (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE); + if ( i->name != backend->default_sink_name && !backend->default_sink_running_) { return; From 951b89ffcb1cdb0d8ccc34fbe13b7bd42971e7ad Mon Sep 17 00:00:00 2001 From: Findus Date: Tue, 23 Jul 2024 15:57:32 +0200 Subject: [PATCH 699/842] Update clang-format.yml workflow dispatch to debug failing workflow manually --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 4a774dbd..89793f5e 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,6 +1,6 @@ name: clang-format -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] concurrency: group: ${{ github.workflow }}-format-${{ github.event.pull_request.number || github.ref }} From e3095c6d1d1647d46b730007ca34c8f8b2ebd452 Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Tue, 23 Jul 2024 16:01:24 +0200 Subject: [PATCH 700/842] clang-format --- src/util/audio_backend.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index d2c2928d..73aac148 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -6,12 +6,12 @@ #include #include #include +#include #include #include #include #include -#include namespace waybar::util { @@ -144,7 +144,7 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i auto running = i->state == PA_SINK_RUNNING; auto idle = i->state == PA_SINK_IDLE; - spdlog::trace("Sink name {} Running:[{}] Idle:[{}]", i->name, running,idle ); + spdlog::trace("Sink name {} Running:[{}] Idle:[{}]", i->name, running, idle); auto *backend = static_cast(data); @@ -162,23 +162,19 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i } } - backend->default_sink_running_ = backend->default_sink_name == i->name - && (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE); + backend->default_sink_running_ = backend->default_sink_name == i->name && + (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE); - - if ( i->name != backend->default_sink_name && !backend->default_sink_running_) { + if (i->name != backend->default_sink_name && !backend->default_sink_running_) { return; } if (backend->current_sink_name_ == i->name) { - backend->current_sink_running_ = - (i->state == PA_SINK_RUNNING || - i->state == PA_SINK_IDLE); + backend->current_sink_running_ = (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE); } - if (!backend->current_sink_running_ && ( - i->state == PA_SINK_RUNNING || - i->state == PA_SINK_IDLE)) { + if (!backend->current_sink_running_ && + (i->state == PA_SINK_RUNNING || i->state == PA_SINK_IDLE)) { backend->current_sink_name_ = i->name; backend->current_sink_running_ = true; } From 57156bce7e1239349661cbcf6c96a73a670d36a3 Mon Sep 17 00:00:00 2001 From: Philipp Hentschel Date: Tue, 23 Jul 2024 16:06:01 +0200 Subject: [PATCH 701/842] removed manual flag from clang format again --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 89793f5e..4a774dbd 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,6 +1,6 @@ name: clang-format -on: [push, pull_request, workflow_dispatch] +on: [push, pull_request] concurrency: group: ${{ github.workflow }}-format-${{ github.event.pull_request.number || github.ref }} From 773b1d4806b01e426516e70f673b949175159d95 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Mon, 23 Sep 2024 15:51:01 +0300 Subject: [PATCH 702/842] Default value for cldYearShift_ = 1900/01/01 Signed-off-by: Viktar Lukashonak --- src/modules/clock.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index db2979eb..0accdd0c 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -24,6 +24,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) m_tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""}, m_tooltip_{new Gtk::Label()}, cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, + cldYearShift_{1900y / 1 / 1}, tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, tzCurrIdx_{0}, ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { From b4e97eb2f49ef2ca111896bf43846b5b24be8361 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Mon, 23 Sep 2024 16:06:50 +0300 Subject: [PATCH 703/842] FreeBSD format fix Signed-off-by: Viktar Lukashonak --- src/modules/clock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 0accdd0c..7f5a4d55 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -24,7 +24,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) m_tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""}, m_tooltip_{new Gtk::Label()}, cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, - cldYearShift_{1900y / 1 / 1}, + cldYearShift_{January / 1 / 1900}, tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, tzCurrIdx_{0}, ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { From 71a53eb79d78a98d6388678c29bf46baf32ae25c Mon Sep 17 00:00:00 2001 From: Rowan Leeder Date: Wed, 25 Sep 2024 03:16:14 +1000 Subject: [PATCH 704/842] Issue-3092 Add source support to wireplumber module - Adds microphone support etc to the wireplumber module. The existing module hardcodes the selected node type to "Audio/Sink". This feature allows the user to override this via `"node-type": "Audio/Source"`. - Unlike the pulseaudio module, this change does not try to see the module manage both input and output. The same effect can be achieved by running two instances of the wireplumber module. This approach: - Works around some of the complexity overhead that seem to have caused similar PRs to stall. - Using separate module instances also allows both the microphone and speaker levels to be controlled with a scroll wheel. This is something a unified module like pulseaudio struggles with. - Similarly, separate instances allows the source volume level to be exposed as the state. Ie- the linear-gradient css patterns can be applied to both input and output. --- include/modules/wireplumber.hpp | 3 ++- man/waybar-wireplumber.5.scd | 27 +++++++++++++++++++++++++++ src/modules/wireplumber.cpp | 17 +++++++++++------ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/include/modules/wireplumber.hpp b/include/modules/wireplumber.hpp index 6255b95f..eb39653a 100644 --- a/include/modules/wireplumber.hpp +++ b/include/modules/wireplumber.hpp @@ -18,7 +18,7 @@ class Wireplumber : public ALabel { private: void asyncLoadRequiredApiModules(); - void prepare(); + void prepare(waybar::modules::Wireplumber* self); void activatePlugins(); static void updateVolume(waybar::modules::Wireplumber* self, uint32_t id); static void updateNodeName(waybar::modules::Wireplumber* self, uint32_t id); @@ -44,6 +44,7 @@ class Wireplumber : public ALabel { double min_step_; uint32_t node_id_{0}; std::string node_name_; + gchar* type_; }; } // namespace waybar::modules diff --git a/man/waybar-wireplumber.5.scd b/man/waybar-wireplumber.5.scd index 9c26ebaf..ae78f184 100644 --- a/man/waybar-wireplumber.5.scd +++ b/man/waybar-wireplumber.5.scd @@ -19,6 +19,11 @@ The *wireplumber* module displays the current volume reported by WirePlumber. typeof: string ++ This format is used when the sound is muted. +*node-type*: ++ + typeof: string ++ + default: *Audio/Sink* ++ + The WirePlumber node type to attach to. Use *Audio/Source* to manage microphones etc. + *tooltip*: ++ typeof: bool ++ default: *true* ++ @@ -108,6 +113,8 @@ The *wireplumber* module displays the current volume reported by WirePlumber. # EXAMPLES +## Basic: + ``` "wireplumber": { "format": "{volume}%", @@ -116,6 +123,26 @@ The *wireplumber* module displays the current volume reported by WirePlumber. } ``` +## Separate Sink and Source Widgets + +``` +"wireplumber#sink": { + "format": "{volume}% {icon}", + "format-muted": "", + "format-icons": ["", "", ""], + "on-click": "helvum", + "on-click-right": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle", + "scroll-step": 5 +}, +"wireplumber#source": { + "node-type": "Audio/Source", + "format": "{volume}% ", + "format-muted": "", + "on-click-right": "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle", + "scroll-step": 5 +} +``` + # STYLE - *#wireplumber* diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index eddc3e6b..d6d1e594 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -16,13 +16,17 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val muted_(false), volume_(0.0), min_step_(0.0), - node_id_(0) { + node_id_(0), + type_(nullptr) { wp_init(WP_INIT_PIPEWIRE); wp_core_ = wp_core_new(nullptr, nullptr, nullptr); apis_ = g_ptr_array_new_with_free_func(g_object_unref); om_ = wp_object_manager_new(); - prepare(); + type_ = g_strdup(config_["node-type"].isString() ? config_["node-type"].asString().c_str() + : "Audio/Sink"); + + prepare(this); spdlog::debug("[{}]: connecting to pipewire...", name_); @@ -46,6 +50,7 @@ waybar::modules::Wireplumber::~Wireplumber() { g_clear_object(&mixer_api_); g_clear_object(&def_nodes_api_); g_free(default_node_name_); + g_free(type_); } void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* self, uint32_t id) { @@ -138,7 +143,7 @@ void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wir spdlog::debug("[{}]: (onDefaultNodesApiChanged)", self->name_); uint32_t defaultNodeId; - g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &defaultNodeId); + g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", self->type_, &defaultNodeId); if (!isValidNodeId(defaultNodeId)) { spdlog::warn("[{}]: '{}' is not a valid node ID. Ignoring node change.", self->name_, @@ -200,9 +205,9 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir throw std::runtime_error("Mixer api is not loaded\n"); } - g_signal_emit_by_name(self->def_nodes_api_, "get-default-configured-node-name", "Audio/Sink", + g_signal_emit_by_name(self->def_nodes_api_, "get-default-configured-node-name", self->type_, &self->default_node_name_); - g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &self->node_id_); + g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", self->type_, &self->node_id_); if (self->default_node_name_ != nullptr) { spdlog::debug("[{}]: (onObjectManagerInstalled) - default configured node name: {} and id: {}", @@ -246,7 +251,7 @@ void waybar::modules::Wireplumber::activatePlugins() { void waybar::modules::Wireplumber::prepare() { spdlog::debug("[{}]: preparing object manager", name_); wp_object_manager_add_interest(om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", - "=s", "Audio/Sink", nullptr); + "=s", self->type_, nullptr); } void waybar::modules::Wireplumber::onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res, From 2dfef1c213fe008e3520b168ba96f59db6d55d58 Mon Sep 17 00:00:00 2001 From: Rowan Leeder Date: Wed, 25 Sep 2024 04:03:31 +1000 Subject: [PATCH 705/842] Issue-3092 Add node type to wireplumber logs - The module only fetches nodes for "node-type". This causes the 'onMixerChanged' log to spam whenever two or more wireplumber modules were registered on different nodes. To reduce this the unknown node warning will now only print if the node is not the focus of any current module. --- include/modules/wireplumber.hpp | 2 + src/modules/wireplumber.cpp | 81 ++++++++++++++++++++------------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/include/modules/wireplumber.hpp b/include/modules/wireplumber.hpp index eb39653a..e0033e8a 100644 --- a/include/modules/wireplumber.hpp +++ b/include/modules/wireplumber.hpp @@ -32,6 +32,8 @@ class Wireplumber : public ALabel { bool handleScroll(GdkEventScroll* e) override; + static std::list modules; + WpCore* wp_core_; GPtrArray* apis_; WpObjectManager* om_; diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index d6d1e594..c25b3b51 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -4,6 +4,8 @@ bool isValidNodeId(uint32_t id) { return id > 0 && id < G_MAXUINT32; } +std::list waybar::modules::Wireplumber::modules; + waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Value& config) : ALabel(config, "wireplumber", id, "{volume}%"), wp_core_(nullptr), @@ -18,6 +20,8 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val min_step_(0.0), node_id_(0), type_(nullptr) { + waybar::modules::Wireplumber::modules.push_back(this); + wp_init(WP_INIT_PIPEWIRE); wp_core_ = wp_core_new(nullptr, nullptr, nullptr); apis_ = g_ptr_array_new_with_free_func(g_object_unref); @@ -28,14 +32,14 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val prepare(this); - spdlog::debug("[{}]: connecting to pipewire...", name_); + spdlog::debug("[{}]: connecting to pipewire: '{}'...", name_, type_); if (wp_core_connect(wp_core_) == 0) { - spdlog::error("[{}]: Could not connect to PipeWire", name_); + spdlog::error("[{}]: Could not connect to PipeWire: '{}'", name_, type_); throw std::runtime_error("Could not connect to PipeWire\n"); } - spdlog::debug("[{}]: connected!", name_); + spdlog::debug("[{}]: {} connected!", name_, type_); g_signal_connect_swapped(om_, "installed", (GCallback)onObjectManagerInstalled, this); @@ -43,6 +47,7 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val } waybar::modules::Wireplumber::~Wireplumber() { + waybar::modules::Wireplumber::modules.remove(this); wp_core_disconnect(wp_core_); g_clear_pointer(&apis_, g_ptr_array_unref); g_clear_object(&om_); @@ -54,10 +59,11 @@ waybar::modules::Wireplumber::~Wireplumber() { } void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* self, uint32_t id) { - spdlog::debug("[{}]: updating node name with node.id {}", self->name_, id); + spdlog::debug("[{}]: updating '{}' node name with node.id {}", self->name_, self->type_, id); if (!isValidNodeId(id)) { - spdlog::warn("[{}]: '{}' is not a valid node ID. Ignoring node name update.", self->name_, id); + spdlog::warn("[{}]: '{}' is not a valid node ID. Ignoring '{}' node name update.", self->name_, + id, self->type_); return; } @@ -85,7 +91,7 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* self->node_name_ = nick != nullptr ? nick : description != nullptr ? description : "Unknown node name"; - spdlog::debug("[{}]: Updating node name to: {}", self->name_, self->node_name_); + spdlog::debug("[{}]: Updating '{}' node name to: {}", self->name_, self->type_, self->node_name_); } void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self, uint32_t id) { @@ -93,7 +99,8 @@ void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* se GVariant* variant = nullptr; if (!isValidNodeId(id)) { - spdlog::error("[{}]: '{}' is not a valid node ID. Ignoring volume update.", self->name_, id); + spdlog::error("[{}]: '{}' is not a valid '{}' node ID. Ignoring volume update.", self->name_, + id, self->type_); return; } @@ -114,13 +121,22 @@ void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* se } void waybar::modules::Wireplumber::onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id) { - spdlog::debug("[{}]: (onMixerChanged) - id: {}", self->name_, id); - g_autoptr(WpNode) node = static_cast(wp_object_manager_lookup( self->om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=u", id, nullptr)); if (node == nullptr) { - spdlog::warn("[{}]: (onMixerChanged) - Object with id {} not found", self->name_, id); + // log a warning only if no other widget is targeting the id. + // this reduces log spam when multiple instances of the module are used on different node types. + if (id != self->node_id_) { + for (auto const& module : waybar::modules::Wireplumber::modules) { + if (module->node_id_ == id) { + return; + } + } + } + + spdlog::warn("[{}]: (onMixerChanged: {}) - Object with id {} not found", self->name_, + self->type_, id); return; } @@ -128,26 +144,27 @@ void waybar::modules::Wireplumber::onMixerChanged(waybar::modules::Wireplumber* if (self->node_id_ != id) { spdlog::debug( - "[{}]: (onMixerChanged) - ignoring mixer update for node: id: {}, name: {} as it is not " - "the default node: {} with id: {}", - self->name_, id, name, self->default_node_name_, self->node_id_); + "[{}]: (onMixerChanged: {}) - ignoring mixer update for node: id: {}, name: {} as it is " + "not the default node: {} with id: {}", + self->name_, self->type_, id, name, self->default_node_name_, self->node_id_); return; } - spdlog::debug("[{}]: (onMixerChanged) - Need to update volume for node with id {} and name {}", - self->name_, id, name); + spdlog::debug( + "[{}]: (onMixerChanged: {}) - Need to update volume for node with id {} and name {}", + self->name_, self->type_, id, name); updateVolume(self, id); } void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wireplumber* self) { - spdlog::debug("[{}]: (onDefaultNodesApiChanged)", self->name_); + spdlog::debug("[{}]: (onDefaultNodesApiChanged: {})", self->name_, self->type_); uint32_t defaultNodeId; g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", self->type_, &defaultNodeId); if (!isValidNodeId(defaultNodeId)) { - spdlog::warn("[{}]: '{}' is not a valid node ID. Ignoring node change.", self->name_, - defaultNodeId); + spdlog::warn("[{}]: '{}' is not a valid node ID. Ignoring '{}' node change.", self->name_, + defaultNodeId, self->type_); return; } @@ -156,8 +173,8 @@ void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wir "=u", defaultNodeId, nullptr)); if (node == nullptr) { - spdlog::warn("[{}]: (onDefaultNodesApiChanged) - Object with id {} not found", self->name_, - defaultNodeId); + spdlog::warn("[{}]: (onDefaultNodesApiChanged: {}) - Object with id {} not found", self->name_, + self->type_, defaultNodeId); return; } @@ -165,21 +182,22 @@ void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wir wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(node), "node.name"); spdlog::debug( - "[{}]: (onDefaultNodesApiChanged) - got the following default node: Node(name: {}, id: {})", - self->name_, defaultNodeName, defaultNodeId); + "[{}]: (onDefaultNodesApiChanged: {}) - got the following default node: Node(name: {}, id: " + "{})", + self->name_, self->type_, defaultNodeName, defaultNodeId); if (g_strcmp0(self->default_node_name_, defaultNodeName) == 0 && self->node_id_ == defaultNodeId) { spdlog::debug( - "[{}]: (onDefaultNodesApiChanged) - Default node has not changed. Node(name: {}, id: {}). " - "Ignoring.", - self->name_, self->default_node_name_, defaultNodeId); + "[{}]: (onDefaultNodesApiChanged: {}) - Default node has not changed. Node(name: {}, id: " + "{}). Ignoring.", + self->name_, self->type_, self->default_node_name_, defaultNodeId); return; } spdlog::debug( - "[{}]: (onDefaultNodesApiChanged) - Default node changed to -> Node(name: {}, id: {})", - self->name_, defaultNodeName, defaultNodeId); + "[{}]: (onDefaultNodesApiChanged: {}) - Default node changed to -> Node(name: {}, id: {})", + self->name_, self->type_, defaultNodeName, defaultNodeId); g_free(self->default_node_name_); self->default_node_name_ = g_strdup(defaultNodeName); @@ -210,8 +228,9 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", self->type_, &self->node_id_); if (self->default_node_name_ != nullptr) { - spdlog::debug("[{}]: (onObjectManagerInstalled) - default configured node name: {} and id: {}", - self->name_, self->default_node_name_, self->node_id_); + spdlog::debug( + "[{}]: (onObjectManagerInstalled: {}) - default configured node name: {} and id: {}", + self->name_, self->type_, self->default_node_name_, self->node_id_); } updateVolume(self, self->node_id_); @@ -248,8 +267,8 @@ void waybar::modules::Wireplumber::activatePlugins() { } } -void waybar::modules::Wireplumber::prepare() { - spdlog::debug("[{}]: preparing object manager", name_); +void waybar::modules::Wireplumber::prepare(waybar::modules::Wireplumber* self) { + spdlog::debug("[{}]: preparing object manager: '{}'", name_, self->type_); wp_object_manager_add_interest(om_, WP_TYPE_NODE, WP_CONSTRAINT_TYPE_PW_PROPERTY, "media.class", "=s", self->type_, nullptr); } From c88a86f510820005426d998bdc0d8d5ac20ba304 Mon Sep 17 00:00:00 2001 From: mslxl Date: Wed, 25 Sep 2024 09:56:42 +0800 Subject: [PATCH 706/842] nix: remove patches from downstream The patches is the modification of downstream, it should not affect upstream. Any changes of upstream would caused patch fail. --- nix/default.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nix/default.nix b/nix/default.nix index 9ce39a9b..e7f07929 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -25,6 +25,9 @@ in mesonFlags = lib.remove "-Dgtk-layer-shell=enabled" oldAttrs.mesonFlags; + # downstream patch should not affect upstream + patches = []; + buildInputs = (builtins.filter (p: p.pname != "wireplumber") oldAttrs.buildInputs) ++ [ pkgs.wireplumber ]; From 04bda9f443a2ab8ac21ce3efe762bd1a907dc248 Mon Sep 17 00:00:00 2001 From: Lars Niesen Date: Tue, 24 Sep 2024 21:41:54 +0200 Subject: [PATCH 707/842] Backlight: Add minimum brightness As currently it is possible to turn the brightness to zero which may not be desirable, this patch add a configurable brightness check. --- src/modules/backlight.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 4ae511eb..6bfe8cee 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -112,6 +112,14 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { step = config_["scroll-step"].asDouble(); } + double min_brightness = 10; + if (config_["min-brightness"].isDouble()) { + min_brightness = config_["min-brightness"].asDouble(); + } + if (backend.get_scaled_brightness(preferred_device_) <= min_brightness && + ct == util::ChangeType::Decrease) { + return true; + } backend.set_brightness(preferred_device_, ct, step); return true; From 47f767b0ee32de13a2ff893a9146f59b47681601 Mon Sep 17 00:00:00 2001 From: Lars Niesen Date: Wed, 25 Sep 2024 06:24:24 +0200 Subject: [PATCH 708/842] Backlight: Add documentation for min-brightness --- man/waybar-backlight.5.scd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index 5286c2ed..a57b8278 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -81,6 +81,11 @@ The *backlight* module displays the current backlight level. default: 1.0 ++ The speed at which to change the brightness when scrolling. +*min-brightness*: ++ + typeof: double ++ + default: 10.0 ++ + The minimum brightness of the backlight. + *menu*: ++ typeof: string ++ Action that popups the menu. From d684a6de21ee78d58f819eb759b905a9091b3dd4 Mon Sep 17 00:00:00 2001 From: Lars Niesen Date: Wed, 25 Sep 2024 16:37:21 +0200 Subject: [PATCH 709/842] Backlight: Set default to 0 to prevent breaking existing setups --- man/waybar-backlight.5.scd | 2 +- src/modules/backlight.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index a57b8278..e1a688db 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -83,7 +83,7 @@ The *backlight* module displays the current backlight level. *min-brightness*: ++ typeof: double ++ - default: 10.0 ++ + default: 0.0 ++ The minimum brightness of the backlight. *menu*: ++ diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 6bfe8cee..ff58951c 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -112,7 +112,7 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { step = config_["scroll-step"].asDouble(); } - double min_brightness = 10; + double min_brightness = 0; if (config_["min-brightness"].isDouble()) { min_brightness = config_["min-brightness"].asDouble(); } From e46a1c6bfc1de05cb267eb80f6ccd2f67f90edd9 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 28 Sep 2024 00:57:02 +0300 Subject: [PATCH 710/842] cava bump Signed-off-by: Viktar Lukashonak --- meson.build | 2 +- subprojects/cava.wrap | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index f4941c8f..726d492b 100644 --- a/meson.build +++ b/meson.build @@ -482,7 +482,7 @@ if get_option('experimental') endif cava = dependency('cava', - version : '>=0.10.2', + version : '>=0.10.3', required: get_option('cava'), fallback : ['cava', 'cava_dep'], not_found_message: 'cava is not found. Building waybar without cava') diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap index 275ba114..f0309bf5 100644 --- a/subprojects/cava.wrap +++ b/subprojects/cava.wrap @@ -1,7 +1,7 @@ [wrap-file] -directory = cava-0.10.2 -source_url = https://github.com/LukashonakV/cava/archive/0.10.2.tar.gz -source_filename = cava-0.10.2.tar.gz -source_hash = dff78c4787c9843583086408a0a6e5bde7a5dee1fa17ae526847366846cb19c3 +directory = cava-0.10.3 +source_url = https://github.com/LukashonakV/cava/archive/0.10.3.tar.gz +source_filename = cava-0.10.3.tar.gz +source_hash = aab0a4ed3f999e8461ad9de63ef8a77f28b6b2011f7dd0c69ba81819d442f6f9 [provide] cava = cava_dep From edab49f2912d29a89325a359261b8b1de878aebb Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 28 Sep 2024 12:41:10 -0500 Subject: [PATCH 711/842] nix/default: cava bump --- nix/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/default.nix b/nix/default.nix index e7f07929..a9ff180b 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -5,12 +5,12 @@ }: let libcava = rec { - version = "0.10.2"; + version = "0.10.3"; src = pkgs.fetchFromGitHub { owner = "LukashonakV"; repo = "cava"; rev = version; - hash = "sha256-jU7RQV2txruu/nUUl0TzjK4nai7G38J1rcTjO7UXumY="; + hash = "sha256-ZDFbI69ECsUTjbhlw2kHRufZbQMu+FQSMmncCJ5pagg="; }; }; in From e394485857d64d63b76a7555c26456051c65d3bf Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 28 Sep 2024 12:51:08 -0500 Subject: [PATCH 712/842] .github/workflows: don't run on forks --- .github/workflows/docker.yml | 1 + .github/workflows/nix-update-flake-lock.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d9fc5d3e..66c465ba 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -8,6 +8,7 @@ on: jobs: build-and-push: runs-on: ubuntu-latest + if: github.repository == 'Alexays/Waybar' strategy: fail-fast: false # don't fail the other jobs if one of the images fails to build matrix: diff --git a/.github/workflows/nix-update-flake-lock.yml b/.github/workflows/nix-update-flake-lock.yml index 2b65c329..6ba18904 100644 --- a/.github/workflows/nix-update-flake-lock.yml +++ b/.github/workflows/nix-update-flake-lock.yml @@ -9,6 +9,7 @@ on: jobs: lockfile: runs-on: ubuntu-latest + if: github.repository == 'Alexays/Waybar' steps: - name: Checkout repository uses: actions/checkout@v4 From e53497bab6f826f7eace501f21f5e6f178253059 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 28 Sep 2024 13:21:55 -0500 Subject: [PATCH 713/842] .github/workflows: allow forks to manually run flake lock update --- .github/workflows/nix-update-flake-lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nix-update-flake-lock.yml b/.github/workflows/nix-update-flake-lock.yml index 6ba18904..a1679ead 100644 --- a/.github/workflows/nix-update-flake-lock.yml +++ b/.github/workflows/nix-update-flake-lock.yml @@ -9,7 +9,7 @@ on: jobs: lockfile: runs-on: ubuntu-latest - if: github.repository == 'Alexays/Waybar' + if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar' steps: - name: Checkout repository uses: actions/checkout@v4 From 95eaffcfb1af0e826a7e93ecd3da7c3be56c02e2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 1 Oct 2024 00:11:21 +0000 Subject: [PATCH 714/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/4f807e8940284ad7925ebd0a0993d2a1791acb2f?narHash=sha256-IiA3jfbR7K/B5%2B9byVi9BZGWTD4VSbWe8VLpp9B/iYk%3D' (2024-09-11) → 'github:NixOS/nixpkgs/06cf0e1da4208d3766d898b7fdab6513366d45b9?narHash=sha256-S5kVU7U82LfpEukbn/ihcyNt2%2BEvG7Z5unsKW9H/yFA%3D' (2024-09-29) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 15bf9f09..4b3e7212 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1726062873, - "narHash": "sha256-IiA3jfbR7K/B5+9byVi9BZGWTD4VSbWe8VLpp9B/iYk=", + "lastModified": 1727634051, + "narHash": "sha256-S5kVU7U82LfpEukbn/ihcyNt2+EvG7Z5unsKW9H/yFA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4f807e8940284ad7925ebd0a0993d2a1791acb2f", + "rev": "06cf0e1da4208d3766d898b7fdab6513366d45b9", "type": "github" }, "original": { From 6df26ccba761af1c41bf0494320f98d6b93b6e09 Mon Sep 17 00:00:00 2001 From: PassiHD Date: Wed, 9 Oct 2024 20:22:58 +0200 Subject: [PATCH 715/842] feat: add warning threshold to temperature module Signed-off-by: PassiHD --- include/modules/temperature.hpp | 1 + src/modules/temperature.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/modules/temperature.hpp b/include/modules/temperature.hpp index 5440df77..918281be 100644 --- a/include/modules/temperature.hpp +++ b/include/modules/temperature.hpp @@ -18,6 +18,7 @@ class Temperature : public ALabel { private: float getTemperature(); bool isCritical(uint16_t); + bool isWarning(uint16_t); std::string file_path_; util::SleeperThread thread_; diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 30287763..356536f9 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -69,12 +69,17 @@ auto waybar::modules::Temperature::update() -> void { uint16_t temperature_f = std::round(temperature * 1.8 + 32); uint16_t temperature_k = std::round(temperature + 273.15); auto critical = isCritical(temperature_c); + auto warning = isWarning(temperature_c); auto format = format_; if (critical) { format = config_["format-critical"].isString() ? config_["format-critical"].asString() : format; label_.get_style_context()->add_class("critical"); - } else { + } else if (warning) { + format = config_["format-warning"].isString() ? config_["format-warning"].asString() : format; + label_.get_style_context()->add_class("warning"); + } else { label_.get_style_context()->remove_class("critical"); + label_.get_style_context()->remove_class("warning"); } if (format.empty()) { @@ -135,7 +140,12 @@ float waybar::modules::Temperature::getTemperature() { #endif } +bool waybar::modules::Temperature::isWarning(uint16_t temperature_c) { + return config_["warning-threshold"].isInt() && + temperature_c >= config_["warning-threshold"].asInt(); +} + bool waybar::modules::Temperature::isCritical(uint16_t temperature_c) { return config_["critical-threshold"].isInt() && temperature_c >= config_["critical-threshold"].asInt(); -} +} \ No newline at end of file From 0e03c7a811ef477eae4465ffb3ff443e1b6e1e87 Mon Sep 17 00:00:00 2001 From: Christian Fillion Date: Wed, 16 Oct 2024 02:12:06 -0400 Subject: [PATCH 716/842] fix a segfault on signals received after main returns The waybar process does not exit instantaneously. Signals may be recevied after main has started freeing resources. When a worker thread is in `fgets` this time window can last forever. An easy way to duplicate the crash is pressing ^C twice with a Hyprland module. Thread 1 "waybar" received signal SIGSEGV, Segmentation fault. spdlog::sinks::sink::should_log (this=0x5f620b542ca5, msg_level=spdlog::level::info) at /usr/src/debug/spdlog/spdlog-1.14.1/include/spdlog/sinks/sink-inl.h:13 13 return msg_level >= level_.load(std::memory_order_relaxed); (gdb) p $_siginfo._sifields._sigfault.si_addr $1 = (void *) 0x5f620b542cad --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 679c66d6..442c530c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -107,6 +107,10 @@ int main(int argc, char* argv[]) { ret = client->main(argc, argv); } while (reload); + std::signal(SIGUSR1, SIG_IGN); + std::signal(SIGUSR2, SIG_IGN); + std::signal(SIGINT, SIG_IGN); + delete client; return ret; } catch (const std::exception& e) { From 92242f0b9d7f37f4ccf87f03b0c0ce30255d2d54 Mon Sep 17 00:00:00 2001 From: Christian Fillion Date: Wed, 16 Oct 2024 09:56:05 -0400 Subject: [PATCH 717/842] hyprland: fix a data race at startup between sockets 1 and 2 `Workspaces::*` and `IPC::startIPC` may both call `getSocketFolder` at the same time. This randomly causes crashes and/or corruption of the socket path. Typical crash A: [2024-10-16 07:42:09.987] [info] Hyprland IPC starting malloc(): unaligned tcache chunk detected [2024-10-16 07:42:09.987] [error] Hyprland IPC: Unable to connect? Thread 1 "waybar" received signal SIGABRT, Aborted. (gdb) bt #0 __pthread_kill_implementation (threadid=, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44 (omitted for brievety) #9 0x00007ffff64ae745 in operator new (sz=sz@entry=296) at /usr/src/debug/gcc/gcc/libstdc++-v3/libsupc++/new_op.cc:50 #10 0x00007ffff65ab1f1 in std::filesystem::__cxx11::path::_List::_Impl::copy (this=0x555555a23350) at /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++17/fs_path.cc:249 #11 0x00007ffff65ab3bd in std::filesystem::__cxx11::path::_List::_List (this=0x7fffffff9d30, other=) at /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/unique_ptr.h:454 #12 0x00005555556f4ab1 in waybar::modules::hyprland::IPC::getSocket1Reply(std::__cxx11::basic_string, std::allocator > const&) () #13 0x00005555556f5e3d in waybar::modules::hyprland::IPC::getSocket1JsonReply(std::__cxx11::basic_string, std::allocator > const&) () #14 0x000055555571289c in waybar::modules::hyprland::Workspaces::setCurrentMonitorId() () Typical crash B: [2024-10-16 10:01:15.859] [info] Hyprland IPC starting [2024-10-16 10:01:15.859] [info] Loading persistent workspaces from Hyprland workspace rules Thread 8 "waybar" received signal SIGSEGV, Segmentation fault. (gdb) bt #0 std::__cxx11::basic_string, std::allocator >::_S_copy (__d=0x5555558fbca8 "/", __s=0x2973961a26d35726 , __n=1) at /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:433 (omitted for brievety) #15 waybar::modules::hyprland::IPC::getSocketFolder[abi:cxx11](char const*) (instanceSig=0x7fffffffe604 "4520b30d498daca8079365bdb909a8dea38e8d55_1729051218_1982280648") at ../src/modules/hyprland/backend.cpp:41 #16 0x000055555564230f in waybar::modules::hyprland::IPC::startIPC()::{lambda()#1}::operator()() const () at ../src/modules/hyprland/backend.cpp:70 #17 0x00007ffff64e1c34 in std::execute_native_thread_routine (__p=0x5555558119c0) at /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++11/thread.cc:104 #18 0x00007ffff62a339d in start_thread (arg=) at pthread_create.c:447 --- src/modules/hyprland/backend.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 77f534e0..39341a14 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -18,6 +18,9 @@ namespace waybar::modules::hyprland { std::filesystem::path IPC::socketFolder_; std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { + static std::mutex folderMutex; + std::unique_lock lock(folderMutex); + // socket path, specified by EventManager of Hyprland if (!socketFolder_.empty()) { return socketFolder_; From bb40e169fd3cb77a46a623ff44db496c9b161749 Mon Sep 17 00:00:00 2001 From: Blexyel Date: Tue, 22 Oct 2024 10:56:26 +0200 Subject: [PATCH 718/842] feat: update man page --- man/waybar-temperature.5.scd | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 541bf3af..bf41ecc8 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -31,6 +31,10 @@ Addressed by *temperature* typeof: string ++ The temperature filename of your *hwmon-path-abs*, e.g. *temp1_input* +*warning-threshold*: ++ + typeof: integer ++ + The threshold before it is considered warning (Celsius). + *critical-threshold*: ++ typeof: integer ++ The threshold before it is considered critical (Celsius). @@ -40,6 +44,10 @@ Addressed by *temperature* default: 10 ++ The interval in which the information gets polled. +*format-warning*: ++ + typeof: string ++ + The format to use when temperature is considered warning + *format-critical*: ++ typeof: string ++ The format to use when temperature is considered critical From 3f80e507fd53f759cc7d7e97635c9d69b375f6a0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 Nov 2024 00:11:22 +0000 Subject: [PATCH 719/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/06cf0e1da4208d3766d898b7fdab6513366d45b9?narHash=sha256-S5kVU7U82LfpEukbn/ihcyNt2%2BEvG7Z5unsKW9H/yFA%3D' (2024-09-29) → 'github:NixOS/nixpkgs/807e9154dcb16384b1b765ebe9cd2bba2ac287fd?narHash=sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU%3D' (2024-10-29) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 4b3e7212..d0ca27b8 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1727634051, - "narHash": "sha256-S5kVU7U82LfpEukbn/ihcyNt2+EvG7Z5unsKW9H/yFA=", + "lastModified": 1730200266, + "narHash": "sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "06cf0e1da4208d3766d898b7fdab6513366d45b9", + "rev": "807e9154dcb16384b1b765ebe9cd2bba2ac287fd", "type": "github" }, "original": { From 77b50b4c7a58408a80328f4f3177bdbeac31c5ce Mon Sep 17 00:00:00 2001 From: Aqa-Ib Date: Wed, 6 Nov 2024 13:07:09 +0000 Subject: [PATCH 720/842] fix hyprland's grouped window flags Both flags are wrong, because: - the active group member can be fullscreened. - technically, a grouped window can be solo as well, because only the active group member is shown, the other members are hidden. Also you can have a group consisting of only one window. --- src/modules/hyprland/window.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index b5ed8f02..152eea03 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -191,12 +191,6 @@ void Window::queryActiveWorkspace() { solo_ = true; } - // Grouped windows have a tab bar and therefore don't look fullscreen or solo - if (windowData_.grouped) { - fullscreen_ = false; - solo_ = false; - } - if (solo_) { soloClass_ = windowData_.class_name; } else { From 724a4a5ed35d74f5c7a7f55aaeeec5c87b4a9e49 Mon Sep 17 00:00:00 2001 From: ArijanJ Date: Sun, 10 Nov 2024 12:50:26 +0100 Subject: [PATCH 721/842] Add signals section to manpage --- man/waybar.5.scd.in | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index f3a89656..45248552 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -181,6 +181,19 @@ A minimal *config* file could look like this: } ``` +# SIGNALS + +Waybar accepts the following signals: + +*SIGUSR1* + Toggles the bar visibility (hides if shown, shows if hidden) +*SIGUSR2* + Reloads (resets) the bar +*SIGINT* + Quits the bar + +For example, to toggle the bar programmatically, you can invoke `killall -SIGUSR1 waybar`. + # MULTI OUTPUT CONFIGURATION ## Limit a configuration to some outputs From 86ce9f7278edd5a27467caf3749e8a07e4588260 Mon Sep 17 00:00:00 2001 From: Kate Adkins Date: Fri, 15 Nov 2024 12:04:56 -0700 Subject: [PATCH 722/842] fix null-deref due to unconstructed PrivacyNodeInfo This would cause Waybar to crash if the privacy module ever got e.g. a empty (but properly null-terminated) string for the application_name. --- src/util/pipewire/pipewire_backend.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/pipewire/pipewire_backend.cpp b/src/util/pipewire/pipewire_backend.cpp index 5bb7c19a..3b897178 100644 --- a/src/util/pipewire/pipewire_backend.cpp +++ b/src/util/pipewire/pipewire_backend.cpp @@ -126,6 +126,7 @@ void PipewireBackend::handleRegistryEventGlobal(uint32_t id, uint32_t permission if (proxy == nullptr) return; auto *pNodeInfo = (PrivacyNodeInfo *)pw_proxy_get_user_data(proxy); + new(pNodeInfo) PrivacyNodeInfo{}; pNodeInfo->id = id; pNodeInfo->data = this; pNodeInfo->type = mediaType; @@ -142,6 +143,7 @@ void PipewireBackend::handleRegistryEventGlobalRemove(uint32_t id) { mutex_.lock(); auto iter = privacy_nodes.find(id); if (iter != privacy_nodes.end()) { + privacy_nodes[id]->~PrivacyNodeInfo(); privacy_nodes.erase(id); } mutex_.unlock(); From f9acc1fed9265a349cf8fb3d75ee34f4f0e93aab Mon Sep 17 00:00:00 2001 From: Lars Niesen Date: Wed, 30 Oct 2024 17:04:13 +0100 Subject: [PATCH 723/842] Add stretching of modules and modules-center toggling This Patch allows the stretching of modules-{left,center,right} as well add a "expand" flag to AModule. This allows one module to consume the leftover space. To allow the left or right modules to fully consume the center, the changes also include a way to remove the center box (center_) altogether. --- include/AModule.hpp | 3 +++ man/waybar-backlight-slider.5.scd | 5 ++++ man/waybar-backlight.5.scd | 5 ++++ man/waybar-battery.5.scd | 5 ++++ man/waybar-bluetooth.5.scd | 5 ++++ man/waybar-cffi.5.scd | 5 ++++ man/waybar-clock.5.scd | 4 +++ man/waybar-cpu.5.scd | 5 ++++ man/waybar-custom.5.scd | 5 ++++ man/waybar-disk.5.scd | 5 ++++ man/waybar-dwl-tags.5.scd | 5 ++++ man/waybar-dwl-window.5.scd | 5 ++++ man/waybar-gamemode.5.scd | 5 ++++ man/waybar-hyprland-language.5.scd | 5 ++++ man/waybar-hyprland-submap.5.scd | 5 ++++ man/waybar-hyprland-window.5.scd | 5 ++++ man/waybar-hyprland-workspaces.5.scd | 5 ++++ man/waybar-idle-inhibitor.5.scd | 5 ++++ man/waybar-image.5.scd | 5 ++++ man/waybar-inhibitor.5.scd | 5 ++++ man/waybar-jack.5.scd | 5 ++++ man/waybar-keyboard-state.5.scd | 5 ++++ man/waybar-memory.5.scd | 5 ++++ man/waybar-mpd.5.scd | 5 ++++ man/waybar-mpris.5.scd | 5 ++++ man/waybar-network.5.scd | 5 ++++ man/waybar-niri-language.5.scd | 5 ++++ man/waybar-niri-window.5.scd | 5 ++++ man/waybar-niri-workspaces.5.scd | 5 ++++ man/waybar-power-profiles-daemon.5.scd | 7 +++++ man/waybar-privacy.5.scd | 5 ++++ man/waybar-pulseaudio-slider.5.scd | 5 ++++ man/waybar-pulseaudio.5.scd | 5 ++++ man/waybar-river-layout.5.scd | 5 ++++ man/waybar-river-mode.5.scd | 5 ++++ man/waybar-river-tags.5.scd | 5 ++++ man/waybar-river-window.5.scd | 5 ++++ man/waybar-sndio.5.scd | 5 ++++ man/waybar-sway-language.5.scd | 5 ++++ man/waybar-sway-mode.5.scd | 5 ++++ man/waybar-sway-scratchpad.5.scd | 5 ++++ man/waybar-sway-window.5.scd | 5 ++++ man/waybar-sway-workspaces.5.scd | 5 ++++ man/waybar-systemd-failed-units.5.scd | 5 ++++ man/waybar-temperature.5.scd | 5 ++++ man/waybar-tray.5.scd | 5 ++++ man/waybar.5.scd.in | 20 ++++++++++++++ src/AModule.cpp | 2 ++ src/bar.cpp | 36 +++++++++++++++++++------- 49 files changed, 277 insertions(+), 10 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 94a88371..2ffc429c 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -23,6 +23,8 @@ class AModule : public IModule { /// Emitting on this dispatcher triggers a update() call Glib::Dispatcher dp; + bool expandEnabled() const; + protected: // Don't need to make an object directly // Derived classes are able to use it @@ -50,6 +52,7 @@ class AModule : public IModule { private: bool handleUserEvent(GdkEventButton *const &ev); const bool isTooltip; + const bool isExpand; bool hasUserEvents_; std::vector pid_; gdouble distance_scrolled_y_; diff --git a/man/waybar-backlight-slider.5.scd b/man/waybar-backlight-slider.5.scd index cd5b5184..8d8353c3 100644 --- a/man/waybar-backlight-slider.5.scd +++ b/man/waybar-backlight-slider.5.scd @@ -31,6 +31,11 @@ The brightness can be controlled by dragging the slider across the bar or clicki typeof: string ++ The name of the preferred device to control. If left empty, a device will be chosen automatically. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLES ``` diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index e1a688db..20810051 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -99,6 +99,11 @@ The *backlight* module displays the current backlight level. typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLE: ``` diff --git a/man/waybar-battery.5.scd b/man/waybar-battery.5.scd index 29cb7d6d..a3c21a20 100644 --- a/man/waybar-battery.5.scd +++ b/man/waybar-battery.5.scd @@ -122,6 +122,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{capacity}*: Capacity in percentage diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd index bd64f457..fd7d5fb5 100644 --- a/man/waybar-bluetooth.5.scd +++ b/man/waybar-bluetooth.5.scd @@ -142,6 +142,11 @@ Addressed by *bluetooth* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{status}*: Status of the bluetooth device. diff --git a/man/waybar-cffi.5.scd b/man/waybar-cffi.5.scd index 926511d8..0203ef77 100644 --- a/man/waybar-cffi.5.scd +++ b/man/waybar-cffi.5.scd @@ -15,6 +15,11 @@ Addressed by *cffi/* typeof: string ++ The path to the dynamic library to load to control the widget. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + Some additional configuration may be required depending on the cffi dynamic library being used. diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 3947266d..f0a88d24 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -96,6 +96,10 @@ $XDG_CONFIG_HOME/waybar/config ++ :[ array :[ :[ The actions corresponding to the buttons of the menu. +|[ *expand*: +:[ bool +:[ false +:[ Enables this module to consume all left over space dynamically. View all valid format options in *strftime(3)* or have a look https://en.cppreference.com/w/cpp/chrono/duration/formatter diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 6b13a563..287bf123 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -82,6 +82,11 @@ The *cpu* module displays the current CPU utilization. default: true ++ Option to disable tooltip on hover. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{load}*: Current CPU load. diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 6b96d2a4..309fc184 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -138,6 +138,11 @@ Addressed by *custom/* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # RETURN-TYPE When *return-type* is set to *json*, Waybar expects the *exec*-script to output its data in JSON format. diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index 1699a511..00af2b90 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -106,6 +106,11 @@ Addressed by *disk* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{percentage_used}*: Percentage of disk in use. diff --git a/man/waybar-dwl-tags.5.scd b/man/waybar-dwl-tags.5.scd index 07c94be9..a2146dfd 100644 --- a/man/waybar-dwl-tags.5.scd +++ b/man/waybar-dwl-tags.5.scd @@ -26,6 +26,11 @@ Addressed by *dwl/tags* 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. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLE ``` diff --git a/man/waybar-dwl-window.5.scd b/man/waybar-dwl-window.5.scd index f185c82c..9ac33d94 100644 --- a/man/waybar-dwl-window.5.scd +++ b/man/waybar-dwl-window.5.scd @@ -84,6 +84,11 @@ Addressed by *dwl/window* default: 24 ++ Option to change the size of the application icon. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{title}*: The title of the focused window. diff --git a/man/waybar-gamemode.5.scd b/man/waybar-gamemode.5.scd index 492e9850..a6ca9af0 100644 --- a/man/waybar-gamemode.5.scd +++ b/man/waybar-gamemode.5.scd @@ -61,6 +61,11 @@ Feral Gamemode optimizations. default: 4 ++ Defines the spacing between the icon and the text. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{glyph}*: The string icon glyph to use instead. diff --git a/man/waybar-hyprland-language.5.scd b/man/waybar-hyprland-language.5.scd index 33b28ae4..a9a18008 100644 --- a/man/waybar-hyprland-language.5.scd +++ b/man/waybar-hyprland-language.5.scd @@ -38,6 +38,11 @@ Addressed by *hyprland/language* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd index e27138e7..f6cdff94 100644 --- a/man/waybar-hyprland-submap.5.scd +++ b/man/waybar-hyprland-submap.5.scd @@ -93,6 +93,11 @@ Addressed by *hyprland/submap* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLES diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd index 4e9c5d18..34ebf89b 100644 --- a/man/waybar-hyprland-window.5.scd +++ b/man/waybar-hyprland-window.5.scd @@ -35,6 +35,11 @@ Addressed by *hyprland/window* default: 24 ++ Option to change the size of the application icon. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS See the output of "hyprctl clients" for examples diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index c71168d4..18c39898 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -78,6 +78,11 @@ Addressed by *hyprland/workspaces* If set to id, workspaces will sort by id. If none of those, workspaces will sort with default behavior. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{id}*: id of workspace assigned by compositor diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index 81a097a7..6d5a2170 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -102,6 +102,11 @@ screensaver, also known as "presentation mode". typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{status}*: status (*activated* or *deactivated*) diff --git a/man/waybar-image.5.scd b/man/waybar-image.5.scd index e3a69e38..a2dcc938 100644 --- a/man/waybar-image.5.scd +++ b/man/waybar-image.5.scd @@ -64,6 +64,11 @@ The *image* module displays an image from a path. default: true ++ Option to enable tooltip on hover. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # SCRIPT OUTPUT Similar to the *custom* module, output values of the script are *newline* separated. diff --git a/man/waybar-inhibitor.5.scd b/man/waybar-inhibitor.5.scd index 5513cc49..fce6f4f8 100644 --- a/man/waybar-inhibitor.5.scd +++ b/man/waybar-inhibitor.5.scd @@ -89,6 +89,11 @@ See *systemd-inhibit*(1) for more information. typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{status}*: status (*activated* or *deactivated*) diff --git a/man/waybar-jack.5.scd b/man/waybar-jack.5.scd index 180143b7..85ce7180 100644 --- a/man/waybar-jack.5.scd +++ b/man/waybar-jack.5.scd @@ -98,6 +98,11 @@ Addressed by *jack* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{load}*: The current CPU load estimated by JACK. diff --git a/man/waybar-keyboard-state.5.scd b/man/waybar-keyboard-state.5.scd index 9ecc5515..79498414 100644 --- a/man/waybar-keyboard-state.5.scd +++ b/man/waybar-keyboard-state.5.scd @@ -53,6 +53,11 @@ You must be a member of the input group to use this module. default: [58, 69, 70] ++ 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. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{name}*: Caps, Num, or Scroll. diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index 5c368ae8..cc42d5a3 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -97,6 +97,11 @@ Addressed by *memory* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{percentage}*: Percentage of memory in use. diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index c576a5c0..84abc2e8 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -175,6 +175,11 @@ Addressed by *mpd* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS ## WHEN PLAYING/PAUSED diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 380a1a19..9c192bd1 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -148,6 +148,11 @@ The *mpris* module displays currently playing media via libplayerctl. typeof: map[string]string ++ Allows setting _{status_icon}_ based on player status (playing, paused, stopped). +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index e1cf810d..ee409d0a 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -142,6 +142,11 @@ Addressed by *network* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{ifname}*: Name of the network interface. diff --git a/man/waybar-niri-language.5.scd b/man/waybar-niri-language.5.scd index 6895d25c..44876fd9 100644 --- a/man/waybar-niri-language.5.scd +++ b/man/waybar-niri-language.5.scd @@ -33,6 +33,11 @@ Addressed by *niri/language* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{short}*: Short name of layout (e.g. "us"). Equals to {}. diff --git a/man/waybar-niri-window.5.scd b/man/waybar-niri-window.5.scd index 9e2e9f63..8e886c29 100644 --- a/man/waybar-niri-window.5.scd +++ b/man/waybar-niri-window.5.scd @@ -35,6 +35,11 @@ Addressed by *niri/window* default: 24 ++ Option to change the size of the application icon. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS See the output of "niri msg windows" for examples diff --git a/man/waybar-niri-workspaces.5.scd b/man/waybar-niri-workspaces.5.scd index 50e497cd..0c0249ca 100644 --- a/man/waybar-niri-workspaces.5.scd +++ b/man/waybar-niri-workspaces.5.scd @@ -45,6 +45,11 @@ Addressed by *niri/workspaces* typeof: string ++ Command to execute when the module is updated. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{value}*: Name of the workspace, or index for unnamed workspaces, diff --git a/man/waybar-power-profiles-daemon.5.scd b/man/waybar-power-profiles-daemon.5.scd index 82fad13b..6488767b 100644 --- a/man/waybar-power-profiles-daemon.5.scd +++ b/man/waybar-power-profiles-daemon.5.scd @@ -35,6 +35,13 @@ $XDG_CONFIG_HOME/waybar/config :[ 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. +|[ *expand*: +:[ bool +:[ false +:[ Enables this module to consume all left over space dynamically. + + + # CONFIGURATION EXAMPLES diff --git a/man/waybar-privacy.5.scd b/man/waybar-privacy.5.scd index d13d8ed3..946fe136 100644 --- a/man/waybar-privacy.5.scd +++ b/man/waybar-privacy.5.scd @@ -32,6 +32,11 @@ the screen or playing audio. Which privacy modules to monitor. See *MODULES CONFIGURATION* for++ more information. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # MODULES CONFIGURATION *type*: ++ diff --git a/man/waybar-pulseaudio-slider.5.scd b/man/waybar-pulseaudio-slider.5.scd index cf07fed1..cb274826 100644 --- a/man/waybar-pulseaudio-slider.5.scd +++ b/man/waybar-pulseaudio-slider.5.scd @@ -27,6 +27,11 @@ The volume can be controlled by dragging the slider across the bar or clicking o default: horizontal ++ The orientation of the slider. Can be either `horizontal` or `vertical`. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLES ``` diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index 5b38e8b7..d47fc744 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -126,6 +126,11 @@ Additionally, you can control the volume by scrolling *up* or *down* while the c typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name. diff --git a/man/waybar-river-layout.5.scd b/man/waybar-river-layout.5.scd index 1368bda9..78e03634 100644 --- a/man/waybar-river-layout.5.scd +++ b/man/waybar-river-layout.5.scd @@ -64,6 +64,11 @@ Addressed by *river/layout* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLE ``` diff --git a/man/waybar-river-mode.5.scd b/man/waybar-river-mode.5.scd index b992fdaf..5837411d 100644 --- a/man/waybar-river-mode.5.scd +++ b/man/waybar-river-mode.5.scd @@ -78,6 +78,11 @@ Addressed by *river/mode* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLES ``` diff --git a/man/waybar-river-tags.5.scd b/man/waybar-river-tags.5.scd index f0b2b84e..5669456a 100644 --- a/man/waybar-river-tags.5.scd +++ b/man/waybar-river-tags.5.scd @@ -26,6 +26,11 @@ Addressed by *river/tags* 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. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLE ``` diff --git a/man/waybar-river-window.5.scd b/man/waybar-river-window.5.scd index 6db9a2fa..82eee0a5 100644 --- a/man/waybar-river-window.5.scd +++ b/man/waybar-river-window.5.scd @@ -62,6 +62,11 @@ Addressed by *river/window* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLES ``` diff --git a/man/waybar-sndio.5.scd b/man/waybar-sndio.5.scd index 03dfe0af..f14d35e9 100644 --- a/man/waybar-sndio.5.scd +++ b/man/waybar-sndio.5.scd @@ -87,6 +87,11 @@ cursor is over the module, and clicking on the module toggles mute. typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{volume}*: Volume in percentage. diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index a1fc5d08..5710e69d 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -45,6 +45,11 @@ Addressed by *sway/language* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{short}*: Short name of layout (e.g. "us"). Equals to {}. diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index 8d5d7c2c..52827376 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -83,6 +83,11 @@ Addressed by *sway/mode* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLES ``` diff --git a/man/waybar-sway-scratchpad.5.scd b/man/waybar-sway-scratchpad.5.scd index 5ae104bc..e51ad12a 100644 --- a/man/waybar-sway-scratchpad.5.scd +++ b/man/waybar-sway-scratchpad.5.scd @@ -49,6 +49,11 @@ Addressed by *sway/scratchpad* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{icon}*: Icon, as defined in *format-icons*. diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 6d1e3196..a7eb4f05 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -103,6 +103,11 @@ Addressed by *sway/window* default: 24 ++ Option to change the size of the application icon. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{title}*: The title of the focused window. diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index fc73a85a..a710ab50 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -99,6 +99,11 @@ warp-on-scroll: ++ default: " " ++ The separator to be used between windows in a workspace. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS diff --git a/man/waybar-systemd-failed-units.5.scd b/man/waybar-systemd-failed-units.5.scd index ada3ab8b..92e74e9d 100644 --- a/man/waybar-systemd-failed-units.5.scd +++ b/man/waybar-systemd-failed-units.5.scd @@ -49,6 +49,11 @@ Addressed by *systemd-failed-units* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{nr_failed_system}*: Number of failed units from systemwide (PID=1) systemd. diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index bf41ecc8..923d643d 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -132,6 +132,11 @@ Addressed by *temperature* typeof: array ++ The actions corresponding to the buttons of the menu. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # FORMAT REPLACEMENTS *{temperatureC}*: Temperature in Celsius. diff --git a/man/waybar-tray.5.scd b/man/waybar-tray.5.scd index 294a261d..381d593d 100644 --- a/man/waybar-tray.5.scd +++ b/man/waybar-tray.5.scd @@ -37,6 +37,11 @@ Addressed by *tray* typeof: string ++ Command to execute when the module is updated. +*expand*: ++ + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + # EXAMPLES ``` diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 45248552..5bb62724 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -23,6 +23,21 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles( # BAR CONFIGURATION +*expand-center* ++ + typeof: bool ++ + default: false ++ + Enables the modules-center to consume all left over space dynamically. + +*expand-left* ++ + typeof: bool ++ + default: false ++ + Enables the modules-left to consume all left over space dynamically. + +*expand-right* ++ + typeof: bool ++ + default: false ++ + Enables the modules-left to consume all left over space dynamically. + *layer* ++ typeof: string ++ default: bottom ++ @@ -68,6 +83,11 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles( typeof: integer ++ Margins value without units. +*no-center* ++ + typeof: bool ++ + default: false ++ + Option to disable the center modules fully usefull together with expand-\*. + *spacing* ++ typeof: integer ++ Size of gaps in between the different modules. diff --git a/src/AModule.cpp b/src/AModule.cpp index c180b480..5abb779a 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -15,6 +15,7 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: : name_(name), config_(config), isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true}, + isExpand{config_["expand"].isBool() ? config_["expand"].asBool() : false}, distance_scrolled_y_(0.0), distance_scrolled_x_(0.0) { // Configure module action Map @@ -273,6 +274,7 @@ bool AModule::handleScroll(GdkEventScroll* e) { } bool AModule::tooltipEnabled() const { return isTooltip; } +bool AModule::expandEnabled() const { return isExpand; } AModule::operator Gtk::Widget&() { return event_box_; } diff --git a/src/bar.cpp b/src/bar.cpp index 5068e90d..b7737d36 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -534,13 +534,22 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, auto waybar::Bar::setupWidgets() -> void { window.add(box_); - box_.pack_start(left_, false, false); - if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) { - box_.set_center_widget(center_); - } else { - box_.pack_start(center_, true, false); + + bool expand_left = config["expand-left"].isBool() ? config["expand-left"].asBool() : false; + bool expand_center = config["expand-center"].isBool() ? config["expand-center"].asBool() : false; + bool expand_right = config["expand-right"].isBool() ? config["expand-right"].asBool() : false; + bool no_center = config["no-center"].isBool() ? config["no-center"].asBool() : false; + + box_.pack_start(left_, expand_left, expand_left); + if (!no_center) { + if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) { + box_.set_center_widget(center_); + } else { + spdlog::error("No fixed center_"); + box_.pack_start(center_, true, expand_center); + } } - box_.pack_end(right_, false, false); + box_.pack_end(right_, expand_right, expand_right); // Convert to button code for every module that is used. setupAltFormatKeyForModuleList("modules-left"); @@ -549,14 +558,21 @@ auto waybar::Bar::setupWidgets() -> void { Factory factory(*this, config); getModules(factory, "modules-left"); - getModules(factory, "modules-center"); + if (!no_center) { + getModules(factory, "modules-center"); + } getModules(factory, "modules-right"); + for (auto const& module : modules_left_) { - left_.pack_start(*module, false, false); + left_.pack_start(*module, module->expandEnabled(), module->expandEnabled()); } - for (auto const& module : modules_center_) { - center_.pack_start(*module, false, false); + + if (!no_center) { + for (auto const& module : modules_center_) { + center_.pack_start(*module, false, false); + } } + std::reverse(modules_right_.begin(), modules_right_.end()); for (auto const& module : modules_right_) { right_.pack_end(*module, false, false); From 1e481b7ac0e7eefa610f5b4b5a6c4126cb189eeb Mon Sep 17 00:00:00 2001 From: Lars Niesen Date: Wed, 30 Oct 2024 17:48:35 +0100 Subject: [PATCH 724/842] fix broken clang-format to run pipeline --- src/main.cpp | 2 +- src/modules/temperature.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 442c530c..045b2cd4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -109,7 +109,7 @@ int main(int argc, char* argv[]) { std::signal(SIGUSR1, SIG_IGN); std::signal(SIGUSR2, SIG_IGN); - std::signal(SIGINT, SIG_IGN); + std::signal(SIGINT, SIG_IGN); delete client; return ret; diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 356536f9..89d3db2d 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -77,7 +77,7 @@ auto waybar::modules::Temperature::update() -> void { } else if (warning) { format = config_["format-warning"].isString() ? config_["format-warning"].asString() : format; label_.get_style_context()->add_class("warning"); - } else { + } else { label_.get_style_context()->remove_class("critical"); label_.get_style_context()->remove_class("warning"); } From 4e05659109f95652fcdb9b582ffc691dff503a46 Mon Sep 17 00:00:00 2001 From: Jan Larres Date: Sun, 1 Dec 2024 12:48:07 +1300 Subject: [PATCH 725/842] sway/workspaces: use X11 class for XWayland windows When using `window-rewrite`, the `class<>` rule would previously only match against the `app_id` of a window. However, XWayland windows don't have an app ID. This change falls back to checking the `class` window property if there is no app ID to support matching against XWayland windows. --- man/waybar-sway-workspaces.5.scd | 1 + src/modules/sway/workspaces.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index a710ab50..1c4360e9 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -88,6 +88,7 @@ warp-on-scroll: ++ Keys are the rules, while the values are the methods of representation. Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching. You may assign an empty value to a rule to have it ignored from generating any representation in workspaces. + For Wayland windows `class` is matched against the `app_id`, and for X11 windows against the `class` property. *window-rewrite-default*: typeof: string ++ diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 8f273300..33d4bb29 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -260,7 +260,9 @@ void Workspaces::updateWindows(const Json::Value &node, std::string &windows) { 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 windowClass = node["app_id"].isString() + ? node["app_id"].asString() + : node["window_properties"]["class"].asString(); // Only add window rewrites that can be looked up if (!windowClass.empty()) { From 0e5728b53388de6104bc6a644009fa87e3008ee9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 1 Dec 2024 00:12:55 +0000 Subject: [PATCH 726/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-compat': 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33?narHash=sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U%3D' (2023-10-04) → 'github:edolstra/flake-compat/9ed2ac151eada2306ca8c418ebd97807bb08f6ac?narHash=sha256-HRJ/18p%2BWoXpWJkcdsk9St5ZiukCqSDgbOGFa8Okehg%3D' (2024-11-27) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/807e9154dcb16384b1b765ebe9cd2bba2ac287fd?narHash=sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU%3D' (2024-10-29) → 'github:NixOS/nixpkgs/970e93b9f82e2a0f3675757eb0bfc73297cc6370?narHash=sha256-jNRNr49UiuIwaarqijgdTR2qLPifxsVhlJrKzQ8XUIE%3D' (2024-11-28) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index d0ca27b8..24b83454 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1732722421, + "narHash": "sha256-HRJ/18p+WoXpWJkcdsk9St5ZiukCqSDgbOGFa8Okehg=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "9ed2ac151eada2306ca8c418ebd97807bb08f6ac", "type": "github" }, "original": { @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1730200266, - "narHash": "sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU=", + "lastModified": 1732837521, + "narHash": "sha256-jNRNr49UiuIwaarqijgdTR2qLPifxsVhlJrKzQ8XUIE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "807e9154dcb16384b1b765ebe9cd2bba2ac287fd", + "rev": "970e93b9f82e2a0f3675757eb0bfc73297cc6370", "type": "github" }, "original": { From 6d28740896b33005cfe6344719bcebb8de0cabfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Tue, 3 Dec 2024 19:37:36 +0100 Subject: [PATCH 727/842] Add locking for interacting with the pulseaudio thread Before this commit, Waybar would sometimes get into a state where it would consume 100% of a CPU core, and the pulseaudio widget would stop responding to volume adjustments. In this state, the pulseaudio mainloop thread would spin, with the counter of enabled defer events at 1, but no actual enabled defer event in the list to get the counter back to zero after an iteration in the mainloop. This could happen if the unsynchronized interactions with the mainloop thread happened to modify the list of deferred events at the same time as the mainloop. This commit introduces locking in accordance with the PulseAudio documentation on the threaded mainloop: > The lock needs to be held whenever you call any PulseAudio function that > uses an object associated with this main loop. Those objects include > pa_mainloop, pa_context, pa_stream and pa_operation, and the various event > objects (pa_io_event, pa_time_event, pa_defer_event). --- src/util/audio_backend.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 73aac148..807b5dc7 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -236,7 +236,9 @@ void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t m volume = std::clamp(volume, min_volume, max_volume); pa_cvolume_set(&pa_volume, pa_volume_.channels, volume * volume_tick); + pa_threaded_mainloop_lock(mainloop_); pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); + pa_threaded_mainloop_unlock(mainloop_); } void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t max_volume) { @@ -265,31 +267,41 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma pa_cvolume_dec(&pa_volume, change); } } + pa_threaded_mainloop_lock(mainloop_); pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); + pa_threaded_mainloop_unlock(mainloop_); } void AudioBackend::toggleSinkMute() { muted_ = !muted_; + pa_threaded_mainloop_lock(mainloop_); pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast(muted_), nullptr, nullptr); + pa_threaded_mainloop_unlock(mainloop_); } void AudioBackend::toggleSinkMute(bool mute) { muted_ = mute; + pa_threaded_mainloop_lock(mainloop_); pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast(muted_), nullptr, nullptr); + pa_threaded_mainloop_unlock(mainloop_); } void AudioBackend::toggleSourceMute() { source_muted_ = !muted_; + pa_threaded_mainloop_lock(mainloop_); pa_context_set_source_mute_by_index(context_, source_idx_, static_cast(source_muted_), nullptr, nullptr); + pa_threaded_mainloop_unlock(mainloop_); } void AudioBackend::toggleSourceMute(bool mute) { source_muted_ = mute; + pa_threaded_mainloop_lock(mainloop_); pa_context_set_source_mute_by_index(context_, source_idx_, static_cast(source_muted_), nullptr, nullptr); + pa_threaded_mainloop_unlock(mainloop_); } bool AudioBackend::isBluetooth() { From 6bac784b51bc319bba7ab91bc1cc5ee8815a2cae Mon Sep 17 00:00:00 2001 From: eritque0arcus Date: Wed, 4 Dec 2024 12:46:09 -0600 Subject: [PATCH 728/842] fix: use auto and add self as fallback --- src/util/backlight_backend.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index bb102cd9..41236462 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -153,7 +153,13 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, // Connect to the login interface login_proxy_ = Gio::DBus::Proxy::create_for_bus_sync( Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.login1", - "/org/freedesktop/login1/session/self", "org.freedesktop.login1.Session"); + "/org/freedesktop/login1/session/auto", "org.freedesktop.login1.Session"); + + if (!login_proxy_) { + 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()}; From db943dae9817d989031cf06309d759f8a7416284 Mon Sep 17 00:00:00 2001 From: Gustaf Ullberg Date: Thu, 5 Dec 2024 15:15:33 +0100 Subject: [PATCH 729/842] Let network module handle ipv4 and ipv6 simultaneously --- include/modules/network.hpp | 1 - src/modules/network.cpp | 34 +++++++--------------------------- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 4a84b02f..df0ba9c3 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -50,7 +50,6 @@ class Network : public ALabel { std::optional> readBandwidthUsage(); int ifid_; - sa_family_t family_; struct sockaddr_nl nladdr_ = {0}; struct nl_sock* sock_ = nullptr; struct nl_sock* ev_sock_ = nullptr; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 0bbea631..393b4296 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -80,7 +80,6 @@ waybar::modules::Network::readBandwidthUsage() { waybar::modules::Network::Network(const std::string &id, const Json::Value &config) : ALabel(config, "network", id, DEFAULT_FORMAT, 60), ifid_(-1), - family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), ev_fd_(-1), want_route_dump_(false), @@ -141,12 +140,7 @@ waybar::modules::Network::~Network() { close(efd_); } if (ev_sock_ != nullptr) { - nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK); - if (family_ == AF_INET) { - nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV4_IFADDR); - } else { - nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); - } + nl_socket_drop_memberships(ev_sock_, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR); nl_close(ev_sock_); nl_socket_free(ev_sock_); } @@ -161,7 +155,7 @@ void waybar::modules::Network::createEventSocket() { nl_socket_disable_seq_check(ev_sock_); nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this); nl_socket_modify_cb(ev_sock_, NL_CB_FINISH, NL_CB_CUSTOM, handleEventsDone, this); - auto groups = RTMGRP_LINK | (family_ == AF_INET ? RTMGRP_IPV4_IFADDR : RTMGRP_IPV6_IFADDR); + auto groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; nl_join_groups(ev_sock_, groups); // Deprecated if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) { throw std::runtime_error("Can't connect network socket"); @@ -169,18 +163,9 @@ void waybar::modules::Network::createEventSocket() { if (nl_socket_set_nonblocking(ev_sock_)) { throw std::runtime_error("Can't set non-blocking on network socket"); } - nl_socket_add_membership(ev_sock_, RTNLGRP_LINK); - if (family_ == AF_INET) { - nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR); - } else { - nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR); - } + nl_socket_add_memberships(ev_sock_, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, 0); if (!config_["interface"].isString()) { - if (family_ == AF_INET) { - nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_ROUTE); - } else { - nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_ROUTE); - } + nl_socket_add_memberships(ev_sock_, RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, 0); } efd_ = epoll_create1(EPOLL_CLOEXEC); @@ -531,10 +516,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { return NL_OK; } - if (ifa->ifa_family != net->family_) { - return NL_OK; - } - // We ignore address mark as scope for the link or host, // which should leave scope global addresses. if (ifa->ifa_scope >= RT_SCOPE_LINK) { @@ -591,6 +572,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { // to find the interface used to reach the outside world struct rtmsg *rtm = static_cast(NLMSG_DATA(nh)); + int family = rtm->rtm_family; ssize_t attrlen = RTM_PAYLOAD(nh); struct rtattr *attr = RTM_RTA(rtm); bool has_gateway = false; @@ -618,14 +600,14 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { * If someone ever needs to figure out the gateway address as well, * it's here as the attribute payload. */ - inet_ntop(net->family_, RTA_DATA(attr), temp_gw_addr, sizeof(temp_gw_addr)); + inet_ntop(family, RTA_DATA(attr), temp_gw_addr, sizeof(temp_gw_addr)); has_gateway = true; break; case RTA_DST: { /* The destination address. * Should be either missing, or maybe all 0s. Accept both. */ - const uint32_t nr_zeroes = (net->family_ == AF_INET) ? 4 : 16; + const uint32_t nr_zeroes = (family == AF_INET) ? 4 : 16; unsigned char c = 0; size_t dstlen = RTA_PAYLOAD(attr); if (dstlen != nr_zeroes) { @@ -717,7 +699,6 @@ void waybar::modules::Network::askForStateDump(void) { }; if (want_route_dump_) { - rt_hdr.rtgen_family = family_; nl_send_simple(ev_sock_, RTM_GETROUTE, NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); want_route_dump_ = false; dump_in_progress_ = true; @@ -728,7 +709,6 @@ void waybar::modules::Network::askForStateDump(void) { dump_in_progress_ = true; } else if (want_addr_dump_) { - rt_hdr.rtgen_family = family_; nl_send_simple(ev_sock_, RTM_GETADDR, NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); want_addr_dump_ = false; dump_in_progress_ = true; From 43af1b9ea00eceb32fec081445959c0e9b5dc94c Mon Sep 17 00:00:00 2001 From: twistedlogic Date: Wed, 11 Dec 2024 22:18:00 -0400 Subject: [PATCH 730/842] feat: implement hide vacant for river --- man/waybar-river-tags.5.scd | 5 +++++ src/modules/river/tags.cpp | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/man/waybar-river-tags.5.scd b/man/waybar-river-tags.5.scd index 5669456a..64621229 100644 --- a/man/waybar-river-tags.5.scd +++ b/man/waybar-river-tags.5.scd @@ -31,6 +31,11 @@ Addressed by *river/tags* default: false ++ Enables this module to consume all left over space dynamically. +*hide-vacant*: ++ + typeof: bool ++ + default: false ++ + Only show relevant tags: tags that are either focused or have a window on them. + # EXAMPLE ``` diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index 9e7cd5aa..26c8e3ad 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -189,11 +189,18 @@ bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) { } void Tags::handle_focused_tags(uint32_t tags) { + auto hide_vacant = config_["hide-vacant"].asBool(); for (size_t i = 0; i < buttons_.size(); ++i) { if ((1 << i) & tags) { + if (hide_vacant) { + buttons_[i].set_visible(true); + } buttons_[i].get_style_context()->add_class("focused"); } else { buttons_[i].get_style_context()->remove_class("focused"); + if (hide_vacant && !buttons_[i].get_style_context()->has_class("occupied")) { + buttons_[i].set_visible(false); + } } } } @@ -205,10 +212,17 @@ void Tags::handle_view_tags(struct wl_array *view_tags) { for (; view_tag < end; ++view_tag) { tags |= *view_tag; } + auto hide_vacant = config_["hide-vacant"].asBool(); for (size_t i = 0; i < buttons_.size(); ++i) { if ((1 << i) & tags) { + if (hide_vacant) { + buttons_[i].set_visible(true); + } buttons_[i].get_style_context()->add_class("occupied"); } else { + if (hide_vacant) { + buttons_[i].set_visible(false); + } buttons_[i].get_style_context()->remove_class("occupied"); } } From 8024df0430b2e031aa70fefb9b9655623fa9d412 Mon Sep 17 00:00:00 2001 From: twistedlogic Date: Wed, 11 Dec 2024 22:50:01 -0400 Subject: [PATCH 731/842] fix: edge case where tags get hidden after all views are killed This fixes an edge case where focused tags would get hidden if all clients on a tag get killed --- src/modules/river/tags.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index 26c8e3ad..5ba67f2f 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -220,7 +220,7 @@ void Tags::handle_view_tags(struct wl_array *view_tags) { } buttons_[i].get_style_context()->add_class("occupied"); } else { - if (hide_vacant) { + if (hide_vacant && !buttons_[i].get_style_context()->has_class("focused")) { buttons_[i].set_visible(false); } buttons_[i].get_style_context()->remove_class("occupied"); From 8e0964ad15688c23c6e0738b76e19686390162e9 Mon Sep 17 00:00:00 2001 From: twistedlogic Date: Thu, 12 Dec 2024 10:11:11 -0400 Subject: [PATCH 732/842] feat: is visible and urgent checks as well --- src/modules/river/tags.cpp | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index 5ba67f2f..359e5a23 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -191,16 +191,19 @@ bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) { void Tags::handle_focused_tags(uint32_t tags) { auto hide_vacant = config_["hide-vacant"].asBool(); for (size_t i = 0; i < buttons_.size(); ++i) { + bool visible = buttons_[i].is_visible(); + bool occupied = buttons_[i].get_style_context()->has_class("occupied"); + bool urgent = buttons_[i].get_style_context()->has_class("urgent"); if ((1 << i) & tags) { - if (hide_vacant) { + if (hide_vacant && !visible) { buttons_[i].set_visible(true); } buttons_[i].get_style_context()->add_class("focused"); } else { - buttons_[i].get_style_context()->remove_class("focused"); - if (hide_vacant && !buttons_[i].get_style_context()->has_class("occupied")) { + if (hide_vacant && !(occupied || urgent)) { buttons_[i].set_visible(false); } + buttons_[i].get_style_context()->remove_class("focused"); } } } @@ -214,13 +217,16 @@ void Tags::handle_view_tags(struct wl_array *view_tags) { } auto hide_vacant = config_["hide-vacant"].asBool(); for (size_t i = 0; i < buttons_.size(); ++i) { + bool visible = buttons_[i].is_visible(); + bool focused = buttons_[i].get_style_context()->has_class("focused"); + bool urgent = buttons_[i].get_style_context()->has_class("urgent"); if ((1 << i) & tags) { - if (hide_vacant) { + if (hide_vacant && !visible) { buttons_[i].set_visible(true); } buttons_[i].get_style_context()->add_class("occupied"); } else { - if (hide_vacant && !buttons_[i].get_style_context()->has_class("focused")) { + if (hide_vacant && !(focused || urgent)) { buttons_[i].set_visible(false); } buttons_[i].get_style_context()->remove_class("occupied"); @@ -229,10 +235,20 @@ void Tags::handle_view_tags(struct wl_array *view_tags) { } void Tags::handle_urgent_tags(uint32_t tags) { + auto hide_vacant = config_["hide-vacant"].asBool(); for (size_t i = 0; i < buttons_.size(); ++i) { + bool visible = buttons_[i].is_visible(); + bool occupied = buttons_[i].get_style_context()->has_class("occupied"); + bool focused = buttons_[i].get_style_context()->has_class("focused"); if ((1 << i) & tags) { + if (hide_vacant && !visible) { + buttons_[i].set_visible(true); + } buttons_[i].get_style_context()->add_class("urgent"); } else { + if (hide_vacant && !(occupied || focused)) { + buttons_[i].set_visible(false); + } buttons_[i].get_style_context()->remove_class("urgent"); } } From 01ae117cfe322fcbd8734e272733c9fca785b564 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Sat, 14 Dec 2024 21:02:10 +0800 Subject: [PATCH 733/842] fix: hyprland/window get empty ipc json data --- src/modules/hyprland/window.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 152eea03..decb2565 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -12,10 +12,18 @@ #include "util/rewrite_string.hpp" #include "util/sanitize_str.hpp" +#include +#include + namespace waybar::modules::hyprland { +std::shared_mutex windowIpcSmtx; + Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { + + std::unique_lock windowIpcUniqueLock(windowIpcSmtx); + modulesReady = true; separateOutputs_ = config["separate-outputs"].asBool(); @@ -23,27 +31,28 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) gIPC = std::make_unique(); } - queryActiveWorkspace(); - update(); - dp.emit(); - // register for hyprland ipc gIPC->registerForIPC("activewindow", this); gIPC->registerForIPC("closewindow", this); gIPC->registerForIPC("movewindow", this); gIPC->registerForIPC("changefloatingmode", this); gIPC->registerForIPC("fullscreen", this); + + windowIpcUniqueLock.unlock(); + + queryActiveWorkspace(); + update(); + dp.emit(); } Window::~Window() { + std::unique_lock windowIpcUniqueLock(windowIpcSmtx); gIPC->unregisterForIPC(this); - // wait for possible event handler to finish - std::lock_guard lg(mutex_); } auto Window::update() -> void { - // fix ampersands - std::lock_guard lg(mutex_); + + std::shared_lock windowIpcShareLock(windowIpcSmtx); std::string windowName = waybar::util::sanitize_string(workspace_.last_window_title); std::string windowAddress = workspace_.last_window; @@ -144,7 +153,8 @@ auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData { } void Window::queryActiveWorkspace() { - std::lock_guard lg(mutex_); + + std::shared_lock windowIpcShareLock(windowIpcSmtx); if (separateOutputs_) { workspace_ = getActiveWorkspace(this->bar_.output->name); From 157ea445102b0bc2db02733095a4350c7b2089d1 Mon Sep 17 00:00:00 2001 From: "K. Adam Christensen" Date: Sat, 14 Dec 2024 09:06:13 -0800 Subject: [PATCH 734/842] Escape markup characters in dwl/window Without this, markup characters like [&><] will be injected directly into the Label. Escaping them makes sure that the values will be printed exactly as they appear in the window title or layout symbol. Signed-off-by: K. Adam Christensen --- src/modules/dwl/window.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/modules/dwl/window.cpp b/src/modules/dwl/window.cpp index 870d87e4..8fa826b1 100644 --- a/src/modules/dwl/window.cpp +++ b/src/modules/dwl/window.cpp @@ -9,6 +9,7 @@ #include "client.hpp" #include "dwl-ipc-unstable-v2-client-protocol.h" +#include "glibmm/markup.h" #include "util/rewrite_string.hpp" namespace waybar::modules::dwl { @@ -97,11 +98,17 @@ Window::~Window() { } } -void Window::handle_title(const char *title) { title_ = title; } +void Window::handle_title(const char *title) { + title_ = Glib::Markup::escape_text(title); +} -void Window::handle_appid(const char *appid) { appid_ = appid; } +void Window::handle_appid(const char *appid) { + appid_ = Glib::Markup::escape_text(appid); +} -void Window::handle_layout_symbol(const char *layout_symbol) { layout_symbol_ = layout_symbol; } +void Window::handle_layout_symbol(const char *layout_symbol) { + layout_symbol_ = Glib::Markup::escape_text(layout_symbol); +} void Window::handle_layout(const uint32_t layout) { layout_ = layout; } From 8e276bb3f66be36740fef5209e96fdd9adcfbe13 Mon Sep 17 00:00:00 2001 From: Carlo Teubner Date: Tue, 17 Dec 2024 22:05:16 +0000 Subject: [PATCH 735/842] sway: fix "Mapping is not an object" warning Fixes #3763. Also a little code simplications while we're at it. --- include/modules/sway/workspaces.hpp | 1 - src/modules/sway/workspaces.cpp | 15 +++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 97f4e950..58c173ec 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -49,7 +49,6 @@ class Workspaces : public AModule, public sigc::trackable { 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_; diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 33d4bb29..dec5cddf 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -62,14 +62,13 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value 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, - [](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); + if (windowRewrite.isObject()) { + const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"]; + std::string windowRewriteDefault = + windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?"; + m_windowRewriteRules = waybar::util::RegexCollection( + windowRewrite, std::move(windowRewriteDefault), windowRewritePriorityFunction); + } ipc_.subscribe(R"(["workspace"])"); ipc_.subscribe(R"(["window"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent)); From 884550964ec8f14283592740bfd4162673b53fd0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 1 Jan 2025 00:11:33 +0000 Subject: [PATCH 736/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-compat': 'github:edolstra/flake-compat/9ed2ac151eada2306ca8c418ebd97807bb08f6ac?narHash=sha256-HRJ/18p%2BWoXpWJkcdsk9St5ZiukCqSDgbOGFa8Okehg%3D' (2024-11-27) → 'github:edolstra/flake-compat/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec?narHash=sha256-NeCCThCEP3eCl2l/%2B27kNNK7QrwZB1IJCrXfrbv5oqU%3D' (2024-12-04) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/970e93b9f82e2a0f3675757eb0bfc73297cc6370?narHash=sha256-jNRNr49UiuIwaarqijgdTR2qLPifxsVhlJrKzQ8XUIE%3D' (2024-11-28) → 'github:NixOS/nixpkgs/88195a94f390381c6afcdaa933c2f6ff93959cb4?narHash=sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs%3D' (2024-12-29) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 24b83454..2b8235c4 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1732722421, - "narHash": "sha256-HRJ/18p+WoXpWJkcdsk9St5ZiukCqSDgbOGFa8Okehg=", + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", "owner": "edolstra", "repo": "flake-compat", - "rev": "9ed2ac151eada2306ca8c418ebd97807bb08f6ac", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "type": "github" }, "original": { @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1732837521, - "narHash": "sha256-jNRNr49UiuIwaarqijgdTR2qLPifxsVhlJrKzQ8XUIE=", + "lastModified": 1735471104, + "narHash": "sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "970e93b9f82e2a0f3675757eb0bfc73297cc6370", + "rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4", "type": "github" }, "original": { From 3555417a4f62d0122d303ae20d756a1f35daec52 Mon Sep 17 00:00:00 2001 From: JasonnnW3000 Date: Wed, 1 Jan 2025 06:34:11 -0500 Subject: [PATCH 737/842] Update LICENSE, fix license year Signed-off-by: JasonnnW3000 --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 41eb81d8..d1bad1b4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Alex +Copyright (c) 2025 Alex Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From d1dac2854a2b58c9baab569370415edff3bba299 Mon Sep 17 00:00:00 2001 From: Ethan Martin Date: Thu, 2 Jan 2025 20:50:39 -0500 Subject: [PATCH 738/842] Allow using wildcards in config include paths Updates `Config::tryExpandPath()` to return a vector of expanded path matches instead of a single path wrapped in an optional, with an empty vector indicating no matches. `Config::resolveConfigIncludes()` iterates over all of these matches, while other instances of path expansion (such as finding the base config path) retain their existing behavior and only use the first match. --- include/config.hpp | 4 ++-- src/ALabel.cpp | 4 ++-- src/config.cpp | 34 ++++++++++++++++++++-------------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/include/config.hpp b/include/config.hpp index 18a1daed..5256bb46 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -20,8 +20,8 @@ class Config { static std::optional findConfigPath( const std::vector &names, const std::vector &dirs = CONFIG_DIRS); - static std::optional tryExpandPath(const std::string &base, - const std::string &filename); + static std::vector tryExpandPath(const std::string &base, + const std::string &filename); Config() = default; diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 467572f1..c218e402 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -68,11 +68,11 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st // there might be "~" or "$HOME" in original path, try to expand it. auto result = Config::tryExpandPath(menuFile, ""); - if (!result.has_value()) { + if (result.empty()) { throw std::runtime_error("Failed to expand file: " + menuFile); } - menuFile = result.value(); + menuFile = result.front(); // Read the menu descriptor file std::ifstream file(menuFile); if (!file.is_open()) { diff --git a/src/config.cpp b/src/config.cpp index 375dc4cb..7096ba89 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -21,8 +21,8 @@ const std::vector Config::CONFIG_DIRS = { const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR"; -std::optional Config::tryExpandPath(const std::string &base, - const std::string &filename) { +std::vector Config::tryExpandPath(const std::string &base, + const std::string &filename) { fs::path path; if (!filename.empty()) { @@ -33,33 +33,35 @@ std::optional Config::tryExpandPath(const std::string &base, spdlog::debug("Try expanding: {}", path.string()); + std::vector results; wordexp_t p; if (wordexp(path.c_str(), &p, 0) == 0) { - if (access(*p.we_wordv, F_OK) == 0) { - std::string result = *p.we_wordv; - wordfree(&p); - spdlog::debug("Found config file: {}", path.string()); - return result; + for (size_t i = 0; i < p.we_wordc; i++) { + if (access(p.we_wordv[i], F_OK) == 0) { + results.emplace_back(p.we_wordv[i]); + spdlog::debug("Found config file: {}", p.we_wordv[i]); + } } wordfree(&p); } - return std::nullopt; + + return results; } std::optional Config::findConfigPath(const std::vector &names, const std::vector &dirs) { if (const char *dir = std::getenv(Config::CONFIG_PATH_ENV)) { for (const auto &name : names) { - if (auto res = tryExpandPath(dir, name); res) { - return res; + if (auto res = tryExpandPath(dir, name); !res.empty()) { + return res.front(); } } } for (const auto &dir : dirs) { for (const auto &name : names) { - if (auto res = tryExpandPath(dir, name); res) { - return res; + if (auto res = tryExpandPath(dir, name); !res.empty()) { + return res.front(); } } } @@ -92,11 +94,15 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) { if (includes.isArray()) { for (const auto &include : includes) { spdlog::info("Including resource file: {}", include.asString()); - setupConfig(config, tryExpandPath(include.asString(), "").value_or(""), ++depth); + for (const auto &match : tryExpandPath(include.asString(), "")) { + setupConfig(config, match, depth + 1); + } } } else if (includes.isString()) { spdlog::info("Including resource file: {}", includes.asString()); - setupConfig(config, tryExpandPath(includes.asString(), "").value_or(""), ++depth); + for (const auto &match : tryExpandPath(includes.asString(), "")) { + setupConfig(config, match, depth + 1); + } } } From 865121b21dcdf3bf6733562e90a94efeeeb006ff Mon Sep 17 00:00:00 2001 From: "Sv. Lockal" Date: Sat, 4 Jan 2025 10:28:14 +0000 Subject: [PATCH 739/842] Fix compilation with libc++ This file uses std::sort and does not import correct header. Compilation with libstdc++ worked due to some indirect import, but compilation with LLVM libc++ fails. --- src/util/regex_collection.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/regex_collection.cpp b/src/util/regex_collection.cpp index 929e67cd..51dd6ff7 100644 --- a/src/util/regex_collection.cpp +++ b/src/util/regex_collection.cpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace waybar::util { From 6a29abb49e9aa1acd591d98dadb9cd1d6b6d3fff Mon Sep 17 00:00:00 2001 From: Alex Murkoff <413x1nkp@gmail.com> Date: Fri, 10 Jan 2025 15:27:41 +0700 Subject: [PATCH 740/842] fix: never sleep cava when sleep_timer is 0 --- src/modules/cava.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index f16d3f63..405a351a 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -139,7 +139,7 @@ auto waybar::modules::Cava::update() -> void { } } - if (silence_ && prm_.sleep_timer) { + if (silence_ && prm_.sleep_timer != 0) { if (sleep_counter_ <= (int)(std::chrono::milliseconds(prm_.sleep_timer * 1s) / frame_time_milsec_)) { ++sleep_counter_; @@ -147,7 +147,7 @@ auto waybar::modules::Cava::update() -> void { } } - if (!silence_) { + if (!silence_ || prm_.sleep_timer == 0) { downThreadDelay(frame_time_milsec_, suspend_silence_delay_); // Process: execute cava pthread_mutex_lock(&audio_data_.lock); From 0992bf1b879a4780aa47d5847ac5e15e3fd2f150 Mon Sep 17 00:00:00 2001 From: Pol Rivero <65060696+pol-rivero@users.noreply.github.com> Date: Thu, 9 Jan 2025 07:33:52 +0100 Subject: [PATCH 741/842] Escape tray tooltip text Fix errors when the tooltip set by the tray apps contains markup characters --- src/modules/sni/item.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 6c4ec8c0..b3e84885 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -124,7 +124,8 @@ ToolTip get_variant(const Glib::VariantBase& value) { result.text = get_variant(container.get_child(2)); auto description = get_variant(container.get_child(3)); if (!description.empty()) { - result.text = fmt::format("{}\n{}", result.text, description); + auto escapedDescription = Glib::Markup::escape_text(description); + result.text = fmt::format("{}\n{}", result.text, escapedDescription); } return result; } From 6004316f1a13217313a2bce9403c261e7fe941e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torstein=20Huseb=C3=B8?= Date: Thu, 16 Jan 2025 13:24:13 +0100 Subject: [PATCH 742/842] Fix typos in function, variable names and in documentation --- include/modules/hyprland/windowcreationpayload.hpp | 2 +- include/modules/hyprland/workspace.hpp | 4 ++-- include/modules/hyprland/workspaces.hpp | 2 +- include/modules/sway/language.hpp | 4 ++-- include/modules/sway/workspaces.hpp | 2 +- include/util/prepare_for_sleep.h | 2 +- man/waybar-menu.5.scd | 2 +- man/waybar.5.scd.in | 2 +- src/AModule.cpp | 4 ++-- src/modules/hyprland/submap.cpp | 2 +- src/modules/hyprland/windowcreationpayload.cpp | 2 +- src/modules/hyprland/workspace.cpp | 14 +++++++------- src/modules/hyprland/workspaces.cpp | 2 +- src/modules/power_profiles_daemon.cpp | 4 ++-- src/modules/sway/language.cpp | 4 ++-- src/modules/sway/workspaces.cpp | 8 ++++---- src/modules/temperature.cpp | 4 ++-- 17 files changed, 32 insertions(+), 32 deletions(-) diff --git a/include/modules/hyprland/windowcreationpayload.hpp b/include/modules/hyprland/windowcreationpayload.hpp index 45229ed4..e4180ed9 100644 --- a/include/modules/hyprland/windowcreationpayload.hpp +++ b/include/modules/hyprland/windowcreationpayload.hpp @@ -42,7 +42,7 @@ class WindowCreationPayload { std::string getWorkspaceName() const { return m_workspaceName; } WindowAddress getAddress() const { return m_windowAddress; } - void moveToWorksace(std::string& new_workspace_name); + void moveToWorkspace(std::string& new_workspace_name); private: void clearAddr(); diff --git a/include/modules/hyprland/workspace.hpp b/include/modules/hyprland/workspace.hpp index f1fea4e8..b67d8e14 100644 --- a/include/modules/hyprland/workspace.hpp +++ b/include/modules/hyprland/workspace.hpp @@ -55,11 +55,11 @@ class Workspace { void setName(std::string const& value) { m_name = value; }; void setOutput(std::string const& value) { m_output = value; }; bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); } - void insertWindow(WindowCreationPayload create_window_paylod); + void insertWindow(WindowCreationPayload create_window_payload); std::string removeWindow(WindowAddress const& addr); void initializeWindowMap(const Json::Value& clients_data); - bool onWindowOpened(WindowCreationPayload const& create_window_paylod); + bool onWindowOpened(WindowCreationPayload const& create_window_payload); std::optional closeWindow(WindowAddress const& addr); void update(const std::string& format, const std::string& icon); diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index a9d56b79..a2a8558c 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -118,7 +118,7 @@ class Workspaces : public AModule, public EventHandler { // 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) + // and doesn't share windows across bars (a.k.a `all-outputs` = false) std::map m_orphanWindowMap; enum class SortMethod { ID, NAME, NUMBER, DEFAULT }; diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index ea58c4f0..91aa181d 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -21,7 +21,7 @@ class Language : public ALabel, public sigc::trackable { auto update() -> void override; private: - enum class DispayedShortFlag { None = 0, ShortName = 1, ShortDescription = 1 << 1 }; + enum class DisplayedShortFlag { None = 0, ShortName = 1, ShortDescription = 1 << 1 }; struct Layout { std::string full_name; @@ -58,7 +58,7 @@ class Language : public ALabel, public sigc::trackable { std::map layouts_map_; bool hide_single_; bool is_variant_displayed; - std::byte displayed_short_flag = static_cast(DispayedShortFlag::None); + std::byte displayed_short_flag = static_cast(DisplayedShortFlag::None); util::JsonParser parser_; std::mutex mutex_; diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 58c173ec..d8a9e18a 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -48,7 +48,7 @@ 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_formatWindowSeparator; util::RegexCollection m_windowRewriteRules; util::JsonParser parser_; std::unordered_map buttons_; diff --git a/include/util/prepare_for_sleep.h b/include/util/prepare_for_sleep.h index 68db8d8e..82f3b627 100644 --- a/include/util/prepare_for_sleep.h +++ b/include/util/prepare_for_sleep.h @@ -4,6 +4,6 @@ namespace waybar::util { -// Get a signal emited with value true when entering sleep, and false when exiting +// Get a signal emitted with value true when entering sleep, and false when exiting SafeSignal& prepare_for_sleep(); } // namespace waybar::util diff --git a/man/waybar-menu.5.scd b/man/waybar-menu.5.scd index 19790ed4..47e10432 100644 --- a/man/waybar-menu.5.scd +++ b/man/waybar-menu.5.scd @@ -7,7 +7,7 @@ waybar - menu property # OVERVIEW -Some modules support a 'menu', which allows to have a popup menu whan a defined +Some modules support a 'menu', which allows to have a popup menu when a defined click is done over the module. # PROPERTIES diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 5bb62724..e2e0627d 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -86,7 +86,7 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles( *no-center* ++ typeof: bool ++ default: false ++ - Option to disable the center modules fully usefull together with expand-\*. + Option to disable the center modules fully useful together with expand-\*. *spacing* ++ typeof: integer ++ diff --git a/src/AModule.cpp b/src/AModule.cpp index 5abb779a..7de1ad9a 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -97,11 +97,11 @@ auto AModule::update() -> void { } } // Get mapping between event name and module action name -// Then call overrided doAction in order to call appropriate module action +// Then call overridden doAction in order to call appropriate module action auto AModule::doAction(const std::string& name) -> void { if (!name.empty()) { const std::map::const_iterator& recA{eventActionMap_.find(name)}; - // Call overrided action if derrived class has implemented it + // Call overridden action if derived class has implemented it if (recA != eventActionMap_.cend() && name != recA->second) this->doAction(recA->second); } } diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 96677d12..67aaeac0 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -20,7 +20,7 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) ALabel::update(); // Displays widget immediately if always_on_ assuming default submap - // Needs an actual way to retrive current submap on startup + // Needs an actual way to retrieve current submap on startup if (always_on_) { submap_ = default_submap_; label_.get_style_context()->add_class(submap_); diff --git a/src/modules/hyprland/windowcreationpayload.cpp b/src/modules/hyprland/windowcreationpayload.cpp index df7fe784..2a62ac12 100644 --- a/src/modules/hyprland/windowcreationpayload.cpp +++ b/src/modules/hyprland/windowcreationpayload.cpp @@ -88,7 +88,7 @@ bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) { int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; } -void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) { +void WindowCreationPayload::moveToWorkspace(std::string &new_workspace_name) { m_workspaceName = new_workspace_name; } diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index e575d1c4..048d50c1 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -90,19 +90,19 @@ void Workspace::initializeWindowMap(const Json::Value &clients_data) { } } -void Workspace::insertWindow(WindowCreationPayload create_window_paylod) { - if (!create_window_paylod.isEmpty(m_workspaceManager)) { - auto repr = create_window_paylod.repr(m_workspaceManager); +void Workspace::insertWindow(WindowCreationPayload create_window_payload) { + if (!create_window_payload.isEmpty(m_workspaceManager)) { + auto repr = create_window_payload.repr(m_workspaceManager); if (!repr.empty()) { - m_windowMap[create_window_paylod.getAddress()] = repr; + m_windowMap[create_window_payload.getAddress()] = repr; } } }; -bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) { - if (create_window_paylod.getWorkspaceName() == name()) { - insertWindow(create_window_paylod); +bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_payload) { + if (create_window_payload.getWorkspaceName() == name()) { + insertWindow(create_window_payload); return true; } return false; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 13364f3f..1cf8a184 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -487,7 +487,7 @@ void Workspaces::onWindowMoved(std::string const &payload) { // and exit for (auto &window : m_windowsToCreate) { if (window.getAddress() == windowAddress) { - window.moveToWorksace(workspaceName); + window.moveToWorkspace(workspaceName); return; } } diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index 3ae3ae83..abad763d 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -29,14 +29,14 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // 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 + // NOTE: the DBus addresses 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. + // addresses for compatibility sake. // // Revisit this in 2026, systems should be updated by then. diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index a005df17..f4cfa6c3 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -22,10 +22,10 @@ Language::Language(const std::string& id, const Json::Value& config) 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); + displayed_short_flag |= static_cast(DisplayedShortFlag::ShortName); } if (format_.find("{shortDescription}") != std::string::npos) { - displayed_short_flag |= static_cast(DispayedShortFlag::ShortDescription); + displayed_short_flag |= static_cast(DisplayedShortFlag::ShortDescription); } if (config.isMember("tooltip-format")) { tooltip_format_ = config["tooltip-format"].asString(); diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index dec5cddf..6349d6cc 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -57,9 +57,9 @@ 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(); + m_formatWindowSeparator = config_["format-window-separator"].asString(); } else { - m_formatWindowSeperator = " "; + m_formatWindowSeparator = " "; } const Json::Value &windowRewrite = config["window-rewrite"]; if (windowRewrite.isObject()) { @@ -271,7 +271,7 @@ void Workspaces::updateWindows(const Json::Value &node, std::string &windows) { window = fmt::format(fmt::runtime(window), fmt::arg("name", title), fmt::arg("class", windowClass)); windows.append(window); - windows.append(m_formatWindowSeperator); + windows.append(m_formatWindowSeparator); } } for (const Json::Value &child : node["nodes"]) { @@ -340,7 +340,7 @@ auto Workspaces::update() -> void { 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())), + windows.substr(0, windows.length() - m_formatWindowSeparator.length())), fmt::arg("output", (*it)["output"].asString())); } if (!config_["disable-markup"].asBool()) { diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 89d3db2d..2904417b 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -45,7 +45,7 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); } - // check if file_path_ can be used to retrive the temperature + // check if file_path_ can be used to retrieve the temperature std::ifstream temp(file_path_); if (!temp.is_open()) { throw std::runtime_error("Can't open " + file_path_); @@ -148,4 +148,4 @@ bool waybar::modules::Temperature::isWarning(uint16_t temperature_c) { bool waybar::modules::Temperature::isCritical(uint16_t temperature_c) { return config_["critical-threshold"].isInt() && temperature_c >= config_["critical-threshold"].asInt(); -} \ No newline at end of file +} From ba12ca71c080704f631a960e321d5fe89b9da407 Mon Sep 17 00:00:00 2001 From: Duckulus Date: Wed, 22 Jan 2025 18:39:50 +0100 Subject: [PATCH 743/842] enable tooltip for hyprland window module --- src/modules/hyprland/window.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index decb2565..ba0a5321 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -59,18 +59,35 @@ auto Window::update() -> void { windowData_.title = windowName; + std::string label_text; if (!format_.empty()) { label_.show(); - label_.set_markup(waybar::util::rewriteString( + label_text = waybar::util::rewriteString( 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"])); + config_["rewrite"]); + label_.set_markup(label_text); } else { label_.hide(); } + if (tooltipEnabled()) { + std::string tooltip_format; + if (config_["tooltip-format"].isString()) { + tooltip_format = config_["tooltip-format"].asString(); + } + if (!tooltip_format.empty()) { + label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName), + fmt::arg("initialTitle", windowData_.initial_title), + fmt::arg("class", windowData_.class_name), + fmt::arg("initialClass", windowData_.initial_class_name))); + } else if (!label_text.empty()){ + label_.set_tooltip_text(label_text); + } + } + if (focused_) { image_.show(); } else { @@ -153,7 +170,6 @@ auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData { } void Window::queryActiveWorkspace() { - std::shared_lock windowIpcShareLock(windowIpcSmtx); if (separateOutputs_) { From bb2c67ebadb0c7c48a4e12a8bf16c1a225e57ed4 Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Sat, 25 Jan 2025 09:24:48 +0100 Subject: [PATCH 744/842] Revert "Make battery module update on plugging/unplugging again (refs #2519)" --- include/modules/battery.hpp | 8 ++----- include/util/udev_deleter.hpp | 21 ------------------ src/modules/battery.cpp | 40 +++++++++++++++------------------- src/util/backlight_backend.cpp | 17 ++++++++++++++- 4 files changed, 35 insertions(+), 51 deletions(-) delete mode 100644 include/util/udev_deleter.hpp diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index fc403be0..8e1a2ad2 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -5,11 +5,8 @@ #include #if defined(__linux__) #include -#include "util/udev_deleter.hpp" #endif -#include - #include #include #include @@ -39,12 +36,11 @@ class Battery : public ALabel { const std::string formatTimeRemaining(float hoursRemaining); void setBarClass(std::string&); + int global_watch; std::map batteries_; - std::unique_ptr udev_; - std::array poll_fds_; - std::unique_ptr mon_; fs::path adapter_; int battery_watch_fd_; + int global_watch_fd_; std::mutex battery_list_mutex_; std::string old_status_; bool warnFirstTime_{true}; diff --git a/include/util/udev_deleter.hpp b/include/util/udev_deleter.hpp deleted file mode 100644 index b2f1e538..00000000 --- a/include/util/udev_deleter.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -namespace waybar::util { -struct UdevDeleter { - void operator()(udev *ptr) const { udev_unref(ptr); } -}; - -struct UdevDeviceDeleter { - void operator()(udev_device *ptr) const { udev_device_unref(ptr); } -}; - -struct UdevEnumerateDeleter { - void operator()(udev_enumerate *ptr) const { udev_enumerate_unref(ptr); } -}; - -struct UdevMonitorDeleter { - void operator()(udev_monitor *ptr) const { udev_monitor_unref(ptr); } -}; -} // namespace waybar::util \ No newline at end of file diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index bad72e6b..d87cc612 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -5,9 +5,6 @@ #include #endif #include -#include -#include -#include #include waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const Json::Value& config) @@ -17,18 +14,17 @@ waybar::modules::Battery::Battery(const std::string& id, const Bar& bar, const J if (battery_watch_fd_ == -1) { throw std::runtime_error("Unable to listen batteries."); } - udev_ = std::unique_ptr(udev_new()); - if (udev_ == nullptr) { - throw std::runtime_error("udev_new failed"); + + global_watch_fd_ = inotify_init1(IN_CLOEXEC); + if (global_watch_fd_ == -1) { + throw std::runtime_error("Unable to listen batteries."); } - mon_ = std::unique_ptr(udev_monitor_new_from_netlink(udev_.get(), "kernel")); - if (mon_ == nullptr) { - throw std::runtime_error("udev monitor new failed"); + + // Watch the directory for any added or removed batteries + global_watch = inotify_add_watch(global_watch_fd_, data_dir_.c_str(), IN_CREATE | IN_DELETE); + if (global_watch < 0) { + throw std::runtime_error("Could not watch for battery plug/unplug"); } - if (udev_monitor_filter_add_match_subsystem_devtype(mon_.get(), "power_supply", nullptr) < 0) { - throw std::runtime_error("udev failed to add monitor filter"); - } - udev_monitor_enable_receiving(mon_.get()); #endif worker(); } @@ -37,6 +33,11 @@ waybar::modules::Battery::~Battery() { #if defined(__linux__) std::lock_guard guard(battery_list_mutex_); + if (global_watch >= 0) { + inotify_rm_watch(global_watch_fd_, global_watch); + } + close(global_watch_fd_); + for (auto it = batteries_.cbegin(), next_it = it; it != batteries_.cend(); it = next_it) { ++next_it; auto watch_id = (*it).second; @@ -73,18 +74,12 @@ void waybar::modules::Battery::worker() { dp.emit(); }; thread_battery_update_ = [this] { - poll_fds_[0].revents = 0; - poll_fds_[0].events = POLLIN; - poll_fds_[0].fd = udev_monitor_get_fd(mon_.get()); - int ret = poll(poll_fds_.data(), poll_fds_.size(), -1); - if (ret < 0) { + struct inotify_event event = {0}; + int nbytes = read(global_watch_fd_, &event, sizeof(event)); + if (nbytes != sizeof(event) || event.mask & IN_IGNORED) { thread_.stop(); return; } - if ((poll_fds_[0].revents & POLLIN) != 0) { - signalfd_siginfo signal_info; - read(poll_fds_[0].fd, &signal_info, sizeof(signal_info)); - } refreshBatteries(); dp.emit(); }; @@ -673,7 +668,6 @@ auto waybar::modules::Battery::update() -> void { status = getAdapterStatus(capacity); } auto status_pretty = status; - puts(status.c_str()); // Transform to lowercase and replace space with dash std::transform(status.begin(), status.end(), status.begin(), [](char ch) { return ch == ' ' ? '-' : std::tolower(ch); }); diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index 86b0f894..41236462 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -1,5 +1,4 @@ #include "util/backlight_backend.hpp" -#include "util/udev_deleter.hpp" #include #include @@ -30,6 +29,22 @@ class FileDescriptor { 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)); From 481b01d9af1e34b7501665bbfc0b19fce5082eb5 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 25 Jan 2025 09:31:32 +0100 Subject: [PATCH 745/842] fix: lint --- include/modules/cava.hpp | 12 ++++++------ include/modules/clock.hpp | 6 +++--- include/modules/hyprland/workspaces.hpp | 4 ++-- include/util/clara.hpp | 12 ++++++------ src/modules/bluetooth.cpp | 17 +++++++++-------- src/modules/clock.cpp | 4 ++-- src/modules/dwl/window.cpp | 8 ++------ src/modules/hyprland/window.cpp | 8 ++------ src/modules/hyprland/workspaces.cpp | 4 ++-- src/modules/wlr/workspace_manager.cpp | 4 ++-- src/util/pipewire/pipewire_backend.cpp | 2 +- 11 files changed, 37 insertions(+), 44 deletions(-) diff --git a/include/modules/cava.hpp b/include/modules/cava.hpp index 219d9302..b4d2325b 100644 --- a/include/modules/cava.hpp +++ b/include/modules/cava.hpp @@ -23,11 +23,11 @@ class Cava final : public ALabel { util::SleeperThread thread_; util::SleeperThread thread_fetch_input_; - 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{}}; + 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 cava::ptr input_source_; // Delay to handle audio source @@ -44,7 +44,7 @@ class Cava final : public ALabel { // Cava method void pause_resume(); // ModuleActionMap - static inline std::map actionMap_{ + static inline std::map actionMap_{ {"mode", &waybar::modules::Cava::pause_resume}}; }; } // namespace waybar::modules diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 0c62b676..40b4f80e 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -51,8 +51,8 @@ class Clock final : public ALabel { day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight) std::string cldText_{""}; // calendar text to print CldMode cldMode_{CldMode::MONTH}; - auto get_calendar(const year_month_day& today, const year_month_day& ymd, - const time_zone* tz) -> const std::string; + auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) + -> const std::string; // get local time zone auto local_zone() -> const time_zone*; @@ -79,7 +79,7 @@ class Clock final : public ALabel { void tz_up(); void tz_down(); // Module Action Map - static inline std::map actionMap_{ + static inline std::map actionMap_{ {"mode", &waybar::modules::Clock::cldModeSwitch}, {"shift_up", &waybar::modules::Clock::cldShift_up}, {"shift_down", &waybar::modules::Clock::cldShift_down}, diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index a9d56b79..f5c20f69 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -61,8 +61,8 @@ class Workspaces : public AModule, public EventHandler { // Config void parseConfig(const Json::Value& config); auto populateIconsMap(const Json::Value& formatIcons) -> void; - static auto populateBoolConfig(const Json::Value& config, const std::string& key, - bool& member) -> void; + static auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member) + -> void; auto populateSortByConfig(const Json::Value& config) -> void; auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; diff --git a/include/util/clara.hpp b/include/util/clara.hpp index da7151fe..73fa5415 100644 --- a/include/util/clara.hpp +++ b/include/util/clara.hpp @@ -622,8 +622,8 @@ inline auto convertInto(std::string const &source, bool &target) -> ParserResult } #ifdef CLARA_CONFIG_OPTIONAL_TYPE template -inline auto convertInto(std::string const &source, - CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult { +inline auto convertInto(std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE &target) + -> ParserResult { T temp; auto result = convertInto(source, temp); if (result) target = std::move(temp); @@ -751,8 +751,8 @@ class ParserBase { public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse(std::string const &exeName, - TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto parse(std::string const &exeName, TokenStream const &tokens) const + -> InternalParseResult = 0; virtual auto cardinality() const -> size_t { return 1; } auto parse(Args const &args) const -> InternalParseResult { @@ -1098,8 +1098,8 @@ struct Parser : ParserBase { using ParserBase::parse; - auto parse(std::string const &exeName, - TokenStream const &tokens) const -> InternalParseResult override { + auto parse(std::string const &exeName, TokenStream const &tokens) const + -> InternalParseResult override { struct ParserInfo { ParserBase const *parser = nullptr; size_t count = 0; diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index c8f1f996..06475a2e 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -49,8 +49,8 @@ auto getBoolProperty(GDBusProxy* proxy, const char* property_name) -> bool { return false; } -auto getOptionalStringProperty(GDBusProxy* proxy, - const char* property_name) -> std::optional { +auto getOptionalStringProperty(GDBusProxy* proxy, const char* property_name) + -> std::optional { auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name); if (gvar) { std::string property_value = g_variant_get_string(gvar, NULL); @@ -345,8 +345,8 @@ auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* m auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged( GDBusObjectManagerClient* manager, GDBusObjectProxy* object_proxy, GDBusProxy* interface_proxy, - GVariant* changed_properties, const gchar* const* invalidated_properties, - gpointer user_data) -> void { + GVariant* changed_properties, const gchar* const* invalidated_properties, gpointer user_data) + -> void { std::string interface_name = g_dbus_proxy_get_interface_name(interface_proxy); std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy)); @@ -395,8 +395,8 @@ auto waybar::modules::Bluetooth::getDeviceBatteryPercentage(GDBusObject* object) return std::nullopt; } -auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, - DeviceInfo& device_info) -> bool { +auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, DeviceInfo& device_info) + -> bool { GDBusProxy* proxy_device = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Device1")); if (proxy_device != NULL) { @@ -462,8 +462,9 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional& connected_devices) -> void { +auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path, + std::vector& connected_devices) + -> void { GList* objects = g_dbus_object_manager_get_objects(manager_.get()); for (GList* l = objects; l != NULL; l = l->next) { GDBusObject* object = G_DBUS_OBJECT(l->data); diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 7f5a4d55..269fa765 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -199,8 +199,8 @@ const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) { return 2u + ceil((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count(); } -auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, - const unsigned line) -> const year_month_weekday { +auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line) + -> const year_month_weekday { unsigned index{line - 2}; if (weekday{ym / 1} == firstdow) ++index; return ym / firstdow[index]; diff --git a/src/modules/dwl/window.cpp b/src/modules/dwl/window.cpp index 8fa826b1..a960a1f0 100644 --- a/src/modules/dwl/window.cpp +++ b/src/modules/dwl/window.cpp @@ -98,13 +98,9 @@ Window::~Window() { } } -void Window::handle_title(const char *title) { - title_ = Glib::Markup::escape_text(title); -} +void Window::handle_title(const char *title) { title_ = Glib::Markup::escape_text(title); } -void Window::handle_appid(const char *appid) { - appid_ = Glib::Markup::escape_text(appid); -} +void Window::handle_appid(const char *appid) { appid_ = Glib::Markup::escape_text(appid); } void Window::handle_layout_symbol(const char *layout_symbol) { layout_symbol_ = Glib::Markup::escape_text(layout_symbol); diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index decb2565..3a879205 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -6,22 +6,20 @@ #include #include +#include +#include #include #include "modules/hyprland/backend.hpp" #include "util/rewrite_string.hpp" #include "util/sanitize_str.hpp" -#include -#include - namespace waybar::modules::hyprland { std::shared_mutex windowIpcSmtx; Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { - std::unique_lock windowIpcUniqueLock(windowIpcSmtx); modulesReady = true; @@ -51,7 +49,6 @@ Window::~Window() { } auto Window::update() -> void { - std::shared_lock windowIpcShareLock(windowIpcSmtx); std::string windowName = waybar::util::sanitize_string(workspace_.last_window_title); @@ -153,7 +150,6 @@ auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData { } void Window::queryActiveWorkspace() { - std::shared_lock windowIpcShareLock(windowIpcSmtx); if (separateOutputs_) { diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 13364f3f..047703cc 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -590,8 +590,8 @@ auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { m_iconsMap.emplace("", ""); } -auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, - bool &member) -> void { +auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member) + -> void { const auto &configValue = config[key]; if (configValue.isBool()) { member = configValue.asBool(); diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 3c630d81..f556a161 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -118,8 +118,8 @@ auto WorkspaceManager::sort_workspaces() -> void { } } -auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, - uint32_t version) -> void { +auto WorkspaceManager::register_manager(wl_registry *registry, uint32_t name, uint32_t version) + -> void { if (workspace_manager_) { spdlog::warn("Register workspace manager again although already registered!"); return; diff --git a/src/util/pipewire/pipewire_backend.cpp b/src/util/pipewire/pipewire_backend.cpp index 3b897178..6d859a91 100644 --- a/src/util/pipewire/pipewire_backend.cpp +++ b/src/util/pipewire/pipewire_backend.cpp @@ -126,7 +126,7 @@ void PipewireBackend::handleRegistryEventGlobal(uint32_t id, uint32_t permission if (proxy == nullptr) return; auto *pNodeInfo = (PrivacyNodeInfo *)pw_proxy_get_user_data(proxy); - new(pNodeInfo) PrivacyNodeInfo{}; + new (pNodeInfo) PrivacyNodeInfo{}; pNodeInfo->id = id; pNodeInfo->data = this; pNodeInfo->type = mediaType; From 8a741f6ec78e7d5a8dcbb0e26730e80987453ffa Mon Sep 17 00:00:00 2001 From: ladenburger Date: Sat, 1 Feb 2025 00:38:52 +0100 Subject: [PATCH 746/842] fix: JSON format for Hyprland keyboard example --- man/waybar-hyprland-language.5.scd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/man/waybar-hyprland-language.5.scd b/man/waybar-hyprland-language.5.scd index a9a18008..5a7ba941 100644 --- a/man/waybar-hyprland-language.5.scd +++ b/man/waybar-hyprland-language.5.scd @@ -59,9 +59,9 @@ Addressed by *hyprland/language* ``` "hyprland/language": { - "format": "Lang: {long}" - "format-en": "AMERICA, HELL YEAH!" - "format-tr": "As bayrakları" + "format": "Lang: {long}", + "format-en": "AMERICA, HELL YEAH!", + "format-tr": "As bayrakları", "keyboard-name": "at-translated-set-2-keyboard" } ``` From 8bdb5c19069a4286512bfae316e90c947b7cdcce Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 1 Feb 2025 00:10:55 +0000 Subject: [PATCH 747/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/88195a94f390381c6afcdaa933c2f6ff93959cb4?narHash=sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs%3D' (2024-12-29) → 'github:NixOS/nixpkgs/9d3ae807ebd2981d593cddd0080856873139aa40?narHash=sha256-NGqpVVxNAHwIicXpgaVqJEJWeyqzoQJ9oc8lnK9%2BWC4%3D' (2025-01-29) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 2b8235c4..c4bb5dee 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1735471104, - "narHash": "sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs=", + "lastModified": 1738142207, + "narHash": "sha256-NGqpVVxNAHwIicXpgaVqJEJWeyqzoQJ9oc8lnK9+WC4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4", + "rev": "9d3ae807ebd2981d593cddd0080856873139aa40", "type": "github" }, "original": { From e32a6784783c9ae9ac9b633a1dec25973b6a5234 Mon Sep 17 00:00:00 2001 From: Konstantin Vukolov Date: Wed, 5 Feb 2025 00:00:58 +0300 Subject: [PATCH 748/842] Hide upower module when specified device disconnected --- src/modules/upower.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modules/upower.cpp b/src/modules/upower.cpp index 5ee6d64c..4b832b7e 100644 --- a/src/modules/upower.cpp +++ b/src/modules/upower.cpp @@ -358,10 +358,12 @@ void UPower::resetDevices() { void UPower::setDisplayDevice() { std::lock_guard guard{mutex_}; - if (nativePath_.empty() && model_.empty()) { - // Unref current upDevice - if (upDevice_.upDevice != NULL) g_object_unref(upDevice_.upDevice); + if (upDevice_.upDevice != NULL) { + g_object_unref(upDevice_.upDevice); + upDevice_.upDevice = NULL; + } + if (nativePath_.empty() && model_.empty()) { upDevice_.upDevice = up_client_get_display_device(upClient_); getUpDeviceInfo(upDevice_); } else { @@ -386,7 +388,6 @@ void UPower::setDisplayDevice() { } // Unref current upDevice if it exists if (displayDevice.upDevice != NULL) { - if (thisPtr->upDevice_.upDevice != NULL) g_object_unref(thisPtr->upDevice_.upDevice); thisPtr->upDevice_ = displayDevice; } }, From a26ed50d0f42657bf09c69c996238aa71d670c69 Mon Sep 17 00:00:00 2001 From: Bruno Andreotti Date: Fri, 7 Feb 2025 14:39:07 -0300 Subject: [PATCH 749/842] Add support for vertical bars in privacy module --- include/modules/privacy/privacy.hpp | 2 +- include/modules/privacy/privacy_item.hpp | 4 ++-- src/factory.cpp | 2 +- src/modules/privacy/privacy.cpp | 9 +++++---- src/modules/privacy/privacy_item.cpp | 19 ++++++++++++++----- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/include/modules/privacy/privacy.hpp b/include/modules/privacy/privacy.hpp index d7656d31..6179098c 100644 --- a/include/modules/privacy/privacy.hpp +++ b/include/modules/privacy/privacy.hpp @@ -13,7 +13,7 @@ namespace waybar::modules::privacy { class Privacy : public AModule { public: - Privacy(const std::string &, const Json::Value &, const std::string &pos); + Privacy(const std::string &, const Json::Value &, Gtk::Orientation, const std::string &pos); auto update() -> void override; void onPrivacyNodesChanged(); diff --git a/include/modules/privacy/privacy_item.hpp b/include/modules/privacy/privacy_item.hpp index 836bd994..f5f572c0 100644 --- a/include/modules/privacy/privacy_item.hpp +++ b/include/modules/privacy/privacy_item.hpp @@ -17,8 +17,8 @@ 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, const uint icon_size, - const uint transition_duration); + std::list *nodes, Gtk::Orientation orientation, + const std::string &pos, const uint icon_size, const uint transition_duration); enum PrivacyNodeType privacy_type; diff --git a/src/factory.cpp b/src/factory.cpp index 6c2313e3..1483397d 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -140,7 +140,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, #endif #ifdef HAVE_PIPEWIRE if (ref == "privacy") { - return new waybar::modules::privacy::Privacy(id, config_[name], pos); + return new waybar::modules::privacy::Privacy(id, config_[name], bar_.orientation, pos); } #endif #ifdef HAVE_MPRIS diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 97996c33..48bba888 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -15,13 +15,14 @@ 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) +Privacy::Privacy(const std::string& id, const Json::Value& config, Gtk::Orientation orientation, + const std::string& pos) : AModule(config, "privacy", id), nodes_screenshare(), nodes_audio_in(), nodes_audio_out(), visibility_conn(), - box_(Gtk::ORIENTATION_HORIZONTAL, 0) { + box_(orientation, 0) { box_.set_name(name_); event_box_.add(box_); @@ -67,8 +68,8 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st auto iter = typeMap.find(type); if (iter != typeMap.end()) { auto& [nodePtr, nodeType] = iter->second; - auto* item = Gtk::make_managed(module, nodeType, nodePtr, pos, iconSize, - transition_duration); + auto* item = Gtk::make_managed(module, nodeType, nodePtr, orientation, pos, + iconSize, transition_duration); box_.add(*item); } } diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index a38b95a4..54e61b43 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -11,8 +11,9 @@ namespace waybar::modules::privacy { PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_, - std::list *nodes_, const std::string &pos, - const uint icon_size, const uint transition_duration) + std::list *nodes_, Gtk::Orientation orientation, + const std::string &pos, const uint icon_size, + const uint transition_duration) : Gtk::Revealer(), privacy_type(privacy_type_), nodes(nodes_), @@ -40,16 +41,24 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac // Set the reveal transition to not look weird when sliding in if (pos == "modules-left") { - set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_SLIDE_RIGHT); + set_transition_type(orientation == Gtk::ORIENTATION_HORIZONTAL + ? Gtk::REVEALER_TRANSITION_TYPE_SLIDE_RIGHT + : Gtk::REVEALER_TRANSITION_TYPE_SLIDE_DOWN); } 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); + set_transition_type(orientation == Gtk::ORIENTATION_HORIZONTAL + ? Gtk::REVEALER_TRANSITION_TYPE_SLIDE_LEFT + : Gtk::REVEALER_TRANSITION_TYPE_SLIDE_UP); } set_transition_duration(transition_duration); box_.set_name("privacy-item"); - box_.add(icon_); + + // We use `set_center_widget` instead of `add` to make sure the icon is + // centered even if the orientation is vertical + box_.set_center_widget(icon_); + icon_.set_pixel_size(icon_size); add(box_); From 0abb2166a40da300597aa3559daade14f6b6a694 Mon Sep 17 00:00:00 2001 From: hansi Date: Sat, 8 Feb 2025 00:29:09 +0400 Subject: [PATCH 750/842] turn off ellipsize for niri/language module --- src/modules/niri/language.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/niri/language.cpp b/src/modules/niri/language.cpp index 1e4d6d10..3b55ff24 100644 --- a/src/modules/niri/language.cpp +++ b/src/modules/niri/language.cpp @@ -9,7 +9,7 @@ namespace waybar::modules::niri { Language::Language(const std::string &id, const Bar &bar, const Json::Value &config) - : ALabel(config, "language", id, "{}", 0, true), bar_(bar) { + : ALabel(config, "language", id, "{}", 0, false), bar_(bar) { label_.hide(); if (!gIPC) gIPC = std::make_unique(); From cc94278c4ed83fa63c72d90eee8c16bf99358b7c Mon Sep 17 00:00:00 2001 From: Denis Kazimirov Date: Mon, 10 Feb 2025 18:32:57 +0500 Subject: [PATCH 751/842] hyprland/workspaces: fixed urgent 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 047703cc..a489bb7f 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -863,7 +863,7 @@ void Workspaces::updateWorkspaceStates() { for (auto &workspace : m_workspaces) { workspace->setActive(workspace->name() == m_activeWorkspaceName || workspace->name() == m_activeSpecialWorkspaceName); - if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { + if (workspace->isActive() && workspace->isUrgent()) { workspace->setUrgent(false); } workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), From 6c4f0af2fe4bb8a35c031efafb0f78cfc3805e4d Mon Sep 17 00:00:00 2001 From: Malix Date: Sat, 15 Feb 2025 20:33:53 +0100 Subject: [PATCH 752/842] update(docs): link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 55a6c7d9..5266e916 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ - River (Mapping mode, Tags, Focused window name) - Hyprland (Window Icons, Workspaces, Focused window name) - Niri (Workspaces, Focused window name, Language) -- DWL (Tags, Focused window name) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc) +- DWL (Tags, Focused window name) [requires dwl ipc patch](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/ipc) - Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Local time - Battery From 937b62ea9a1cd32474855c1241d1dd95f91c0a6e Mon Sep 17 00:00:00 2001 From: Kaosu Date: Sun, 16 Feb 2025 14:21:08 +0100 Subject: [PATCH 753/842] add SNI custom icon manager --- include/modules/sni/icon_manager.hpp | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 include/modules/sni/icon_manager.hpp diff --git a/include/modules/sni/icon_manager.hpp b/include/modules/sni/icon_manager.hpp new file mode 100644 index 00000000..614d42d9 --- /dev/null +++ b/include/modules/sni/icon_manager.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include +#include + +class IconManager { + public: + static IconManager& instance() { + static IconManager instance; + return instance; + } + + void setIconsConfig(const Json::Value& icons_config) { + if (icons_config.isObject()) { + for (const auto& key : icons_config.getMemberNames()) { + std::string app_name = key; + const Json::Value& icon_value = icons_config[key]; + + if (icon_value.isString()) { + std::string icon_path = icon_value.asString(); + icons_map_[app_name] = icon_path; + } + } + } else { + spdlog::warn("Invalid icon config format."); + } + } + + std::string getIconForApp(const std::string& app_name) const { + auto it = icons_map_.find(app_name); + if (it != icons_map_.end()) { + return it->second; + } + return ""; + } + + private: + IconManager() = default; + std::unordered_map icons_map_; +}; From 78d5c3ef3a3bd78b92eaae2a67cb624d65ab3bf5 Mon Sep 17 00:00:00 2001 From: Kaosu Date: Sun, 16 Feb 2025 14:21:34 +0100 Subject: [PATCH 754/842] init custom icons from config per tray --- src/modules/sni/tray.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index a2c56808..abd23ebd 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -1,4 +1,5 @@ #include "modules/sni/tray.hpp" +#include "modules/sni/icon_manager.hpp" #include @@ -20,6 +21,9 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) box_.set_spacing(config_["spacing"].asUInt()); } nb_hosts_ += 1; + if (config_["icons"].isObject()) { + IconManager::instance().setIconsConfig(config_["icons"]); + } dp.emit(); } From d1998de47a0e51688d29f8d08e1b1477315ccbbd Mon Sep 17 00:00:00 2001 From: Kaosu Date: Sun, 16 Feb 2025 14:22:10 +0100 Subject: [PATCH 755/842] add setCustomIcon and try to apply such when ID is known --- include/modules/sni/item.hpp | 1 + src/modules/sni/item.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index ebc08d45..c5e86d37 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -62,6 +62,7 @@ class Item : public sigc::trackable { void proxyReady(Glib::RefPtr& result); void setProperty(const Glib::ustring& name, Glib::VariantBase& value); void setStatus(const Glib::ustring& value); + void setCustomIcon(const std::string& id); void getUpdatedProperties(); void processUpdatedProperties(Glib::RefPtr& result); void onSignal(const Glib::ustring& sender_name, const Glib::ustring& signal_name, diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index b3e84885..978e8b07 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -1,4 +1,5 @@ #include "modules/sni/item.hpp" +#include "modules/sni/icon_manager.hpp" #include #include @@ -7,6 +8,7 @@ #include #include +#include #include "gdk/gdk.h" #include "util/format.hpp" @@ -138,6 +140,7 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) { category = get_variant(value); } else if (name == "Id") { id = get_variant(value); + setCustomIcon(id); } else if (name == "Title") { title = get_variant(value); if (tooltip.text.empty()) { @@ -199,6 +202,19 @@ void Item::setStatus(const Glib::ustring& value) { style->add_class(lower); } +void Item::setCustomIcon(const std::string& id) { + std::string custom_icon = IconManager::instance().getIconForApp(id); + if (!custom_icon.empty()) { + if (std::filesystem::exists(custom_icon)) { + Glib::RefPtr custom_pixbuf = Gdk::Pixbuf::create_from_file(custom_icon); + icon_name = ""; // icon_name has priority over pixmap + icon_pixmap = custom_pixbuf; + } else { // if file doesn't exist it's most likely an icon_name + icon_name = custom_icon; + } + } +} + void Item::getUpdatedProperties() { auto params = Glib::VariantContainerBase::create_tuple( {Glib::Variant::create(SNI_INTERFACE_NAME)}); From ddf5b3e07b4b5c9484f7bb5ca81e90973e5ade27 Mon Sep 17 00:00:00 2001 From: Kaosu Date: Sun, 16 Feb 2025 14:30:08 +0100 Subject: [PATCH 756/842] add tray icons docs --- man/waybar-tray.5.scd | 6 +++++- resources/config.jsonc | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/man/waybar-tray.5.scd b/man/waybar-tray.5.scd index 381d593d..dec5347f 100644 --- a/man/waybar-tray.5.scd +++ b/man/waybar-tray.5.scd @@ -47,7 +47,11 @@ Addressed by *tray* ``` "tray": { "icon-size": 21, - "spacing": 10 + "spacing": 10, + "icons": { + "blueman": "bluetooth", + "TelegramDesktop": "$HOME/.local/share/icons/hicolor/16x16/apps/telegram.png" + } } ``` diff --git a/resources/config.jsonc b/resources/config.jsonc index 6ac1aa50..67d5ff5b 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -104,7 +104,11 @@ }, "tray": { // "icon-size": 21, - "spacing": 10 + "spacing": 10, + // "icons": { + // "blueman": "bluetooth", + // "TelegramDesktop": "$HOME/.local/share/icons/hicolor/16x16/apps/telegram.png" + // } }, "clock": { // "timezone": "America/New_York", From 4be1f3bf42328b0053ab8d4ec14e6fe6cce1148c Mon Sep 17 00:00:00 2001 From: Anthony Ruhier Date: Sun, 16 Feb 2025 18:23:59 +0100 Subject: [PATCH 757/842] fix: battery runtime estimation with negative sysfs values Some drivers (example: qualcomm-battmgr, present on Snapdragon X1 laptops) expose the current_now and power_now values in sysfs as negative int when the device is discharging, positive when charging. This breaks the battery runtime estimation in Waybar, as it expects a uint32 for power_now. Change the battery module to use the absolute values of current_now and power_now. --- src/modules/battery.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index d87cc612..44481448 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -273,14 +273,18 @@ waybar::modules::Battery::getInfos() { // Scale these by the voltage to get μW/μWh. uint32_t current_now = 0; + int32_t _current_now_int = 0; bool current_now_exists = false; if (fs::exists(bat / "current_now")) { current_now_exists = true; - std::ifstream(bat / "current_now") >> current_now; + std::ifstream(bat / "current_now") >> _current_now_int; } else if (fs::exists(bat / "current_avg")) { current_now_exists = true; - std::ifstream(bat / "current_avg") >> current_now; + std::ifstream(bat / "current_avg") >> _current_now_int; } + // Documentation ABI allows a negative value when discharging, positive + // value when charging. + current_now = std::abs(_current_now_int); if (fs::exists(bat / "time_to_empty_now")) { time_to_empty_now_exists = true; @@ -324,11 +328,15 @@ waybar::modules::Battery::getInfos() { } uint32_t power_now = 0; + int32_t _power_now_int = 0; bool power_now_exists = false; if (fs::exists(bat / "power_now")) { power_now_exists = true; - std::ifstream(bat / "power_now") >> power_now; + std::ifstream(bat / "power_now") >> _power_now_int; } + // Some drivers (example: Qualcomm) exposes use a negative value when + // discharging, positive value when charging. + power_now = std::abs(_power_now_int); uint32_t energy_now = 0; bool energy_now_exists = false; From 5b8839ab5cffb9dd8027588d749504df363569fe Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Wed, 19 Feb 2025 00:58:08 +0100 Subject: [PATCH 758/842] Hyprland IPC improvements Fixes IPC being blocked at shutdown --- include/modules/hyprland/backend.hpp | 11 ++- include/modules/hyprland/language.hpp | 2 + include/modules/hyprland/submap.hpp | 2 + include/modules/hyprland/window.hpp | 2 + include/modules/hyprland/workspace.hpp | 1 + include/modules/hyprland/workspaces.hpp | 1 + src/modules/hyprland/backend.cpp | 120 ++++++++++++++---------- src/modules/hyprland/language.cpp | 12 +-- src/modules/hyprland/submap.cpp | 10 +- src/modules/hyprland/window.cpp | 99 +++++++++---------- src/modules/hyprland/workspace.cpp | 15 +-- src/modules/hyprland/workspaces.cpp | 67 +++++++------ 12 files changed, 188 insertions(+), 154 deletions(-) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index 11e73d8f..52f1c3a7 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "util/json.hpp" @@ -19,7 +20,9 @@ class EventHandler { class IPC { public: - IPC() { startIPC(); } + IPC(); + ~IPC(); + static IPC& inst(); void registerForIPC(const std::string& ev, EventHandler* ev_handler); void unregisterForIPC(EventHandler* handler); @@ -32,14 +35,16 @@ class IPC { static std::filesystem::path socketFolder_; private: - void startIPC(); + void socketListener(); void parseIPC(const std::string&); + std::jthread ipcThread_; std::mutex callbackMutex_; util::JsonParser parser_; std::list> callbacks_; + int socketfd_; // the hyprland socket file descriptor + bool running_ = true; }; -inline std::unique_ptr gIPC; inline bool modulesReady = false; }; // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/language.hpp b/include/modules/hyprland/language.hpp index 47a4d69c..ec59e5c3 100644 --- a/include/modules/hyprland/language.hpp +++ b/include/modules/hyprland/language.hpp @@ -37,6 +37,8 @@ class Language : public waybar::ALabel, public EventHandler { util::JsonParser parser_; Layout layout_; + + IPC& m_ipc; }; } // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/submap.hpp b/include/modules/hyprland/submap.hpp index ce980f36..7e3425ef 100644 --- a/include/modules/hyprland/submap.hpp +++ b/include/modules/hyprland/submap.hpp @@ -28,6 +28,8 @@ class Submap : public waybar::ALabel, public EventHandler { std::string submap_; bool always_on_ = false; std::string default_submap_ = "Default"; + + IPC& m_ipc; }; } // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index f2c266bd..2be64594 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -60,6 +60,8 @@ class Window : public waybar::AAppIconLabel, public EventHandler { bool swallowing_; bool fullscreen_; bool focused_; + + IPC& m_ipc; }; } // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/workspace.hpp b/include/modules/hyprland/workspace.hpp index f1fea4e8..3dedba4c 100644 --- a/include/modules/hyprland/workspace.hpp +++ b/include/modules/hyprland/workspace.hpp @@ -83,6 +83,7 @@ class Workspace { Gtk::Button m_button; Gtk::Box m_content; Gtk::Label m_label; + IPC& m_ipc; }; } // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index f5c20f69..3f0252c8 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -150,6 +150,7 @@ class Workspaces : public AModule, public EventHandler { std::mutex m_mutex; const Bar& m_bar; Gtk::Box m_box; + IPC& m_ipc; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 39341a14..0561fb61 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -11,7 +11,6 @@ #include #include -#include namespace waybar::modules::hyprland { @@ -44,71 +43,96 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { return socketFolder_; } -void IPC::startIPC() { +IPC::IPC() { // will start IPC and relay events to parseIPC + ipcThread_ = std::jthread([this]() { socketListener(); }); +} - std::thread([&]() { - // check for hyprland - const char* his = getenv("HYPRLAND_INSTANCE_SIGNATURE"); - - if (his == nullptr) { - spdlog::warn("Hyprland is not running, Hyprland IPC will not be available."); - return; +IPC::~IPC() { + running_ = false; + spdlog::info("Hyprland IPC stopping..."); + if (socketfd_ != -1) { + spdlog::trace("Shutting down socket"); + if (shutdown(socketfd_, SHUT_RDWR) == -1) { + spdlog::error("Hyprland IPC: Couldn't shutdown socket"); } - - if (!modulesReady) return; - - spdlog::info("Hyprland IPC starting"); - - struct sockaddr_un addr; - int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); - - if (socketfd == -1) { - spdlog::error("Hyprland IPC: socketfd failed"); - return; + spdlog::trace("Closing socket"); + if (close(socketfd_) == -1) { + spdlog::error("Hyprland IPC: Couldn't close socket"); } + } + ipcThread_.join(); +} - addr.sun_family = AF_UNIX; +IPC& IPC::inst() { + static IPC ipc; + return ipc; +} - auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock"; - strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); +void IPC::socketListener() { + // check for hyprland + const char* his = getenv("HYPRLAND_INSTANCE_SIGNATURE"); - addr.sun_path[sizeof(addr.sun_path) - 1] = 0; + if (his == nullptr) { + spdlog::warn("Hyprland is not running, Hyprland IPC will not be available."); + return; + } - int l = sizeof(struct sockaddr_un); + if (!modulesReady) return; - if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) { - spdlog::error("Hyprland IPC: Unable to connect?"); - return; - } + spdlog::info("Hyprland IPC starting"); - auto* file = fdopen(socketfd, "r"); + struct sockaddr_un addr; + socketfd_ = socket(AF_UNIX, SOCK_STREAM, 0); - while (true) { - std::array buffer; // Hyprland socket2 events are max 1024 bytes + if (socketfd_ == -1) { + spdlog::error("Hyprland IPC: socketfd failed"); + return; + } - auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file); + addr.sun_family = AF_UNIX; - if (receivedCharPtr == nullptr) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } + auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock"; + strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); - std::string messageReceived(buffer.data()); - messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n')); - spdlog::debug("hyprland IPC received {}", messageReceived); + addr.sun_path[sizeof(addr.sun_path) - 1] = 0; - try { - parseIPC(messageReceived); - } catch (std::exception& e) { - spdlog::warn("Failed to parse IPC message: {}, reason: {}", messageReceived, e.what()); - } catch (...) { - throw; - } + int l = sizeof(struct sockaddr_un); + if (connect(socketfd_, (struct sockaddr*)&addr, l) == -1) { + spdlog::error("Hyprland IPC: Unable to connect?"); + return; + } + auto* file = fdopen(socketfd_, "r"); + if (file == nullptr) { + spdlog::error("Hyprland IPC: Couldn't open file descriptor"); + return; + } + while (running_) { + std::array buffer; // Hyprland socket2 events are max 1024 bytes + + auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file); + + if (receivedCharPtr == nullptr) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; } - }).detach(); + + std::string messageReceived(buffer.data()); + messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n')); + spdlog::debug("hyprland IPC received {}", messageReceived); + + try { + parseIPC(messageReceived); + } catch (std::exception& e) { + spdlog::warn("Failed to parse IPC message: {}, reason: {}", messageReceived, e.what()); + } catch (...) { + throw; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + spdlog::debug("Hyprland IPC stopped"); } void IPC::parseIPC(const std::string& ev) { diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index d86393af..da56e578 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -10,13 +10,9 @@ namespace waybar::modules::hyprland { Language::Language(const std::string& id, const Bar& bar, const Json::Value& config) - : ALabel(config, "language", id, "{}", 0, true), bar_(bar) { + : ALabel(config, "language", id, "{}", 0, true), bar_(bar), m_ipc(IPC::inst()) { modulesReady = true; - if (!gIPC) { - gIPC = std::make_unique(); - } - // get the active layout when open initLanguage(); @@ -24,11 +20,11 @@ Language::Language(const std::string& id, const Bar& bar, const Json::Value& con update(); // register for hyprland ipc - gIPC->registerForIPC("activelayout", this); + m_ipc.registerForIPC("activelayout", this); } Language::~Language() { - gIPC->unregisterForIPC(this); + m_ipc.unregisterForIPC(this); // wait for possible event handler to finish std::lock_guard lg(mutex_); } @@ -85,7 +81,7 @@ void Language::onEvent(const std::string& ev) { } void Language::initLanguage() { - const auto inputDevices = gIPC->getSocket1Reply("devices"); + const auto inputDevices = m_ipc.getSocket1Reply("devices"); const auto kbName = config_["keyboard-name"].asString(); diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 96677d12..97c4bb62 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -7,15 +7,11 @@ namespace waybar::modules::hyprland { Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) - : ALabel(config, "submap", id, "{}", 0, true), bar_(bar) { + : ALabel(config, "submap", id, "{}", 0, true), bar_(bar), m_ipc(IPC::inst()) { modulesReady = true; parseConfig(config); - if (!gIPC) { - gIPC = std::make_unique(); - } - label_.hide(); ALabel::update(); @@ -27,12 +23,12 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) } // register for hyprland ipc - gIPC->registerForIPC("submap", this); + m_ipc.registerForIPC("submap", this); dp.emit(); } Submap::~Submap() { - gIPC->unregisterForIPC(this); + m_ipc.unregisterForIPC(this); // wait for possible event handler to finish std::lock_guard lg(mutex_); } diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 08c99363..874d619c 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -19,22 +19,18 @@ namespace waybar::modules::hyprland { std::shared_mutex windowIpcSmtx; Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { + : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar), m_ipc(IPC::inst()) { std::unique_lock windowIpcUniqueLock(windowIpcSmtx); modulesReady = true; separateOutputs_ = config["separate-outputs"].asBool(); - if (!gIPC) { - gIPC = std::make_unique(); - } - // register for hyprland ipc - gIPC->registerForIPC("activewindow", this); - gIPC->registerForIPC("closewindow", this); - gIPC->registerForIPC("movewindow", this); - gIPC->registerForIPC("changefloatingmode", this); - gIPC->registerForIPC("fullscreen", this); + m_ipc.registerForIPC("activewindow", this); + m_ipc.registerForIPC("closewindow", this); + m_ipc.registerForIPC("movewindow", this); + m_ipc.registerForIPC("changefloatingmode", this); + m_ipc.registerForIPC("fullscreen", this); windowIpcUniqueLock.unlock(); @@ -45,7 +41,7 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) Window::~Window() { std::unique_lock windowIpcUniqueLock(windowIpcSmtx); - gIPC->unregisterForIPC(this); + m_ipc.unregisterForIPC(this); } auto Window::update() -> void { @@ -114,7 +110,7 @@ auto Window::update() -> void { } auto Window::getActiveWorkspace() -> Workspace { - const auto workspace = gIPC->getSocket1JsonReply("activeworkspace"); + const auto workspace = IPC::inst().getSocket1JsonReply("activeworkspace"); if (workspace.isObject()) { return Workspace::parse(workspace); @@ -124,24 +120,33 @@ auto Window::getActiveWorkspace() -> Workspace { } auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { - const auto monitors = gIPC->getSocket1JsonReply("monitors"); + const auto monitors = IPC::inst().getSocket1JsonReply("monitors"); if (monitors.isArray()) { - auto monitor = std::find_if(monitors.begin(), monitors.end(), [&](Json::Value monitor) { - return monitor["name"] == monitorName; - }); + auto monitor = std::ranges::find_if( + monitors, [&](Json::Value monitor) { return monitor["name"] == monitorName; }); if (monitor == std::end(monitors)) { spdlog::warn("Monitor not found: {}", monitorName); - return Workspace{-1, 0, "", ""}; + return Workspace{ + .id = -1, + .windows = 0, + .last_window = "", + .last_window_title = "", + }; } const int id = (*monitor)["activeWorkspace"]["id"].asInt(); - const auto workspaces = gIPC->getSocket1JsonReply("workspaces"); + const auto workspaces = IPC::inst().getSocket1JsonReply("workspaces"); if (workspaces.isArray()) { - auto workspace = std::find_if(workspaces.begin(), workspaces.end(), - [&](Json::Value workspace) { return workspace["id"] == id; }); + auto workspace = std::ranges::find_if( + workspaces, [&](Json::Value workspace) { return workspace["id"] == id; }); if (workspace == std::end(workspaces)) { spdlog::warn("No workspace with id {}", id); - return Workspace{-1, 0, "", ""}; + return Workspace{ + .id = -1, + .windows = 0, + .last_window = "", + .last_window_title = "", + }; } return Workspace::parse(*workspace); }; @@ -152,18 +157,22 @@ 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(), + .id = value["id"].asInt(), + .windows = value["windows"].asInt(), + .last_window = value["lastwindow"].asString(), + .last_window_title = value["lastwindowtitle"].asString(), }; } auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData { - return WindowData{value["floating"].asBool(), value["monitor"].asInt(), - value["class"].asString(), value["initialClass"].asString(), - value["title"].asString(), value["initialTitle"].asString(), - value["fullscreen"].asBool(), !value["grouped"].empty()}; + return WindowData{.floating = value["floating"].asBool(), + .monitor = value["monitor"].asInt(), + .class_name = value["class"].asString(), + .initial_class_name = value["initialClass"].asString(), + .title = value["title"].asString(), + .initial_title = value["initialTitle"].asString(), + .fullscreen = value["fullscreen"].asBool(), + .grouped = !value["grouped"].empty()}; } void Window::queryActiveWorkspace() { @@ -177,11 +186,10 @@ void Window::queryActiveWorkspace() { focused_ = true; if (workspace_.windows > 0) { - const auto clients = gIPC->getSocket1JsonReply("clients"); + const auto clients = m_ipc.getSocket1JsonReply("clients"); if (clients.isArray()) { - auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) { - return window["address"] == workspace_.last_window; - }); + auto activeWindow = std::ranges::find_if( + clients, [&](Json::Value window) { return window["address"] == workspace_.last_window; }); if (activeWindow == std::end(clients)) { focused_ = false; @@ -191,22 +199,19 @@ void Window::queryActiveWorkspace() { windowData_ = WindowData::parse(*activeWindow); updateAppIconName(windowData_.class_name, windowData_.initial_class_name); std::vector workspaceWindows; - std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows), - [&](Json::Value window) { - return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); - }); - swallowing_ = - std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) { - return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; - }); + std::ranges::copy_if(clients, std::back_inserter(workspaceWindows), [&](Json::Value window) { + return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); + }); + swallowing_ = std::ranges::any_of(workspaceWindows, [&](Json::Value window) { + return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; + }); std::vector visibleWindows; - std::copy_if(workspaceWindows.begin(), workspaceWindows.end(), - std::back_inserter(visibleWindows), - [&](Json::Value window) { return !window["hidden"].asBool(); }); + std::ranges::copy_if(workspaceWindows, std::back_inserter(visibleWindows), + [&](Json::Value window) { return !window["hidden"].asBool(); }); solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), [&](Json::Value window) { return !window["floating"].asBool(); }); - allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(), - [&](Json::Value window) { return window["floating"].asBool(); }); + allFloating_ = std::ranges::all_of( + visibleWindows, [&](Json::Value window) { return window["floating"].asBool(); }); fullscreen_ = windowData_.fullscreen; // Fullscreen windows look like they are solo @@ -219,7 +224,7 @@ void Window::queryActiveWorkspace() { } else { soloClass_ = ""; } - }; + } } else { focused_ = false; windowData_ = WindowData{}; diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index e575d1c4..4655096f 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -18,7 +18,8 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma m_windows(workspace_data["windows"].asInt()), m_isActive(true), m_isPersistentRule(workspace_data["persistent-rule"].asBool()), - m_isPersistentConfig(workspace_data["persistent-config"].asBool()) { + m_isPersistentConfig(workspace_data["persistent-config"].asBool()), + m_ipc(IPC::inst()) { if (m_name.starts_with("name:")) { m_name = m_name.substr(5); } else if (m_name.starts_with("special")) { @@ -58,20 +59,20 @@ bool Workspace::handleClicked(GdkEventButton *bt) const { try { if (id() > 0) { // normal if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); + m_ipc.getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); } else { - gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id())); + m_ipc.getSocket1Reply("dispatch workspace " + std::to_string(id())); } } else if (!isSpecial()) { // named (this includes persistent) if (m_workspaceManager.moveToMonitor()) { - gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); + m_ipc.getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); } else { - gIPC->getSocket1Reply("dispatch workspace name:" + name()); + m_ipc.getSocket1Reply("dispatch workspace name:" + name()); } } else if (id() != -99) { // named special - gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name()); + m_ipc.getSocket1Reply("dispatch togglespecialworkspace " + name()); } else { // special - gIPC->getSocket1Reply("dispatch togglespecialworkspace"); + m_ipc.getSocket1Reply("dispatch togglespecialworkspace"); } return true; } catch (const std::exception &e) { diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 047703cc..02d85cd9 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -13,7 +13,10 @@ namespace waybar::modules::hyprland { Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) - : AModule(config, "workspaces", id, false, false), m_bar(bar), m_box(bar.orientation, 0) { + : AModule(config, "workspaces", id, false, false), + m_bar(bar), + m_box(bar.orientation, 0), + m_ipc(IPC::inst()) { modulesReady = true; parseConfig(config); @@ -24,23 +27,19 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value m_box.get_style_context()->add_class(MODULE_CLASS); event_box_.add(m_box); - if (!gIPC) { - gIPC = std::make_unique(); - } - setCurrentMonitorId(); init(); registerIpc(); } Workspaces::~Workspaces() { - gIPC->unregisterForIPC(this); + m_ipc.unregisterForIPC(this); // wait for possible event handler to finish std::lock_guard lg(m_mutex); } void Workspaces::init() { - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + m_activeWorkspaceName = (m_ipc.getSocket1JsonReply("activeworkspace"))["name"].asString(); initializeWorkspaces(); dp.emit(); @@ -161,7 +160,7 @@ std::string Workspaces::getRewrite(std::string window_class, std::string window_ std::vector Workspaces::getVisibleWorkspaces() { std::vector visibleWorkspaces; - auto monitors = gIPC->getSocket1JsonReply("monitors"); + auto monitors = IPC::inst().getSocket1JsonReply("monitors"); for (const auto &monitor : monitors) { auto ws = monitor["activeWorkspace"]; if (ws.isObject() && ws["name"].isString()) { @@ -185,8 +184,8 @@ void Workspaces::initializeWorkspaces() { } // get all current workspaces - auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - auto const clientsJson = gIPC->getSocket1JsonReply("clients"); + auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces"); + auto const clientsJson = m_ipc.getSocket1JsonReply("clients"); for (Json::Value workspaceJson : workspacesJson) { std::string workspaceName = workspaceJson["name"].asString(); @@ -285,7 +284,7 @@ void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJs void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &clientsJson) { spdlog::info("Loading persistent workspaces from Hyprland workspace rules"); - auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); + auto const workspaceRules = m_ipc.getSocket1JsonReply("workspacerules"); for (Json::Value const &rule : workspaceRules) { if (!rule["workspaceString"].isString()) { spdlog::warn("Workspace rules: invalid workspaceString, skipping: {}", rule); @@ -365,10 +364,10 @@ void Workspaces::onWorkspaceDestroyed(std::string const &payload) { void Workspaces::onWorkspaceCreated(std::string const &workspaceName, Json::Value const &clientsData) { spdlog::debug("Workspace created: {}", workspaceName); - auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces"); if (!isWorkspaceIgnored(workspaceName)) { - auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); + auto const workspaceRules = m_ipc.getSocket1JsonReply("workspacerules"); for (Json::Value workspaceJson : workspacesJson) { std::string name = workspaceJson["name"].asString(); if (name == workspaceName) { @@ -397,7 +396,7 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { spdlog::debug("Workspace moved: {}", payload); // Update active workspace - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); + m_activeWorkspaceName = (m_ipc.getSocket1JsonReply("activeworkspace"))["name"].asString(); if (allOutputs()) return; @@ -405,7 +404,7 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { std::string monitorName = payload.substr(payload.find(',') + 1); if (m_bar.output->name == monitorName) { - Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); + Json::Value clientsData = m_ipc.getSocket1JsonReply("clients"); onWorkspaceCreated(workspaceName, clientsData); } else { spdlog::debug("Removing workspace because it was moved to another monitor: {}"); @@ -434,7 +433,7 @@ void Workspaces::onMonitorFocused(std::string const &payload) { spdlog::trace("Monitor focused: {}", payload); m_activeWorkspaceName = payload.substr(payload.find(',') + 1); - for (Json::Value &monitor : gIPC->getSocket1JsonReply("monitors")) { + for (Json::Value &monitor : m_ipc.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); @@ -542,7 +541,7 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { } if (inserter.has_value()) { - Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); + Json::Value clientsData = m_ipc.getSocket1JsonReply("clients"); std::string jsonWindowAddress = fmt::format("0x{}", payload); auto client = @@ -660,24 +659,24 @@ void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payloa } auto Workspaces::registerIpc() -> void { - gIPC->registerForIPC("workspace", this); - gIPC->registerForIPC("activespecial", this); - gIPC->registerForIPC("createworkspace", this); - gIPC->registerForIPC("destroyworkspace", this); - gIPC->registerForIPC("focusedmon", this); - gIPC->registerForIPC("moveworkspace", this); - gIPC->registerForIPC("renameworkspace", this); - gIPC->registerForIPC("openwindow", this); - gIPC->registerForIPC("closewindow", this); - gIPC->registerForIPC("movewindow", this); - gIPC->registerForIPC("urgent", this); - gIPC->registerForIPC("configreloaded", this); + m_ipc.registerForIPC("workspace", this); + m_ipc.registerForIPC("activespecial", this); + m_ipc.registerForIPC("createworkspace", this); + m_ipc.registerForIPC("destroyworkspace", this); + m_ipc.registerForIPC("focusedmon", this); + m_ipc.registerForIPC("moveworkspace", this); + m_ipc.registerForIPC("renameworkspace", this); + m_ipc.registerForIPC("openwindow", this); + m_ipc.registerForIPC("closewindow", this); + m_ipc.registerForIPC("movewindow", this); + m_ipc.registerForIPC("urgent", this); + m_ipc.registerForIPC("configreloaded", this); if (windowRewriteConfigUsesTitle()) { spdlog::info( "Registering for Hyprland's 'windowtitle' events because a user-defined window " "rewrite rule uses the 'title' field."); - gIPC->registerForIPC("windowtitle", this); + m_ipc.registerForIPC("windowtitle", this); } } @@ -712,7 +711,7 @@ void Workspaces::removeWorkspace(std::string const &name) { void Workspaces::setCurrentMonitorId() { // get monitor ID from name (used by persistent workspaces) m_monitorId = 0; - auto monitors = gIPC->getSocket1JsonReply("monitors"); + auto monitors = m_ipc.getSocket1JsonReply("monitors"); auto currentMonitor = std::find_if( monitors.begin(), monitors.end(), [this](const Json::Value &m) { return m["name"].asString() == m_bar.output->name; }); @@ -788,7 +787,7 @@ void Workspaces::sortWorkspaces() { } void Workspaces::setUrgentWorkspace(std::string const &windowaddress) { - const Json::Value clientsJson = gIPC->getSocket1JsonReply("clients"); + const Json::Value clientsJson = m_ipc.getSocket1JsonReply("clients"); int workspaceId = -1; for (Json::Value clientJson : clientsJson) { @@ -812,7 +811,7 @@ auto Workspaces::update() -> void { } void Workspaces::updateWindowCount() { - const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + const Json::Value workspacesJson = m_ipc.getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { auto workspaceJson = std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) { @@ -859,7 +858,7 @@ bool Workspaces::updateWindowsToCreate() { void Workspaces::updateWorkspaceStates() { const std::vector visibleWorkspaces = getVisibleWorkspaces(); - auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces"); + auto updatedWorkspaces = m_ipc.getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { workspace->setActive(workspace->name() == m_activeWorkspaceName || workspace->name() == m_activeSpecialWorkspaceName); From a4989cedae1990dfe79a7ace4165ba234fc8f282 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Wed, 19 Feb 2025 21:02:59 +0100 Subject: [PATCH 759/842] formatting --- src/modules/hyprland/window.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 874d619c..6d4331ad 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -72,11 +72,12 @@ auto Window::update() -> void { tooltip_format = config_["tooltip-format"].asString(); } if (!tooltip_format.empty()) { - label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName), - fmt::arg("initialTitle", windowData_.initial_title), - fmt::arg("class", windowData_.class_name), - fmt::arg("initialClass", windowData_.initial_class_name))); - } else if (!label_text.empty()){ + label_.set_tooltip_text( + fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName), + fmt::arg("initialTitle", windowData_.initial_title), + fmt::arg("class", windowData_.class_name), + fmt::arg("initialClass", windowData_.initial_class_name))); + } else if (!label_text.empty()) { label_.set_tooltip_text(label_text); } } From 37c6cd42f5ddb0d8e3d59fc5349f5b126e5cd6bf Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Wed, 19 Feb 2025 21:15:18 +0100 Subject: [PATCH 760/842] fix freebsd compilation --- include/modules/hyprland/backend.hpp | 3 +-- src/modules/hyprland/backend.cpp | 2 +- src/modules/hyprland/window.cpp | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index 52f1c3a7..cfd0b258 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -38,7 +37,7 @@ class IPC { void socketListener(); void parseIPC(const std::string&); - std::jthread ipcThread_; + std::thread ipcThread_; std::mutex callbackMutex_; util::JsonParser parser_; std::list> callbacks_; diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 0561fb61..2bd3b509 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -45,7 +45,7 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { IPC::IPC() { // will start IPC and relay events to parseIPC - ipcThread_ = std::jthread([this]() { socketListener(); }); + ipcThread_ = std::thread([this]() { socketListener(); }); } IPC::~IPC() { diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 6d4331ad..815fbad8 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include "modules/hyprland/backend.hpp" From bcee548f5e40ca5721e25e7697f5f35d57df09e6 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 20 Feb 2025 00:06:05 +0100 Subject: [PATCH 761/842] Fix workspacerules not taking into account defaultName --- src/modules/hyprland/workspaces.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 047703cc..66851c22 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -294,7 +294,8 @@ void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &c if (!rule["persistent"].asBool()) { continue; } - auto const &workspace = rule["workspaceString"].asString(); + auto const &workspace = rule.isMember("defaultName") ? rule["defaultName"].asString() + : rule["workspaceString"].asString(); auto const &monitor = rule["monitor"].asString(); // create this workspace persistently if: // 1. the allOutputs config option is enabled @@ -375,7 +376,10 @@ void Workspaces::onWorkspaceCreated(std::string const &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) { + auto ruleWorkspaceName = rule.isMember("defaultName") + ? rule["defaultName"].asString() + : rule["workspaceString"].asString(); + if (ruleWorkspaceName == workspaceName) { workspaceJson["persistent-rule"] = rule["persistent"].asBool(); break; } From a3ee5f1125b6fc5de17a255f55b85f21d16bbf2b Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Fri, 21 Feb 2025 09:04:22 +0100 Subject: [PATCH 762/842] Update clang-format.yml --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 4a774dbd..3c62819e 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -17,4 +17,4 @@ jobs: source: "." extensions: "hpp,h,cpp,c" style: "file:.clang-format" - clangFormatVersion: 18 + clangFormatVersion: 19 From 8490a1d9b9a5c8770dd63e9faca0a5e01acedcae Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 21 Feb 2025 09:04:53 +0100 Subject: [PATCH 763/842] chore: 0.12.0 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 726d492b..5524b49c 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.11.0', + version: '0.12.0', license: 'MIT', meson_version: '>= 0.59.0', default_options : [ From 212c676251d44939d1f89dc3abbb50e8f75f8588 Mon Sep 17 00:00:00 2001 From: Harishankar G Date: Wed, 26 Feb 2025 15:59:33 +0530 Subject: [PATCH 764/842] Provide an option to show ipv4 or ipv6 or both of them --- include/modules/network.hpp | 5 ++++ man/waybar-network.5.scd | 2 +- src/modules/network.cpp | 54 ++++++++++++++++++++++++++++++++----- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index df0ba9c3..dcf7d499 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -16,6 +16,8 @@ #include "util/rfkill.hpp" #endif +enum ip_addr_pref : uint8_t { IPV4, IPV6, IPV4_6 }; + namespace waybar::modules { class Network : public ALabel { @@ -50,6 +52,7 @@ class Network : public ALabel { std::optional> readBandwidthUsage(); int ifid_; + ip_addr_pref addr_pref_; struct sockaddr_nl nladdr_ = {0}; struct nl_sock* sock_ = nullptr; struct nl_sock* ev_sock_ = nullptr; @@ -73,9 +76,11 @@ class Network : public ALabel { bool carrier_; std::string ifname_; std::string ipaddr_; + std::string ipaddr6_; std::string gwaddr_; std::string netmask_; int cidr_; + int cidr6_; int32_t signal_strength_dbm_; uint8_t signal_strength_; std::string signal_strength_app_; diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index ee409d0a..c2dfc544 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -24,7 +24,7 @@ Addressed by *network* *family*: ++ typeof: string ++ default: *ipv4* ++ - The address family that is used for the format replacement {ipaddr} and to determine if a network connection is present. + The address family that is used for the format replacement {ipaddr} and to determine if a network connection is present. Set it to ipv4_6 to display both. *format*: ++ typeof: string ++ diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 393b4296..ce40340c 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -80,6 +80,7 @@ waybar::modules::Network::readBandwidthUsage() { waybar::modules::Network::Network(const std::string &id, const Json::Value &config) : ALabel(config, "network", id, DEFAULT_FORMAT, 60), ifid_(-1), + addr_pref_(IPV4), efd_(-1), ev_fd_(-1), want_route_dump_(false), @@ -88,6 +89,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf dump_in_progress_(false), is_p2p_(false), cidr_(0), + cidr6_(0), signal_strength_dbm_(0), signal_strength_(0), #ifdef WANT_RFKILL @@ -101,6 +103,12 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf // the module start with no text, but the event_box_ is shown. label_.set_markup(""); + if (config_["family"] == "ipv6") { + addr_pref_ = IPV6; + } else if (config["family"] == "ipv4_6") { + addr_pref_ = IPV4_6; + } + auto bandwidth = readBandwidthUsage(); if (bandwidth.has_value()) { bandwidth_down_total_ = (*bandwidth).first; @@ -270,7 +278,7 @@ const std::string waybar::modules::Network::getNetworkState() const { return "disconnected"; } if (!carrier_) return "disconnected"; - if (ipaddr_.empty()) return "linked"; + if (ipaddr_.empty() && ipaddr6_.empty()) return "linked"; if (essid_.empty()) return "ethernet"; return "wifi"; } @@ -316,11 +324,22 @@ auto waybar::modules::Network::update() -> void { } getState(signal_strength_); + std::string final_ipaddr_; + if (addr_pref_ == ip_addr_pref::IPV4) { + final_ipaddr_ = ipaddr_; + } else if (addr_pref_ == ip_addr_pref::IPV6) { + final_ipaddr_ = ipaddr6_; + } else if (addr_pref_ == ip_addr_pref::IPV4_6) { + final_ipaddr_ = ipaddr_; + final_ipaddr_ += " | "; + final_ipaddr_ += ipaddr6_; + } + auto text = fmt::format( fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), - fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_), + fmt::arg("netmask", netmask_), fmt::arg("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), fmt::arg("icon", getIcon(signal_strength_, state_)), fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")), @@ -352,8 +371,9 @@ auto waybar::modules::Network::update() -> void { fmt::runtime(tooltip_format), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), - fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_), - fmt::arg("cidr", cidr_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), + fmt::arg("netmask", netmask_), fmt::arg("ipaddr", final_ipaddr_), + fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), + fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), fmt::arg("icon", getIcon(signal_strength_, state_)), fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")), @@ -394,10 +414,12 @@ void waybar::modules::Network::clearIface() { essid_.clear(); bssid_.clear(); ipaddr_.clear(); + ipaddr6_.clear(); gwaddr_.clear(); netmask_.clear(); carrier_ = false; cidr_ = 0; + cidr6_ = 0; signal_strength_dbm_ = 0; signal_strength_ = 0; signal_strength_app_.clear(); @@ -516,12 +538,16 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { return NL_OK; } + if ((ifa->ifa_family != AF_INET && net->addr_pref_ == ip_addr_pref::IPV4) || + (ifa->ifa_family != AF_INET6 && net->addr_pref_ == ip_addr_pref::IPV6)) { + return NL_OK; + } + // We ignore address mark as scope for the link or host, // which should leave scope global addresses. if (ifa->ifa_scope >= RT_SCOPE_LINK) { return NL_OK; } - for (; RTA_OK(ifa_rta, attrlen); ifa_rta = RTA_NEXT(ifa_rta, attrlen)) { switch (ifa_rta->rta_type) { case IFA_ADDRESS: @@ -529,8 +555,20 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { case IFA_LOCAL: char ipaddr[INET6_ADDRSTRLEN]; if (!is_del_event) { - net->ipaddr_ = inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)); - net->cidr_ = ifa->ifa_prefixlen; + if ((net->addr_pref_ == ip_addr_pref::IPV4 || + net->addr_pref_ == ip_addr_pref::IPV4_6) && + net->cidr_ == 0 && ifa->ifa_family == AF_INET) { + net->ipaddr_ = + inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)); + net->cidr_ = ifa->ifa_prefixlen; + } else if ((net->addr_pref_ == ip_addr_pref::IPV6 || + net->addr_pref_ == ip_addr_pref::IPV4_6) && + net->cidr6_ == 0 && ifa->ifa_family == AF_INET6) { + net->ipaddr6_ = + inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)); + net->cidr6_ = ifa->ifa_prefixlen; + } + switch (ifa->ifa_family) { case AF_INET: { struct in_addr netmask; @@ -551,7 +589,9 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { spdlog::debug("network: {}, new addr {}/{}", net->ifname_, net->ipaddr_, net->cidr_); } else { net->ipaddr_.clear(); + net->ipaddr6_.clear(); net->cidr_ = 0; + net->cidr6_ = 0; net->netmask_.clear(); spdlog::debug("network: {} addr deleted {}/{}", net->ifname_, inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)), From 8bd0285c889244c7c730053c111c8a2431dc64e0 Mon Sep 17 00:00:00 2001 From: Harishankar G Date: Wed, 26 Feb 2025 16:06:58 +0530 Subject: [PATCH 765/842] Remove redundant if condition --- src/modules/network.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index ce40340c..2f5d19c8 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -538,11 +538,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { return NL_OK; } - if ((ifa->ifa_family != AF_INET && net->addr_pref_ == ip_addr_pref::IPV4) || - (ifa->ifa_family != AF_INET6 && net->addr_pref_ == ip_addr_pref::IPV6)) { - return NL_OK; - } - // We ignore address mark as scope for the link or host, // which should leave scope global addresses. if (ifa->ifa_scope >= RT_SCOPE_LINK) { From 26a344b131d23caf972955fd7aac9550a5082d27 Mon Sep 17 00:00:00 2001 From: Matt White Date: Fri, 10 Jan 2025 10:01:40 -0700 Subject: [PATCH 766/842] feat(hyprland): support createworkspacev2 --- src/modules/hyprland/workspaces.cpp | 39 ++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index ef057d6d..03a7c44e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -322,7 +322,7 @@ void Workspaces::onEvent(const std::string &ev) { onSpecialWorkspaceActivated(payload); } else if (eventName == "destroyworkspace") { onWorkspaceDestroyed(payload); - } else if (eventName == "createworkspace") { + } else if (eventName == "createworkspacev2") { onWorkspaceCreated(payload); } else if (eventName == "focusedmon") { onMonitorFocused(payload); @@ -362,18 +362,31 @@ void Workspaces::onWorkspaceDestroyed(std::string const &payload) { } } -void Workspaces::onWorkspaceCreated(std::string const &workspaceName, +void Workspaces::onWorkspaceCreated(std::string const &payload, Json::Value const &clientsData) { - spdlog::debug("Workspace created: {}", workspaceName); - auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces"); + spdlog::debug("Workspace created: {}", payload); + std::string workspaceIdStr = payload.substr(0, payload.find(',')); + std::string workspaceName = payload.substr(workspaceIdStr.size() + 1); + int workspaceId = std::stoi(workspaceIdStr); - if (!isWorkspaceIgnored(workspaceName)) { - auto const workspaceRules = m_ipc.getSocket1JsonReply("workspacerules"); + auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + + auto workspaceIgnored = isWorkspaceIgnored(workspaceName); + if (!workspaceIgnored) { + auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); for (Json::Value workspaceJson : workspacesJson) { - std::string name = workspaceJson["name"].asString(); - if (name == workspaceName) { + int currentId = workspaceJson["id"].asInt(); + std::string currentName = workspaceJson["name"].asString(); + if (currentId == workspaceId) { + // The workspace may have been renamed since creation + // Check if the configured ignore rules apply to the new name + workspaceIgnored = isWorkspaceIgnored(currentName); + if (workspaceIgnored) { + break; + } + if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && - (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(workspaceName)) { + (showSpecial() || !currentName.starts_with("special")) && !isDoubleSpecial(currentName)) { for (Json::Value const &rule : workspaceRules) { auto ruleWorkspaceName = rule.isMember("defaultName") ? rule["defaultName"].asString() @@ -388,11 +401,13 @@ void Workspaces::onWorkspaceCreated(std::string const &workspaceName, break; } } else { - extendOrphans(workspaceJson["id"].asInt(), clientsData); + extendOrphans(workspaceId, clientsData); } } - } else { - spdlog::trace("Not creating workspace because it is ignored: {}", workspaceName); + } + if (workspaceIgnored) { + spdlog::trace("Not creating workspace because it is ignored: id={} name={}", workspaceId, + workspaceName); } } From 0c6ca8321c9d083c3a2ae73f0f94b2193a1619ef Mon Sep 17 00:00:00 2001 From: Matt White Date: Fri, 10 Jan 2025 11:27:40 -0700 Subject: [PATCH 767/842] feat(hyprland): support destroyworkspacev2 --- src/modules/hyprland/workspaces.cpp | 81 +++++++++++++++++------------ 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 03a7c44e..894d77a8 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -180,7 +180,7 @@ void Workspaces::initializeWorkspaces() { // if the workspace rules changed since last initialization, make sure we reset everything: for (auto &workspace : m_workspaces) { - m_workspacesToRemove.push_back(workspace->name()); + m_workspacesToRemove.push_back(std::to_string(workspace->id())); } // get all current workspaces @@ -320,7 +320,7 @@ void Workspaces::onEvent(const std::string &ev) { onWorkspaceActivated(payload); } else if (eventName == "activespecial") { onSpecialWorkspaceActivated(payload); - } else if (eventName == "destroyworkspace") { + } else if (eventName == "destroyworkspacev2") { onWorkspaceDestroyed(payload); } else if (eventName == "createworkspacev2") { onWorkspaceCreated(payload); @@ -357,8 +357,10 @@ void Workspaces::onSpecialWorkspaceActivated(std::string const &payload) { } void Workspaces::onWorkspaceDestroyed(std::string const &payload) { - if (!isDoubleSpecial(payload)) { - m_workspacesToRemove.push_back(payload); + std::string workspaceIdStr = payload.substr(0, payload.find(',')); + std::string workspaceName = payload.substr(workspaceIdStr.size() + 1); + if (!isDoubleSpecial(workspaceName)) { + m_workspacesToRemove.push_back(workspaceIdStr); } } @@ -366,24 +368,21 @@ void Workspaces::onWorkspaceCreated(std::string const &payload, Json::Value const &clientsData) { spdlog::debug("Workspace created: {}", payload); std::string workspaceIdStr = payload.substr(0, payload.find(',')); - std::string workspaceName = payload.substr(workspaceIdStr.size() + 1); int workspaceId = std::stoi(workspaceIdStr); + auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - auto workspaceIgnored = isWorkspaceIgnored(workspaceName); - if (!workspaceIgnored) { - auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); - for (Json::Value workspaceJson : workspacesJson) { - int currentId = workspaceJson["id"].asInt(); - std::string currentName = workspaceJson["name"].asString(); - if (currentId == workspaceId) { - // The workspace may have been renamed since creation - // Check if the configured ignore rules apply to the new name - workspaceIgnored = isWorkspaceIgnored(currentName); - if (workspaceIgnored) { - break; - } + for (Json::Value workspaceJson : workspacesJson) { + int currentId = workspaceJson["id"].asInt(); + if (currentId == workspaceId) { + std::string name = workspaceJson["name"].asString(); + // This workspace name is more up-to-date than the one in the event payload. + if (isWorkspaceIgnored(name)) { + spdlog::trace("Not creating workspace because it is ignored: id={} name={}", workspaceId, + name); + break; + } if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && (showSpecial() || !currentName.starts_with("special")) && !isDoubleSpecial(currentName)) { @@ -397,18 +396,13 @@ void Workspaces::onWorkspaceCreated(std::string const &payload, } } - m_workspacesToCreate.emplace_back(workspaceJson, clientsData); - break; - } - } else { - extendOrphans(workspaceId, clientsData); + m_workspacesToCreate.emplace_back(workspaceJson, clientsData); + break; } + } else { + extendOrphans(workspaceId, clientsData); } } - if (workspaceIgnored) { - spdlog::trace("Not creating workspace because it is ignored: id={} name={}", workspaceId, - workspaceName); - } } void Workspaces::onWorkspaceMoved(std::string const &payload) { @@ -700,17 +694,38 @@ auto Workspaces::registerIpc() -> void { } void Workspaces::removeWorkspacesToRemove() { - for (const auto &workspaceName : m_workspacesToRemove) { - removeWorkspace(workspaceName); + for (const auto &workspaceString: m_workspacesToRemove) { + removeWorkspace(workspaceString); } m_workspacesToRemove.clear(); } -void Workspaces::removeWorkspace(std::string const &name) { - spdlog::debug("Removing workspace {}", name); +void Workspaces::removeWorkspace(std::string const &workspaceString) { + spdlog::debug("Removing workspace {}", workspaceString); + + int id = -100; // workspace IDs range from -99 upwards, so -100 is a good "invalid" value + std::string name; + + // TODO: we need to support workspace selectors here + // https://wiki.hyprland.org/Configuring/Workspace-Rules/#workspace-selectors + try { + id = std::stoi(workspaceString); + } catch (const std::exception &e) { + if (workspaceString.starts_with("special:")) { + name = workspaceString.substr(8); + } else if (workspaceString.starts_with("name:")) { + name = workspaceString.substr(5); + } else { + name = workspaceString; + } + } + 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(); + if (name.empty()) { + return id == x->id(); + } + return name == x->name(); }); if (workspace == m_workspaces.end()) { @@ -719,7 +734,7 @@ void Workspaces::removeWorkspace(std::string const &name) { } if ((*workspace)->isPersistentConfig()) { - spdlog::trace("Not removing config persistent workspace {}", name); + spdlog::trace("Not removing config persistent workspace {}", (*workspace)->name()); return; } From 17cee0d8766c4dc8a5d39c429ef1d0820ec0a41c Mon Sep 17 00:00:00 2001 From: Matt White Date: Fri, 10 Jan 2025 11:27:40 -0700 Subject: [PATCH 768/842] feat(hyprland): support workspacev2 --- include/modules/hyprland/workspaces.hpp | 2 +- src/modules/hyprland/workspaces.cpp | 78 +++++++++++++++++++++---- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 3f0252c8..364e55dd 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -138,7 +138,7 @@ class Workspaces : public AModule, public EventHandler { bool m_withIcon; uint64_t m_monitorId; - std::string m_activeWorkspaceName; + int m_activeWorkspaceId; std::string m_activeSpecialWorkspaceName; std::vector> m_workspaces; std::vector> m_workspacesToCreate; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 894d77a8..03ab4c56 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -39,7 +39,13 @@ Workspaces::~Workspaces() { } void Workspaces::init() { +<<<<<<< HEAD m_activeWorkspaceName = (m_ipc.getSocket1JsonReply("activeworkspace"))["name"].asString(); +||||||| parent of 24d391b9 (feat(hyprland): support workspacev2) + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); +======= + m_activeWorkspaceId = (gIPC->getSocket1JsonReply("activeworkspace"))["id"].asInt(); +>>>>>>> 24d391b9 (feat(hyprland): support workspacev2) initializeWorkspaces(); dp.emit(); @@ -306,6 +312,7 @@ void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &c workspaceData["persistent-rule"] = true; m_workspacesToCreate.emplace_back(workspaceData, clientsJson); } else { + // This can be any workspace selector. m_workspacesToRemove.emplace_back(workspace); } } @@ -316,7 +323,7 @@ void Workspaces::onEvent(const std::string &ev) { std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>')); std::string payload = ev.substr(eventName.size() + 2); - if (eventName == "workspace") { + if (eventName == "workspacev2") { onWorkspaceActivated(payload); } else if (eventName == "activespecial") { onSpecialWorkspaceActivated(payload); @@ -348,7 +355,8 @@ void Workspaces::onEvent(const std::string &ev) { } void Workspaces::onWorkspaceActivated(std::string const &payload) { - m_activeWorkspaceName = payload; + std::string workspaceIdStr = payload.substr(0, payload.find(',')); + m_activeWorkspaceId = std::stoi(workspaceIdStr); } void Workspaces::onSpecialWorkspaceActivated(std::string const &payload) { @@ -409,7 +417,13 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { spdlog::debug("Workspace moved: {}", payload); // Update active workspace +<<<<<<< HEAD m_activeWorkspaceName = (m_ipc.getSocket1JsonReply("activeworkspace"))["name"].asString(); +||||||| parent of 24d391b9 (feat(hyprland): support workspacev2) + m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); +======= + m_activeWorkspaceId = (gIPC->getSocket1JsonReply("activeworkspace"))["id"].asInt(); +>>>>>>> 24d391b9 (feat(hyprland): support workspacev2) if (allOutputs()) return; @@ -432,9 +446,6 @@ void Workspaces::onWorkspaceRenamed(std::string const &payload) { 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->setName(newName); break; } @@ -444,7 +455,16 @@ 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); + + std::string workspaceName = payload.substr(payload.find(',') + 1); + + // TODO this will be in the payload when we upgrade to focusedmonv2 + for (auto &workspace : m_workspaces) { + if (workspace->name() == workspaceName) { + m_activeWorkspaceId = workspace->id(); + break; + } + } for (Json::Value &monitor : m_ipc.getSocket1JsonReply("monitors")) { if (monitor["name"].asString() == payload.substr(0, payload.find(','))) { @@ -672,6 +692,7 @@ void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payloa } auto Workspaces::registerIpc() -> void { +<<<<<<< HEAD m_ipc.registerForIPC("workspace", this); m_ipc.registerForIPC("activespecial", this); m_ipc.registerForIPC("createworkspace", this); @@ -684,6 +705,33 @@ auto Workspaces::registerIpc() -> void { m_ipc.registerForIPC("movewindow", this); m_ipc.registerForIPC("urgent", this); m_ipc.registerForIPC("configreloaded", this); +||||||| parent of 24d391b9 (feat(hyprland): support workspacev2) + gIPC->registerForIPC("workspace", this); + gIPC->registerForIPC("activespecial", this); + gIPC->registerForIPC("createworkspacev2", this); + gIPC->registerForIPC("destroyworkspacev2", this); + gIPC->registerForIPC("focusedmon", this); + gIPC->registerForIPC("moveworkspace", this); + gIPC->registerForIPC("renameworkspace", this); + gIPC->registerForIPC("openwindow", this); + gIPC->registerForIPC("closewindow", this); + gIPC->registerForIPC("movewindow", this); + gIPC->registerForIPC("urgent", this); + gIPC->registerForIPC("configreloaded", this); +======= + gIPC->registerForIPC("workspacev2", this); + gIPC->registerForIPC("activespecial", this); + gIPC->registerForIPC("createworkspacev2", this); + gIPC->registerForIPC("destroyworkspacev2", this); + gIPC->registerForIPC("focusedmon", this); + gIPC->registerForIPC("moveworkspace", this); + gIPC->registerForIPC("renameworkspace", this); + gIPC->registerForIPC("openwindow", this); + gIPC->registerForIPC("closewindow", this); + gIPC->registerForIPC("movewindow", this); + gIPC->registerForIPC("urgent", this); + gIPC->registerForIPC("configreloaded", this); +>>>>>>> 24d391b9 (feat(hyprland): support workspacev2) if (windowRewriteConfigUsesTitle()) { spdlog::info( @@ -703,14 +751,16 @@ void Workspaces::removeWorkspacesToRemove() { void Workspaces::removeWorkspace(std::string const &workspaceString) { spdlog::debug("Removing workspace {}", workspaceString); - int id = -100; // workspace IDs range from -99 upwards, so -100 is a good "invalid" value + int id; std::string name; - // TODO: we need to support workspace selectors here - // https://wiki.hyprland.org/Configuring/Workspace-Rules/#workspace-selectors try { + // If this succeeds, we have a workspace ID. id = std::stoi(workspaceString); } catch (const std::exception &e) { + // TODO: At some point we want to support all workspace selectors + // This is just a subset. + // https://wiki.hyprland.org/Configuring/Workspace-Rules/#workspace-selectors if (workspaceString.starts_with("special:")) { name = workspaceString.substr(8); } else if (workspaceString.starts_with("name:")) { @@ -734,7 +784,7 @@ void Workspaces::removeWorkspace(std::string const &workspaceString) { } if ((*workspace)->isPersistentConfig()) { - spdlog::trace("Not removing config persistent workspace {}", (*workspace)->name()); + spdlog::trace("Not removing config persistent workspace id={} name={}", (*workspace)->id(), (*workspace)->name()); return; } @@ -894,9 +944,15 @@ void Workspaces::updateWorkspaceStates() { const std::vector visibleWorkspaces = getVisibleWorkspaces(); auto updatedWorkspaces = m_ipc.getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { - workspace->setActive(workspace->name() == m_activeWorkspaceName || + workspace->setActive(workspace->id() == m_activeWorkspaceId || workspace->name() == m_activeSpecialWorkspaceName); +<<<<<<< HEAD if (workspace->isActive() && workspace->isUrgent()) { +||||||| parent of 24d391b9 (feat(hyprland): support workspacev2) + if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { +======= + if (workspace->id() == m_activeWorkspaceId && workspace->isUrgent()) { +>>>>>>> 24d391b9 (feat(hyprland): support workspacev2) workspace->setUrgent(false); } workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), From 9f71de5227620dc2d884cfda946c8d7e5966337a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 1 Mar 2025 00:11:29 +0000 Subject: [PATCH 769/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/9d3ae807ebd2981d593cddd0080856873139aa40?narHash=sha256-NGqpVVxNAHwIicXpgaVqJEJWeyqzoQJ9oc8lnK9%2BWC4%3D' (2025-01-29) → 'github:NixOS/nixpkgs/5135c59491985879812717f4c9fea69604e7f26f?narHash=sha256-Vr3Qi346M%2B8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic%3D' (2025-02-26) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index c4bb5dee..e64cbe2c 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1738142207, - "narHash": "sha256-NGqpVVxNAHwIicXpgaVqJEJWeyqzoQJ9oc8lnK9+WC4=", + "lastModified": 1740560979, + "narHash": "sha256-Vr3Qi346M+8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9d3ae807ebd2981d593cddd0080856873139aa40", + "rev": "5135c59491985879812717f4c9fea69604e7f26f", "type": "github" }, "original": { From 4a6c417ef5e3b511b40f13057b401cabe90ff197 Mon Sep 17 00:00:00 2001 From: Harishankar G Date: Tue, 4 Mar 2025 19:09:21 +0530 Subject: [PATCH 770/842] Add format replacements For cidr6, netmask6 --- include/modules/network.hpp | 1 + man/waybar-network.5.scd | 8 ++++++-- src/modules/network.cpp | 19 +++++++++++-------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index dcf7d499..5fd0c180 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -79,6 +79,7 @@ class Network : public ALabel { std::string ipaddr6_; std::string gwaddr_; std::string netmask_; + std::string netmask6_; int cidr_; int cidr6_; int32_t signal_strength_dbm_; diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index c2dfc544..15f15395 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -155,9 +155,13 @@ Addressed by *network* *{gwaddr}*: The default gateway for the interface -*{netmask}*: The subnetmask corresponding to the IP. +*{netmask}*: The subnetmask corresponding to the IP(V4). -*{cidr}*: The subnetmask corresponding to the IP in CIDR notation. +*{netmask6}*: The subnetmask corresponding to the IP(V6). + +*{cidr}*: The subnetmask corresponding to the IP(V4) in CIDR notation. + +*{cidr6}*: The subnetmask corresponding to the IP(V6) in CIDR notation. *{essid}*: Name (SSID) of the wireless network. diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 2f5d19c8..716cd497 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -339,8 +339,9 @@ auto waybar::modules::Network::update() -> void { fmt::runtime(format_), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), - fmt::arg("netmask", netmask_), fmt::arg("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), - fmt::arg("cidr", cidr_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), + fmt::arg("netmask", netmask_), fmt::arg("netmask6", netmask6_), + fmt::arg("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), + fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), fmt::arg("icon", getIcon(signal_strength_, state_)), fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")), fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")), @@ -371,9 +372,9 @@ auto waybar::modules::Network::update() -> void { fmt::runtime(tooltip_format), fmt::arg("essid", essid_), fmt::arg("bssid", bssid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), - fmt::arg("netmask", netmask_), fmt::arg("ipaddr", final_ipaddr_), - fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), - fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), + fmt::arg("netmask", netmask_), fmt::arg("netmask6", netmask6_), + fmt::arg("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), + fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), fmt::arg("icon", getIcon(signal_strength_, state_)), fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")), @@ -417,6 +418,7 @@ void waybar::modules::Network::clearIface() { ipaddr6_.clear(); gwaddr_.clear(); netmask_.clear(); + netmask6_.clear(); carrier_ = false; cidr_ = 0; cidr6_ = 0; @@ -571,14 +573,14 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->netmask_ = inet_ntop(ifa->ifa_family, &netmask, ipaddr, sizeof(ipaddr)); } case AF_INET6: { - struct in6_addr netmask; + struct in6_addr netmask6; for (int i = 0; i < 16; i++) { int v = (i + 1) * 8 - ifa->ifa_prefixlen; if (v < 0) v = 0; if (v > 8) v = 8; - netmask.s6_addr[i] = ~0 << v; + netmask6.s6_addr[i] = ~0 << v; } - net->netmask_ = inet_ntop(ifa->ifa_family, &netmask, ipaddr, sizeof(ipaddr)); + net->netmask6_ = inet_ntop(ifa->ifa_family, &netmask6, ipaddr, sizeof(ipaddr)); } } spdlog::debug("network: {}, new addr {}/{}", net->ifname_, net->ipaddr_, net->cidr_); @@ -588,6 +590,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->cidr_ = 0; net->cidr6_ = 0; net->netmask_.clear(); + net->netmask6_.clear(); spdlog::debug("network: {} addr deleted {}/{}", net->ifname_, inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)), ifa->ifa_prefixlen); From f7b4451564dd860bfacfc293d0bbd414e6bb4e54 Mon Sep 17 00:00:00 2001 From: Matthew White Date: Wed, 26 Feb 2025 18:34:18 -0700 Subject: [PATCH 771/842] fix(hyprland): support additional v2 events --- include/modules/hyprland/workspaces.hpp | 15 +- src/modules/hyprland/workspaces.cpp | 421 ++++++++++++------------ 2 files changed, 220 insertions(+), 216 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 364e55dd..6b33baea 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,7 @@ class Workspaces : public AModule, public EventHandler { static Json::Value createMonitorWorkspaceData(std::string const& name, std::string const& monitor); - void removeWorkspace(std::string const& name); + void removeWorkspace(std::string const& workspaceString); void setUrgentWorkspace(std::string const& windowaddress); // Config @@ -74,10 +75,11 @@ class Workspaces : public AModule, public EventHandler { void onWorkspaceActivated(std::string const& payload); void onSpecialWorkspaceActivated(std::string const& payload); void onWorkspaceDestroyed(std::string const& payload); - void onWorkspaceCreated(std::string const& workspaceName, + void onWorkspaceCreated(std::string const& payload, Json::Value const& clientsData = Json::Value::nullRef); void onWorkspaceMoved(std::string const& payload); void onWorkspaceRenamed(std::string const& payload); + static std::optional parseWorkspaceId(std::string const& workspaceIdStr); // monitor events void onMonitorFocused(std::string const& payload); @@ -93,11 +95,18 @@ class Workspaces : public AModule, public EventHandler { int windowRewritePriorityFunction(std::string const& window_rule); + // event payload management + template + static std::string makePayload(Args const&... args); + static std::pair splitDoublePayload(std::string const& payload); + static std::tuple splitTriplePayload( + std::string const& payload); + // Update methods void doUpdate(); void removeWorkspacesToRemove(); void createWorkspacesToCreate(); - static std::vector getVisibleWorkspaces(); + static std::vector getVisibleWorkspaces(); void updateWorkspaceStates(); bool updateWindowsToCreate(); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 03ab4c56..c7d397e3 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -39,13 +40,7 @@ Workspaces::~Workspaces() { } void Workspaces::init() { -<<<<<<< HEAD - m_activeWorkspaceName = (m_ipc.getSocket1JsonReply("activeworkspace"))["name"].asString(); -||||||| parent of 24d391b9 (feat(hyprland): support workspacev2) - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); -======= - m_activeWorkspaceId = (gIPC->getSocket1JsonReply("activeworkspace"))["id"].asInt(); ->>>>>>> 24d391b9 (feat(hyprland): support workspacev2) + m_activeWorkspaceId = m_ipc.getSocket1JsonReply("activeworkspace")["id"].asInt(); initializeWorkspaces(); dp.emit(); @@ -55,13 +50,12 @@ Json::Value Workspaces::createMonitorWorkspaceData(std::string const &name, std::string const &monitor) { spdlog::trace("Creating persistent workspace: {} on monitor {}", name, monitor); Json::Value workspaceData; - try { - // numbered persistent workspaces get the name as ID - workspaceData["id"] = name == "special" ? -99 : std::stoi(name); - } catch (const std::exception &e) { - // named persistent workspaces start with ID=0 - workspaceData["id"] = 0; + + auto workspaceId = parseWorkspaceId(name); + if (!workspaceId.has_value()) { + workspaceId = 0; } + workspaceData["id"] = *workspaceId; workspaceData["name"] = name; workspaceData["monitor"] = monitor; workspaceData["windows"] = 0; @@ -74,9 +68,8 @@ void Workspaces::createWorkspace(Json::Value const &workspace_data, spdlog::debug("Creating workspace {}", workspaceName); // avoid recreating existing workspaces - auto workspace = std::find_if( - m_workspaces.begin(), m_workspaces.end(), - [workspaceName](std::unique_ptr const &w) { + auto workspace = + std::ranges::find_if(m_workspaces, [workspaceName](std::unique_ptr const &w) { return (workspaceName.starts_with("special:") && workspaceName.substr(8) == w->name()) || workspaceName == w->name(); }); @@ -164,18 +157,18 @@ std::string Workspaces::getRewrite(std::string window_class, std::string window_ fmt::arg("title", window_title)); } -std::vector Workspaces::getVisibleWorkspaces() { - std::vector visibleWorkspaces; +std::vector Workspaces::getVisibleWorkspaces() { + std::vector visibleWorkspaces; auto monitors = IPC::inst().getSocket1JsonReply("monitors"); for (const auto &monitor : monitors) { auto ws = monitor["activeWorkspace"]; - if (ws.isObject() && ws["name"].isString()) { - visibleWorkspaces.push_back(ws["name"].asString()); + if (ws.isObject() && ws["id"].isInt()) { + visibleWorkspaces.push_back(ws["id"].asInt()); } 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)); + if (sws.isObject() && sws["id"].isInt() && !name.empty()) { + visibleWorkspaces.push_back(sws["id"].asInt()); } } return visibleWorkspaces; @@ -253,7 +246,7 @@ void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJs 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)); + persistentWorkspacesToCreate.emplace_back(std::to_string((m_monitorId * amount) + i + 1)); } } } else if (value.isArray() && !value.empty()) { @@ -331,21 +324,21 @@ void Workspaces::onEvent(const std::string &ev) { onWorkspaceDestroyed(payload); } else if (eventName == "createworkspacev2") { onWorkspaceCreated(payload); - } else if (eventName == "focusedmon") { + } else if (eventName == "focusedmonv2") { onMonitorFocused(payload); - } else if (eventName == "moveworkspace") { + } else if (eventName == "moveworkspacev2") { onWorkspaceMoved(payload); } else if (eventName == "openwindow") { onWindowOpened(payload); } else if (eventName == "closewindow") { onWindowClosed(payload); - } else if (eventName == "movewindow") { + } else if (eventName == "movewindowv2") { onWindowMoved(payload); } else if (eventName == "urgent") { setUrgentWorkspace(payload); } else if (eventName == "renameworkspace") { onWorkspaceRenamed(payload); - } else if (eventName == "windowtitle") { + } else if (eventName == "windowtitlev2") { onWindowTitleEvent(payload); } else if (eventName == "configreloaded") { onConfigReloaded(); @@ -355,8 +348,11 @@ void Workspaces::onEvent(const std::string &ev) { } void Workspaces::onWorkspaceActivated(std::string const &payload) { - std::string workspaceIdStr = payload.substr(0, payload.find(',')); - m_activeWorkspaceId = std::stoi(workspaceIdStr); + const auto [workspaceIdStr, workspaceName] = splitDoublePayload(payload); + const auto workspaceId = parseWorkspaceId(workspaceIdStr); + if (workspaceId.has_value()) { + m_activeWorkspaceId = *workspaceId; + } } void Workspaces::onSpecialWorkspaceActivated(std::string const &payload) { @@ -365,50 +361,54 @@ void Workspaces::onSpecialWorkspaceActivated(std::string const &payload) { } void Workspaces::onWorkspaceDestroyed(std::string const &payload) { - std::string workspaceIdStr = payload.substr(0, payload.find(',')); - std::string workspaceName = payload.substr(workspaceIdStr.size() + 1); + const auto [workspaceId, workspaceName] = splitDoublePayload(payload); if (!isDoubleSpecial(workspaceName)) { - m_workspacesToRemove.push_back(workspaceIdStr); + m_workspacesToRemove.push_back(workspaceId); } } -void Workspaces::onWorkspaceCreated(std::string const &payload, - Json::Value const &clientsData) { +void Workspaces::onWorkspaceCreated(std::string const &payload, Json::Value const &clientsData) { spdlog::debug("Workspace created: {}", payload); - std::string workspaceIdStr = payload.substr(0, payload.find(',')); - int workspaceId = std::stoi(workspaceIdStr); - auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules"); - auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces"); + const auto [workspaceIdStr, _] = splitDoublePayload(payload); + + const auto workspaceId = parseWorkspaceId(workspaceIdStr); + if (!workspaceId.has_value()) { + return; + } + + auto const workspaceRules = m_ipc.getSocket1JsonReply("workspacerules"); + auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces"); for (Json::Value workspaceJson : workspacesJson) { - int currentId = workspaceJson["id"].asInt(); - if (currentId == workspaceId) { - std::string name = workspaceJson["name"].asString(); + const auto currentId = workspaceJson["id"].asInt(); + if (currentId == *workspaceId) { + std::string workspaceName = workspaceJson["name"].asString(); // This workspace name is more up-to-date than the one in the event payload. - if (isWorkspaceIgnored(name)) { - spdlog::trace("Not creating workspace because it is ignored: id={} name={}", workspaceId, - name); + if (isWorkspaceIgnored(workspaceName)) { + spdlog::trace("Not creating workspace because it is ignored: id={} name={}", *workspaceId, + workspaceName); break; } - if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && - (showSpecial() || !currentName.starts_with("special")) && !isDoubleSpecial(currentName)) { - for (Json::Value const &rule : workspaceRules) { - auto ruleWorkspaceName = rule.isMember("defaultName") - ? rule["defaultName"].asString() - : rule["workspaceString"].asString(); - if (ruleWorkspaceName == workspaceName) { - workspaceJson["persistent-rule"] = rule["persistent"].asBool(); - break; - } + if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && + (showSpecial() || !workspaceName.starts_with("special")) && + !isDoubleSpecial(workspaceName)) { + for (Json::Value const &rule : workspaceRules) { + auto ruleWorkspaceName = rule.isMember("defaultName") + ? rule["defaultName"].asString() + : rule["workspaceString"].asString(); + if (ruleWorkspaceName == workspaceName) { + workspaceJson["persistent-rule"] = rule["persistent"].asBool(); + break; } + } m_workspacesToCreate.emplace_back(workspaceJson, clientsData); break; } } else { - extendOrphans(workspaceId, clientsData); + extendOrphans(*workspaceId, clientsData); } } } @@ -417,35 +417,34 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) { spdlog::debug("Workspace moved: {}", payload); // Update active workspace -<<<<<<< HEAD - m_activeWorkspaceName = (m_ipc.getSocket1JsonReply("activeworkspace"))["name"].asString(); -||||||| parent of 24d391b9 (feat(hyprland): support workspacev2) - m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); -======= - m_activeWorkspaceId = (gIPC->getSocket1JsonReply("activeworkspace"))["id"].asInt(); ->>>>>>> 24d391b9 (feat(hyprland): support workspacev2) + m_activeWorkspaceId = (m_ipc.getSocket1JsonReply("activeworkspace"))["id"].asInt(); if (allOutputs()) return; - std::string workspaceName = payload.substr(0, payload.find(',')); - std::string monitorName = payload.substr(payload.find(',') + 1); + const auto [workspaceIdStr, workspaceName, monitorName] = splitTriplePayload(payload); + + const auto subPayload = makePayload(workspaceIdStr, workspaceName); if (m_bar.output->name == monitorName) { Json::Value clientsData = m_ipc.getSocket1JsonReply("clients"); - onWorkspaceCreated(workspaceName, clientsData); + onWorkspaceCreated(subPayload, clientsData); } else { - spdlog::debug("Removing workspace because it was moved to another monitor: {}"); - onWorkspaceDestroyed(workspaceName); + spdlog::debug("Removing workspace because it was moved to another monitor: {}", subPayload); + onWorkspaceDestroyed(subPayload); } } 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); + const auto [workspaceIdStr, newName] = splitDoublePayload(payload); + + const auto workspaceId = parseWorkspaceId(workspaceIdStr); + if (!workspaceId.has_value()) { + return; + } + for (auto &workspace : m_workspaces) { - if (workspace->id() == workspaceId) { + if (workspace->id() == *workspaceId) { workspace->setName(newName); break; } @@ -456,19 +455,18 @@ void Workspaces::onWorkspaceRenamed(std::string const &payload) { void Workspaces::onMonitorFocused(std::string const &payload) { spdlog::trace("Monitor focused: {}", payload); - std::string workspaceName = payload.substr(payload.find(',') + 1); + const auto [monitorName, workspaceIdStr] = splitDoublePayload(payload); - // TODO this will be in the payload when we upgrade to focusedmonv2 - for (auto &workspace : m_workspaces) { - if (workspace->name() == workspaceName) { - m_activeWorkspaceId = workspace->id(); - break; - } + const auto workspaceId = parseWorkspaceId(workspaceIdStr); + if (!workspaceId.has_value()) { + return; } + m_activeWorkspaceId = *workspaceId; + for (Json::Value &monitor : m_ipc.getSocket1JsonReply("monitors")) { - if (monitor["name"].asString() == payload.substr(0, payload.find(','))) { - auto name = monitor["specialWorkspace"]["name"].asString(); + if (monitor["name"].asString() == monitorName) { + const auto name = monitor["specialWorkspace"]["name"].asString(); m_activeSpecialWorkspaceName = !name.starts_with("special:") ? name : name.substr(8); } } @@ -507,11 +505,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(','); - std::string windowAddress = payload.substr(lastCommaIdx, nextCommaIdx - lastCommaIdx); - - std::string workspaceName = payload.substr(nextCommaIdx + 1, payload.length() - nextCommaIdx); + auto [windowAddress, _, workspaceName] = splitTriplePayload(payload); std::string windowRepr; @@ -547,13 +541,15 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { spdlog::trace("Window title changed: {}", payload); std::optional> inserter; + const auto [windowAddress, _] = splitDoublePayload(payload); + // If the window was an orphan, rename it at the orphan's vector - if (m_orphanWindowMap.contains(payload)) { + if (m_orphanWindowMap.contains(windowAddress)) { 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); }); + auto windowWorkspace = std::ranges::find_if(m_workspaces, [windowAddress](auto &workspace) { + return workspace->containsWindow(windowAddress); + }); // If the window exists on a workspace, rename it at the workspace's window // map @@ -562,9 +558,9 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { (*windowWorkspace)->insertWindow(std::move(wcp)); }; } else { - auto queuedWindow = std::find_if( - m_windowsToCreate.begin(), m_windowsToCreate.end(), - [payload](auto &windowPayload) { return windowPayload.getAddress() == payload; }); + auto queuedWindow = std::ranges::find_if(m_windowsToCreate, [payload](auto &windowPayload) { + return windowPayload.getAddress() == payload; + }); // If the window was queued, rename it in the queue if (queuedWindow != m_windowsToCreate.end()) { @@ -692,57 +688,29 @@ void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payloa } auto Workspaces::registerIpc() -> void { -<<<<<<< HEAD - m_ipc.registerForIPC("workspace", this); + m_ipc.registerForIPC("workspacev2", this); m_ipc.registerForIPC("activespecial", this); - m_ipc.registerForIPC("createworkspace", this); - m_ipc.registerForIPC("destroyworkspace", this); - m_ipc.registerForIPC("focusedmon", this); - m_ipc.registerForIPC("moveworkspace", this); + m_ipc.registerForIPC("createworkspacev2", this); + m_ipc.registerForIPC("destroyworkspacev2", this); + m_ipc.registerForIPC("focusedmonv2", this); + m_ipc.registerForIPC("moveworkspacev2", this); m_ipc.registerForIPC("renameworkspace", this); m_ipc.registerForIPC("openwindow", this); m_ipc.registerForIPC("closewindow", this); - m_ipc.registerForIPC("movewindow", this); + m_ipc.registerForIPC("movewindowv2", this); m_ipc.registerForIPC("urgent", this); m_ipc.registerForIPC("configreloaded", this); -||||||| parent of 24d391b9 (feat(hyprland): support workspacev2) - gIPC->registerForIPC("workspace", this); - gIPC->registerForIPC("activespecial", this); - gIPC->registerForIPC("createworkspacev2", this); - gIPC->registerForIPC("destroyworkspacev2", this); - gIPC->registerForIPC("focusedmon", this); - gIPC->registerForIPC("moveworkspace", this); - gIPC->registerForIPC("renameworkspace", this); - gIPC->registerForIPC("openwindow", this); - gIPC->registerForIPC("closewindow", this); - gIPC->registerForIPC("movewindow", this); - gIPC->registerForIPC("urgent", this); - gIPC->registerForIPC("configreloaded", this); -======= - gIPC->registerForIPC("workspacev2", this); - gIPC->registerForIPC("activespecial", this); - gIPC->registerForIPC("createworkspacev2", this); - gIPC->registerForIPC("destroyworkspacev2", this); - gIPC->registerForIPC("focusedmon", this); - gIPC->registerForIPC("moveworkspace", this); - gIPC->registerForIPC("renameworkspace", this); - gIPC->registerForIPC("openwindow", this); - gIPC->registerForIPC("closewindow", this); - gIPC->registerForIPC("movewindow", this); - gIPC->registerForIPC("urgent", this); - gIPC->registerForIPC("configreloaded", this); ->>>>>>> 24d391b9 (feat(hyprland): support workspacev2) if (windowRewriteConfigUsesTitle()) { spdlog::info( - "Registering for Hyprland's 'windowtitle' events because a user-defined window " + "Registering for Hyprland's 'windowtitlev2' events because a user-defined window " "rewrite rule uses the 'title' field."); - m_ipc.registerForIPC("windowtitle", this); + m_ipc.registerForIPC("windowtitlev2", this); } } void Workspaces::removeWorkspacesToRemove() { - for (const auto &workspaceString: m_workspacesToRemove) { + for (const auto &workspaceString : m_workspacesToRemove) { removeWorkspace(workspaceString); } m_workspacesToRemove.clear(); @@ -751,32 +719,27 @@ void Workspaces::removeWorkspacesToRemove() { void Workspaces::removeWorkspace(std::string const &workspaceString) { spdlog::debug("Removing workspace {}", workspaceString); - int id; - std::string name; + // If this succeeds, we have a workspace ID. + const auto workspaceId = parseWorkspaceId(workspaceString); - try { - // If this succeeds, we have a workspace ID. - id = std::stoi(workspaceString); - } catch (const std::exception &e) { - // TODO: At some point we want to support all workspace selectors - // This is just a subset. - // https://wiki.hyprland.org/Configuring/Workspace-Rules/#workspace-selectors - if (workspaceString.starts_with("special:")) { - name = workspaceString.substr(8); - } else if (workspaceString.starts_with("name:")) { - name = workspaceString.substr(5); - } else { - name = workspaceString; - } + std::string name; + // TODO: At some point we want to support all workspace selectors + // This is just a subset. + // https://wiki.hyprland.org/Configuring/Workspace-Rules/#workspace-selectors + if (workspaceString.starts_with("special:")) { + name = workspaceString.substr(8); + } else if (workspaceString.starts_with("name:")) { + name = workspaceString.substr(5); + } else { + name = workspaceString; } - auto workspace = - std::find_if(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr &x) { - if (name.empty()) { - return id == x->id(); - } - return name == x->name(); - }); + const auto workspace = std::ranges::find_if(m_workspaces, [&](std::unique_ptr &x) { + if (workspaceId.has_value()) { + return *workspaceId == x->id(); + } + return name == x->name(); + }); if (workspace == m_workspaces.end()) { // happens when a workspace on another monitor is destroyed @@ -784,7 +747,8 @@ void Workspaces::removeWorkspace(std::string const &workspaceString) { } if ((*workspace)->isPersistentConfig()) { - spdlog::trace("Not removing config persistent workspace id={} name={}", (*workspace)->id(), (*workspace)->name()); + spdlog::trace("Not removing config persistent workspace id={} name={}", (*workspace)->id(), + (*workspace)->name()); return; } @@ -808,62 +772,63 @@ void Workspaces::setCurrentMonitorId() { } void Workspaces::sortWorkspaces() { - std::sort(m_workspaces.begin(), m_workspaces.end(), - [&](std::unique_ptr &a, std::unique_ptr &b) { - // Helper comparisons - auto isIdLess = a->id() < b->id(); - auto isNameLess = a->name() < b->name(); + std::ranges::sort( // + m_workspaces, [&](std::unique_ptr &a, std::unique_ptr &b) { + // Helper comparisons + auto isIdLess = a->id() < b->id(); + auto isNameLess = a->name() < b->name(); - 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 SortMethod::DEFAULT: - default: - // Handle the default case here. - // normal -> named persistent -> named -> special -> named special + 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 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 isIdLess; - } + // both normal (includes numbered persistent) => sort by ID + if (a->id() > 0 && b->id() > 0) { + return isIdLess; + } - // one normal, one special => normal first - if ((a->isSpecial()) ^ (b->isSpecial())) { - return b->isSpecial(); - } + // one normal, one special => normal first + if ((a->isSpecial()) ^ (b->isSpecial())) { + return b->isSpecial(); + } - // only one normal, one named - if ((a->id() > 0) ^ (b->id() > 0)) { - return a->id() > 0; - } + // only one normal, one named + if ((a->id() > 0) ^ (b->id() > 0)) { + return a->id() > 0; + } - // both 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) / named specials (-98 <= ID <= -1) - return isNameLess; - } - - // sort non-special named workspaces by name (ID <= -1377) - return isNameLess; - break; + // both 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) / named specials + // (-98 <= ID <= -1) + return isNameLess; + } - // Return a default value if none of the cases match. - return isNameLess; // You can adjust this to your specific needs. - }); + // sort non-special named workspaces by name (ID <= -1377) + return isNameLess; + break; + } + + // Return a default value if none of the cases match. + return isNameLess; // You can adjust this to your specific needs. + }); for (size_t i = 0; i < m_workspaces.size(); ++i) { m_box.reorder_child(m_workspaces[i]->button(), i); @@ -941,22 +906,17 @@ bool Workspaces::updateWindowsToCreate() { } void Workspaces::updateWorkspaceStates() { - const std::vector visibleWorkspaces = getVisibleWorkspaces(); + const std::vector visibleWorkspaces = getVisibleWorkspaces(); auto updatedWorkspaces = m_ipc.getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { - workspace->setActive(workspace->id() == m_activeWorkspaceId || - workspace->name() == m_activeSpecialWorkspaceName); -<<<<<<< HEAD + workspace->setActive( + workspace->id() == m_activeWorkspaceId || + (workspace->isSpecial() && workspace->name() == m_activeSpecialWorkspaceName)); if (workspace->isActive() && workspace->isUrgent()) { -||||||| parent of 24d391b9 (feat(hyprland): support workspacev2) - if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) { -======= - if (workspace->id() == m_activeWorkspaceId && workspace->isUrgent()) { ->>>>>>> 24d391b9 (feat(hyprland): support workspacev2) workspace->setUrgent(false); } - workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(), - workspace->name()) != visibleWorkspaces.end()); + workspace->setVisible(std::ranges::find(visibleWorkspaces, workspace->id()) != + visibleWorkspaces.end()); std::string &workspaceIcon = m_iconsMap[""]; if (m_withIcon) { workspaceIcon = workspace->selectIcon(m_iconsMap); @@ -994,4 +954,39 @@ int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) { return 0; } +template +std::string Workspaces::makePayload(Args const &...args) { + std::ostringstream result; + bool first = true; + ((result << (first ? "" : ",") << args, first = false), ...); + return result.str(); +} + +std::pair Workspaces::splitDoublePayload(std::string const &payload) { + const std::string part1 = payload.substr(0, payload.find(',')); + const std::string part2 = payload.substr(part1.size() + 1); + return {part1, part2}; +} + +std::tuple Workspaces::splitTriplePayload( + std::string const &payload) { + const size_t firstComma = payload.find(','); + const size_t secondComma = payload.find(',', firstComma + 1); + + const std::string part1 = payload.substr(0, firstComma); + const std::string part2 = payload.substr(firstComma + 1, secondComma - (firstComma + 1)); + const std::string part3 = payload.substr(secondComma + 1); + + return {part1, part2, part3}; +} + +std::optional Workspaces::parseWorkspaceId(std::string const &workspaceIdStr) { + try { + return workspaceIdStr == "special" ? -99 : std::stoi(workspaceIdStr); + } catch (std::exception const &e) { + spdlog::error("Failed to parse workspace ID: {}", e.what()); + return std::nullopt; + } +} + } // namespace waybar::modules::hyprland From 5e4dac1c0aebd6c4ad1f358f09e1cfd06a95d529 Mon Sep 17 00:00:00 2001 From: Harishankar G Date: Wed, 5 Mar 2025 15:27:28 +0530 Subject: [PATCH 772/842] Newline as a seperator when displaying IPv4 and 6 at the same time --- 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 716cd497..0a77c00e 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -331,7 +331,7 @@ auto waybar::modules::Network::update() -> void { final_ipaddr_ = ipaddr6_; } else if (addr_pref_ == ip_addr_pref::IPV4_6) { final_ipaddr_ = ipaddr_; - final_ipaddr_ += " | "; + final_ipaddr_ += '\n'; final_ipaddr_ += ipaddr6_; } From 906170400efd29b8151cbbac7e744485b48457a2 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Wed, 5 Mar 2025 16:53:56 -0800 Subject: [PATCH 773/842] cffi: always return config values as JSON Previously, string JSON values were special cased to be provided as bare strings, which means that CFFI modules have to either know what type each value is expected to be, or use a heuristic such as trying to decode and then treating the value as a string on failure. Instead, we can always return JSON, and let the downstream consumer handle deserialising the value into whatever type is expected. The new behaviour is gated on a new ABI version 2: modules built against version 1 will continue to get the old behaviour. --- resources/custom_modules/cffi_example/main.c | 4 ++-- .../custom_modules/cffi_example/waybar_cffi_module.h | 10 ++++++++-- src/modules/cffi.cpp | 12 ++++++++---- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/resources/custom_modules/cffi_example/main.c b/resources/custom_modules/cffi_example/main.c index ba2c8cf4..7618de58 100644 --- a/resources/custom_modules/cffi_example/main.c +++ b/resources/custom_modules/cffi_example/main.c @@ -17,7 +17,7 @@ void onclicked(GtkButton* button) { } // You must -const size_t wbcffi_version = 1; +const size_t wbcffi_version = 2; void* wbcffi_init(const wbcffi_init_info* init_info, const wbcffi_config_entry* config_entries, size_t config_entries_len) { @@ -67,4 +67,4 @@ void wbcffi_refresh(void* instance, int signal) { void wbcffi_doaction(void* instance, const char* name) { printf("cffi_example inst=%p: doAction(%s)\n", instance, name); -} \ No newline at end of file +} diff --git a/resources/custom_modules/cffi_example/waybar_cffi_module.h b/resources/custom_modules/cffi_example/waybar_cffi_module.h index a7886bea..c1a82f59 100644 --- a/resources/custom_modules/cffi_example/waybar_cffi_module.h +++ b/resources/custom_modules/cffi_example/waybar_cffi_module.h @@ -7,7 +7,7 @@ extern "C" { #endif -/// Waybar ABI version. 1 is the latest version +/// Waybar ABI version. 2 is the latest version extern const size_t wbcffi_version; /// Private Waybar CFFI module @@ -35,7 +35,13 @@ typedef struct { typedef struct { /// Entry key const char* key; - /// Entry value as string. JSON object and arrays are serialized. + /// Entry value + /// + /// In ABI version 1, this may be either a bare string if the value is a + /// string, or the JSON representation of any other JSON object as a string. + /// + /// From ABI version 2 onwards, this is always the JSON representation of the + /// value as a string. const char* value; } wbcffi_config_entry; diff --git a/src/modules/cffi.cpp b/src/modules/cffi.cpp index e560659b..5c095f46 100644 --- a/src/modules/cffi.cpp +++ b/src/modules/cffi.cpp @@ -28,7 +28,7 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co } // Fetch functions - if (*wbcffi_version == 1) { + if (*wbcffi_version == 1 || *wbcffi_version == 2) { // Mandatory functions hooks_.init = reinterpret_cast(dlsym(handle, "wbcffi_init")); if (!hooks_.init) { @@ -58,10 +58,14 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co 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()); + if (*wbcffi_version == 1) { + if (value.isConvertibleTo(Json::ValueType::stringValue)) { + config_entries_stringstor.push_back(value.asString()); + } else { + config_entries_stringstor.push_back(value.toStyledString()); + } } else { - config_entries_stringstor.push_back(config[keys[i]].toStyledString()); + config_entries_stringstor.push_back(value.toStyledString()); } } From f631d5eaf90f0e366d1ae73911cbc0cf67ad6ffa Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Wed, 5 Mar 2025 22:44:55 -0600 Subject: [PATCH 774/842] nix/default: disable version check Downstream added version check, causes this flake to fail building. --- nix/default.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nix/default.nix b/nix/default.nix index a9ff180b..fc564f0a 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -27,6 +27,8 @@ in # downstream patch should not affect upstream patches = []; + # nixpkgs checks version, no need when building locally + nativeInstallCheckInputs = []; buildInputs = (builtins.filter (p: p.pname != "wireplumber") oldAttrs.buildInputs) ++ [ pkgs.wireplumber From 8a15cbad5cf1e116c331108e9ed168e20ef041b4 Mon Sep 17 00:00:00 2001 From: Lars Niesen Date: Mon, 13 Jan 2025 09:10:25 +0100 Subject: [PATCH 775/842] Fixes: Add stretching of modules and modules-center toggling Thanks to tmccombs this commit fixes some inconsitencies in #3730. These inconsitencies were: - Fixed the oversight of missing the implementation of expand_center for center_ and right_ - Removes a last minut printf debugging statment I missed. --- src/bar.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bar.cpp b/src/bar.cpp index b7737d36..3c3ab690 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -545,7 +545,6 @@ auto waybar::Bar::setupWidgets() -> void { if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) { box_.set_center_widget(center_); } else { - spdlog::error("No fixed center_"); box_.pack_start(center_, true, expand_center); } } @@ -569,13 +568,13 @@ auto waybar::Bar::setupWidgets() -> void { if (!no_center) { for (auto const& module : modules_center_) { - center_.pack_start(*module, false, false); + center_.pack_start(*module, module->expandEnabled(), module->expandEnabled()); } } std::reverse(modules_right_.begin(), modules_right_.end()); for (auto const& module : modules_right_) { - right_.pack_end(*module, false, false); + right_.pack_end(*module, module->expandEnabled(), module->expandEnabled()); } } From 6fd859c0c4230cb45fe4503446ba548d09d64158 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Sat, 22 Mar 2025 18:33:03 +0100 Subject: [PATCH 776/842] add login-proxy option There are cases where systemd-logind is not used/running. Result is that bcklight module will not run. Add an option that, when set to false, allows backlight module to work without systemd-logind. --- meson.build | 4 ++++ meson_options.txt | 1 + src/util/backlight_backend.cpp | 2 ++ 3 files changed, 7 insertions(+) diff --git a/meson.build b/meson.build index 5524b49c..1bc1f656 100644 --- a/meson.build +++ b/meson.build @@ -333,6 +333,10 @@ if get_option('niri') ) endif +if get_option('login-proxy') + add_project_arguments('-DHAVE_LOGIN_PROXY', language: 'cpp') +endif + if libnl.found() and libnlgen.found() add_project_arguments('-DHAVE_LIBNL', language: 'cpp') src_files += files('src/modules/network.cpp') diff --git a/meson_options.txt b/meson_options.txt index 303ef038..d83fe01f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -20,3 +20,4 @@ option('jack', type: 'feature', value: 'auto', description: 'Enable support for option('wireplumber', type: 'feature', value: 'auto', description: 'Enable support for WirePlumber') option('cava', type: 'feature', value: 'auto', description: 'Enable support for Cava') option('niri', type: 'boolean', description: 'Enable support for niri') +option('login-proxy', type: 'boolean', description: 'Enable interfacing with dbus login interface') diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index 41236462..863896d5 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -150,6 +150,7 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, throw std::runtime_error("No backlight found"); } +#ifdef HAVE_LOGIN_PROXY // Connect to the login interface login_proxy_ = Gio::DBus::Proxy::create_for_bus_sync( Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.login1", @@ -160,6 +161,7 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.login1", "/org/freedesktop/login1/session/self", "org.freedesktop.login1.Session"); } +#endif udev_thread_ = [this] { std::unique_ptr udev{udev_new()}; From 4ba1947a50d9af7578dc4febdc99bfdc9f88ec8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bartoletti?= Date: Mon, 24 Mar 2025 15:30:57 +0100 Subject: [PATCH 777/842] fix(FreeBSD): Use dev.cpu temperature sysctl --- src/modules/temperature.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 89d3db2d..93f427ee 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -114,13 +114,17 @@ float waybar::modules::Temperature::getTemperature() { auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; - if (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone).c_str(), &temp, &size, - NULL, 0) != 0) { - throw std::runtime_error(fmt::format( - "sysctl hw.acpi.thermal.tz{}.temperature or dev.cpu.{}.temperature failed", zone, zone)); + // First, try with dev.cpu + if ( (sysctlbyname(fmt::format("dev.cpu.{}.temperature", zone).c_str(), &temp, &size, + NULL, 0) == 0) || + (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone).c_str(), &temp, &size, + NULL, 0) == 0) ) { + auto temperature_c = ((float)temp - 2732) / 10; + return temperature_c; } - auto temperature_c = ((float)temp - 2732) / 10; - return temperature_c; + + throw std::runtime_error(fmt::format( + "sysctl hw.acpi.thermal.tz{}.temperature and dev.cpu.{}.temperature failed", zone, zone)); #else // Linux std::ifstream temp(file_path_); @@ -148,4 +152,4 @@ bool waybar::modules::Temperature::isWarning(uint16_t temperature_c) { bool waybar::modules::Temperature::isCritical(uint16_t temperature_c) { return config_["critical-threshold"].isInt() && temperature_c >= config_["critical-threshold"].asInt(); -} \ No newline at end of file +} From 567ae16a680dfc8196f493d4318e9d0cd36e2580 Mon Sep 17 00:00:00 2001 From: tea Date: Fri, 28 Mar 2025 09:42:48 +0100 Subject: [PATCH 778/842] fix incorrect type for `weeks-pos` in waybar-clock man page --- man/waybar-clock.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index f0a88d24..50a5fc07 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -117,7 +117,7 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe :[ 3 :[ Relevant for *mode=year*. Count of months per row |[ *weeks-pos* -:[ integer +:[ string :[ :[ The position where week numbers should be displayed. Disabled when is empty. Possible values: left|right From 9d2b137594313f8bfd79e6c6c2ecfef2f798e035 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Mon, 31 Mar 2025 18:36:12 +0200 Subject: [PATCH 779/842] fix manpage for backlight/slider --- man/waybar-backlight-slider.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-backlight-slider.5.scd b/man/waybar-backlight-slider.5.scd index 8d8353c3..c6f027a1 100644 --- a/man/waybar-backlight-slider.5.scd +++ b/man/waybar-backlight-slider.5.scd @@ -40,7 +40,7 @@ The brightness can be controlled by dragging the slider across the bar or clicki ``` "modules-right": [ - "backlight-slider", + "backlight/slider", ], "backlight/slider": { "min": 0, From c0b8c4d46865bedf721988f73d64b76e41cd6f41 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 1 Apr 2025 00:12:37 +0000 Subject: [PATCH 780/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/5135c59491985879812717f4c9fea69604e7f26f?narHash=sha256-Vr3Qi346M%2B8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic%3D' (2025-02-26) → 'github:NixOS/nixpkgs/52faf482a3889b7619003c0daec593a1912fddc1?narHash=sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om%2BD4UnDhlDW9BE%3D' (2025-03-30) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index e64cbe2c..27290894 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1740560979, - "narHash": "sha256-Vr3Qi346M+8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic=", + "lastModified": 1743315132, + "narHash": "sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om+D4UnDhlDW9BE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5135c59491985879812717f4c9fea69604e7f26f", + "rev": "52faf482a3889b7619003c0daec593a1912fddc1", "type": "github" }, "original": { From c5bc3bc59a19fdc77c4066ec5132617289206d0f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 3 Apr 2025 23:11:33 -0500 Subject: [PATCH 781/842] hyprland/workspaces: fix crash --- 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 c7d397e3..770947ea 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -578,7 +578,7 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { return client["address"].asString() == jsonWindowAddress; }); - if (!client->empty()) { + if (client != clientsData.end() && !client->empty()) { (*inserter)({*client}); } } From 91ef6e51ed5e24d89827d9e8c127a96fef4a2c60 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 3 Apr 2025 23:23:42 -0500 Subject: [PATCH 782/842] hyprland/workspaces: range find lint cleanup --- src/modules/hyprland/workspaces.cpp | 45 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 770947ea..0e225935 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -79,14 +79,14 @@ void Workspaces::createWorkspace(Json::Value const &workspace_data, const auto keys = workspace_data.getMemberNames(); const auto *k = "persistent-rule"; - if (std::find(keys.begin(), keys.end(), k) != keys.end()) { + if (std::ranges::find(keys, 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()) { + if (std::ranges::find(keys, k) != keys.end()) { spdlog::debug("Set config persistency of workspace {} to: {}", workspaceName, workspace_data[k].asBool() ? "true" : "false"); (*workspace)->setPersistentConfig(workspace_data[k].asBool()); @@ -231,7 +231,7 @@ void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJs std::vector persistentWorkspacesToCreate; const std::string currentMonitor = m_bar.output->name; - const bool monitorInConfig = std::find(keys.begin(), keys.end(), currentMonitor) != keys.end(); + const bool monitorInConfig = std::ranges::find(keys, currentMonitor) != keys.end(); for (const std::string &key : keys) { // only add if either: // 1. key is the current monitor name @@ -573,10 +573,9 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { Json::Value clientsData = m_ipc.getSocket1JsonReply("clients"); std::string jsonWindowAddress = fmt::format("0x{}", payload); - auto client = - std::find_if(clientsData.begin(), clientsData.end(), [jsonWindowAddress](auto &client) { - return client["address"].asString() == jsonWindowAddress; - }); + auto client = std::ranges::find_if(clientsData, [jsonWindowAddress](auto &client) { + return client["address"].asString() == jsonWindowAddress; + }); if (client != clientsData.end() && !client->empty()) { (*inserter)({*client}); @@ -760,9 +759,9 @@ void Workspaces::setCurrentMonitorId() { // get monitor ID from name (used by persistent workspaces) m_monitorId = 0; auto monitors = m_ipc.getSocket1JsonReply("monitors"); - auto currentMonitor = std::find_if( - monitors.begin(), monitors.end(), - [this](const Json::Value &m) { return m["name"].asString() == m_bar.output->name; }); + auto currentMonitor = std::ranges::find_if(monitors, [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 { @@ -846,9 +845,9 @@ void Workspaces::setUrgentWorkspace(std::string const &windowaddress) { } } - auto workspace = - std::find_if(m_workspaces.begin(), m_workspaces.end(), - [workspaceId](std::unique_ptr &x) { return x->id() == workspaceId; }); + auto workspace = std::ranges::find_if(m_workspaces, [workspaceId](std::unique_ptr &x) { + return x->id() == workspaceId; + }); if (workspace != m_workspaces.end()) { workspace->get()->setUrgent(); } @@ -862,11 +861,10 @@ auto Workspaces::update() -> void { void Workspaces::updateWindowCount() { const Json::Value workspacesJson = m_ipc.getSocket1JsonReply("workspaces"); for (auto &workspace : m_workspaces) { - auto workspaceJson = - std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) { - return x["name"].asString() == workspace->name() || - (workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name()); - }); + auto workspaceJson = std::ranges::find_if(workspacesJson, [&](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 { @@ -921,12 +919,11 @@ void Workspaces::updateWorkspaceStates() { if (m_withIcon) { workspaceIcon = workspace->selectIcon(m_iconsMap); } - auto updatedWorkspace = std::find_if( - updatedWorkspaces.begin(), updatedWorkspaces.end(), [&workspace](const auto &w) { - auto wNameRaw = w["name"].asString(); - auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; - return wName == workspace->name(); - }); + auto updatedWorkspace = std::ranges::find_if(updatedWorkspaces, [&workspace](const auto &w) { + auto wNameRaw = w["name"].asString(); + auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw; + return wName == workspace->name(); + }); if (updatedWorkspace != updatedWorkspaces.end()) { workspace->setOutput((*updatedWorkspace)["monitor"].asString()); } From 84162ec60437756fd3ad089ba1b8596bdad64b63 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 3 Apr 2025 23:48:01 -0500 Subject: [PATCH 783/842] .github/workflows/clang-format: bump github action --- .github/workflows/clang-format.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 3c62819e..b5bfbe36 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -11,7 +11,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: DoozyX/clang-format-lint-action@v0.16.2 + # TODO: bump to clang 19 release + # - uses: DoozyX/clang-format-lint-action@v0.18.2 + - uses: DoozyX/clang-format-lint-action@558090054b3f39e3d6af24f0cd73b319535da809 name: clang-format with: source: "." From 5ff6b0ad0ff1072a15fb7ed16a5595d083e48661 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 3 Apr 2025 23:56:32 -0500 Subject: [PATCH 784/842] .github/workflows: tweak job names They didn't seem to correspond to the workflow, properly. Making triggering them locally weird. --- .github/workflows/clang-format.yml | 2 +- .github/workflows/clang-tidy.yml.bak | 2 +- .github/workflows/freebsd.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index b5bfbe36..0fad47c4 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -7,7 +7,7 @@ concurrency: cancel-in-progress: true jobs: - build: + lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/clang-tidy.yml.bak b/.github/workflows/clang-tidy.yml.bak index a39bd23d..ec67fb7e 100644 --- a/.github/workflows/clang-tidy.yml.bak +++ b/.github/workflows/clang-tidy.yml.bak @@ -7,7 +7,7 @@ concurrency: cancel-in-progress: true jobs: - build: + lint: runs-on: ubuntu-latest container: image: alexays/waybar:debian diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index bbb97198..ca0dcbc8 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -7,7 +7,7 @@ concurrency: cancel-in-progress: true jobs: - clang: + build: # Run actions in a FreeBSD VM on the ubuntu runner # https://github.com/actions/runner/issues/385 - for FreeBSD runner support runs-on: ubuntu-latest From 9ca52a48c86c362c28aa3b9102192756daf82a16 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 6 Apr 2025 09:46:06 +0200 Subject: [PATCH 785/842] wireplumber: fix potential nullpointer deref --- 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 c25b3b51..106ca403 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -299,7 +299,7 @@ void waybar::modules::Wireplumber::onMixerApiLoaded(WpObject* p, GAsyncResult* r gboolean success = FALSE; g_autoptr(GError) error = nullptr; - success = wp_core_load_component_finish(self->wp_core_, res, nullptr); + success = wp_core_load_component_finish(self->wp_core_, res, &error); if (success == FALSE) { spdlog::error("[{}]: mixer API load failed", self->name_); From e92b0a86b5595a3c130f9c2fded724c71ac89d35 Mon Sep 17 00:00:00 2001 From: Clemens Horn Date: Mon, 7 Apr 2025 20:33:18 +0200 Subject: [PATCH 786/842] wlr/taskbar: find icon by title as fallback --- src/modules/wlr/taskbar.cpp | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 30e4ee48..3c188dd0 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -382,9 +382,40 @@ std::string Task::state_string(bool shortened) const { return res.substr(0, res.size() - 1); } -void Task::handle_title(const char *title) { - title_ = title; - hide_if_ignored(); +void Task::handle_title(const char* title) { + title_ = title; + hide_if_ignored(); + + // Skip if we already have app info or no title + if (app_info_ || title_.empty()) { + return; + } + + // Try exact title match + app_info_ = get_desktop_app_info(title_); + + // Try lowercase version if still needed + if (!app_info_) { + std::string lower_title = title_; + std::transform(lower_title.begin(), lower_title.end(), lower_title.begin(), ::tolower); + app_info_ = get_desktop_app_info(lower_title); + } + + // If we found a match, update name and icon + if (app_info_) { + name_ = app_info_->get_display_name(); + spdlog::info("Found desktop file via title fallback: {}", name_); + + if (with_icon_) { + const int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16; + for (auto& icon_theme : tbar_->icon_themes()) { + if (image_load_icon(icon_, icon_theme, app_info_, icon_size)) { + icon_.show(); + break; + } + } + } + } } void Task::set_minimize_hint() { From addf44d9457ae6f17e73cb4b428b7d3be242390c Mon Sep 17 00:00:00 2001 From: Clemens Horn Date: Mon, 7 Apr 2025 20:51:35 +0200 Subject: [PATCH 787/842] test --- src/modules/wlr/taskbar.cpp | 59 ++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 3c188dd0..1169425a 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -383,39 +383,38 @@ std::string Task::state_string(bool shortened) const { } void Task::handle_title(const char* title) { - title_ = title; - hide_if_ignored(); + if (title_.empty()) { + spdlog::debug(fmt::format("Task ({}) setting title to {}", id_, title_)); + } else { + spdlog::debug(fmt::format("Task ({}) overwriting title '{}' with '{}'", id_, title_, title)); + } + title_ = title; + hide_if_ignored(); - // Skip if we already have app info or no title - if (app_info_ || title_.empty()) { - return; + if (!with_icon_ && !with_name_ || app_info_) { + return; + } + + set_app_info_from_app_id_list(title_); + name_ = app_info_ ? app_info_->get_display_name() : title; + + if (!with_icon_) { + return; + } + + int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16; + bool found = false; + for (auto &icon_theme : tbar_->icon_themes()) { + if (image_load_icon(icon_, icon_theme, app_info_, icon_size)) { + found = true; + break; } + } - // Try exact title match - app_info_ = get_desktop_app_info(title_); - - // Try lowercase version if still needed - if (!app_info_) { - std::string lower_title = title_; - std::transform(lower_title.begin(), lower_title.end(), lower_title.begin(), ::tolower); - app_info_ = get_desktop_app_info(lower_title); - } - - // If we found a match, update name and icon - if (app_info_) { - name_ = app_info_->get_display_name(); - spdlog::info("Found desktop file via title fallback: {}", name_); - - if (with_icon_) { - const int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16; - for (auto& icon_theme : tbar_->icon_themes()) { - if (image_load_icon(icon_, icon_theme, app_info_, icon_size)) { - icon_.show(); - break; - } - } - } - } + if (found) + icon_.show(); + else + spdlog::debug("Couldn't find icon for {}", title_); } void Task::set_minimize_hint() { From 633bf9e00fab791084682d4442a47f5d739d9b82 Mon Sep 17 00:00:00 2001 From: Corey Doughty Date: Thu, 10 Apr 2025 06:56:00 -0400 Subject: [PATCH 788/842] Hyprland submap allow pango markup. --- src/modules/hyprland/submap.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 97c4bb62..9ded789b 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -68,8 +68,9 @@ void Submap::onEvent(const std::string& ev) { return; } - auto submapName = ev.substr(ev.find_last_of('>') + 1); - submapName = waybar::util::sanitize_string(submapName); + //auto submapName = ev.substr(ev.find_last_of('>') + 1); + //submapName = waybar::util::sanitize_string(submapName); + auto submapName = ev.substr(ev.find_first_of('>') + 2 ); if (!submap_.empty()) { label_.get_style_context()->remove_class(submap_); From 682492f7a93331e930f9f57dd82dd681e0f78f49 Mon Sep 17 00:00:00 2001 From: Corey Doughty Date: Thu, 10 Apr 2025 07:05:45 -0400 Subject: [PATCH 789/842] This commit fixes #4023 --- src/modules/hyprland/submap.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 9ded789b..33de79f5 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -68,8 +68,6 @@ void Submap::onEvent(const std::string& ev) { return; } - //auto submapName = ev.substr(ev.find_last_of('>') + 1); - //submapName = waybar::util::sanitize_string(submapName); auto submapName = ev.substr(ev.find_first_of('>') + 2 ); if (!submap_.empty()) { From afb1ee54221838c98c8583ec6da8fa4ad7913aa9 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 11 Apr 2025 14:05:39 -0500 Subject: [PATCH 790/842] audio_backend: fix crash Getting crashes when called before we have proper information. --- src/util/audio_backend.cpp | 138 +++++++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 28 deletions(-) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 807b5dc7..860168fd 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -24,6 +24,8 @@ AudioBackend::AudioBackend(std::function on_updated_cb, private_construc source_volume_(0), source_muted_(false), on_updated_cb_(std::move(on_updated_cb)) { + // Initialize pa_volume_ with safe defaults + pa_cvolume_init(&pa_volume_); mainloop_ = pa_threaded_mainloop_new(); if (mainloop_ == nullptr) { throw std::runtime_error("pa_mainloop_new() failed."); @@ -131,7 +133,12 @@ void AudioBackend::subscribeCb(pa_context *context, pa_subscription_event_type_t 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); + if ((backend->context_ != nullptr) && + pa_context_get_state(backend->context_) == PA_CONTEXT_READY) { + pa_context_get_sink_info_by_index(backend->context_, backend->sink_idx_, sinkInfoCb, data); + } + } else { + spdlog::debug("Volume modification failed"); } } @@ -180,11 +187,20 @@ void AudioBackend::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i, i } if (backend->current_sink_name_ == i->name) { - backend->pa_volume_ = i->volume; - float volume = - static_cast(pa_cvolume_avg(&(backend->pa_volume_))) / float{PA_VOLUME_NORM}; - backend->sink_idx_ = i->index; - backend->volume_ = std::round(volume * 100.0F); + // Safely copy the volume structure + if (pa_cvolume_valid(&i->volume) != 0) { + 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); + } else { + spdlog::error("Invalid volume structure received from PulseAudio"); + // Initialize with safe defaults + pa_cvolume_init(&backend->pa_volume_); + backend->volume_ = 0; + } + backend->muted_ = i->mute != 0; backend->desc_ = i->description; backend->monitor_ = i->monitor_source_name; @@ -230,43 +246,109 @@ void AudioBackend::serverInfoCb(pa_context *context, const pa_server_info *i, vo } void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t max_volume) { - double volume_tick = static_cast(PA_VOLUME_NORM) / 100; - pa_cvolume pa_volume = pa_volume_; + // Early return if context is not ready + if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) { + spdlog::error("PulseAudio context not ready"); + return; + } + // Prepare volume structure + pa_cvolume pa_volume; + + pa_cvolume_init(&pa_volume); + + // Use existing volume structure if valid, otherwise create a safe default + if ((pa_cvolume_valid(&pa_volume_) != 0) && (pa_channels_valid(pa_volume_.channels) != 0)) { + pa_volume = pa_volume_; + } else { + // Set stereo as a safe default + pa_volume.channels = 2; + spdlog::debug("Using default stereo volume structure"); + } + + // Set the volume safely volume = std::clamp(volume, min_volume, max_volume); - pa_cvolume_set(&pa_volume, pa_volume_.channels, volume * volume_tick); + pa_volume_t vol = volume * (static_cast(PA_VOLUME_NORM) / 100); + // Set all channels to the same volume manually to avoid pa_cvolume_set + for (uint8_t i = 0; i < pa_volume.channels; i++) { + pa_volume.values[i] = vol; + } + + // Apply the volume change pa_threaded_mainloop_lock(mainloop_); pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); pa_threaded_mainloop_unlock(mainloop_); } 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_; + // Early return if context is not ready + if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) { + spdlog::error("PulseAudio context not ready"); + return; + } + // Prepare volume structure + pa_cvolume pa_volume; + pa_cvolume_init(&pa_volume); + + // Use existing volume structure if valid, otherwise create a safe default + if ((pa_cvolume_valid(&pa_volume_) != 0) && (pa_channels_valid(pa_volume_.channels) != 0)) { + pa_volume = pa_volume_; + } else { + // Set stereo as a safe default + pa_volume.channels = 2; + spdlog::debug("Using default stereo volume structure"); + + // Initialize all channels to current volume level + double volume_tick = static_cast(PA_VOLUME_NORM) / 100; + pa_volume_t vol = volume_ * volume_tick; + for (uint8_t i = 0; i < pa_volume.channels; i++) { + pa_volume.values[i] = vol; + } + + // No need to continue with volume change if we had to create a new structure + pa_threaded_mainloop_lock(mainloop_); + pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); + pa_threaded_mainloop_unlock(mainloop_); + return; + } + + // Calculate volume change + double volume_tick = static_cast(PA_VOLUME_NORM) / 100; + pa_volume_t change; 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); + if (change_type == ChangeType::Increase && volume_ < max_volume) { + // Calculate how much to increase + if (volume_ + step > max_volume) { + change = round((max_volume - volume_) * volume_tick); + } else { + change = round(step * volume_tick); } - } 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); + + // Manually increase each channel's volume + for (uint8_t i = 0; i < pa_volume.channels; i++) { + pa_volume.values[i] = std::min(pa_volume.values[i] + change, PA_VOLUME_MAX); } + } else if (change_type == ChangeType::Decrease && volume_ > 0) { + // Calculate how much to decrease + if (volume_ - step < 0) { + change = round(volume_ * volume_tick); + } else { + change = round(step * volume_tick); + } + + // Manually decrease each channel's volume + for (uint8_t i = 0; i < pa_volume.channels; i++) { + pa_volume.values[i] = (pa_volume.values[i] > change) ? (pa_volume.values[i] - change) : 0; + } + } else { + // No change needed + return; } + + // Apply the volume change pa_threaded_mainloop_lock(mainloop_); pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); pa_threaded_mainloop_unlock(mainloop_); From 7e845f506e6fe666e6f5ba9e2907511adb373740 Mon Sep 17 00:00:00 2001 From: Almarhoon Ibraheem Date: Sat, 12 Apr 2025 18:31:34 +0300 Subject: [PATCH 791/842] sway workspace: fix workspace button not shown in nested layouts --- src/modules/sway/workspaces.cpp | 34 +++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index dec5cddf..b8ed73d4 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -494,16 +494,34 @@ std::string Workspaces::trimWorkspaceName(std::string name) { return name; } +bool is_focused_recursive(const Json::Value& node) { + // If a workspace has a focused container then get_tree will say + // that the workspace itself isn't focused. Therefore we need to + // check if any of its nodes are focused as well. + // some layouts like tabbed have many nested nodes + // all nested nodes must be checked for focused flag + if (node["focused"].asBool()) { + return true; + } + + for (const auto& child : node["nodes"]) { + if (is_focused_recursive(child)) { + return true; + } + } + + for (const auto& child : node["floating_nodes"]) { + if (is_focused_recursive(child)) { + return true; + } + } + + return false; +} + void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) { if (config_["current-only"].asBool()) { - // If a workspace has a focused container then get_tree will say - // that the workspace itself isn't focused. Therefore we need to - // check if any of its nodes are focused as well. - bool focused = node["focused"].asBool() || - std::any_of(node["nodes"].begin(), node["nodes"].end(), - [](const auto &child) { return child["focused"].asBool(); }); - - if (focused) { + if (is_focused_recursive(node)) { button.show(); } else { button.hide(); From b03ecb3d744a6d60d01ff1ace0c01b08eef01307 Mon Sep 17 00:00:00 2001 From: literallyvoid Date: Sat, 12 Apr 2025 17:21:57 -0700 Subject: [PATCH 792/842] Move signal handling to main thread --- src/main.cpp | 130 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 105 insertions(+), 25 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 045b2cd4..94355e1b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,6 +8,7 @@ #include #include "client.hpp" +#include "util/SafeSignal.hpp" std::mutex reap_mtx; std::list reap; @@ -70,37 +72,115 @@ void startSignalThread() { } } +static int signal_pipe_write_fd; + +// Write a single signal to `signal_pipe_write_fd`. +// This function is set as a signal handler, so it must be async-signal-safe. +static void writeSignalToPipe(int signum) { + ssize_t amt = write(signal_pipe_write_fd, &signum, sizeof(int)); + + // There's not much we can safely do inside of a signal handler. + // Let's just ignore any errors. + (void) amt; +} + +// This initializes `signal_pipe_write_fd`, and sets up signal handlers. +// +// This function will run forever, emitting every `SIGUSR1`, `SIGUSR2`, +// `SIGINT`, and `SIGRTMIN + 1`...`SIGRTMAX` signal received to +// `signal_handler`. +static void catchSignals(waybar::SafeSignal &signal_handler) { + int fd[2]; + pipe(fd); + + int signal_pipe_read_fd = fd[0]; + signal_pipe_write_fd = fd[1]; + + // This pipe should be able to buffer ~thousands of signals. If it fills up, + // we'll drop signals instead of blocking. + + // We can't allow the write end to block because we'll be writing to it in a + // signal handler, which could interrupt the loop that's reading from it and + // deadlock. + + fcntl(signal_pipe_write_fd, F_SETFL, O_NONBLOCK); + + std::signal(SIGUSR1, writeSignalToPipe); + std::signal(SIGUSR2, writeSignalToPipe); + std::signal(SIGINT, writeSignalToPipe); + + for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) { + std::signal(sig, writeSignalToPipe); + } + + while (true) { + int signum; + ssize_t amt = read(signal_pipe_read_fd, &signum, sizeof(int)); + if (amt < 0) { + spdlog::error("read from signal pipe failed with error {}, closing thread", strerror(errno)); + break; + } + + if (amt != sizeof(int)) { + continue; + } + + signal_handler.emit(signum); + } +} + +// Must be called on the main thread. +static void handleSignalMainThread(int signum) { + if (signum >= SIGRTMIN + 1 && signum <= SIGRTMAX) { + for (auto& bar : waybar::Client::inst()->bars) { + bar->handleSignal(signum); + } + + return; + } + + switch (signum) { + case SIGUSR1: + spdlog::debug("Visibility toggled"); + for (auto& bar : waybar::Client::inst()->bars) { + bar->toggle(); + } + break; + case SIGUSR2: + spdlog::info("Reloading..."); + reload = true; + waybar::Client::inst()->reset(); + break; + case SIGINT: + spdlog::info("Quitting."); + reload = false; + waybar::Client::inst()->reset(); + break; + default: + spdlog::debug("Received signal with number {}, but not handling", signum); + break; + } +} + int main(int argc, char* argv[]) { try { auto* client = waybar::Client::inst(); - std::signal(SIGUSR1, [](int /*signal*/) { - for (auto& bar : waybar::Client::inst()->bars) { - bar->toggle(); - } - }); - - std::signal(SIGUSR2, [](int /*signal*/) { - spdlog::info("Reloading..."); - reload = true; - waybar::Client::inst()->reset(); - }); - - std::signal(SIGINT, [](int /*signal*/) { - spdlog::info("Quitting."); - reload = false; - waybar::Client::inst()->reset(); - }); - - for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) { - std::signal(sig, [](int sig) { - for (auto& bar : waybar::Client::inst()->bars) { - bar->handleSignal(sig); - } - }); - } startSignalThread(); + waybar::SafeSignal posix_signal_received; + posix_signal_received.connect([&](int signum) { + handleSignalMainThread(signum); + }); + + std::thread signal_thread([&]() { + catchSignals(posix_signal_received); + }); + + // Every `std::thread` must be joined or detached. + // This thread should run forever, so detach it. + signal_thread.detach(); + auto ret = 0; do { reload = false; From 97591c825a394fc3bedde47b26bed9d605459cdc Mon Sep 17 00:00:00 2001 From: literallyvoid Date: Sat, 12 Apr 2025 16:41:18 -0700 Subject: [PATCH 793/842] Remove `signalThread` and move reaping to `catchSignals` --- src/main.cpp | 78 +++++++++++----------------------------------------- 1 file changed, 16 insertions(+), 62 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 94355e1b..ba6e00dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,64 +14,6 @@ std::mutex reap_mtx; std::list reap; volatile bool reload; -void* signalThread(void* args) { - int err; - int signum; - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGCHLD); - - while (true) { - err = sigwait(&mask, &signum); - if (err != 0) { - spdlog::error("sigwait failed: {}", strerror(errno)); - continue; - } - - switch (signum) { - case SIGCHLD: - spdlog::debug("Received SIGCHLD in signalThread"); - if (!reap.empty()) { - reap_mtx.lock(); - for (auto it = reap.begin(); it != reap.end(); ++it) { - if (waitpid(*it, nullptr, WNOHANG) == *it) { - spdlog::debug("Reaped child with PID: {}", *it); - it = reap.erase(it); - } - } - reap_mtx.unlock(); - } - break; - default: - spdlog::debug("Received signal with number {}, but not handling", signum); - break; - } - } -} - -void startSignalThread() { - int err; - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGCHLD); - - // Block SIGCHLD so it can be handled by the signal thread - // Any threads created by this one (the main thread) should not - // modify their signal mask to unblock SIGCHLD - err = pthread_sigmask(SIG_BLOCK, &mask, nullptr); - if (err != 0) { - spdlog::error("pthread_sigmask failed in startSignalThread: {}", strerror(err)); - exit(1); - } - - pthread_t thread_id; - err = pthread_create(&thread_id, nullptr, signalThread, nullptr); - if (err != 0) { - spdlog::error("pthread_create failed in startSignalThread: {}", strerror(err)); - exit(1); - } -} - static int signal_pipe_write_fd; // Write a single signal to `signal_pipe_write_fd`. @@ -87,8 +29,8 @@ static void writeSignalToPipe(int signum) { // This initializes `signal_pipe_write_fd`, and sets up signal handlers. // // This function will run forever, emitting every `SIGUSR1`, `SIGUSR2`, -// `SIGINT`, and `SIGRTMIN + 1`...`SIGRTMAX` signal received to -// `signal_handler`. +// `SIGINT`, `SIGCHLD`, and `SIGRTMIN + 1`...`SIGRTMAX` signal received +// to `signal_handler`. static void catchSignals(waybar::SafeSignal &signal_handler) { int fd[2]; pipe(fd); @@ -108,6 +50,7 @@ static void catchSignals(waybar::SafeSignal &signal_handler) { std::signal(SIGUSR1, writeSignalToPipe); std::signal(SIGUSR2, writeSignalToPipe); std::signal(SIGINT, writeSignalToPipe); + std::signal(SIGCHLD, writeSignalToPipe); for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) { std::signal(sig, writeSignalToPipe); @@ -156,6 +99,19 @@ static void handleSignalMainThread(int signum) { reload = false; waybar::Client::inst()->reset(); break; + case SIGCHLD: + spdlog::debug("Received SIGCHLD in signalThread"); + if (!reap.empty()) { + reap_mtx.lock(); + for (auto it = reap.begin(); it != reap.end(); ++it) { + if (waitpid(*it, nullptr, WNOHANG) == *it) { + spdlog::debug("Reaped child with PID: {}", *it); + it = reap.erase(it); + } + } + reap_mtx.unlock(); + } + break; default: spdlog::debug("Received signal with number {}, but not handling", signum); break; @@ -166,8 +122,6 @@ int main(int argc, char* argv[]) { try { auto* client = waybar::Client::inst(); - startSignalThread(); - waybar::SafeSignal posix_signal_received; posix_signal_received.connect([&](int signum) { handleSignalMainThread(signum); From dbd3ffd73217506ce519179f460820a870b334b6 Mon Sep 17 00:00:00 2001 From: literallyvoid Date: Sat, 12 Apr 2025 17:48:16 -0700 Subject: [PATCH 794/842] Convert `reload` to a local --- src/main.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index ba6e00dc..ce493ca1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,7 +12,6 @@ std::mutex reap_mtx; std::list reap; -volatile bool reload; static int signal_pipe_write_fd; @@ -73,7 +72,10 @@ static void catchSignals(waybar::SafeSignal &signal_handler) { } // Must be called on the main thread. -static void handleSignalMainThread(int signum) { +// +// If this signal should restart or close the bar, this function will write +// `true` or `false`, respectively, into `reload`. +static void handleSignalMainThread(int signum, bool &reload) { if (signum >= SIGRTMIN + 1 && signum <= SIGRTMAX) { for (auto& bar : waybar::Client::inst()->bars) { bar->handleSignal(signum); @@ -122,9 +124,11 @@ int main(int argc, char* argv[]) { try { auto* client = waybar::Client::inst(); + bool reload; + waybar::SafeSignal posix_signal_received; posix_signal_received.connect([&](int signum) { - handleSignalMainThread(signum); + handleSignalMainThread(signum, reload); }); std::thread signal_thread([&]() { From 252e4f78bfb18d515c99c55afbe47a3987deb2d7 Mon Sep 17 00:00:00 2001 From: Kaiyang Wu Date: Sun, 13 Apr 2025 22:11:41 -0700 Subject: [PATCH 795/842] fix: support libcava 0.10.4 Signed-off-by: Kaiyang Wu --- include/modules/cava.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/modules/cava.hpp b/include/modules/cava.hpp index b4d2325b..1a88c7b7 100644 --- a/include/modules/cava.hpp +++ b/include/modules/cava.hpp @@ -5,7 +5,16 @@ namespace cava { extern "C" { +// Need sdl_glsl output feature to be enabled on libcava +#ifndef SDL_GLSL +#define SDL_GLSL +#endif + #include + +#ifdef SDL_GLSL +#undef SDL_GLSL +#endif } } // namespace cava From 517eb7651e3ea6c7d731e7d9e21d216350d39150 Mon Sep 17 00:00:00 2001 From: literallyvoid Date: Mon, 14 Apr 2025 12:30:08 -0700 Subject: [PATCH 796/842] Run `clang-format` on main.cpp --- src/main.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index ce493ca1..6e7650a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,7 +22,7 @@ static void writeSignalToPipe(int signum) { // There's not much we can safely do inside of a signal handler. // Let's just ignore any errors. - (void) amt; + (void)amt; } // This initializes `signal_pipe_write_fd`, and sets up signal handlers. @@ -30,7 +30,7 @@ static void writeSignalToPipe(int signum) { // This function will run forever, emitting every `SIGUSR1`, `SIGUSR2`, // `SIGINT`, `SIGCHLD`, and `SIGRTMIN + 1`...`SIGRTMAX` signal received // to `signal_handler`. -static void catchSignals(waybar::SafeSignal &signal_handler) { +static void catchSignals(waybar::SafeSignal& signal_handler) { int fd[2]; pipe(fd); @@ -72,10 +72,10 @@ static void catchSignals(waybar::SafeSignal &signal_handler) { } // Must be called on the main thread. -// +// // If this signal should restart or close the bar, this function will write // `true` or `false`, respectively, into `reload`. -static void handleSignalMainThread(int signum, bool &reload) { +static void handleSignalMainThread(int signum, bool& reload) { if (signum >= SIGRTMIN + 1 && signum <= SIGRTMAX) { for (auto& bar : waybar::Client::inst()->bars) { bar->handleSignal(signum); @@ -114,7 +114,7 @@ static void handleSignalMainThread(int signum, bool &reload) { reap_mtx.unlock(); } break; - default: + default: spdlog::debug("Received signal with number {}, but not handling", signum); break; } @@ -127,13 +127,9 @@ int main(int argc, char* argv[]) { bool reload; waybar::SafeSignal posix_signal_received; - posix_signal_received.connect([&](int signum) { - handleSignalMainThread(signum, reload); - }); + posix_signal_received.connect([&](int signum) { handleSignalMainThread(signum, reload); }); - std::thread signal_thread([&]() { - catchSignals(posix_signal_received); - }); + std::thread signal_thread([&]() { catchSignals(posix_signal_received); }); // Every `std::thread` must be joined or detached. // This thread should run forever, so detach it. From e85025f8052fbf25730c4153548677222d935490 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 15 Apr 2025 16:33:07 +0300 Subject: [PATCH 797/842] libCava bump: 0.10.4 --- meson.build | 2 +- subprojects/cava.wrap | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index 1bc1f656..7f9854d5 100644 --- a/meson.build +++ b/meson.build @@ -486,7 +486,7 @@ if get_option('experimental') endif cava = dependency('cava', - version : '>=0.10.3', + version : '>=0.10.4', required: get_option('cava'), fallback : ['cava', 'cava_dep'], not_found_message: 'cava is not found. Building waybar without cava') diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap index f0309bf5..f220207c 100644 --- a/subprojects/cava.wrap +++ b/subprojects/cava.wrap @@ -1,7 +1,7 @@ [wrap-file] -directory = cava-0.10.3 -source_url = https://github.com/LukashonakV/cava/archive/0.10.3.tar.gz -source_filename = cava-0.10.3.tar.gz -source_hash = aab0a4ed3f999e8461ad9de63ef8a77f28b6b2011f7dd0c69ba81819d442f6f9 +directory = cava-0.10.4 +source_url = https://github.com/LukashonakV/cava/archive/0.10.4.tar.gz +source_filename = cava-0.10.4.tar.gz +source_hash =7bc1c1f9535f2bcc5cd2ae8a2434a2e3a05f5670b1c96316df304137ffc65756 [provide] cava = cava_dep From bf4f3ab064a4b8aa3b37868a1a6a8abede373e6e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 15 Apr 2025 12:06:41 -0500 Subject: [PATCH 798/842] nix: cava bump --- nix/default.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/default.nix b/nix/default.nix index fc564f0a..2e621272 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -5,12 +5,12 @@ }: let libcava = rec { - version = "0.10.3"; + version = "0.10.4"; src = pkgs.fetchFromGitHub { owner = "LukashonakV"; repo = "cava"; - rev = version; - hash = "sha256-ZDFbI69ECsUTjbhlw2kHRufZbQMu+FQSMmncCJ5pagg="; + tag = version; + hash = "sha256-9eTDqM+O1tA/3bEfd1apm8LbEcR9CVgELTIspSVPMKM="; }; }; in From 5c48373cfedb32300a51bb0e567fb9eeedc48253 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Thu, 3 Apr 2025 23:43:47 -0500 Subject: [PATCH 799/842] flake.nix: add treefmt formatter Easier to format everything properly. --- flake.nix | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/flake.nix b/flake.nix index 571c4934..d19d4a1f 100644 --- a/flake.nix +++ b/flake.nix @@ -45,12 +45,41 @@ # overrides for local development nativeBuildInputs = pkgs.waybar.nativeBuildInputs ++ (with pkgs; [ + nixfmt-rfc-style clang-tools gdb ]); }; }); + formatter = genSystems ( + pkgs: + pkgs.treefmt.withConfig { + settings = [ + { + formatter = { + clang-format = { + options = [ "-i" ]; + command = lib.getExe' pkgs.clang-tools "clang-format"; + excludes = []; + includes = [ + "*.c" + "*.cpp" + "*.h" + "*.hpp" + ]; + }; + nixfmt = { + command = lib.getExe pkgs.nixfmt-rfc-style; + includes = [ "*.nix" ]; + }; + }; + tree-root-file = ".git/index"; + } + ]; + } + ); + overlays = { default = self.overlays.waybar; waybar = final: prev: { From 55f52c345775535649d8861b83a0e936a207830c Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 15 Apr 2025 14:56:28 -0500 Subject: [PATCH 800/842] treewide: clang and nix format --- default.nix | 19 +++---- flake.nix | 106 ++++++++++++++++++++++-------------- nix/default.nix | 53 +++++++++--------- src/modules/sni/item.cpp | 12 ++-- src/modules/sni/tray.cpp | 3 +- src/modules/temperature.cpp | 8 +-- src/modules/wlr/taskbar.cpp | 2 +- 7 files changed, 113 insertions(+), 90 deletions(-) diff --git a/default.nix b/default.nix index 2cccff28..6466507b 100644 --- a/default.nix +++ b/default.nix @@ -1,10 +1,9 @@ -(import - ( - let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in - fetchTarball { - url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; - sha256 = lock.nodes.flake-compat.locked.narHash; - } - ) - { src = ./.; } -).defaultNix +(import ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + in + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; + } +) { src = ./.; }).defaultNix diff --git a/flake.nix b/flake.nix index d19d4a1f..7c7a2281 100644 --- a/flake.nix +++ b/flake.nix @@ -9,48 +9,68 @@ }; }; - outputs = { self, nixpkgs, ... }: + outputs = + { self, nixpkgs, ... }: let inherit (nixpkgs) lib; - genSystems = func: lib.genAttrs [ - "x86_64-linux" - "aarch64-linux" - ] - (system: func (import nixpkgs { - inherit system; - overlays = with self.overlays; [ - waybar - ]; - })); + genSystems = + func: + lib.genAttrs + [ + "x86_64-linux" + "aarch64-linux" + ] + ( + system: + func ( + import nixpkgs { + inherit system; + overlays = with self.overlays; [ + waybar + ]; + } + ) + ); - mkDate = longDate: (lib.concatStringsSep "-" [ - (builtins.substring 0 4 longDate) - (builtins.substring 4 2 longDate) - (builtins.substring 6 2 longDate) - ]); + mkDate = + longDate: + (lib.concatStringsSep "-" [ + (builtins.substring 0 4 longDate) + (builtins.substring 4 2 longDate) + (builtins.substring 6 2 longDate) + ]); in { - devShells = genSystems - (pkgs: - { - default = - pkgs.mkShell - { - name = "waybar-shell"; + 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; + # 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; [ - nixfmt-rfc-style - clang-tools - gdb - ]); - }; - }); + # overrides for local development + nativeBuildInputs = + pkgs.waybar.nativeBuildInputs + ++ (with pkgs; [ + nixfmt-rfc-style + clang-tools + gdb + ]); + }; + }); formatter = genSystems ( pkgs: @@ -61,7 +81,7 @@ clang-format = { options = [ "-i" ]; command = lib.getExe' pkgs.clang-tools "clang-format"; - excludes = []; + excludes = [ ]; includes = [ "*.c" "*.cpp" @@ -87,11 +107,15 @@ waybar = prev.waybar; # take the first "version: '...'" from meson.build version = - (builtins.head (builtins.split "'" - (builtins.elemAt - (builtins.split " version: '" (builtins.readFile ./meson.build)) - 2))) - + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); + (builtins.head ( + builtins.split "'" ( + builtins.elemAt (builtins.split " version: '" (builtins.readFile ./meson.build)) 2 + ) + )) + + "+date=" + + (mkDate (self.lastModifiedDate or "19700101")) + + "_" + + (self.shortRev or "dirty"); }; }; }; diff --git a/nix/default.nix b/nix/default.nix index 2e621272..4cfd75c0 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,7 +1,8 @@ -{ lib -, pkgs -, waybar -, version +{ + lib, + pkgs, + waybar, + version, }: let libcava = rec { @@ -14,31 +15,29 @@ let }; }; in -(waybar.overrideAttrs ( - oldAttrs: { - inherit version; +(waybar.overrideAttrs (oldAttrs: { + inherit version; - src = lib.cleanSourceWith { - filter = name: type: type != "regular" || !lib.hasSuffix ".nix" name; - src = lib.cleanSource ../.; - }; + src = lib.cleanSourceWith { + filter = name: type: type != "regular" || !lib.hasSuffix ".nix" name; + src = lib.cleanSource ../.; + }; - mesonFlags = lib.remove "-Dgtk-layer-shell=enabled" oldAttrs.mesonFlags; + mesonFlags = lib.remove "-Dgtk-layer-shell=enabled" oldAttrs.mesonFlags; - # downstream patch should not affect upstream - patches = []; - # nixpkgs checks version, no need when building locally - nativeInstallCheckInputs = []; + # downstream patch should not affect upstream + patches = [ ]; + # nixpkgs checks version, no need when building locally + nativeInstallCheckInputs = [ ]; - buildInputs = (builtins.filter (p: p.pname != "wireplumber") oldAttrs.buildInputs) ++ [ - pkgs.wireplumber - ]; + buildInputs = (builtins.filter (p: p.pname != "wireplumber") oldAttrs.buildInputs) ++ [ + pkgs.wireplumber + ]; - postUnpack = '' - pushd "$sourceRoot" - cp -R --no-preserve=mode,ownership ${libcava.src} subprojects/cava-${libcava.version} - patchShebangs . - popd - ''; - } -)) + postUnpack = '' + pushd "$sourceRoot" + cp -R --no-preserve=mode,ownership ${libcava.src} subprojects/cava-${libcava.version} + patchShebangs . + popd + ''; +})) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 978e8b07..9dc13158 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -1,16 +1,16 @@ #include "modules/sni/item.hpp" -#include "modules/sni/icon_manager.hpp" #include #include #include #include +#include #include #include -#include #include "gdk/gdk.h" +#include "modules/sni/icon_manager.hpp" #include "util/format.hpp" #include "util/gtk_icon.hpp" @@ -206,10 +206,10 @@ void Item::setCustomIcon(const std::string& id) { std::string custom_icon = IconManager::instance().getIconForApp(id); if (!custom_icon.empty()) { if (std::filesystem::exists(custom_icon)) { - Glib::RefPtr custom_pixbuf = Gdk::Pixbuf::create_from_file(custom_icon); - icon_name = ""; // icon_name has priority over pixmap - icon_pixmap = custom_pixbuf; - } else { // if file doesn't exist it's most likely an icon_name + Glib::RefPtr custom_pixbuf = Gdk::Pixbuf::create_from_file(custom_icon); + icon_name = ""; // icon_name has priority over pixmap + icon_pixmap = custom_pixbuf; + } else { // if file doesn't exist it's most likely an icon_name icon_name = custom_icon; } } diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index abd23ebd..f657c855 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -1,8 +1,9 @@ #include "modules/sni/tray.hpp" -#include "modules/sni/icon_manager.hpp" #include +#include "modules/sni/icon_manager.hpp" + namespace waybar::modules::SNI { Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index 93f427ee..a3e1c1ee 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -115,10 +115,10 @@ float waybar::modules::Temperature::getTemperature() { auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; // First, try with dev.cpu - if ( (sysctlbyname(fmt::format("dev.cpu.{}.temperature", zone).c_str(), &temp, &size, - NULL, 0) == 0) || - (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone).c_str(), &temp, &size, - NULL, 0) == 0) ) { + if ((sysctlbyname(fmt::format("dev.cpu.{}.temperature", zone).c_str(), &temp, &size, NULL, 0) == + 0) || + (sysctlbyname(fmt::format("hw.acpi.thermal.tz{}.temperature", zone).c_str(), &temp, &size, + NULL, 0) == 0)) { auto temperature_c = ((float)temp - 2732) / 10; return temperature_c; } diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 1169425a..8e3b2542 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -382,7 +382,7 @@ std::string Task::state_string(bool shortened) const { return res.substr(0, res.size() - 1); } -void Task::handle_title(const char* title) { +void Task::handle_title(const char *title) { if (title_.empty()) { spdlog::debug(fmt::format("Task ({}) setting title to {}", id_, title_)); } else { From ba8ea3d952c95311a25758a5e9f5420f83e58236 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 24 Apr 2025 09:29:40 +0000 Subject: [PATCH 801/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/52faf482a3889b7619003c0daec593a1912fddc1?narHash=sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om%2BD4UnDhlDW9BE%3D' (2025-03-30) → 'github:NixOS/nixpkgs/8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7?narHash=sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo%3D' (2025-04-23) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 27290894..480f004f 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1743315132, - "narHash": "sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om+D4UnDhlDW9BE=", + "lastModified": 1745391562, + "narHash": "sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "52faf482a3889b7619003c0daec593a1912fddc1", + "rev": "8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7", "type": "github" }, "original": { From 72184b2205355452037725c96a6f7ce681635af2 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Fri, 2 May 2025 09:53:11 +0200 Subject: [PATCH 802/842] Issue 3981: try and fix memory leak --- src/modules/privacy/privacy_item.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index 54e61b43..36efd3e5 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -1,5 +1,7 @@ #include "modules/privacy/privacy_item.hpp" +#include + #include #include "glibmm/main.h" @@ -94,22 +96,22 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac } void PrivacyItem::update_tooltip() { + spdlog::trace("update privacy tooltip"); // Removes all old nodes for (auto *child : tooltip_window.get_children()) { - delete child; + tooltip_window.remove(*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(); + Gtk::Image *node_icon = Gtk::make_managed(); node_icon->set_pixel_size(tooltipIconSize); node_icon->set_from_icon_name(node->getIconName(), Gtk::ICON_SIZE_INVALID); box->add(*node_icon); // Set model - auto *nodeName = new Gtk::Label(node->getName()); + auto *nodeName = Gtk::make_managed(node->getName()); box->add(*nodeName); tooltip_window.add(*box); From ff4ed826931209ac333cb270f2463027718c668f Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sat, 3 May 2025 11:26:03 +0200 Subject: [PATCH 803/842] memory leak: 2nd attempt --- src/modules/privacy/privacy_item.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index 36efd3e5..536c9180 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -100,12 +100,15 @@ void PrivacyItem::update_tooltip() { // Removes all old nodes for (auto *child : tooltip_window.get_children()) { tooltip_window.remove(*child); + // despite the remove, still needs a delete to prevent memory leak. Speculating that this might + // work differently in GTK4. + delete child; } for (auto *node : *nodes) { - Gtk::Box *box = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4); + auto *box = Gtk::make_managed(Gtk::ORIENTATION_HORIZONTAL, 4); // Set device icon - Gtk::Image *node_icon = Gtk::make_managed(); + auto *node_icon = Gtk::make_managed(); node_icon->set_pixel_size(tooltipIconSize); node_icon->set_from_icon_name(node->getIconName(), Gtk::ICON_SIZE_INVALID); box->add(*node_icon); From 9bf8c8277add598e62e7d16a8676505cfc09eab0 Mon Sep 17 00:00:00 2001 From: Fengerros <83514190+Fengerros@users.noreply.github.com> Date: Sat, 3 May 2025 15:52:05 +0200 Subject: [PATCH 804/842] Update mediaplayer.py - Fix artist name display in mediaplayer.py Fixed an issue where artist names like Earth, Wind & Fire were not displayed correctly. The change ensures that artist names containing commas or special characters are now shown properly. --- resources/custom_modules/mediaplayer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index d1bb72b4..524d4d2a 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -112,6 +112,7 @@ class PlayerManager: logger.debug(f"Metadata changed for player {player.props.player_name}") player_name = player.props.player_name artist = player.get_artist() + artist = artist.replace("&", "&") title = player.get_title() title = title.replace("&", "&") From 7b5206128ce7960791b97fe0212f5e95e8575a88 Mon Sep 17 00:00:00 2001 From: Aidan Epstein Date: Sat, 3 May 2025 10:41:07 -0700 Subject: [PATCH 805/842] Add idle_inhibitor style docs. --- man/waybar-idle-inhibitor.5.scd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/man/waybar-idle-inhibitor.5.scd b/man/waybar-idle-inhibitor.5.scd index 6d5a2170..405c8fc5 100644 --- a/man/waybar-idle-inhibitor.5.scd +++ b/man/waybar-idle-inhibitor.5.scd @@ -125,3 +125,9 @@ screensaver, also known as "presentation mode". "timeout": 30.5 } ``` + +# STYLE + +- *#idle_inhibitor* +- *#idle_inhibitor.activated* +- *#idle_inhibitor.deactivated* From 0340760e12829239078c0d21fa6d227561e30ff2 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Wed, 7 May 2025 16:57:54 +0800 Subject: [PATCH 806/842] Use load_symbolic for gtk icon instead of load_icon. --- include/util/gtk_icon.hpp | 4 +++- src/modules/sni/item.cpp | 15 +++++++++------ src/util/gtk_icon.cpp | 15 ++++++++++++--- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/include/util/gtk_icon.hpp b/include/util/gtk_icon.hpp index 44555f65..06f15abe 100644 --- a/include/util/gtk_icon.hpp +++ b/include/util/gtk_icon.hpp @@ -10,5 +10,7 @@ class DefaultGtkIconThemeWrapper { public: static bool has_icon(const std::string&); - static Glib::RefPtr load_icon(const char*, int, Gtk::IconLookupFlags); + static Glib::RefPtr load_icon( + const char*, int, Gtk::IconLookupFlags, + Glib::RefPtr style = Glib::RefPtr()); }; diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 9dc13158..407d7e72 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -388,14 +388,17 @@ Glib::RefPtr Item::getIconPixbuf() { Glib::RefPtr Item::getIconByName(const std::string& name, int request_size) { icon_theme->rescan_if_needed(); - if (!icon_theme_path.empty() && - icon_theme->lookup_icon(name.c_str(), request_size, - Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE)) { - return icon_theme->load_icon(name.c_str(), request_size, - Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); + if (!icon_theme_path.empty()) { + auto icon_info = icon_theme->lookup_icon(name.c_str(), request_size, + Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); + if (icon_info) { + bool is_sym = false; + return icon_info.load_symbolic(event_box.get_style_context(), is_sym); + } } return DefaultGtkIconThemeWrapper::load_icon(name.c_str(), request_size, - Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); + Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE, + event_box.get_style_context()); } double Item::getScaledIconSize() { diff --git a/src/util/gtk_icon.cpp b/src/util/gtk_icon.cpp index 5dd741f9..4b4d3d69 100644 --- a/src/util/gtk_icon.cpp +++ b/src/util/gtk_icon.cpp @@ -15,11 +15,20 @@ bool DefaultGtkIconThemeWrapper::has_icon(const std::string& value) { return Gtk::IconTheme::get_default()->has_icon(value); } -Glib::RefPtr DefaultGtkIconThemeWrapper::load_icon(const char* name, int tmp_size, - Gtk::IconLookupFlags flags) { +Glib::RefPtr DefaultGtkIconThemeWrapper::load_icon( + const char* name, int tmp_size, Gtk::IconLookupFlags flags, + Glib::RefPtr style) { const std::lock_guard lock(default_theme_mutex); auto default_theme = Gtk::IconTheme::get_default(); default_theme->rescan_if_needed(); - return default_theme->load_icon(name, tmp_size, flags); + + auto icon_info = default_theme->lookup_icon(name, tmp_size, flags); + + if (style.get() == nullptr) { + return icon_info.load_icon(); + } + + bool is_sym = false; + return icon_info.load_symbolic(style, is_sym); } From d53135f834fea98fa0ef140f30d048e7cfa0d228 Mon Sep 17 00:00:00 2001 From: Max Gautier Date: Sat, 31 May 2025 23:07:26 +0200 Subject: [PATCH 807/842] Fix Description= in systemd service file Description= should be a noun phrase, and not a full sentence, according to man 5 systemd.unit. In particular, using a dot at the end result in messages like this in journalctl when running as a user service (not the superfluous dot at the end): May 31 16:03:38 framework systemd[1180]: Started Highly customizable Wayland bar for Sway and Wlroots based compositors.. May 31 16:20:39 framework systemd[1180]: Stopping Highly customizable Wayland bar for Sway and Wlroots based compositors.... May 31 16:20:39 framework systemd[1180]: Stopped Highly customizable Wayland bar for Sway and Wlroots based compositors.. --- resources/waybar.service.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/waybar.service.in b/resources/waybar.service.in index 81ac6779..18bac54c 100644 --- a/resources/waybar.service.in +++ b/resources/waybar.service.in @@ -1,5 +1,5 @@ [Unit] -Description=Highly customizable Wayland bar for Sway and Wlroots based compositors. +Description=Highly customizable Wayland bar for Sway and Wlroots based compositors Documentation=https://github.com/Alexays/Waybar/wiki/ PartOf=graphical-session.target After=graphical-session.target From c0e7aad60e69381062fb8d2b82c42dba6bc11726 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 1 Jun 2025 00:14:43 +0000 Subject: [PATCH 808/842] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-compat': 'github:edolstra/flake-compat/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec?narHash=sha256-NeCCThCEP3eCl2l/%2B27kNNK7QrwZB1IJCrXfrbv5oqU%3D' (2024-12-04) → 'github:edolstra/flake-compat/9100a0f413b0c601e0533d1d94ffd501ce2e7885?narHash=sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX%2BfjA8Xf8PUmqCY%3D' (2025-05-12) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7?narHash=sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo%3D' (2025-04-23) → 'github:NixOS/nixpkgs/96ec055edbe5ee227f28cdbc3f1ddf1df5965102?narHash=sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg%3D' (2025-05-28) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 480f004f..34545ed4 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", "owner": "edolstra", "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", "type": "github" }, "original": { @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1745391562, - "narHash": "sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo=", + "lastModified": 1748460289, + "narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7", + "rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102", "type": "github" }, "original": { From 05cfd738045d5da918a9d8bbbe65cb69da4d8854 Mon Sep 17 00:00:00 2001 From: Mateus Eto Date: Sun, 1 Jun 2025 21:27:37 +0900 Subject: [PATCH 809/842] Fix calendar extra padding if there are wide characters --- src/modules/clock.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 269fa765..0ef63c7b 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,5 +1,6 @@ #include "modules/clock.hpp" +#include #include #include @@ -358,10 +359,23 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea } } - os << Glib::ustring::format((cldWPos_ != WS::LEFT || line == 0) ? std::left : std::right, - std::setfill(L' '), - std::setw(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)), - getCalendarLine(today, ymTmp, line, firstdow, &m_locale_)); + // Count wide characters to avoid extra padding + size_t wideCharCount = 0; + std::string calendarLine = getCalendarLine(today, ymTmp, line, firstdow, &m_locale_); + if (line < 2) { + for (gchar *data = calendarLine.data(), *end = data + calendarLine.size(); + data != nullptr;) { + gunichar c = g_utf8_get_char_validated(data, end - data); + if (g_unichar_iswide(c)) { + wideCharCount++; + } + data = g_utf8_find_next_char(data, end); + } + } + os << Glib::ustring::format( + (cldWPos_ != WS::LEFT || line == 0) ? std::left : std::right, std::setfill(L' '), + std::setw(cldMonColLen_ + ((line < 2) ? cldWnLen_ - wideCharCount : 0)), + calendarLine); // Week numbers on the right if (cldWPos_ == WS::RIGHT && line > 0) { From 34484919d6c2b1c808cdf43f9bd8cb9a32d44412 Mon Sep 17 00:00:00 2001 From: Davide Manini Date: Mon, 2 Jun 2025 17:23:06 +0300 Subject: [PATCH 810/842] AIconLabel: honour `rotation' option; add `swap-icon-label' option --- src/AIconLabel.cpp | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/AIconLabel.cpp b/src/AIconLabel.cpp index d7ee666e..130ba60c 100644 --- a/src/AIconLabel.cpp +++ b/src/AIconLabel.cpp @@ -1,6 +1,7 @@ #include "AIconLabel.hpp" #include +#include namespace waybar { @@ -17,14 +18,37 @@ AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const box_.get_style_context()->add_class(id); } - box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL); + int rot = 0; + + if (config_["rotate"].isUInt()) { + rot = config["rotate"].asUInt() % 360; + if ((rot % 90) != 00) + rot = 0; + rot /= 90; + } + + if ((rot % 2) == 0) + box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL); + else + box_.set_orientation(Gtk::Orientation::ORIENTATION_VERTICAL); box_.set_name(name); int spacing = config_["icon-spacing"].isInt() ? config_["icon-spacing"].asInt() : 8; box_.set_spacing(spacing); - box_.add(image_); - box_.add(label_); + bool swap_icon_label = false; + if (not config_["swap-icon-label"].isBool()) + spdlog::warn("'swap-icon-label' must be a bool."); + else + swap_icon_label = config_["swap-icon-label"].asBool(); + + if ( (rot == 0 || rot == 3) ^ swap_icon_label ) { + box_.add(image_); + box_.add(label_); + } else { + box_.add(label_); + box_.add(image_); + } event_box_.add(box_); } From c26978eca8e2e0e7e609085a7af0c9097d73717b Mon Sep 17 00:00:00 2001 From: Davide Manini Date: Mon, 2 Jun 2025 20:38:49 +0300 Subject: [PATCH 811/842] Update documentation --- man/waybar.5.scd.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 5bb62724..a9b76f8d 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -272,6 +272,17 @@ 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. +## Swapping icon and label + +If a module displays both a label and an icon, it might be desirable to swap them (for instance, for panels on the left or right of the screen, or for user adopting a right-to-left script). This can be achieved with the "swap-icon-label" property, taking a boolean. Example: +``` +{ + "sway/window": { + "swap-icon-label": true + } +} +``` + ## Grouping modules 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. From 703be13b002342625debbab9b3393d7cb8e50e16 Mon Sep 17 00:00:00 2001 From: Gregor Kleen <20089782+gkleen@users.noreply.github.com> Date: Sat, 17 Aug 2024 21:11:07 +0200 Subject: [PATCH 812/842] privacy: introduce `ignore` option --- include/modules/privacy/privacy.hpp | 1 + src/modules/privacy/privacy.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/modules/privacy/privacy.hpp b/include/modules/privacy/privacy.hpp index 6179098c..3ea41d12 100644 --- a/include/modules/privacy/privacy.hpp +++ b/include/modules/privacy/privacy.hpp @@ -31,6 +31,7 @@ class Privacy : public AModule { uint iconSpacing = 4; uint iconSize = 20; uint transition_duration = 250; + std::set> ignore; std::shared_ptr backend = nullptr; }; diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 48bba888..42875976 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -74,6 +74,18 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, Gtk::Orientat } } + for (const auto& ignore_item : config_["ignore"]) { + if (!ignore_item.isObject() || !ignore_item["type"].isString() || !ignore_item["name"].isString()) continue; + const std::string type = ignore_item["type"].asString(); + const std::string name = ignore_item["name"].asString(); + + auto iter = typeMap.find(type); + if (iter != typeMap.end()) { + auto& [_, nodeType] = iter->second; + ignore.emplace(nodeType, std::move(name)); + } + } + backend = util::PipewireBackend::PipewireBackend::getInstance(); backend->privacy_nodes_changed_signal_event.connect( sigc::mem_fun(*this, &Privacy::onPrivacyNodesChanged)); @@ -88,6 +100,10 @@ void Privacy::onPrivacyNodesChanged() { nodes_screenshare.clear(); for (auto& node : backend->privacy_nodes) { + auto iter = ignore.find(std::pair(node.second->type, node.second->node_name)); + if (iter != ignore.end()) + continue; + switch (node.second->state) { case PW_NODE_STATE_RUNNING: switch (node.second->type) { From 831602a9134deced66c3d4b8c4caa8a068a80cba Mon Sep 17 00:00:00 2001 From: Gregor Kleen <20089782+gkleen@users.noreply.github.com> Date: Tue, 20 Aug 2024 22:09:22 +0200 Subject: [PATCH 813/842] privacy: default to ignoring all stream.monitor pw nodes --- include/modules/privacy/privacy.hpp | 1 + include/util/pipewire/privacy_node_info.hpp | 1 + src/modules/privacy/privacy.cpp | 7 +++++++ src/util/pipewire/privacy_node_info.cpp | 2 ++ 4 files changed, 11 insertions(+) diff --git a/include/modules/privacy/privacy.hpp b/include/modules/privacy/privacy.hpp index 3ea41d12..cb6a34da 100644 --- a/include/modules/privacy/privacy.hpp +++ b/include/modules/privacy/privacy.hpp @@ -32,6 +32,7 @@ class Privacy : public AModule { uint iconSize = 20; uint transition_duration = 250; std::set> ignore; + bool ignore_monitor = true; std::shared_ptr backend = nullptr; }; diff --git a/include/util/pipewire/privacy_node_info.hpp b/include/util/pipewire/privacy_node_info.hpp index 7b8df018..54da7d16 100644 --- a/include/util/pipewire/privacy_node_info.hpp +++ b/include/util/pipewire/privacy_node_info.hpp @@ -25,6 +25,7 @@ class PrivacyNodeInfo { std::string media_name; std::string node_name; std::string application_name; + bool is_monitor = false; std::string pipewire_access_portal_app_id; std::string application_icon_name; diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 42875976..6f6d1395 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -86,6 +86,10 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, Gtk::Orientat } } + if (config_["ignore-monitor"].isBool()) { + ignore_monitor = config_["ignore-monitor"].asBool(); + } + backend = util::PipewireBackend::PipewireBackend::getInstance(); backend->privacy_nodes_changed_signal_event.connect( sigc::mem_fun(*this, &Privacy::onPrivacyNodesChanged)); @@ -100,6 +104,9 @@ void Privacy::onPrivacyNodesChanged() { nodes_screenshare.clear(); for (auto& node : backend->privacy_nodes) { + if (ignore_monitor && node.second->is_monitor) + continue; + auto iter = ignore.find(std::pair(node.second->type, node.second->node_name)); if (iter != ignore.end()) continue; diff --git a/src/util/pipewire/privacy_node_info.cpp b/src/util/pipewire/privacy_node_info.cpp index 739dc528..ec110b86 100644 --- a/src/util/pipewire/privacy_node_info.cpp +++ b/src/util/pipewire/privacy_node_info.cpp @@ -49,6 +49,8 @@ void PrivacyNodeInfo::handleNodeEventInfo(const struct pw_node_info *info) { pipewire_access_portal_app_id = item->value; } else if (strcmp(item->key, PW_KEY_APP_ICON_NAME) == 0) { application_icon_name = item->value; + } else if (strcmp(item->key, "stream.monitor") == 0) { + is_monitor = strcmp(item->value, "true") == 0; } } } From 6cfaf4ff63b9f30bdcd9c0fc3247ff537ae78966 Mon Sep 17 00:00:00 2001 From: Gregor Kleen <20089782+gkleen@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:18:04 +0200 Subject: [PATCH 814/842] privacy: document ignore options --- man/waybar-privacy.5.scd | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/man/waybar-privacy.5.scd b/man/waybar-privacy.5.scd index 946fe136..28b55f1a 100644 --- a/man/waybar-privacy.5.scd +++ b/man/waybar-privacy.5.scd @@ -37,6 +37,17 @@ the screen or playing audio. default: false ++ Enables this module to consume all left over space dynamically. +*ignore-monitor* ++ + typeof: bool ++ + default: true ++ + Ignore streams with *stream.monitor* property. + +*ignore* ++ + typeof: array of objects ++ + default: [] ++ + Additional streams to be ignored. See *IGNORE CONFIGURATION* for++ + more information. + # MODULES CONFIGURATION *type*: ++ @@ -54,6 +65,14 @@ the screen or playing audio. default: 24 ++ The size of each icon in the tooltip. +# IGNORE CONFIGURATION + +*type*: ++ + typeof: string + +*name*: ++ + typeof: string + # EXAMPLES ``` From f73d26722c58b9a27b8390cfb37c5a07333a2732 Mon Sep 17 00:00:00 2001 From: Sonter <108224581+S0nter@users.noreply.github.com> Date: Sun, 8 Jun 2025 12:39:10 +0300 Subject: [PATCH 815/842] privacy: add example configuration --- man/waybar-privacy.5.scd | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/man/waybar-privacy.5.scd b/man/waybar-privacy.5.scd index 28b55f1a..1c4ef59d 100644 --- a/man/waybar-privacy.5.scd +++ b/man/waybar-privacy.5.scd @@ -96,6 +96,17 @@ the screen or playing audio. "tooltip": true, "tooltip-icon-size": 24 } + ], + "ignore-monitor": true, + "ignore": [ + { + "type": "audio-in", + "name": "cava" + }, + { + "type": "screenshare", + "name": "obs" + } ] }, ``` From 4d9403601a621b947aba81bc27b89431b173e709 Mon Sep 17 00:00:00 2001 From: Sonter <108224581+S0nter@users.noreply.github.com> Date: Sun, 8 Jun 2025 12:53:46 +0300 Subject: [PATCH 816/842] privacy: format with clang-format --- src/modules/privacy/privacy.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/privacy/privacy.cpp b/src/modules/privacy/privacy.cpp index 6f6d1395..904c8fd9 100644 --- a/src/modules/privacy/privacy.cpp +++ b/src/modules/privacy/privacy.cpp @@ -75,7 +75,9 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, Gtk::Orientat } for (const auto& ignore_item : config_["ignore"]) { - if (!ignore_item.isObject() || !ignore_item["type"].isString() || !ignore_item["name"].isString()) continue; + if (!ignore_item.isObject() || !ignore_item["type"].isString() || + !ignore_item["name"].isString()) + continue; const std::string type = ignore_item["type"].asString(); const std::string name = ignore_item["name"].asString(); @@ -104,12 +106,10 @@ void Privacy::onPrivacyNodesChanged() { nodes_screenshare.clear(); for (auto& node : backend->privacy_nodes) { - if (ignore_monitor && node.second->is_monitor) - continue; + if (ignore_monitor && node.second->is_monitor) continue; auto iter = ignore.find(std::pair(node.second->type, node.second->node_name)); - if (iter != ignore.end()) - continue; + if (iter != ignore.end()) continue; switch (node.second->state) { case PW_NODE_STATE_RUNNING: From 3ebf2d96e529c0ccba05273805693adbd863527d Mon Sep 17 00:00:00 2001 From: Taimase Date: Wed, 11 Jun 2025 01:12:08 -0600 Subject: [PATCH 817/842] fix continuousWorker in the custom module by capturing the buffer by reference. --- 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 e023aaf6..052247e0 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -62,7 +62,7 @@ void waybar::modules::Custom::continuousWorker() { } thread_ = [this, cmd] { char* buff = nullptr; - waybar::util::ScopeGuard buff_deleter([buff]() { + waybar::util::ScopeGuard buff_deleter([&buff]() { if (buff) { free(buff); } From 5e14698b4eb824189513bdbbd57484316453c6b4 Mon Sep 17 00:00:00 2001 From: peelz Date: Thu, 12 Jun 2025 17:01:05 -0400 Subject: [PATCH 818/842] fix: network frequency is reported in GHz --- man/waybar-network.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index 15f15395..3b63e3ee 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -171,7 +171,7 @@ Addressed by *network* *{signaldBm}*: Signal strength of the wireless network in dBm. -*{frequency}*: Frequency of the wireless network in MHz. +*{frequency}*: Frequency of the wireless network in GHz. *{bandwidthUpBits}*: Instant up speed in bits/seconds. From 07468357f478c42fabc85e5ca849bba9e4c2df70 Mon Sep 17 00:00:00 2001 From: peelz Date: Thu, 12 Jun 2025 19:19:43 -0400 Subject: [PATCH 819/842] fix: network module not displaying rfkill state --- src/modules/network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 0a77c00e..c46b8188 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -223,8 +223,8 @@ void waybar::modules::Network::worker() { std::lock_guard lock(mutex_); if (ifid_ > 0) { getInfo(); - dp.emit(); } + dp.emit(); } thread_timer_.sleep_for(interval_); }; @@ -271,10 +271,10 @@ void waybar::modules::Network::worker() { } const std::string waybar::modules::Network::getNetworkState() const { - if (ifid_ == -1) { #ifdef WANT_RFKILL if (rfkill_.getState()) return "disabled"; #endif + if (ifid_ == -1) { return "disconnected"; } if (!carrier_) return "disconnected"; From f4496c9648bf651b871eb6fa61ddb115c25cd90a Mon Sep 17 00:00:00 2001 From: peelz Date: Fri, 13 Jun 2025 22:51:03 -0400 Subject: [PATCH 820/842] fix: length_error thrown in handleOutputDescription --- src/client.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 63a9276a..b4fe2a8b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -106,11 +106,11 @@ void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * auto *client = waybar::Client::inst(); try { auto &output = client->getOutput(data); - const char *open_paren = strrchr(description, '('); // Description format: "identifier (name)" - size_t identifier_length = open_paren - description; - output.identifier = std::string(description, identifier_length - 1); + auto s = std::string(description); + auto pos = s.find(" ("); + output.identifier = pos != std::string::npos ? s.substr(0, pos) : s; } catch (const std::exception &e) { std::cerr << e.what() << '\n'; } From bd28bb959fdcd06685e9f4848c11dbf2b056d14b Mon Sep 17 00:00:00 2001 From: peelz Date: Fri, 13 Jun 2025 22:57:31 -0400 Subject: [PATCH 821/842] fix: use spdlog in zxdg_output_v1_listener callbacks --- src/client.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index b4fe2a8b..e363f236 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -86,7 +86,7 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_ } } } catch (const std::exception &e) { - std::cerr << e.what() << '\n'; + spdlog::warn("caught exception in zxdg_output_v1_listener::done: {}", e.what()); } } @@ -97,7 +97,7 @@ void waybar::Client::handleOutputName(void *data, struct zxdg_output_v1 * /*xdg_ auto &output = client->getOutput(data); output.name = name; } catch (const std::exception &e) { - std::cerr << e.what() << '\n'; + spdlog::warn("caught exception in zxdg_output_v1_listener::name: {}", e.what()); } } @@ -112,7 +112,7 @@ void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * auto pos = s.find(" ("); output.identifier = pos != std::string::npos ? s.substr(0, pos) : s; } catch (const std::exception &e) { - std::cerr << e.what() << '\n'; + spdlog::warn("caught exception in zxdg_output_v1_listener::description: {}", e.what()); } } From af9d61fa8f150317d2de908600a14ac856ee740d Mon Sep 17 00:00:00 2001 From: markx86 <25851052+markx86@users.noreply.github.com> Date: Thu, 19 Jun 2025 00:35:12 +0200 Subject: [PATCH 822/842] fix: `cldMonShift_` not getting initialized in `Clock()` constructor Initialize `cldMonShift_` member in the `clock` module constructor. This fixes a bug where the calendar tooltip would break after a reload, when in month mode. --- src/modules/clock.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 269fa765..f00d79fc 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -25,6 +25,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) m_tooltip_{new Gtk::Label()}, cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, cldYearShift_{January / 1 / 1900}, + cldMonShift_{1900y / January}, tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, tzCurrIdx_{0}, ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { From bdcab011ee9daa316452a8764f67b2aca699103e Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Thu, 19 Jun 2025 15:55:35 +0000 Subject: [PATCH 823/842] modules: memory: Add swapState format argument Add an argument to the memory module which displays the state of the swap configuration of the local system. Usage of swap does not necessarily indicate if swap is on or off. Signed-off-by: Steffen Kothe --- man/waybar-memory.5.scd | 2 ++ src/modules/memory/common.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index cc42d5a3..567c2c72 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -120,6 +120,8 @@ Addressed by *memory* *{swapAvail}*: Amount of available swap in GiB. +*{swapState}*: Signals if swap is activated or not + # EXAMPLES ``` diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index 544d7814..18600cd2 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -60,6 +60,7 @@ auto waybar::modules::Memory::update() -> void { fmt::arg("icon", getIcon(used_ram_percentage, icons)), fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes), fmt::arg("percentage", used_ram_percentage), + fmt::arg("swapState", swaptotal == 0 ? "Off" : "On"), fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes), fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes), fmt::arg("swapAvail", available_swap_gigabytes))); @@ -72,6 +73,7 @@ auto waybar::modules::Memory::update() -> void { fmt::runtime(tooltip_format), used_ram_percentage, fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes), fmt::arg("percentage", used_ram_percentage), + fmt::arg("swapState", swaptotal == 0 ? "Off" : "On"), fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes), fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes), fmt::arg("swapAvail", available_swap_gigabytes))); From 76d0b44214751cf334dae07072e1f7cff9a45661 Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Thu, 19 Jun 2025 18:57:59 +0000 Subject: [PATCH 824/842] Dockerfiles: Use debian-slim for container Slim images of Debian tend to be smaller even during development. Hence replace the full-fledged variant with the slim one. Signed-off-by: Steffen Kothe --- Dockerfiles/debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/debian b/Dockerfiles/debian index f479062d..c2584ccf 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -1,6 +1,6 @@ # vim: ft=Dockerfile -FROM debian:sid +FROM debian:sid-slim RUN apt update && \ apt install --no-install-recommends --no-install-suggests -y \ From c266befe0a9b80109d4b19c2a7482e19a16c0679 Mon Sep 17 00:00:00 2001 From: markx86 <25851052+markx86@users.noreply.github.com> Date: Fri, 20 Jun 2025 14:02:01 +0200 Subject: [PATCH 825/842] fix: MPRIS widget not hiding when no player is active --- src/modules/mpris/mpris.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index ed383b0c..47bb9c05 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -425,9 +425,11 @@ auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlaye auto* mpris = static_cast(data); if (!mpris) return; - spdlog::debug("mpris: player-vanished callback: {}", player_name->name); + spdlog::debug("mpris: name-vanished callback: {}", player_name->name); - if (std::string(player_name->name) == mpris->player_) { + if (mpris->player_ == "playerctld") { + mpris->dp.emit(); + } else if (mpris->player_ == player_name->name) { mpris->player = nullptr; mpris->event_box_.set_visible(false); mpris->dp.emit(); @@ -499,6 +501,7 @@ auto Mpris::getPlayerInfo() -> std::optional { // https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249 players = g_list_first(players); if (players) player_name = static_cast(players->data)->name; + else return std::nullopt; // no players found, hide the widget } if (std::any_of(ignored_players_.begin(), ignored_players_.end(), From b8a985d606d8f33c92a8e539d4978c32ace3e9df Mon Sep 17 00:00:00 2001 From: peelz Date: Sat, 21 Jun 2025 10:54:16 -0400 Subject: [PATCH 826/842] style: fix formatting --- src/modules/network.cpp | 2 +- src/modules/sway/workspaces.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index c46b8188..955f9f1d 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -272,7 +272,7 @@ void waybar::modules::Network::worker() { const std::string waybar::modules::Network::getNetworkState() const { #ifdef WANT_RFKILL - if (rfkill_.getState()) return "disabled"; + if (rfkill_.getState()) return "disabled"; #endif if (ifid_ == -1) { return "disconnected"; diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index b8ed73d4..86c2029b 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -494,7 +494,7 @@ std::string Workspaces::trimWorkspaceName(std::string name) { return name; } -bool is_focused_recursive(const Json::Value& node) { +bool is_focused_recursive(const Json::Value &node) { // If a workspace has a focused container then get_tree will say // that the workspace itself isn't focused. Therefore we need to // check if any of its nodes are focused as well. @@ -504,13 +504,13 @@ bool is_focused_recursive(const Json::Value& node) { return true; } - for (const auto& child : node["nodes"]) { + for (const auto &child : node["nodes"]) { if (is_focused_recursive(child)) { return true; } } - for (const auto& child : node["floating_nodes"]) { + for (const auto &child : node["floating_nodes"]) { if (is_focused_recursive(child)) { return true; } From 37a6106d3e62efc19861f1ea80f04bf145d4252b Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Sat, 21 Jun 2025 15:17:59 +0000 Subject: [PATCH 827/842] modules: systemd_failed_units: Introduce nr_failed as member Keeping nr_failed as member allows to split-out calculation of overall failed units into updateData. Signed-off-by: Steffen Kothe --- include/modules/systemd_failed_units.hpp | 2 +- src/modules/systemd_failed_units.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/modules/systemd_failed_units.hpp b/include/modules/systemd_failed_units.hpp index 9c3fbcee..df78a5bb 100644 --- a/include/modules/systemd_failed_units.hpp +++ b/include/modules/systemd_failed_units.hpp @@ -19,7 +19,7 @@ class SystemdFailedUnits : public ALabel { std::string format_ok; bool update_pending; - uint32_t nr_failed_system, nr_failed_user; + uint32_t nr_failed_system, nr_failed_user, nr_failed; std::string last_status; Glib::RefPtr system_proxy, user_proxy; diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp index 56e624cf..e2cad8f9 100644 --- a/src/modules/systemd_failed_units.cpp +++ b/src/modules/systemd_failed_units.cpp @@ -16,6 +16,7 @@ SystemdFailedUnits::SystemdFailedUnits(const std::string& id, const Json::Value& update_pending(false), nr_failed_system(0), nr_failed_user(0), + nr_failed(0), last_status() { if (config["hide-on-ok"].isBool()) { hide_on_ok = config["hide-on-ok"].asBool(); @@ -100,7 +101,7 @@ void SystemdFailedUnits::updateData() { } auto SystemdFailedUnits::update() -> void { - uint32_t nr_failed = nr_failed_system + nr_failed_user; + nr_failed = nr_failed_system + nr_failed_user; // Hide if needed. if (nr_failed == 0 && hide_on_ok) { From dcbbe3bb97b973fe792e313dbddd4b8709f96d86 Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Sat, 21 Jun 2025 15:19:43 +0000 Subject: [PATCH 828/842] modules: systemd_failed_units: Move nr_failed calculation to updateData Signed-off-by: Steffen Kothe --- src/modules/systemd_failed_units.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp index e2cad8f9..2518b5cc 100644 --- a/src/modules/systemd_failed_units.cpp +++ b/src/modules/systemd_failed_units.cpp @@ -97,12 +97,12 @@ void SystemdFailedUnits::updateData() { if (user_proxy) { nr_failed_user = load("user", user_proxy); } + + nr_failed = nr_failed_system + nr_failed_user; dp.emit(); } auto SystemdFailedUnits::update() -> void { - nr_failed = nr_failed_system + nr_failed_user; - // Hide if needed. if (nr_failed == 0 && hide_on_ok) { event_box_.set_visible(false); From 5c2cf4c65c5e8138db7206664d292165b9aad32f Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Sat, 21 Jun 2025 15:22:24 +0000 Subject: [PATCH 829/842] modules: systemd_failed_units: Fail early if state did not change Prefer early exit if last status matches the current status. Signed-off-by: Steffen Kothe --- src/modules/systemd_failed_units.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp index 2518b5cc..8ca183ae 100644 --- a/src/modules/systemd_failed_units.cpp +++ b/src/modules/systemd_failed_units.cpp @@ -103,6 +103,10 @@ void SystemdFailedUnits::updateData() { } auto SystemdFailedUnits::update() -> void { + const std::string status = nr_failed == 0 ? "ok" : "degraded"; + + if (last_status == status) return; + // Hide if needed. if (nr_failed == 0 && hide_on_ok) { event_box_.set_visible(false); @@ -113,7 +117,6 @@ auto SystemdFailedUnits::update() -> void { } // 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); } From 74255d0c7e6cd23bf29bfd51b1254576b65d01f5 Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Sat, 21 Jun 2025 15:25:21 +0000 Subject: [PATCH 830/842] modules: systemd_failed_units: Move DBUS proxy check into lambda function Checking for the availability of a given proxy member can be done in the lambda function as well. Signed-off-by: Steffen Kothe --- src/modules/systemd_failed_units.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp index 8ca183ae..3ff3c2ee 100644 --- a/src/modules/systemd_failed_units.cpp +++ b/src/modules/systemd_failed_units.cpp @@ -73,6 +73,7 @@ void SystemdFailedUnits::updateData() { auto load = [](const char* kind, Glib::RefPtr& proxy) -> uint32_t { try { + if (!proxy) return 0; auto parameters = Glib::VariantContainerBase( g_variant_new("(ss)", "org.freedesktop.systemd1.Manager", "NFailedUnits")); Glib::VariantContainerBase data = proxy->call_sync("Get", parameters); @@ -91,13 +92,8 @@ void SystemdFailedUnits::updateData() { return 0; }; - if (system_proxy) { - nr_failed_system = load("systemwide", system_proxy); - } - if (user_proxy) { - nr_failed_user = load("user", user_proxy); - } - + nr_failed_system = load("systemwide", system_proxy); + nr_failed_user = load("user", user_proxy); nr_failed = nr_failed_system + nr_failed_user; dp.emit(); } From 4bb06b86bccccdceaa55828a2de9c84c874ccf21 Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Sat, 21 Jun 2025 15:27:43 +0000 Subject: [PATCH 831/842] modules: systemd_failed_units: Use explicit g_variant_get_uint32 Determining of failed units can be done by usage of explicit uint32 function with direct return due to auto lambda expression. Signed-off-by: Steffen Kothe --- src/modules/systemd_failed_units.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp index 3ff3c2ee..a4457671 100644 --- a/src/modules/systemd_failed_units.cpp +++ b/src/modules/systemd_failed_units.cpp @@ -81,9 +81,7 @@ void SystemdFailedUnits::updateData() { 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; + return g_variant_get_uint32(variant.gobj_copy()); } } } catch (Glib::Error& e) { From 07311176797cf476e7a51bb774a1eb4d4326ad8a Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Sat, 21 Jun 2025 15:34:44 +0000 Subject: [PATCH 832/842] modules: systemd_failed_units: Introduce RequestFailedUnits member Split-out request of failed units from systemd into a separate member function. This increases the readability and extendability, but preserves the current functionality (non-functional change). Signed-off-by: Steffen Kothe --- include/modules/systemd_failed_units.hpp | 1 + src/modules/systemd_failed_units.cpp | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/modules/systemd_failed_units.hpp b/include/modules/systemd_failed_units.hpp index df78a5bb..7801a5d6 100644 --- a/include/modules/systemd_failed_units.hpp +++ b/include/modules/systemd_failed_units.hpp @@ -25,6 +25,7 @@ class SystemdFailedUnits : public ALabel { void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name, const Glib::VariantContainerBase &arguments); + void RequestFailedUnits(); void updateData(); }; diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp index a4457671..f3f04fae 100644 --- a/src/modules/systemd_failed_units.cpp +++ b/src/modules/systemd_failed_units.cpp @@ -68,9 +68,7 @@ auto SystemdFailedUnits::notify_cb(const Glib::ustring& sender_name, } } -void SystemdFailedUnits::updateData() { - update_pending = false; - +void SystemdFailedUnits::RequestFailedUnits() { auto load = [](const char* kind, Glib::RefPtr& proxy) -> uint32_t { try { if (!proxy) return 0; @@ -93,6 +91,11 @@ void SystemdFailedUnits::updateData() { nr_failed_system = load("systemwide", system_proxy); nr_failed_user = load("user", user_proxy); nr_failed = nr_failed_system + nr_failed_user; +} + +void SystemdFailedUnits::updateData() { + update_pending = false; + RequestFailedUnits(); dp.emit(); } From d5e3a9f894e5e5ed160a9329d198d5f0f81d494c Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Sat, 21 Jun 2025 16:19:51 +0000 Subject: [PATCH 833/842] modules: systemd_failed_units: Enforce visibility of event box on every update Instead if guarding visibility in if condition, enforce visibility regardless of the state of the current update. Signed-off-by: Steffen Kothe --- src/modules/systemd_failed_units.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp index f3f04fae..92f4105c 100644 --- a/src/modules/systemd_failed_units.cpp +++ b/src/modules/systemd_failed_units.cpp @@ -109,9 +109,8 @@ auto SystemdFailedUnits::update() -> void { event_box_.set_visible(false); return; } - if (!event_box_.get_visible()) { - event_box_.set_visible(true); - } + + event_box_.set_visible(true); // Set state class. if (!last_status.empty() && label_.get_style_context()->has_class(last_status)) { From cfb47790adaafd9508072632d35612e578f3ad84 Mon Sep 17 00:00:00 2001 From: Steffen Kothe Date: Sat, 21 Jun 2025 15:09:05 +0000 Subject: [PATCH 834/842] modules: systemd_failed_units: Introduce systemd state variables Systemd provides the status of a given user and system session as a human readable string. Retrieve this information via RequestSystemState and guard the retrieve of failed units depending on this request. The functionality is extended but does not change, which means that failed units in any granularity are displayed as before. Update documentation in the meantime. Signed-off-by: Steffen Kothe --- include/modules/systemd_failed_units.hpp | 2 + man/waybar-systemd-failed-units.5.scd | 6 +++ src/modules/systemd_failed_units.cpp | 50 +++++++++++++++++++----- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/include/modules/systemd_failed_units.hpp b/include/modules/systemd_failed_units.hpp index 7801a5d6..48b0074e 100644 --- a/include/modules/systemd_failed_units.hpp +++ b/include/modules/systemd_failed_units.hpp @@ -19,6 +19,7 @@ class SystemdFailedUnits : public ALabel { std::string format_ok; bool update_pending; + std::string system_state, user_state, overall_state; uint32_t nr_failed_system, nr_failed_user, nr_failed; std::string last_status; Glib::RefPtr system_proxy, user_proxy; @@ -26,6 +27,7 @@ class SystemdFailedUnits : public ALabel { void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name, const Glib::VariantContainerBase &arguments); void RequestFailedUnits(); + void RequestSystemState(); void updateData(); }; diff --git a/man/waybar-systemd-failed-units.5.scd b/man/waybar-systemd-failed-units.5.scd index 92e74e9d..8d7c980a 100644 --- a/man/waybar-systemd-failed-units.5.scd +++ b/man/waybar-systemd-failed-units.5.scd @@ -62,6 +62,12 @@ Addressed by *systemd-failed-units* *{nr_failed}*: Number of total failed units. +*{systemd_state}:* State of the systemd system session + +*{user_state}:* State of the systemd user session + +*{overall_state}:* Overall state of the systemd and user session. ("Ok" or "Degraded") + # EXAMPLES ``` diff --git a/src/modules/systemd_failed_units.cpp b/src/modules/systemd_failed_units.cpp index 92f4105c..90f33be7 100644 --- a/src/modules/systemd_failed_units.cpp +++ b/src/modules/systemd_failed_units.cpp @@ -68,6 +68,34 @@ auto SystemdFailedUnits::notify_cb(const Glib::ustring& sender_name, } } +void SystemdFailedUnits::RequestSystemState() { + auto load = [](const char* kind, Glib::RefPtr& proxy) -> std::string { + try { + if (!proxy) return "unknown"; + auto parameters = Glib::VariantContainerBase( + g_variant_new("(ss)", "org.freedesktop.systemd1.Manager", "SystemState")); + 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_STRING)) { + return g_variant_get_string(variant.gobj_copy(), NULL); + } + } + } catch (Glib::Error& e) { + spdlog::error("Failed to get {} state: {}", kind, e.what().c_str()); + } + return "unknown"; + }; + + system_state = load("systemwide", system_proxy); + user_state = load("user", user_proxy); + if (system_state == "running" && user_state == "running") + overall_state = "ok"; + else + overall_state = "degraded"; +} + void SystemdFailedUnits::RequestFailedUnits() { auto load = [](const char* kind, Glib::RefPtr& proxy) -> uint32_t { try { @@ -95,17 +123,18 @@ void SystemdFailedUnits::RequestFailedUnits() { void SystemdFailedUnits::updateData() { update_pending = false; - RequestFailedUnits(); + + RequestSystemState(); + if (overall_state == "degraded") RequestFailedUnits(); + dp.emit(); } auto SystemdFailedUnits::update() -> void { - const std::string status = nr_failed == 0 ? "ok" : "degraded"; - - if (last_status == status) return; + if (last_status == overall_state) return; // Hide if needed. - if (nr_failed == 0 && hide_on_ok) { + if (overall_state == "ok" && hide_on_ok) { event_box_.set_visible(false); return; } @@ -116,14 +145,17 @@ auto SystemdFailedUnits::update() -> void { 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); + if (!label_.get_style_context()->has_class(overall_state)) { + label_.get_style_context()->add_class(overall_state); } - last_status = status; + + last_status = overall_state; 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::arg("nr_failed_system", nr_failed_system), fmt::arg("nr_failed_user", nr_failed_user), + fmt::arg("system_state", system_state), fmt::arg("user_state", user_state), + fmt::arg("overall_state", overall_state))); ALabel::update(); } From c6fceb03c8dfb8d2254534d96b777d94a6b9d33a Mon Sep 17 00:00:00 2001 From: peelz Date: Sat, 21 Jun 2025 16:05:00 -0400 Subject: [PATCH 835/842] ci: relax gentoo gtkmm dependency requirement --- Dockerfiles/gentoo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/gentoo b/Dockerfiles/gentoo index f2ec0dc9..f7023825 100644 --- a/Dockerfiles/gentoo +++ b/Dockerfiles/gentoo @@ -6,6 +6,6 @@ RUN export FEATURES="-ipc-sandbox -network-sandbox -pid-sandbox -sandbox -usersa emerge --sync && \ eselect news read --quiet new 1>/dev/null 2>&1 && \ emerge --verbose --update --deep --with-bdeps=y --backtrack=30 --newuse @world && \ - USE="wayland gtk3 gtk -doc X pulseaudio minimal" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols =dev-cpp/gtkmm-3.24.6 x11-libs/libxkbcommon \ + USE="wayland gtk3 gtk -doc X pulseaudio minimal" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols dev-cpp/gtkmm:3.0 x11-libs/libxkbcommon \ x11-libs/gtk+:3 dev-libs/libdbusmenu dev-libs/libnl sys-power/upower media-libs/libpulse dev-libs/libevdev media-libs/libmpdclient \ media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl dev-libs/iniparser sci-libs/fftw From d4f61ad2717aa31ab2bf21e0b0175c5726f4ff8a Mon Sep 17 00:00:00 2001 From: peelz Date: Sat, 21 Jun 2025 16:05:00 -0400 Subject: [PATCH 836/842] ci: allow manual triggering of docker workflow --- .github/workflows/docker.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 66c465ba..c84b3c29 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,6 +1,7 @@ name: Build and Push Docker Image on: + workflow_dispatch: schedule: # run every night at midnight - cron: '0 0 * * *' @@ -8,7 +9,7 @@ on: jobs: build-and-push: runs-on: ubuntu-latest - if: github.repository == 'Alexays/Waybar' + if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar' strategy: fail-fast: false # don't fail the other jobs if one of the images fails to build matrix: From 84bd0d452ef80ade073632982081f519bb90c2d3 Mon Sep 17 00:00:00 2001 From: peelz Date: Sat, 21 Jun 2025 16:05:00 -0400 Subject: [PATCH 837/842] ci: run the docker workflow monthly instead of daily Rebuilding the gentoo docker image daily is most definitely going to bust through the free CI tier limits. --- .github/workflows/docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c84b3c29..0e7e2944 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -3,8 +3,8 @@ name: Build and Push Docker Image on: workflow_dispatch: schedule: - # run every night at midnight - - cron: '0 0 * * *' + # run monthly + - cron: '0 0 1 * *' jobs: build-and-push: From e4dd2ecc5a2ee8752b4ce664037aa7dee38501a9 Mon Sep 17 00:00:00 2001 From: peelz Date: Sun, 22 Jun 2025 00:50:25 -0400 Subject: [PATCH 838/842] refactor: avoid namespace pollution in util/date.hpp --- include/modules/clock.hpp | 44 +++++++++++++++++++-------------------- include/util/date.hpp | 4 +--- src/modules/clock.cpp | 9 ++++---- test/utils/date.cpp | 2 ++ 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 40b4f80e..e34b7a8e 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -38,39 +38,39 @@ 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 - int cldShift_{1}; // calendar months shift factor - year_month_day cldYearShift_; // calendar Year mode. Cached ymd - std::string cldYearCached_; // calendar Year mode. Cached calendar - year_month cldMonShift_; // calendar Month mode. Cached ym - 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 + 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 + date::months cldCurrShift_{0}; // calendar months shift + int cldShift_{1}; // calendar months shift factor + date::year_month_day cldYearShift_; // calendar Year mode. Cached ymd + std::string cldYearCached_; // calendar Year mode. Cached calendar + date::year_month cldMonShift_; // calendar Month mode. Cached ym + std::string cldMonCached_; // calendar Month mode. Cached calendar + date::day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight) + std::string cldText_{""}; // calendar text to print CldMode cldMode_{CldMode::MONTH}; - auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz) - -> const std::string; + auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd, + const date::time_zone* tz) -> const std::string; // get local time zone - auto local_zone() -> const time_zone*; + auto local_zone() -> const date::time_zone*; // 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 + 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_; // ordinal date in tooltip const bool ordInTooltip_; std::string ordText_{""}; - auto get_ordinal_date(const year_month_day& today) -> std::string; + auto get_ordinal_date(const date::year_month_day& today) -> std::string; - auto getTZtext(sys_seconds now) -> std::string; - auto first_day_of_week() -> weekday; + auto getTZtext(date::sys_seconds now) -> std::string; + auto first_day_of_week() -> date::weekday; // Module actions void cldModeSwitch(); void cldShift_up(); diff --git a/include/util/date.hpp b/include/util/date.hpp index a467cc56..d8653faf 100644 --- a/include/util/date.hpp +++ b/include/util/date.hpp @@ -15,7 +15,7 @@ namespace date { #if HAVE_CHRONO_TIMEZONES using namespace std::chrono; -using namespace std; +using std::format; #else using system_clock = std::chrono::system_clock; @@ -73,5 +73,3 @@ struct fmt::formatter> { } }; #endif - -using namespace date; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index f00d79fc..42c52dfd 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -16,6 +16,7 @@ #include #endif +using namespace date; namespace fmt_lib = waybar::util::date::format; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) @@ -349,9 +350,9 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea m_locale_, fmtMap_[4], fmt_lib::make_format_args( (line == 2) - ? static_cast( + ? static_cast( zoned_seconds{tz, local_days{ymTmp / 1}}) - : static_cast(zoned_seconds{ + : static_cast(zoned_seconds{ tz, local_days{cldGetWeekForLine(ymTmp, firstdow, line)}}))) << ' '; } else @@ -372,9 +373,9 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea << fmt_lib::vformat( m_locale_, fmtMap_[4], fmt_lib::make_format_args( - (line == 2) ? static_cast( + (line == 2) ? static_cast( zoned_seconds{tz, local_days{ymTmp / 1}}) - : static_cast( + : static_cast( zoned_seconds{tz, local_days{cldGetWeekForLine( ymTmp, firstdow, line)}}))); else diff --git a/test/utils/date.cpp b/test/utils/date.cpp index d317f98a..576a4799 100644 --- a/test/utils/date.cpp +++ b/test/utils/date.cpp @@ -18,8 +18,10 @@ return #endif +using namespace date; 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. */ From 25f432b0ceb8e89321f7be3b07e779176e3cb049 Mon Sep 17 00:00:00 2001 From: peelz Date: Sun, 22 Jun 2025 00:50:25 -0400 Subject: [PATCH 839/842] refactor: avoid Gio namespace pollution --- include/util/portal.hpp | 4 +--- src/util/portal.cpp | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/include/util/portal.hpp b/include/util/portal.hpp index 23619169..bff74b11 100644 --- a/include/util/portal.hpp +++ b/include/util/portal.hpp @@ -6,14 +6,12 @@ namespace waybar { -using namespace Gio; - enum class Appearance { UNKNOWN = 0, DARK = 1, LIGHT = 2, }; -class Portal : private DBus::Proxy { +class Portal : private Gio::DBus::Proxy { public: Portal(); void refreshAppearance(); diff --git a/src/util/portal.cpp b/src/util/portal.cpp index 5874871b..6df2a6b6 100644 --- a/src/util/portal.cpp +++ b/src/util/portal.cpp @@ -17,8 +17,6 @@ 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) { @@ -36,8 +34,8 @@ auto fmt::formatter::format(waybar::Appearance c, format_con } waybar::Portal::Portal() - : DBus::Proxy(DBus::Connection::get_sync(DBus::BusType::BUS_TYPE_SESSION), PORTAL_BUS_NAME, - PORTAL_OBJ_PATH, PORTAL_INTERFACE), + : Gio::DBus::Proxy(Gio::DBus::Connection::get_sync(Gio::DBus::BusType::BUS_TYPE_SESSION), + PORTAL_BUS_NAME, PORTAL_OBJ_PATH, PORTAL_INTERFACE), currentMode(Appearance::UNKNOWN) { refreshAppearance(); }; From 8daaad1e1396d8084a2f05c9cd8cefca71e2ba60 Mon Sep 17 00:00:00 2001 From: peelz Date: Sun, 22 Jun 2025 00:56:56 -0400 Subject: [PATCH 840/842] fix: don't use c++20 chrono literals Unfortunately we can't use these yet because the freebsd build (clang) still uses HowardHinnant/date, which doesn't provide literal suffixes. --- 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 f00d79fc..a1bd1d1f 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -25,7 +25,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) m_tooltip_{new Gtk::Label()}, cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos}, cldYearShift_{January / 1 / 1900}, - cldMonShift_{1900y / January}, + cldMonShift_{year(1900) / January}, tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos}, tzCurrIdx_{0}, ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} { From 0bdea1e46fa9ced094fb6d0c6ab0df57e80f0471 Mon Sep 17 00:00:00 2001 From: peelz Date: Sun, 22 Jun 2025 01:02:53 -0400 Subject: [PATCH 841/842] ci: bump FreeBSD to 14.3 --- .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 ca0dcbc8..e45a8dc4 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -21,7 +21,7 @@ jobs: LDFLAGS: '-L/usr/local/lib' with: operating_system: freebsd - version: "14.1" + version: "14.3" environment_variables: CPPFLAGS LDFLAGS sync_files: runner-to-vm run: | From bef539e4de9a3ea5c62c2de34b88d55f13345fd1 Mon Sep 17 00:00:00 2001 From: Alexis Rouillard Date: Sun, 22 Jun 2025 09:30:46 +0200 Subject: [PATCH 842/842] Update privacy_item.cpp --- src/modules/privacy/privacy_item.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/privacy/privacy_item.cpp b/src/modules/privacy/privacy_item.cpp index 536c9180..6424da9e 100644 --- a/src/modules/privacy/privacy_item.cpp +++ b/src/modules/privacy/privacy_item.cpp @@ -96,7 +96,6 @@ PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privac } void PrivacyItem::update_tooltip() { - spdlog::trace("update privacy tooltip"); // Removes all old nodes for (auto *child : tooltip_window.get_children()) { tooltip_window.remove(*child);