From f5a24d12e564fb5da5228d188476fadedaffdb50 Mon Sep 17 00:00:00 2001 From: Bao Trinh Date: Mon, 13 Jun 2022 14:17:17 -0500 Subject: [PATCH 001/217] group module: configurable orientation currently, the orientation of group modules is always the opposite of the bar. Change it so that: * the default orientation of the group module is always the opposite of its parent, even for nested groups * the orientation can be overridden in the config * css ID and class are set for the group element --- include/group.hpp | 2 +- man/waybar.5.scd.in | 3 +++ src/bar.cpp | 12 +++++++++--- src/group.cpp | 27 ++++++++++++++++++++++++--- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/include/group.hpp b/include/group.hpp index 5e82867..60801c7 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 Bar&, const Json::Value&); + Group(const std::string&, const std::string&, const Json::Value&, bool); ~Group() = default; auto update() -> void; operator Gtk::Widget&(); diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index fafc2b3..ea72447 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -242,6 +242,7 @@ A module group is defined by specifying a module named "group/some-group-name". "modules-right": ["group/hardware", "clock"], "group/hardware": { + "orientation": "vertical", "modules": [ "cpu", "memory", @@ -253,6 +254,8 @@ 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). + # SUPPORTED MODULES - *waybar-backlight(5)* diff --git a/src/bar.cpp b/src/bar.cpp index f46b7d0..67e97d1 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -742,7 +742,13 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, AModule* module; if (ref.compare(0, 6, "group/") == 0 && ref.size() > 6) { - auto group_module = new waybar::Group(ref, *this, config[ref]); + auto hash_pos = ref.find('#'); + 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 group_module = new waybar::Group(id_name, class_name, config[ref], vertical); getModules(factory, ref, &group_module->box); module = group_module; } else { @@ -764,11 +770,11 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, modules_right_.emplace_back(module_sp); } } - module->dp.connect([module, name] { + module->dp.connect([module, ref] { try { module->update(); } catch (const std::exception& e) { - spdlog::error("{}: {}", name.asString(), e.what()); + spdlog::error("{}: {}", ref, e.what()); } }); } catch (const std::exception& e) { diff --git a/src/group.cpp b/src/group.cpp index 8324365..548fb0d 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -6,9 +6,30 @@ namespace waybar { -Group::Group(const std::string& name, const Bar& bar, const Json::Value& config) - : AModule(config, name, "", false, false), - box{bar.vertical ? Gtk::ORIENTATION_HORIZONTAL : Gtk::ORIENTATION_VERTICAL, 0} {} +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} { + box.set_name(name_); + if (!id.empty()) { + box.get_style_context()->add_class(id); + } + + // default orientation: orthogonal to parent + auto orientation = + config_["orientation"].empty() ? "orthogonal" : config_["orientation"].asString(); + if (orientation == "inherit") { + // keep orientation passed + } else if (orientation == "orthogonal") { + box.set_orientation(vertical ? Gtk::ORIENTATION_HORIZONTAL : Gtk::ORIENTATION_VERTICAL); + } else if (orientation == "vertical") { + box.set_orientation(Gtk::ORIENTATION_VERTICAL); + } else if (orientation == "horizontal") { + box.set_orientation(Gtk::ORIENTATION_HORIZONTAL); + } else { + throw std::runtime_error("Invalid orientation value: " + orientation); + } +} auto Group::update() -> void { // noop From 385726e701257574aa4c64b4b29ec87a9b07816f Mon Sep 17 00:00:00 2001 From: "Soc Virnyl S. Estela" Date: Fri, 2 Dec 2022 21:36:14 +0800 Subject: [PATCH 002/217] fix: use getaddrinfo() instead of gethostbyname() --- src/modules/hyprland/backend.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 76c071c..0c82aba 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -130,6 +130,8 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { callbackMutex.unlock(); } + + std::string IPC::getSocket1Reply(const std::string& rq) { // basically hyprctl @@ -140,7 +142,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) { return ""; } - const auto SERVER = gethostbyname("localhost"); + const auto SERVER = getaddrinfo("localhost", NULL, NULL, 0); if (!SERVER) { spdlog::error("Hyprland IPC: Couldn't get host (2)"); From 0540977e45bf677ffcd6b33f25e7e88655f02ec5 Mon Sep 17 00:00:00 2001 From: "Soc Virnyl S. Estela" Date: Fri, 2 Dec 2022 21:42:58 +0800 Subject: [PATCH 003/217] format: remove some newlines --- src/modules/hyprland/backend.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 0c82aba..f110825 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -130,8 +130,6 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { callbackMutex.unlock(); } - - std::string IPC::getSocket1Reply(const std::string& rq) { // basically hyprctl From 55d7868f86b3ebb22116e0889fba60b4cdff3f4a Mon Sep 17 00:00:00 2001 From: "Soc Virnyl S. Estela" Date: Fri, 2 Dec 2022 22:18:32 +0800 Subject: [PATCH 004/217] fix(2): use getaddrinfo() instead of gethostbyname() --- src/modules/hyprland/backend.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index f110825..33212c7 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -132,15 +132,20 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { 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); if (SERVERSOCKET < 0) { spdlog::error("Hyprland IPC: Couldn't open a socket (1)"); return ""; } - - const auto SERVER = getaddrinfo("localhost", NULL, NULL, 0); + + memset(&ai_hints, 0, sizeof(struct addrinfo)); + ai_hints.ai_family = AF_UNSPEC; + ai_hints.ai_socktype = SOCK_STREAM; + const auto SERVER = getaddrinfo("localhost", NULL, &ai_hints, &ai_res); if (!SERVER) { spdlog::error("Hyprland IPC: Couldn't get host (2)"); From 22084691ff290ad2b8690e91e6ef3a595f42c137 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 4 Dec 2022 00:14:42 -0800 Subject: [PATCH 005/217] fix(battery): ignore non-system power supplies Linux power_supply sysfs interface allows checking if the battery powers the whole system or a specific device/tree of devices with `scope` attribute[1]. We can use it to skip the non-system power supplies in the battery module and avoid adding HIDs or other peripheral devices to the total. The logic is based on UPower, where it is assumed that "Unknown" devices or devices without a `scope` are system power supplies. [1]: https://lore.kernel.org/lkml/alpine.LNX.2.00.1201031556460.24984@pobox.suse.cz/T/ --- src/modules/battery.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index bb06781..54ed8ca 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -107,6 +107,15 @@ void waybar::modules::Battery::refreshBatteries() { std::ifstream(node.path() / "type") >> type; if (!type.compare("Battery")) { + // Ignore non-system power supplies unless explicitly requested + if (!bat_defined && fs::exists(node.path() / "scope")) { + std::string scope; + std::ifstream(node.path() / "scope") >> scope; + if (g_ascii_strcasecmp(scope.data(), "device") == 0) { + continue; + } + } + check_map[node.path()] = true; auto search = batteries_.find(node.path()); if (search == batteries_.end()) { From d5a86526bca19ec9032976a5397e34915edd5324 Mon Sep 17 00:00:00 2001 From: Narice Date: Tue, 6 Dec 2022 18:44:26 +0100 Subject: [PATCH 006/217] dev: Added Nix Flake support - Enables Nix users to get the git version of waybar - Enables Nix users to develop waybar easily - Adds a fully reproducible development environment - The user only has to install Nix, no other depencencies - Automatic dev env on directory entry through .envrc --- .envrc | 1 + .gitignore | 1 + flake.lock | 94 ++++++++++++++++++++++++++++++++ flake.nix | 65 ++++++++++++++++++++++ nix/default.nix | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 300 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 nix/default.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index 56a2f73..11cc390 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ packagecache *.exe *.out *.app +/.direnv/ diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..6d3b6b8 --- /dev/null +++ b/flake.lock @@ -0,0 +1,94 @@ +{ + "nodes": { + "devshell": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1667210711, + "narHash": "sha256-IoErjXZAkzYWHEpQqwu/DeRNJGFdR7X2OGbkhMqMrpw=", + "owner": "numtide", + "repo": "devshell", + "rev": "96a9dd12b8a447840cc246e17a47b81a4268bba7", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1642700792, + "narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "846b2ae0fc4cc943637d3d1def4454213e203cba", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1643381941, + "narHash": "sha256-pHTwvnN4tTsEKkWlXQ8JMY423epos8wUOhthpwJjtpc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5efc8ca954272c4376ac929f4c5ffefcc20551d5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1670152712, + "narHash": "sha256-LJttwIvJqsZIj8u1LxVRv82vwUtkzVqQVi7Wb8gxPS4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "14ddeaebcbe9a25748221d1d7ecdf98e20e2325e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devshell": "devshell", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..d909186 --- /dev/null +++ b/flake.nix @@ -0,0 +1,65 @@ +{ + description = "Highly customizable Wayland bar for Sway and Wlroots based compositors."; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + devshell.url = "github:numtide/devshell"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, flake-utils, devshell, nixpkgs }: + let + inherit (nixpkgs) lib; + genSystems = lib.genAttrs [ + "x86_64-linux" + ]; + + pkgsFor = genSystems (system: + import nixpkgs { + inherit system; + }); + + mkDate = longDate: (lib.concatStringsSep "-" [ + (builtins.substring 0 4 longDate) + (builtins.substring 4 2 longDate) + (builtins.substring 6 2 longDate) + ]); + in + { + overlays.default = _: prev: rec { + waybar = prev.callPackage ./nix/default.nix { + version = "0.9.16" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); + }; + }; + packages = genSystems + (system: + (self.overlays.default null pkgsFor.${system}) + // { + default = self.packages.${system}.waybar; + }); + } // + flake-utils.lib.eachDefaultSystem (system: { + devShell = + let pkgs = import nixpkgs { + inherit system; + + overlays = [ devshell.overlay ]; + }; + 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 + ]; + language.c.libraries = with pkgs; [ + ]; + }; + }); +} diff --git a/nix/default.nix b/nix/default.nix new file mode 100644 index 0000000..2665446 --- /dev/null +++ b/nix/default.nix @@ -0,0 +1,139 @@ +{ lib +, stdenv +, fetchFromGitHub +, meson +, pkg-config +, ninja +, wrapGAppsHook +, wayland +, wlroots +, gtkmm3 +, libsigcxx +, jsoncpp +, scdoc +, spdlog +, gtk-layer-shell +, howard-hinnant-date +, libinotify-kqueue +, libxkbcommon +, evdevSupport ? true +, libevdev +, inputSupport ? true +, libinput +, jackSupport ? true +, libjack2 +, mpdSupport ? true +, libmpdclient +, nlSupport ? true +, libnl +, pulseSupport ? true +, libpulseaudio +, rfkillSupport ? true +, runTests ? true +, catch2_3 +, sndioSupport ? true +, sndio +, swaySupport ? true +, sway +, traySupport ? true +, libdbusmenu-gtk3 +, udevSupport ? true +, udev +, upowerSupport ? true +, upower +, wireplumberSupport ? true +, wireplumber +, withMediaPlayer ? false +, glib +, gobject-introspection +, python3 +, playerctl +, version +}: + +stdenv.mkDerivation rec { + pname = "waybar"; + inherit version; + # version = "0.9.16"; + + src = lib.cleanSourceWith { + filter = name: type: + let + baseName = baseNameOf (toString name); + in + ! ( + lib.hasSuffix ".nix" baseName + ); + src = lib.cleanSource ../.; + }; + + nativeBuildInputs = [ + meson + ninja + pkg-config + scdoc + wrapGAppsHook + ] ++ lib.optional withMediaPlayer gobject-introspection; + + propagatedBuildInputs = lib.optionals withMediaPlayer [ + glib + playerctl + python3.pkgs.pygobject3 + ]; + strictDeps = false; + + buildInputs = with lib; + [ wayland wlroots gtkmm3 libsigcxx jsoncpp spdlog gtk-layer-shell howard-hinnant-date libxkbcommon ] + ++ optional (!stdenv.isLinux) libinotify-kqueue + ++ optional evdevSupport libevdev + ++ optional inputSupport libinput + ++ optional jackSupport libjack2 + ++ optional mpdSupport libmpdclient + ++ optional nlSupport libnl + ++ optional pulseSupport libpulseaudio + ++ optional sndioSupport sndio + ++ optional swaySupport sway + ++ optional traySupport libdbusmenu-gtk3 + ++ optional udevSupport udev + ++ optional upowerSupport upower + ++ optional wireplumberSupport wireplumber; + + checkInputs = [ catch2_3 ]; + doCheck = runTests; + + mesonFlags = (lib.mapAttrsToList + (option: enable: "-D${option}=${if enable then "enabled" else "disabled"}") + { + dbusmenu-gtk = traySupport; + jack = jackSupport; + libinput = inputSupport; + libnl = nlSupport; + libudev = udevSupport; + mpd = mpdSupport; + pulseaudio = pulseSupport; + rfkill = rfkillSupport; + sndio = sndioSupport; + tests = runTests; + upower_glib = upowerSupport; + wireplumber = wireplumberSupport; + } + ) ++ [ + "-Dsystemd=disabled" + "-Dgtk-layer-shell=enabled" + "-Dman-pages=enabled" + ]; + + preFixup = lib.optionalString withMediaPlayer '' + cp $src/resources/custom_modules/mediaplayer.py $out/bin/waybar-mediaplayer.py + wrapProgram $out/bin/waybar-mediaplayer.py \ + --prefix PYTHONPATH : "$PYTHONPATH:$out/${python3.sitePackages}" + ''; + + meta = with lib; { + description = "Highly customizable Wayland bar for Sway and Wlroots based compositors"; + license = licenses.mit; + maintainers = with maintainers; [ FlorianFranzen minijackson synthetica lovesegfault ]; + platforms = platforms.unix; + homepage = "https://github.com/alexays/waybar"; + }; +} From 57ad7f9536c7f08255a6a04efc164f3375d6f822 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 10 Dec 2022 14:02:15 +0300 Subject: [PATCH 007/217] ISSUE#1877 Calendar week numbers 1. Let's do code simplier 2. Week format using regexp. Needs when user provide additional characters in format string and need to align week days according 3. Week format has got default formats: ":%U",":%V" 4. Week number is based on the first day of the week now. The output is the same as of date library now. 5. Avoiding of unnecessary operations --- include/modules/clock.hpp | 2 + src/modules/clock.cpp | 84 ++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 08ab05e..e97f05f 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -33,6 +33,8 @@ class Clock : public ALabel { bool handleScroll(GdkEventScroll* e); + std::string weeks_format_; + int weeks_format_left_gaps{0}; auto calendar_text(const waybar_time& wtime) -> std::string; auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void; auto first_day_of_week() -> date::weekday; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index a40d412..d6a9570 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "util/ustring_clen.hpp" @@ -74,6 +75,13 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) locale_ = std::locale(""); } + if (config_["format-calendar-weeks"].isString()) { + weeks_format_ = std::regex_replace(config_["format-calendar-weeks"].asString(), std::regex("\\{\\}"), (first_day_of_week() == date::Monday) ? "{:%V}" : "{:%U}"); + weeks_format_left_gaps = std::regex_replace(weeks_format_, std::regex(".*|.*|\\{.?+\\}"), "").length(); + } else { + weeks_format_ = ""; + } + thread_ = [this] { dp.emit(); auto now = std::chrono::system_clock::now(); @@ -180,70 +188,63 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str ? date::day{0} : ymd.day()}; const date::year_month ym{ymd.year(), ymd.month()}; - const auto weeks_format{config_["format-calendar-weeks"].isString() - ? config_["format-calendar-weeks"].asString() - : ""}; + const auto first_dow = first_day_of_week(); std::stringstream os; - const date::weekday first_week_day = first_day_of_week(); - enum class WeeksPlacement { + enum class WeeksSide { LEFT, RIGHT, HIDDEN, }; - WeeksPlacement weeks_pos = WeeksPlacement::HIDDEN; + WeeksSide weeks_pos = WeeksSide::HIDDEN; if (config_["calendar-weeks-pos"].isString()) { if (config_["calendar-weeks-pos"].asString() == "left") { - weeks_pos = WeeksPlacement::LEFT; + weeks_pos = WeeksSide::LEFT; // Add paddings before the header - os << std::string(4, ' '); + os << std::string(3 + weeks_format_left_gaps, ' '); } else if (config_["calendar-weeks-pos"].asString() == "right") { - weeks_pos = WeeksPlacement::RIGHT; + weeks_pos = WeeksSide::RIGHT; } } - weekdays_header(first_week_day, os); + weekdays_header(first_dow, os); - // First week prefixed with spaces if needed. - auto first_month_day = date::weekday(ym / 1); - int empty_days = (first_week_day - first_month_day).count() + 1; - date::sys_days last_week_day{static_cast(ym / 1) + date::days{7 - empty_days}}; + // First week day prefixed with spaces if needed. + date::sys_days print_wd{ym/1}; + auto wd{date::weekday{print_wd}}; + auto empty_days = (wd - first_dow).count(); - if (first_week_day == date::Monday) { - last_week_day -= date::days{1}; - } /* Print weeknumber on the left for the first row*/ - if (weeks_pos == WeeksPlacement::LEFT) { - os << fmt::format(weeks_format, date::format("%U", last_week_day)) << ' '; - last_week_day += date::weeks{1}; + if (weeks_pos == WeeksSide::LEFT) { + os << fmt::format(weeks_format_, print_wd) << ' '; } if (empty_days > 0) { os << std::string(empty_days * 3 - 1, ' '); } + const auto last_day = (ym / date::literals::last).day(); - auto weekday = first_month_day; - for (auto d = date::day(1); d <= last_day; ++d, ++weekday) { - if (weekday != first_week_day) { + + for (auto d{date::day{1}}; d <= last_day; ++d, ++wd) { + if (wd != first_dow) { os << ' '; - } else if (unsigned(d) != 1) { - last_week_day -= date::days{1}; - if (weeks_pos == WeeksPlacement::RIGHT) { - os << ' '; - os << fmt::format(weeks_format, date::format("%U", last_week_day)); + } else if (unsigned(d)!= 1) { + if (weeks_pos == WeeksSide::RIGHT) { + os << ' ' << fmt::format(weeks_format_, print_wd); } - os << "\n"; + os << '\n'; - if (weeks_pos == WeeksPlacement::LEFT) { - os << fmt::format(weeks_format, date::format("%U", last_week_day)); - os << ' '; + print_wd = {ym/d}; + + if (weeks_pos == WeeksSide::LEFT) { + os << fmt::format(weeks_format_, print_wd) << ' '; } - last_week_day += date::weeks{1} + date::days{1}; } + if (d == curr_day) { if (config_["today-format"].isString()) { auto today_format = config_["today-format"].asString(); @@ -257,12 +258,13 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str os << date::format("%e", d); } /*Print weeks on the right when the endings with spaces*/ - if (weeks_pos == WeeksPlacement::RIGHT && d == last_day) { - last_week_day -= date::days{1}; - empty_days = 6 - (weekday - first_week_day).count(); - os << std::string(empty_days * 3 + 1, ' '); - os << fmt::format(weeks_format, date::format("%U", last_week_day)); - last_week_day += date::days{1}; + if (weeks_pos == WeeksSide::RIGHT && d == last_day) { + empty_days = 6 - (wd.c_encoding() - first_dow.c_encoding()); + if (empty_days > 0) { + os << std::string(empty_days * 3, ' '); + } + + os << ' ' << fmt::format(weeks_format_, print_wd); } } @@ -291,7 +293,7 @@ auto waybar::modules::Clock::weekdays_header(const date::weekday& first_week_day const std::string pad(2 - clen, ' '); res << pad << wd_ustring; } while (++wd != first_week_day); - res << "\n"; + res << '\n'; if (config_["format-calendar-weekdays"].isString()) { os << fmt::format(config_["format-calendar-weekdays"].asString(), res.str()); @@ -315,7 +317,7 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin timezone = date::current_zone(); } wtime = {locale_, date::make_zoned(timezone, date::floor(*now))}; - os << fmt::format(format_, wtime) << "\n"; + os << fmt::format(format_, wtime) << '\n'; } return os.str(); } From 272c638f7e603a1c93199f614e5d1c591cd66a23 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 10 Dec 2022 16:48:22 +0300 Subject: [PATCH 008/217] Happy linter --- src/modules/clock.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index d6a9570..3114e16 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -5,8 +5,8 @@ #include #include -#include #include +#include #include #include "util/ustring_clen.hpp" @@ -76,8 +76,11 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } if (config_["format-calendar-weeks"].isString()) { - weeks_format_ = std::regex_replace(config_["format-calendar-weeks"].asString(), std::regex("\\{\\}"), (first_day_of_week() == date::Monday) ? "{:%V}" : "{:%U}"); - weeks_format_left_gaps = std::regex_replace(weeks_format_, std::regex(".*|.*|\\{.?+\\}"), "").length(); + weeks_format_ = + std::regex_replace(config_["format-calendar-weeks"].asString(), std::regex("\\{\\}"), + (first_day_of_week() == date::Monday) ? "{:%V}" : "{:%U}"); + weeks_format_left_gaps = + std::regex_replace(weeks_format_, std::regex(".*|.*|\\{.?+\\}"), "").length(); } else { weeks_format_ = ""; } @@ -192,7 +195,6 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str std::stringstream os; - enum class WeeksSide { LEFT, RIGHT, @@ -213,7 +215,7 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str weekdays_header(first_dow, os); // First week day prefixed with spaces if needed. - date::sys_days print_wd{ym/1}; + date::sys_days print_wd{ym / 1}; auto wd{date::weekday{print_wd}}; auto empty_days = (wd - first_dow).count(); @@ -231,14 +233,14 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str for (auto d{date::day{1}}; d <= last_day; ++d, ++wd) { if (wd != first_dow) { os << ' '; - } else if (unsigned(d)!= 1) { + } else if (unsigned(d) != 1) { if (weeks_pos == WeeksSide::RIGHT) { os << ' ' << fmt::format(weeks_format_, print_wd); } os << '\n'; - print_wd = {ym/d}; + print_wd = {ym / d}; if (weeks_pos == WeeksSide::LEFT) { os << fmt::format(weeks_format_, print_wd) << ' '; From a08967e0088b9f201e96529613c8549241eef6ae Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 10 Dec 2022 16:54:26 +0300 Subject: [PATCH 009/217] Happy linter --- src/modules/battery.cpp | 3 ++- src/modules/mpd/mpd.cpp | 1 - src/modules/sndio.cpp | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index b7a9cd0..f577e06 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -500,7 +500,8 @@ const std::tuple waybar::modules::Battery::g } else if (status == "Discharging" && total_power_exists && total_energy_exists) { if (total_power != 0) time_remaining = (float)total_energy / total_power; } else if (status == "Charging" && time_to_full_now_exists) { - if (time_to_full_now_exists && (time_to_full_now != 0)) time_remaining = -(float)time_to_full_now / 1000.0f; + if (time_to_full_now_exists && (time_to_full_now != 0)) + time_remaining = -(float)time_to_full_now / 1000.0f; // If we've turned positive it means the battery is past 100% and so just report that as no // time remaining if (time_remaining > 0.0f) time_remaining = 0.0f; diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 2c855d3..401b759 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -102,7 +102,6 @@ void waybar::modules::MPD::setLabel() { } else { label_.hide(); } - if (tooltipEnabled()) { std::string tooltip_format; diff --git a/src/modules/sndio.cpp b/src/modules/sndio.cpp index 7a358c1..e6f1bd0 100644 --- a/src/modules/sndio.cpp +++ b/src/modules/sndio.cpp @@ -117,7 +117,6 @@ auto Sndio::update() -> void { label_.set_markup(text); label_.show(); } - ALabel::update(); } From 9218968d2fa88e1003dba9a901d63f29da12e6c2 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 10 Dec 2022 17:55:21 +0300 Subject: [PATCH 010/217] Wrong assigning --- 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 3114e16..910637b 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -240,7 +240,7 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str os << '\n'; - print_wd = {ym / d}; + print_wd = (ym / d); if (weeks_pos == WeeksSide::LEFT) { os << fmt::format(weeks_format_, print_wd) << ' '; From 4c4d09992e9d80a6fe6a914fb0c20055b08700fe Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 10 Dec 2022 18:36:58 +0300 Subject: [PATCH 011/217] Regular expression improved --- 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 910637b..776ae11 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -80,7 +80,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) std::regex_replace(config_["format-calendar-weeks"].asString(), std::regex("\\{\\}"), (first_day_of_week() == date::Monday) ? "{:%V}" : "{:%U}"); weeks_format_left_gaps = - std::regex_replace(weeks_format_, std::regex(".*|.*|\\{.?+\\}"), "").length(); + std::regex_replace(weeks_format_, std::regex("]+>|\\{.*\\}"), "").length(); } else { weeks_format_ = ""; } From 0079092699e636c2f4515a6d140df9057bd1af0d Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Wed, 14 Dec 2022 16:43:23 +0300 Subject: [PATCH 012/217] ISSUE#1874 1. Calendar. Weeks. Fix right paddings when first days of the week is Monday 2. Fix small perfomrance penalty(avoid of defining parameter in the month loop) 3. Small name convention for format string variables --- include/modules/clock.hpp | 5 +++-- src/modules/clock.cpp | 31 ++++++++++++++++++------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index e97f05f..ef129fb 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -33,8 +33,9 @@ class Clock : public ALabel { bool handleScroll(GdkEventScroll* e); - std::string weeks_format_; - int weeks_format_left_gaps{0}; + std::string fmt_str_weeks_; + std::string fmt_str_calendar_; + int fmt_weeks_left_pad_{0}; auto calendar_text(const waybar_time& wtime) -> std::string; auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void; auto first_day_of_week() -> date::weekday; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 776ae11..b99e26b 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -76,13 +76,19 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } if (config_["format-calendar-weeks"].isString()) { - weeks_format_ = + fmt_str_weeks_ = std::regex_replace(config_["format-calendar-weeks"].asString(), std::regex("\\{\\}"), (first_day_of_week() == date::Monday) ? "{:%V}" : "{:%U}"); - weeks_format_left_gaps = - std::regex_replace(weeks_format_, std::regex("]+>|\\{.*\\}"), "").length(); + fmt_weeks_left_pad_ = + std::regex_replace(fmt_str_weeks_, std::regex("]+>|\\{.*\\}"), "").length(); } else { - weeks_format_ = ""; + fmt_str_weeks_ = ""; + } + + if (config_["format-calendar"].isString()) { + fmt_str_calendar_ = config_["format-calendar"].asString(); + } else { + fmt_str_calendar_ = "{}"; } thread_ = [this] { @@ -206,7 +212,7 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str if (config_["calendar-weeks-pos"].asString() == "left") { weeks_pos = WeeksSide::LEFT; // Add paddings before the header - os << std::string(3 + weeks_format_left_gaps, ' '); + os << std::string(3 + fmt_weeks_left_pad_, ' '); } else if (config_["calendar-weeks-pos"].asString() == "right") { weeks_pos = WeeksSide::RIGHT; } @@ -221,7 +227,7 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str /* Print weeknumber on the left for the first row*/ if (weeks_pos == WeeksSide::LEFT) { - os << fmt::format(weeks_format_, print_wd) << ' '; + os << fmt::format(fmt_str_weeks_, print_wd) << ' '; } if (empty_days > 0) { @@ -235,7 +241,7 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str os << ' '; } else if (unsigned(d) != 1) { if (weeks_pos == WeeksSide::RIGHT) { - os << ' ' << fmt::format(weeks_format_, print_wd); + os << ' ' << fmt::format(fmt_str_weeks_, print_wd); } os << '\n'; @@ -243,7 +249,7 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str print_wd = (ym / d); if (weeks_pos == WeeksSide::LEFT) { - os << fmt::format(weeks_format_, print_wd) << ' '; + os << fmt::format(fmt_str_weeks_, print_wd) << ' '; } } @@ -254,19 +260,18 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str } else { os << "" << date::format("%e", d) << ""; } - } else if (config_["format-calendar"].isString()) { - os << fmt::format(config_["format-calendar"].asString(), date::format("%e", d)); } else { - os << date::format("%e", d); + os << fmt::format(fmt_str_calendar_, date::format("%e", d)); } /*Print weeks on the right when the endings with spaces*/ if (weeks_pos == WeeksSide::RIGHT && d == last_day) { empty_days = 6 - (wd.c_encoding() - first_dow.c_encoding()); - if (empty_days > 0) { + if (empty_days > 0 && + empty_days < 7) { os << std::string(empty_days * 3, ' '); } - os << ' ' << fmt::format(weeks_format_, print_wd); + os << ' ' << fmt::format(fmt_str_weeks_, print_wd); } } From 995802e8ae25d6a0fb0ef88c2a58a69e39c252a5 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Wed, 14 Dec 2022 16:49:03 +0300 Subject: [PATCH 013/217] ISSUE#1874 - happy linter --- 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 b99e26b..151c048 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -266,8 +266,7 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str /*Print weeks on the right when the endings with spaces*/ if (weeks_pos == WeeksSide::RIGHT && d == last_day) { empty_days = 6 - (wd.c_encoding() - first_dow.c_encoding()); - if (empty_days > 0 && - empty_days < 7) { + if (empty_days > 0 && empty_days < 7) { os << std::string(empty_days * 3, ' '); } From 531bdfb8bbaaa7fde0a0b955cb124c704fe94de2 Mon Sep 17 00:00:00 2001 From: Enes Hecan Date: Thu, 15 Dec 2022 01:48:14 +0100 Subject: [PATCH 014/217] Fix hyprland language initialization issues --- src/modules/hyprland/language.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 13e30ec..738c835 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -79,16 +79,26 @@ void Language::onEvent(const std::string& ev) { void Language::initLanguage() { const auto INPUTDEVICES = gIPC->getSocket1Reply("devices"); - if (!config_.isMember("keyboard-name")) return; - const auto KEEBNAME = config_["keyboard-name"].asString(); try { - auto searcher = INPUTDEVICES.substr(INPUTDEVICES.find(KEEBNAME) + KEEBNAME.length()); - searcher = searcher.substr(searcher.find("keymap:") + 7); + auto searcher = KEEBNAME.empty() ? INPUTDEVICES : INPUTDEVICES.substr(INPUTDEVICES.find(KEEBNAME) + KEEBNAME.length()); + searcher = searcher.substr(searcher.find("keymap:") + 8); searcher = searcher.substr(0, searcher.find_first_of("\n\t")); - layoutName_ = searcher; + auto layoutName = std::string{}; + const auto BRIEFNAME = getShortFrom(searcher); + + if (config_.isMember("format-" + BRIEFNAME)) { + const auto PROPNAME = "format-" + BRIEFNAME; + layoutName = fmt::format(format_, config_[PROPNAME].asString()); + } else { + layoutName = fmt::format(format_, searcher); + } + + layoutName = waybar::util::sanitize_string(layoutName); + + layoutName_ = layoutName; spdlog::debug("hyprland language initLanguage found {}", layoutName_); From 4136ffaecb067b6bf93c66d6b9c9783316dfd6f9 Mon Sep 17 00:00:00 2001 From: Enes Hecan Date: Fri, 16 Dec 2022 10:01:58 +0100 Subject: [PATCH 015/217] Minor refactorings and formatting fixes for hyprland language module --- src/modules/hyprland/language.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 738c835..19a0632 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -50,17 +50,17 @@ auto Language::update() -> void { void Language::onEvent(const std::string& ev) { std::lock_guard lg(mutex_); auto layoutName = ev.substr(ev.find_last_of(',') + 1); - auto keebName = ev.substr(0, ev.find_last_of(',')); - keebName = keebName.substr(keebName.find_first_of('>') + 2); + auto kbName = ev.substr(0, ev.find_last_of(',')); + kbName = kbName.substr(kbName.find_first_of('>') + 2); - if (config_.isMember("keyboard-name") && keebName != config_["keyboard-name"].asString()) + if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString()) return; // ignore - const auto BRIEFNAME = getShortFrom(layoutName); + const auto briefName = getShortFrom(layoutName); - if (config_.isMember("format-" + BRIEFNAME)) { - const auto PROPNAME = "format-" + BRIEFNAME; - layoutName = fmt::format(format_, config_[PROPNAME].asString()); + if (config_.isMember("format-" + briefName)) { + const auto propName = "format-" + briefName; + layoutName = fmt::format(format_, config_[propName].asString()); } else { layoutName = fmt::format(format_, layoutName); } @@ -77,21 +77,23 @@ void Language::onEvent(const std::string& ev) { } void Language::initLanguage() { - const auto INPUTDEVICES = gIPC->getSocket1Reply("devices"); + const auto inputDevices = gIPC->getSocket1Reply("devices"); - const auto KEEBNAME = config_["keyboard-name"].asString(); + const auto kbName = config_["keyboard-name"].asString(); try { - auto searcher = KEEBNAME.empty() ? INPUTDEVICES : INPUTDEVICES.substr(INPUTDEVICES.find(KEEBNAME) + KEEBNAME.length()); + auto searcher = kbName.empty() + ? inputDevices + : inputDevices.substr(inputDevices.find(kbName) + kbName.length()); searcher = searcher.substr(searcher.find("keymap:") + 8); searcher = searcher.substr(0, searcher.find_first_of("\n\t")); auto layoutName = std::string{}; - const auto BRIEFNAME = getShortFrom(searcher); + const auto briefName = getShortFrom(searcher); - if (config_.isMember("format-" + BRIEFNAME)) { - const auto PROPNAME = "format-" + BRIEFNAME; - layoutName = fmt::format(format_, config_[PROPNAME].asString()); + if (config_.isMember("format-" + briefName)) { + const auto propName = "format-" + briefName; + layoutName = fmt::format(format_, config_[propName].asString()); } else { layoutName = fmt::format(format_, searcher); } From 6e296838e4bacb88107d8174fa15fe7a94a8f5c9 Mon Sep 17 00:00:00 2001 From: Enes Hecan Date: Wed, 21 Dec 2022 00:20:16 +0100 Subject: [PATCH 016/217] Update hyprland language module docs --- man/waybar-hyprland-language.5.scd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/man/waybar-hyprland-language.5.scd b/man/waybar-hyprland-language.5.scd index cb16995..4a4e175 100644 --- a/man/waybar-hyprland-language.5.scd +++ b/man/waybar-hyprland-language.5.scd @@ -23,7 +23,7 @@ Addressed by *hyprland/language* *keyboard-name*: ++ typeof: string ++ - Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "AT Translated set..." is recommended. + Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended. @@ -32,9 +32,9 @@ Addressed by *hyprland/language* ``` "hyprland/language": { "format": "Lang: {}" - "format-us": "AMERICA, HELL YEAH!" // For American English - "format-tr": "As bayrakları" // For Turkish - "keyboard-name": "AT Translated Set 2 keyboard" + "format-en": "AMERICA, HELL YEAH!" + "format-tr": "As bayrakları" + "keyboard-name": "at-translated-set-2-keyboard" } ``` From 4d59de42af6de70a39cc1c6e6c71cdf3e05cdceb Mon Sep 17 00:00:00 2001 From: Enes Hecan Date: Wed, 21 Dec 2022 01:45:57 +0100 Subject: [PATCH 017/217] Implement hyprland submap module --- include/factory.hpp | 1 + include/modules/hyprland/submap.hpp | 25 +++++++++ man/waybar-hyprland-submap.5.scd | 82 +++++++++++++++++++++++++++++ meson.build | 1 + src/factory.cpp | 3 ++ src/modules/hyprland/submap.cpp | 60 +++++++++++++++++++++ 6 files changed, 172 insertions(+) create mode 100644 include/modules/hyprland/submap.hpp create mode 100644 man/waybar-hyprland-submap.5.scd create mode 100644 src/modules/hyprland/submap.cpp diff --git a/include/factory.hpp b/include/factory.hpp index d69930f..2ec3cf0 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -26,6 +26,7 @@ #include "modules/hyprland/backend.hpp" #include "modules/hyprland/language.hpp" #include "modules/hyprland/window.hpp" +#include "modules/hyprland/submap.hpp" #endif #if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM)) #include "modules/battery.hpp" diff --git a/include/modules/hyprland/submap.hpp b/include/modules/hyprland/submap.hpp new file mode 100644 index 0000000..e33cdae --- /dev/null +++ b/include/modules/hyprland/submap.hpp @@ -0,0 +1,25 @@ +#include +#include "ALabel.hpp" +#include "bar.hpp" +#include "modules/hyprland/backend.hpp" +#include "util/json.hpp" + +namespace waybar::modules::hyprland { + +class Submap : public waybar::ALabel, public EventHandler { + public: + Submap(const std::string&, const waybar::Bar&, const Json::Value&); + ~Submap(); + + auto update() -> void; + + private: + void onEvent(const std::string&); + + std::mutex mutex_; + const Bar& bar_; + util::JsonParser parser_; + std::string submap_; +}; + +} // namespace waybar::modules::hyprland diff --git a/man/waybar-hyprland-submap.5.scd b/man/waybar-hyprland-submap.5.scd new file mode 100644 index 0000000..a00a276 --- /dev/null +++ b/man/waybar-hyprland-submap.5.scd @@ -0,0 +1,82 @@ +waybar-hyprland-submap(5) + +# NAME + +waybar - hyprland submap module + +# DESCRIPTION + +The *submap* module displays the currently active submap similar to *sway/mode*. + +# CONFIGURATION + +Addressed by *hyprland/submap* + +*format*: ++ + typeof: string ++ + default: {} ++ + The format, how information should be displayed. On {} the currently active submap is 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 take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*on-click*: ++ + typeof: string ++ + Command to execute when clicked on the module. + +*on-click-middle*: ++ + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. + +*on-click-right*: ++ + typeof: string ++ + Command to execute when you right clicked on the module. + +*on-update*: ++ + typeof: string ++ + Command to execute when the module is updated. + +*on-scroll-up*: ++ + typeof: string ++ + Command to execute when scrolling up on the module. + +*on-scroll-down*: ++ + typeof: string ++ + Command to execute when scrolling down on the module. + +*smooth-scrolling-threshold*: ++ + typeof: double ++ + Threshold to be used when scrolling. + +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to disable tooltip on hover. + + +# EXAMPLES + +``` +"hyprland/submap": { + "format": "✌️ {}", + "max-length": 8, + "tooltip": false +} +``` + +# STYLE + +- *#submap* diff --git a/meson.build b/meson.build index 557a02d..d6b9e17 100644 --- a/meson.build +++ b/meson.build @@ -220,6 +220,7 @@ if true 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' endif if libnl.found() and libnlgen.found() diff --git a/src/factory.cpp b/src/factory.cpp index d16cb52..9e3890e 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -67,6 +67,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "hyprland/language") { return new waybar::modules::hyprland::Language(id, bar_, config_[name]); } + if (ref == "hyprland/submap") { + return new waybar::modules::hyprland::Submap(id, bar_, config_[name]); + } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp new file mode 100644 index 0000000..6a12292 --- /dev/null +++ b/src/modules/hyprland/submap.cpp @@ -0,0 +1,60 @@ +#include "modules/hyprland/submap.hpp" +#include +#include + +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) { + modulesReady = true; + + if (!gIPC.get()) { + gIPC = std::make_unique(); + } + + label_.hide(); + ALabel::update(); + + // register for hyprland ipc + gIPC->registerForIPC("submap", this); +} + +Submap::~Submap() { + gIPC->unregisterForIPC(this); + // wait for possible event handler to finish + std::lock_guard lg(mutex_); +} + +auto Submap::update() -> void { + std::lock_guard lg(mutex_); + + if (submap_.empty()) { + event_box_.hide(); + } else { + label_.set_markup(fmt::format(format_, submap_)); + if (tooltipEnabled()) { + label_.set_tooltip_text(submap_); + } + event_box_.show(); + } + // Call parent update + ALabel::update(); +} + +void Submap::onEvent(const std::string& ev) { + std::lock_guard lg(mutex_); + + if (ev.find("submap") == std::string::npos) { + return; + } + + auto submapName = ev.substr(ev.find_last_of('>') + 1); + submapName = waybar::util::sanitize_string(submapName); + + submap_ = submapName; + + spdlog::debug("hyprland submap onevent with {}", submap_); + + dp.emit(); +} +} // namespace waybar::modules::hyprland From c05f41d732e8a9bd66e32b3888f58b39cc298933 Mon Sep 17 00:00:00 2001 From: Enes Hecan Date: Wed, 21 Dec 2022 01:55:39 +0100 Subject: [PATCH 018/217] Make linter happy --- src/modules/hyprland/submap.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 6a12292..6eb0942 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -1,5 +1,7 @@ #include "modules/hyprland/submap.hpp" + #include + #include namespace waybar::modules::hyprland { From 91357f210d0b5989c295785924f31b6611a4964f Mon Sep 17 00:00:00 2001 From: Arisa Snowbell Date: Mon, 26 Dec 2022 06:38:04 +0100 Subject: [PATCH 019/217] Ignore .cache generated by clangd --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 56a2f73..d153566 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *~ vgcore.* /.vscode +/.cache *.swp packagecache /subprojects/**/ From f724cc3f9dde9f8ef94b4fd5e8c2385746668ccf Mon Sep 17 00:00:00 2001 From: Enes Hecan Date: Tue, 27 Dec 2022 15:28:20 +0100 Subject: [PATCH 020/217] Fix wrong layout name in hyprland language module when a variant is used --- .gitignore | 1 + src/modules/hyprland/language.cpp | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d153566..bf2bdbf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *~ vgcore.* /.vscode +/.idea /.cache *.swp packagecache diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 19a0632..6cadd62 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -49,9 +49,8 @@ auto Language::update() -> void { void Language::onEvent(const std::string& ev) { std::lock_guard lg(mutex_); - auto layoutName = ev.substr(ev.find_last_of(',') + 1); - auto kbName = ev.substr(0, ev.find_last_of(',')); - kbName = kbName.substr(kbName.find_first_of('>') + 2); + auto kbName = ev.substr(ev.find_last_of('>') + 1, ev.find_first_of(',')); + auto layoutName = ev.substr(ev.find_first_of(',') + 1); if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString()) return; // ignore From f795e7a3084c0c9bf2bc53b142a53015662d85fd Mon Sep 17 00:00:00 2001 From: Maxim Andreev Date: Tue, 27 Dec 2022 21:23:16 +0300 Subject: [PATCH 021/217] modules/clock: improve ux when calendar_shift is used: 1. change only date, but not time 2. use shifted values only in tooltip 3. reset shift when mouse leaves (popup closes) --- src/modules/clock.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 151c048..55f2c5b 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -66,6 +66,11 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) if (config_["on-scroll"][kCalendarPlaceholder].isInt()) { calendar_shift_init_ = date::months{config_["on-scroll"].get(kCalendarPlaceholder, 0).asInt()}; + event_box_.add_events(Gdk::LEAVE_NOTIFY_MASK); + event_box_.signal_leave_notify_event().connect([this](GdkEventCrossing*) { + calendar_shift_ = date::months{0}; + return false; + }); } } @@ -113,8 +118,14 @@ bool waybar::modules::Clock::is_timezone_fixed() { auto waybar::modules::Clock::update() -> void { auto time_zone = current_timezone(); auto now = std::chrono::system_clock::now(); - waybar_time wtime = {locale_, date::make_zoned(time_zone, date::floor(now) + - calendar_shift_)}; + waybar_time wtime = {locale_, + date::make_zoned(time_zone, date::floor(now))}; + + auto shifted_date = date::year_month_day{date::floor(now)} + calendar_shift_; + auto now_shifted = date::sys_days{shifted_date} + (now - date::floor(now)); + waybar_time shifted_wtime = { + locale_, date::make_zoned(time_zone, date::floor(now_shifted))}; + std::string text = ""; if (!is_timezone_fixed()) { // As date dep is not fully compatible, prefer fmt @@ -131,15 +142,15 @@ auto waybar::modules::Clock::update() -> void { std::string calendar_lines{""}; std::string timezoned_time_lines{""}; if (is_calendar_in_tooltip_) { - calendar_lines = calendar_text(wtime); + calendar_lines = calendar_text(shifted_wtime); } if (is_timezoned_list_in_tooltip_) { timezoned_time_lines = timezones_text(&now); } auto tooltip_format = config_["tooltip-format"].asString(); - text = - fmt::format(tooltip_format, wtime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines), - fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines)); + text = fmt::format(tooltip_format, shifted_wtime, + fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines), + fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines)); label_.set_tooltip_markup(text); } } From 8b512e7b22f4014b32cbbaa0221cd37afca88494 Mon Sep 17 00:00:00 2001 From: Louis DeLosSantos Date: Mon, 2 Jan 2023 17:03:28 -0500 Subject: [PATCH 022/217] sway,feat: allow alphabetical sort for users who do not utilize any form of "workspace prev/next" commands, it can be very handle to sort the workspaces alphabetically. this commit adds a new "alphabetical_sort" to the `sway/workspaces` module which allows the module to alway sort workspaces alphabetically. this docs are updated to warn the user of the implications involved. Signed-off-by: Louis DeLosSantos --- man/waybar-sway-workspaces.5.scd | 4 ++++ src/modules/sway/workspaces.cpp | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index b575e09..644cba4 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -73,6 +73,10 @@ Addressed by *sway/workspaces* 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. + # FORMAT REPLACEMENTS *{value}*: Name of the workspace, as defined by sway. diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index b7e51e4..f8c1891 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -130,6 +130,10 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { // In a first pass, the maximum "num" value is computed to enqueue // unnumbered workspaces behind numbered ones when computing the sort // attribute. + // + // Note: if the 'alphabetical_sort' option is true, the user is in + // agreement that the "workspace prev/next" commands may not follow + // the order displayed in Waybar. int max_num = -1; for (auto &workspace : workspaces_) { max_num = std::max(workspace["num"].asInt(), max_num); @@ -143,16 +147,19 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { } } std::sort(workspaces_.begin(), workspaces_.end(), - [](const Json::Value &lhs, const Json::Value &rhs) { + [this](const Json::Value &lhs, const Json::Value &rhs) { auto lname = lhs["name"].asString(); auto rname = rhs["name"].asString(); int l = lhs["sort"].asInt(); int r = rhs["sort"].asInt(); - if (l == r) { + if (l == r || config_["alphabetical_sort"].asBool()) { // In case both integers are the same, lexicographical // sort. The code above already ensure that this will only // happend in case of explicitly numbered workspaces. + // + // Additionally, if the config specifies to sort workspaces + // alphabetically do this here. return lname < rname; } From 1938bb5d28d2e777a29462758f652e28ae888918 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 4 Jan 2023 16:26:50 +0100 Subject: [PATCH 023/217] fix: lint --- include/factory.hpp | 2 +- include/modules/hyprland/submap.hpp | 1 + include/modules/image.hpp | 6 +++--- src/modules/sway/workspaces.cpp | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/factory.hpp b/include/factory.hpp index 2ec3cf0..e2548a9 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -25,8 +25,8 @@ #ifdef HAVE_HYPRLAND #include "modules/hyprland/backend.hpp" #include "modules/hyprland/language.hpp" -#include "modules/hyprland/window.hpp" #include "modules/hyprland/submap.hpp" +#include "modules/hyprland/window.hpp" #endif #if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM)) #include "modules/battery.hpp" diff --git a/include/modules/hyprland/submap.hpp b/include/modules/hyprland/submap.hpp index e33cdae..c36578c 100644 --- a/include/modules/hyprland/submap.hpp +++ b/include/modules/hyprland/submap.hpp @@ -1,4 +1,5 @@ #include + #include "ALabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" diff --git a/include/modules/image.hpp b/include/modules/image.hpp index 00b8393..06b61ee 100644 --- a/include/modules/image.hpp +++ b/include/modules/image.hpp @@ -23,10 +23,10 @@ class Image : public AModule { void delayWorker(); void handleEvent(); - Gtk::Image image_; + Gtk::Image image_; std::string path_; - int size_; - int interval_; + int size_; + int interval_; util::SleeperThread thread_; }; diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index f8c1891..b621b83 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -131,7 +131,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { // unnumbered workspaces behind numbered ones when computing the sort // attribute. // - // Note: if the 'alphabetical_sort' option is true, the user is in + // Note: if the 'alphabetical_sort' option is true, the user is in // agreement that the "workspace prev/next" commands may not follow // the order displayed in Waybar. int max_num = -1; From 2a3ebc12deec72510ead5432319e3bacc8b6074b Mon Sep 17 00:00:00 2001 From: Jonathan Herlin Date: Thu, 5 Jan 2023 01:10:04 +0100 Subject: [PATCH 024/217] fix: cpu_load pushed twice to the vector --- src/modules/cpu/common.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index cdbbc3d..8fedf84 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -39,7 +39,6 @@ auto waybar::modules::Cpu::update() -> void { 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", cpu_load)); 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)); From 0bc5314e089de7b30d166676e7fd1148023dfbdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20G=C3=BCnzler?= Date: Mon, 1 Nov 2021 19:17:29 +0100 Subject: [PATCH 025/217] Add mpris module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uses libplayerctl to use the MPRIS dbus protocol to query, listen and control media players. Signed-off-by: Robert Günzler --- include/factory.hpp | 3 + include/modules/mpris/mpris.hpp | 67 ++++++ man/waybar-mpris.5.scd | 103 +++++++++ man/waybar.5.scd.in | 1 + meson.build | 13 +- meson_options.txt | 1 + src/factory.cpp | 5 + src/modules/mpris/mpris.cpp | 394 ++++++++++++++++++++++++++++++++ 8 files changed, 586 insertions(+), 1 deletion(-) create mode 100644 include/modules/mpris/mpris.hpp create mode 100644 man/waybar-mpris.5.scd create mode 100644 src/modules/mpris/mpris.cpp diff --git a/include/factory.hpp b/include/factory.hpp index d69930f..688b9ac 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -41,6 +41,9 @@ #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 diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp new file mode 100644 index 0000000..4f8ddb1 --- /dev/null +++ b/include/modules/mpris/mpris.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include + +#include "gtkmm/box.h" +#include "gtkmm/label.h" + +extern "C" { +#include +} + +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules::mpris { + +class Mpris : public AModule { + public: + Mpris(const std::string&, const Json::Value&); + ~Mpris(); + auto update() -> void; + bool handleToggle(GdkEventButton* const&); + + private: + static auto onPlayerNameAppeared(PlayerctlPlayerManager*, PlayerctlPlayerName*, gpointer) -> void; + static auto onPlayerNameVanished(PlayerctlPlayerManager*, PlayerctlPlayerName*, gpointer) -> void; + static auto onPlayerPlay(PlayerctlPlayer*, gpointer) -> void; + static auto onPlayerPause(PlayerctlPlayer*, gpointer) -> void; + static auto onPlayerStop(PlayerctlPlayer*, gpointer) -> void; + static auto onPlayerMetadata(PlayerctlPlayer*, GVariant*, gpointer) -> void; + + struct PlayerInfo { + std::string name; + PlayerctlPlaybackStatus status; + std::string status_string; + + std::optional artist; + std::optional album; + std::optional title; + std::optional length; // as HH:MM:SS + }; + + auto getPlayerInfo() -> std::optional; + auto getIcon(const Json::Value&, const std::string&) -> std::string; + + Gtk::Box box_; + Gtk::Label label_; + + // config + std::string format_; + std::string format_playing_; + std::string format_paused_; + std::string format_stopped_; + std::chrono::seconds interval_; + std::string player_; + std::vector ignored_players_; + + PlayerctlPlayerManager* manager; + PlayerctlPlayer* player; + std::string lastStatus; + std::string lastPlayer; + + util::SleeperThread thread_; +}; + +} // namespace waybar::modules::mpris diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd new file mode 100644 index 0000000..d2a72d9 --- /dev/null +++ b/man/waybar-mpris.5.scd @@ -0,0 +1,103 @@ +waybar-mpris(5) + +# NAME + +waybar - MPRIS module + +# DESCRIPTION + +The *mpris* module displays currently playing media via libplayerctl. + +# CONFIGURATION + +*player*: ++ + typeof: string ++ + default: playerctld ++ + Name of the MPRIS player to attach to. Using the default value always + follows the currenly active player. + +*ignored-players*: ++ + typeof: []string ++ + Ignore updates of the listed players, when using playerctld. + +*interval*: ++ + typeof: integer ++ + Refresh MPRIS information on a timer. + +*format*: ++ + typeof: string ++ + default: {player} ({status}) {dynamic} ++ + The text format. + +*format-[status]*: ++ + typeof: string ++ + The status-specific text format. + +*on-click*: ++ + typeof: string ++ + default: play-pause ++ + Overwrite default action toggles. + +*on-middle-click*: ++ + typeof: string ++ + default: previous track ++ + Overwrite default action toggles. + +*on-right-click*: ++ + typeof: string ++ + default: next track ++ + Overwrite default action toggles. + +*player-icons*: ++ + typeof: map[string]string + 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). + + +# FORMAT REPLACEMENTS + +*{player}*: The name of the current media player + +*{status}*: The current status (playing, paused, stopped) + +*{artist}*: The artist of the current track + +*{album}*: The album title of the current track + +*{title}*: The title of the current track + +*{length}*: Length of the track, formatted as HH:MM:SS + +*{dynamic}*: Use _{artist}_, _{album}_, _{title}_ and _{length}_, automatically omit++ + empty values + +*{player-icon}*: Chooses an icon from _player-icons_ based on _{player}_ + +*{status-icon}*: Chooses an icon from _status-icons_ based on _{status}_ + +# EXAMPLES + +``` +"mpris": { + "format": "DEFAULT: {player_icon} {dynamic}", + "format-paused": "DEFAULT: {status_icon} {dynamic}", + "player-icons": { + "default": "▶", + "mpv": "🎵" + }, + "status-icons": { + "paused": "⏸" + }, + // "ignored-players": ["firefox"] +} +``` + +# STYLE + +- *#mpris* +- *#mpris.${status}* +- *#mpris.${player}* diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 54340f2..b1ed4c5 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -266,6 +266,7 @@ A module group is defined by specifying a module named "group/some-group-name". - *waybar-keyboard-state(5)* - *waybar-memory(5)* - *waybar-mpd(5)* +- *waybar-mpris(5)* - *waybar-network(5)* - *waybar-pulseaudio(5)* - *waybar-river-mode(5)* diff --git a/meson.build b/meson.build index 557a02d..83f5998 100644 --- a/meson.build +++ b/meson.build @@ -86,7 +86,10 @@ 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())) +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())) jsoncpp = dependency('jsoncpp', version : ['>=1.9.2'], fallback : ['jsoncpp', 'jsoncpp_dep']) sigcpp = dependency('sigc++-2.0') libinotify = dependency('libinotify', required: false) @@ -95,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')) +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')) libevdev = dependency('libevdev', required: get_option('libevdev')) @@ -238,6 +242,11 @@ if (upower_glib.found() and giounix.found() and not get_option('logind').disable src_files += 'src/modules/upower/upower_tooltip.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' +endif + if libpulse.found() add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp') src_files += 'src/modules/pulseaudio.cpp' @@ -334,6 +343,7 @@ executable( libnl, libnlgen, upower_glib, + playerctl, libpulse, libjack, libwireplumber, @@ -387,6 +397,7 @@ if scdoc.found() '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-river-mode.5.scd', diff --git a/meson_options.txt b/meson_options.txt index 402912f..98cd494 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('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') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') diff --git a/src/factory.cpp b/src/factory.cpp index d16cb52..3ccf258 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -22,6 +22,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { return new waybar::modules::upower::UPower(id, config_[name]); } #endif +#ifdef HAVE_MPRIS + if (ref == "mpris") { + return new waybar::modules::mpris::Mpris(id, config_[name]); + } +#endif #ifdef HAVE_SWAY if (ref == "sway/mode") { return new waybar::modules::sway::Mode(id, config_[name]); diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp new file mode 100644 index 0000000..651dfd5 --- /dev/null +++ b/src/modules/mpris/mpris.cpp @@ -0,0 +1,394 @@ +#include "modules/mpris/mpris.hpp" + +#include + +#include +#include +#include + +extern "C" { +#include +} + +#include + +namespace waybar::modules::mpris { + +const std::string DEFAULT_FORMAT = "{player} ({status}): {dynamic}"; + +Mpris::Mpris(const std::string& id, const Json::Value& config) + : AModule(config, "mpris", id), + box_(Gtk::ORIENTATION_HORIZONTAL, 0), + label_(), + format_(DEFAULT_FORMAT), + interval_(0), + player_("playerctld"), + manager(), + player() { + box_.pack_start(label_); + box_.set_name(name_); + event_box_.add(box_); + event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &Mpris::handleToggle)); + + if (config_["format"].isString()) { + format_ = config_["format"].asString(); + } + if (config_["format-playing"].isString()) { + format_playing_ = config_["format-playing"].asString(); + } + if (config_["format-paused"].isString()) { + format_paused_ = config_["format-paused"].asString(); + } + if (config_["format-stopped"].isString()) { + format_stopped_ = config_["format-stopped"].asString(); + } + if (config_["interval"].isUInt()) { + interval_ = std::chrono::seconds(config_["interval"].asUInt()); + } + if (config_["player"].isString()) { + player_ = config_["player"].asString(); + } + if (config_["ignored-players"].isArray()) { + for (auto it = config_["ignored-players"].begin(); it != config_["ignored-players"].end(); + ++it) { + ignored_players_.push_back(it->asString()); + } + } + + GError* error = nullptr; + manager = playerctl_player_manager_new(&error); + if (error) { + throw std::runtime_error(fmt::format("unable to create MPRIS client: {}", error->message)); + } + + g_object_connect(manager, "signal::name-appeared", G_CALLBACK(onPlayerNameAppeared), this, NULL); + g_object_connect(manager, "signal::name-vanished", G_CALLBACK(onPlayerNameVanished), this, NULL); + + if (player_ == "playerctld") { + // use playerctld proxy + PlayerctlPlayerName name = { + .instance = (gchar*)player_.c_str(), + .source = PLAYERCTL_SOURCE_DBUS_SESSION, + }; + player = playerctl_player_new_from_name(&name, &error); + + } 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); + } + + for (auto p = players; p != NULL; 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; + } + } + } + + if (error) { + throw std::runtime_error( + fmt::format("unable to connect to player {}: {}", player_, error->message)); + } + + if (player) { + g_object_connect(player, "signal::play", G_CALLBACK(onPlayerPlay), this, "signal::pause", + G_CALLBACK(onPlayerPause), this, "signal::stop", G_CALLBACK(onPlayerStop), + this, "signal::stop", G_CALLBACK(onPlayerStop), this, "signal::metadata", + G_CALLBACK(onPlayerMetadata), this, NULL); + } + + // allow setting an interval count that triggers periodic refreshes + if (interval_.count() > 0) { + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; + } + + // trigger initial update + dp.emit(); +} + +Mpris::~Mpris() { + if (manager != NULL) g_object_unref(manager); + if (player != NULL) g_object_unref(player); +} + +auto Mpris::getIcon(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(); + } + } + return ""; +} + +auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name, + gpointer data) -> void { + Mpris* mpris = static_cast(data); + if (!mpris) return; + + spdlog::debug("mpris: name-appeared callback: {}", player_name->name); + + if (std::string(player_name->name) != mpris->player_) { + return; + } + + 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", + G_CALLBACK(onPlayerMetadata), mpris, NULL); + + mpris->dp.emit(); +} + +auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name, + gpointer data) -> void { + Mpris* mpris = static_cast(data); + if (!mpris) return; + + spdlog::debug("mpris: player-vanished callback: {}", player_name->name); + + if (std::string(player_name->name) == mpris->player_) { + mpris->player = nullptr; + mpris->dp.emit(); + } +} + +auto Mpris::onPlayerPlay(PlayerctlPlayer* player, gpointer data) -> void { + Mpris* mpris = static_cast(data); + if (!mpris) return; + + spdlog::debug("mpris: player-play callback"); + // update widget + mpris->dp.emit(); +} + +auto Mpris::onPlayerPause(PlayerctlPlayer* player, gpointer data) -> void { + Mpris* mpris = static_cast(data); + if (!mpris) return; + + spdlog::debug("mpris: player-pause callback"); + // update widget + mpris->dp.emit(); +} + +auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void { + Mpris* mpris = static_cast(data); + if (!mpris) return; + + spdlog::debug("mpris: player-stop callback"); + + // hide widget + mpris->event_box_.set_visible(false); + // update widget + mpris->dp.emit(); +} + +auto Mpris::onPlayerMetadata(PlayerctlPlayer* player, GVariant* metadata, gpointer data) -> void { + Mpris* mpris = static_cast(data); + if (!mpris) return; + + spdlog::debug("mpris: player-metadata callback"); + // update widget + mpris->dp.emit(); +} + +auto Mpris::getPlayerInfo() -> std::optional { + if (!player) { + return std::nullopt; + } + + GError* error = nullptr; + + char* player_status = nullptr; + auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED; + g_object_get(player, "status", &player_status, "playback-status", &player_playback_status, NULL); + + std::string player_name = player_; + 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); + } + // > get the list of players [..] in order of activity + // 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; + } + + if (std::any_of(ignored_players_.begin(), ignored_players_.end(), + [&](const std::string& pn) { return player_name == pn; })) { + spdlog::warn("mpris[{}]: ignoring player update", player_name); + return std::nullopt; + } + + // make status lowercase + player_status[0] = std::tolower(player_status[0]); + + PlayerInfo info = { + .name = player_name, + .status = player_playback_status, + .status_string = player_status, + }; + + if (auto artist_ = playerctl_player_get_artist(player, &error)) { + spdlog::debug("mpris[{}]: artist = {}", info.name, artist_); + info.artist = Glib::Markup::escape_text(artist_); + g_free(artist_); + } + if (error) goto errorexit; + + if (auto album_ = playerctl_player_get_album(player, &error)) { + spdlog::debug("mpris[{}]: album = {}", info.name, album_); + info.album = Glib::Markup::escape_text(album_); + g_free(album_); + } + if (error) goto errorexit; + + if (auto title_ = playerctl_player_get_title(player, &error)) { + spdlog::debug("mpris[{}]: title = {}", info.name, title_); + info.title = Glib::Markup::escape_text(title_); + g_free(title_); + } + if (error) goto errorexit; + + 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_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_m); + info.length = fmt::format("{:02}:{:02}:{:02}", len_h.count(), len_m.count(), len_s.count()); + g_free(length_); + } + if (error) goto errorexit; + + return info; + +errorexit: + spdlog::error("mpris[{}]: {}", info.name, error->message); + g_error_free(error); + return std::nullopt; +} + +bool Mpris::handleToggle(GdkEventButton* const& e) { + GError* error = nullptr; + + auto info = getPlayerInfo(); + if (!info) return false; + + if (e->type == GdkEventType::GDK_BUTTON_PRESS) { + switch (e->button) { + case 1: // left-click + if (config_["on-click"].isString()) { + return AModule::handleToggle(e); + } + playerctl_player_play_pause(player, &error); + break; + case 2: // middle-click + if (config_["on-middle-click"].isString()) { + return AModule::handleToggle(e); + } + playerctl_player_previous(player, &error); + break; + case 3: // right-click + if (config_["on-right-click"].isString()) { + return AModule::handleToggle(e); + } + playerctl_player_next(player, &error); + break; + } + } + if (error) { + spdlog::error("mpris[{}]: error running builtin on-click action: {}", (*info).name, + error->message); + g_error_free(error); + return false; + } + return true; +} + +auto Mpris::update() -> void { + auto opt = getPlayerInfo(); + if (!opt) { + event_box_.set_visible(false); + AModule::update(); + return; + } + auto info = *opt; + + if (info.status == PLAYERCTL_PLAYBACK_STATUS_STOPPED) { + spdlog::debug("mpris[{}]: player stopped, skipping update", info.name); + return; + } + + spdlog::debug("mpris[{}]: running update", info.name); + + // dynamic is the auto-formatted string containing a nice out-of-the-box + // format text + std::stringstream dynamic; + if (info.artist) dynamic << *info.artist << " - "; + if (info.album) dynamic << *info.album << " - "; + if (info.title) dynamic << *info.title; + if (info.length) + dynamic << " " + << "" + << "[" << *info.length << "]" + << ""; + + // set css class for player status + if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) { + box_.get_style_context()->remove_class(lastStatus); + } + if (!box_.get_style_context()->has_class(info.status_string)) { + box_.get_style_context()->add_class(info.status_string); + } + lastStatus = info.status_string; + + // set css class for player name + if (!lastPlayer.empty() && box_.get_style_context()->has_class(lastPlayer)) { + box_.get_style_context()->remove_class(lastPlayer); + } + if (!box_.get_style_context()->has_class(info.name)) { + box_.get_style_context()->add_class(info.name); + } + lastPlayer = info.name; + + auto formatstr = format_; + switch (info.status) { + case PLAYERCTL_PLAYBACK_STATUS_PLAYING: + if (!format_playing_.empty()) formatstr = format_playing_; + break; + case PLAYERCTL_PLAYBACK_STATUS_PAUSED: + if (!format_paused_.empty()) formatstr = format_paused_; + break; + case PLAYERCTL_PLAYBACK_STATUS_STOPPED: + if (!format_stopped_.empty()) formatstr = format_stopped_; + break; + } + auto label_format = + fmt::format(formatstr, fmt::arg("player", info.name), fmt::arg("status", info.status_string), + fmt::arg("artist", *info.artist), fmt::arg("title", *info.title), + fmt::arg("album", *info.album), fmt::arg("length", *info.length), + fmt::arg("dynamic", dynamic.str()), + fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), + fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); + label_.set_markup(label_format); + + event_box_.set_visible(true); + // call parent update + AModule::update(); +} + +} // namespace waybar::modules::mpris From b3b5d8f9ab73668664a439a1966f5e9a13fbd84d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20G=C3=BCnzler?= Date: Sat, 7 Jan 2023 01:42:42 +0100 Subject: [PATCH 026/217] Activate ci for mpris module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Günzler --- Dockerfiles/alpine | 2 +- Dockerfiles/archlinux | 2 +- Dockerfiles/debian | 2 +- Dockerfiles/fedora | 3 ++- Dockerfiles/gentoo | 2 +- Dockerfiles/opensuse | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Dockerfiles/alpine b/Dockerfiles/alpine index 03836aa..d9a3dd5 100644 --- a/Dockerfiles/alpine +++ b/Dockerfiles/alpine @@ -2,4 +2,4 @@ FROM alpine:latest -RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon tzdata +RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon tzdata playerctl-dev diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index 40a1b2e..e7cbba6 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,4 +3,4 @@ FROM archlinux:base-devel RUN pacman -Syu --noconfirm && \ - pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm libxkbcommon + 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 diff --git a/Dockerfiles/debian b/Dockerfiles/debian index 026d8fd..578588c 100644 --- a/Dockerfiles/debian +++ b/Dockerfiles/debian @@ -3,5 +3,5 @@ FROM debian:sid RUN apt-get update && \ - apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 && \ + apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 libplayerctl-dev && \ apt-get clean diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index a61dcd3..e1abd44 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -8,5 +8,6 @@ RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ - 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' && \ + 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' \ + 'pkgconfig(playerctl)' && \ dnf clean all -y diff --git a/Dockerfiles/gentoo b/Dockerfiles/gentoo index 536ef63..2b68398 100644 --- a/Dockerfiles/gentoo +++ b/Dockerfiles/gentoo @@ -8,4 +8,4 @@ RUN export FEATURES="-ipc-sandbox -network-sandbox -pid-sandbox -sandbox -usersa emerge --verbose --update --deep --with-bdeps=y --backtrack=30 --newuse @world && \ USE="wayland gtk3 gtk -doc X" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols =dev-cpp/gtkmm-3.24.6 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/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl diff --git a/Dockerfiles/opensuse b/Dockerfiles/opensuse index 49dea27..bdb42fb 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 + 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 From 24d03d13cebe998bee510c570f9e73a0c593a5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20G=C3=BCnzler?= Date: Sat, 7 Jan 2023 01:42:57 +0100 Subject: [PATCH 027/217] mpris: fix build errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit to address https://github.com/Alexays/Waybar/pull/1520#issuecomment-1374229080 Signed-off-by: Robert Günzler --- include/modules/mpris/mpris.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index 4f8ddb1..040401f 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "gtkmm/box.h" From 2045aac5b05fab8cf620fbc72c3e79b5d1ecb2ff Mon Sep 17 00:00:00 2001 From: Julian Schuler <31921487+julianschuler@users.noreply.github.com> Date: Sun, 8 Jan 2023 18:49:24 +0100 Subject: [PATCH 028/217] Fix crash upon reconnecting monitor --- src/modules/wlr/workspace_manager.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 6f11e1f..aa80b54 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -9,6 +9,7 @@ #include #include +#include "client.hpp" #include "gtkmm/widget.h" #include "modules/wlr/workspace_manager_binding.hpp" @@ -166,8 +167,20 @@ WorkspaceManager::~WorkspaceManager() { return; } - zext_workspace_manager_v1_destroy(workspace_manager_); - workspace_manager_ = nullptr; + wl_display *display = Client::inst()->wl_display; + + // Send `stop` request and wait for one roundtrip. This is not quite correct as + // the protocol encourages us to wait for the .finished event, but it should work + // with wlroots workspace manager implementation. + zext_workspace_manager_v1_stop(workspace_manager_); + wl_display_roundtrip(display); + + // If the .finished handler is still not executed, destroy the workspace manager here. + if (workspace_manager_) { + spdlog::warn("Foreign toplevel manager destroyed before .finished event"); + zext_workspace_manager_v1_destroy(workspace_manager_); + workspace_manager_ = nullptr; + } } auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void { From d6bd44002772148a8436d2c6b188b6b1860fd5cf Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 9 Jan 2023 15:48:31 +0100 Subject: [PATCH 029/217] fix: lint --- src/modules/wlr/workspace_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index aa80b54..ade0269 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -169,7 +169,7 @@ WorkspaceManager::~WorkspaceManager() { wl_display *display = Client::inst()->wl_display; - // Send `stop` request and wait for one roundtrip. This is not quite correct as + // Send `stop` request and wait for one roundtrip. This is not quite correct as // the protocol encourages us to wait for the .finished event, but it should work // with wlroots workspace manager implementation. zext_workspace_manager_v1_stop(workspace_manager_); From f0bead34d4f3a36cb9c94d60152d22dc6a773d60 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 11 Jan 2023 11:39:30 +0100 Subject: [PATCH 030/217] chore: 0.9.17 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 5b3c475..ebf68d4 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.16', + version: '0.9.17', license: 'MIT', meson_version: '>= 0.49.0', default_options : [ From df0fdce92b34406262ee522ad3910cefcc6ffd9e Mon Sep 17 00:00:00 2001 From: "Victor \"multun\" Collod" Date: Wed, 11 Jan 2023 23:02:09 +0100 Subject: [PATCH 031/217] get_desktop_app_info: fix crash on failed DesktopAppInfo::create Even though it makes little sense for this call to fail, it sometimes randomly does, and takes down waybar with it. --- src/modules/wlr/taskbar.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 97d84bd..5460244 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -102,8 +102,11 @@ Glib::RefPtr get_desktop_app_info(const std::string &app_id desktop_file = desktop_list[0][i]; } else { auto tmp_info = Gio::DesktopAppInfo::create(desktop_list[0][i]); - auto startup_class = tmp_info->get_startup_wm_class(); + if (!tmp_info) + // see https://github.com/Alexays/Waybar/issues/1446 + continue; + auto startup_class = tmp_info->get_startup_wm_class(); if (startup_class == app_id) { desktop_file = desktop_list[0][i]; break; From 328573332f7f48c0c2befd3a5d35ba4371c69d27 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 17 Apr 2022 07:13:35 +0200 Subject: [PATCH 032/217] sway-window, Issue 1399: new style classes Provides CSS classes empty, floating, tabbed, tiled, solo, stacked and app_id. Adds offscreen-css bool option (default false), only effective when "all-outputs" is true. This adds styles on outputs without focused node, according to its focused workspaces window situation. Adds an "offscreen-css-text" string option (default empty), only effective when "all-outputs" and "offscreen-style" are set. This is shown as a text on outputs without a focused node. Adds a "show-focused-workspace" bool option (default false) to indicate the workspace name if the whole workspace is focused when nodes are also present. If not set, empty text is shown, but css classes according to nodes in the workspace are still applied. Limitation: When the top level layout changes, there is no sway event so the module cannot react. Perhaps in the future recurring polling can be added to go around this limitation. --- include/modules/sway/window.hpp | 7 +- man/waybar-sway-window.5.scd | 27 +++- src/modules/sway/window.cpp | 238 +++++++++++++++++++++++--------- 3 files changed, 199 insertions(+), 73 deletions(-) diff --git a/include/modules/sway/window.hpp b/include/modules/sway/window.hpp index c13d5ce..e99e94f 100644 --- a/include/modules/sway/window.hpp +++ b/include/modules/sway/window.hpp @@ -19,10 +19,11 @@ class Window : public AIconLabel, public sigc::trackable { auto update() -> void; private: + void setClass(std::string classname, bool enable); void onEvent(const struct Ipc::ipc_response&); void onCmd(const struct Ipc::ipc_response&); - std::tuple getFocusedNode( - const Json::Value& nodes, std::string& output); + std::tuple + getFocusedNode(const Json::Value& nodes, std::string& output); void getTree(); void updateAppIconName(); void updateAppIcon(); @@ -32,12 +33,14 @@ class Window : public AIconLabel, public sigc::trackable { int windowId_; std::string app_id_; std::string app_class_; + std::string layout_; 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_; Ipc ipc_; diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 6e5ebdb..2ad1a2b 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -66,6 +66,25 @@ Addressed by *sway/window* default: true ++ Option to disable tooltip on hover. +*all-outputs*: ++ + typeof: bool ++ + default: false ++ + Option to show the focused window along with its workspace styles on all outputs. + +*offscreen-css*: ++ + typeof: bool ++ + default: false ++ + Only effective when all-outputs is true. Adds style according to present windows on unfocused outputs instead of showing the focused window and style. + +*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. + +*show-focused-workspace-name*: ++ + 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 window title. See *rewrite rules*. @@ -117,6 +136,10 @@ Invalid expressions (e.g., mismatched parentheses) are skipped. # STYLE - *#window* -- *window#waybar.empty* When no windows is in the workspace -- *window#waybar.solo* When one window is in the workspace +- *window#waybar.empty* When no windows are in the workspace, or screen is not focused and offscreen-text 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 +- *window#waybar.stacked* When there is more than one window in the workspace and the workspace layout is stacked +- *window#waybar.tabbed* When there is more than one window in the workspace and the workspace layout is tabbed +- *window#waybar.tiled* When there is more than one window in the workspace and the workspace layout is splith or splitv - *window#waybar.* Where *app_id* is the app_id or *instance* name like (*chromium*) of the only window in the workspace diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 5da7d3d..0e74b76 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -17,7 +17,7 @@ namespace waybar::modules::sway { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : AIconLabel(config, "window", id, "{title}", 0, true), bar_(bar), windowId_(-1) { + : AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) { // Icon size if (config_["icon-size"].isUInt()) { app_icon_size_ = config["icon-size"].asUInt(); @@ -35,6 +35,7 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) ipc_.handleEvent(); } catch (const std::exception& e) { spdlog::error("Window: {}", e.what()); + spdlog::trace("Window::Window exception"); } }); } @@ -46,12 +47,13 @@ void Window::onCmd(const struct Ipc::ipc_response& res) { std::lock_guard lock(mutex_); auto payload = parser_.parse(res.payload); auto output = payload["output"].isString() ? payload["output"].asString() : ""; - std::tie(app_nb_, windowId_, window_, app_id_, app_class_, shell_) = + std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) = getFocusedNode(payload["nodes"], output); updateAppIconName(); dp.emit(); } catch (const std::exception& e) { spdlog::error("Window: {}", e.what()); + spdlog::trace("Window::onCmd exception"); } } @@ -156,27 +158,52 @@ void Window::updateAppIcon() { } auto Window::update() -> void { - if (!old_app_id_.empty()) { - bar_.window.get_style_context()->remove_class(old_app_id_); - } + spdlog::trace("workspace layout {}, tiled count {}, floating count {}", layout_, app_nb_, + floating_count_); + + int mode = 0; if (app_nb_ == 0) { - bar_.window.get_style_context()->remove_class("solo"); - if (!bar_.window.get_style_context()->has_class("empty")) { - bar_.window.get_style_context()->add_class("empty"); + if (floating_count_ == 0) { + mode += 1; + } else { + mode += 4; } } else if (app_nb_ == 1) { - bar_.window.get_style_context()->remove_class("empty"); - if (!bar_.window.get_style_context()->has_class("solo")) { - bar_.window.get_style_context()->add_class("solo"); + mode += 2; + } else { + if (layout_ == "tabbed") { + mode += 8; + } else if (layout_ == "stacked") { + mode += 16; + } else { + mode += 32; } if (!app_id_.empty() && !bar_.window.get_style_context()->has_class(app_id_)) { bar_.window.get_style_context()->add_class(app_id_); old_app_id_ = app_id_; } - } else { - bar_.window.get_style_context()->remove_class("solo"); - bar_.window.get_style_context()->remove_class("empty"); } + + if (!old_app_id_.empty() && ((mode & 2) == 0 || old_app_id_ != app_id_) && + bar_.window.get_style_context()->has_class(old_app_id_)) { + spdlog::trace("Removing app_id class: {}", old_app_id_); + bar_.window.get_style_context()->remove_class(old_app_id_); + old_app_id_ = ""; + } + + setClass("empty", ((mode & 1) > 0)); + setClass("solo", ((mode & 2) > 0)); + setClass("floating", ((mode & 4) > 0)); + setClass("tabbed", ((mode & 8) > 0)); + setClass("stacked", ((mode & 16) > 0)); + setClass("tiled", ((mode & 32) > 0)); + + if ((mode & 2) > 0 && !app_id_.empty() && !bar_.window.get_style_context()->has_class(app_id_)) { + spdlog::trace("Adding app_id class: {}", app_id_); + bar_.window.get_style_context()->add_class(app_id_); + old_app_id_ = app_id_; + } + label_.set_markup(fmt::format( format_, fmt::arg("title", waybar::util::rewriteTitle(window_, config_["rewrite"])), fmt::arg("app_id", app_id_), fmt::arg("shell", shell_))); @@ -190,71 +217,143 @@ auto Window::update() -> void { AIconLabel::update(); } -int leafNodesInWorkspace(const Json::Value& node) { +void Window::setClass(std::string classname, bool enable) { + if (enable) { + if (!bar_.window.get_style_context()->has_class(classname)) { + bar_.window.get_style_context()->add_class(classname); + } + } else { + bar_.window.get_style_context()->remove_class(classname); + } +} + +std::pair leafNodesInWorkspace(const Json::Value& node) { auto const& nodes = node["nodes"]; auto const& floating_nodes = node["floating_nodes"]; if (nodes.empty() && floating_nodes.empty()) { - if (node["type"] == "workspace") - return 0; - else - return 1; + if (node["type"].asString() == "workspace") + return {0, 0}; + else if (node["type"].asString() == "floating_con") { + return {0, 1}; + } else { + return {1, 0}; + } } int sum = 0; - if (!nodes.empty()) { - for (auto const& node : nodes) sum += leafNodesInWorkspace(node); - } - if (!floating_nodes.empty()) { - for (auto const& node : floating_nodes) sum += leafNodesInWorkspace(node); - } - return sum; -} - -std::tuple gfnWithWorkspace( - const Json::Value& nodes, std::string& output, const Json::Value& config_, const Bar& bar_, - Json::Value& parentWorkspace) { + int floating_sum = 0; for (auto const& node : nodes) { - if (node["output"].isString()) { - output = node["output"].asString(); - } - // found node - if (node["focused"].asBool() && (node["type"] == "con" || node["type"] == "floating_con")) { - if ((!config_["all-outputs"].asBool() && output == bar_.output->name) || - config_["all-outputs"].asBool()) { - 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() : ""; - - int nb = node.size(); - if (parentWorkspace != 0) nb = leafNodesInWorkspace(parentWorkspace); - return {nb, node["id"].asInt(), Glib::Markup::escape_text(node["name"].asString()), - app_id, app_class, shell}; - } - } - // iterate - if (node["type"] == "workspace") parentWorkspace = node; - auto [nb, id, name, app_id, app_class, shell] = - gfnWithWorkspace(node["nodes"], output, config_, bar_, parentWorkspace); - if (id > -1 && !name.empty()) { - return {nb, id, name, app_id, app_class, shell}; - } - // Search for floating node - std::tie(nb, id, name, app_id, app_class, shell) = - gfnWithWorkspace(node["floating_nodes"], output, config_, bar_, parentWorkspace); - if (id > -1 && !name.empty()) { - return {nb, id, name, app_id, app_class, shell}; - } + std::pair all_leaf_nodes = leafNodesInWorkspace(node); + sum += all_leaf_nodes.first; + floating_sum += all_leaf_nodes.second; } - return {0, -1, "", "", "", ""}; + for (auto const& node : floating_nodes) { + std::pair all_leaf_nodes = leafNodesInWorkspace(node); + sum += all_leaf_nodes.first; + floating_sum += all_leaf_nodes.second; + } + return {sum, floating_sum}; } -std::tuple +std::tuple +gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Value& config_, + const Bar& bar_, Json::Value& parentWorkspace, + const Json::Value& immediateParent) { + for (auto const& node : nodes) { + if (node["type"].asString() == "output") { + if ((!config_["all-outputs"].asBool() || config_["offscreen-css"].asBool()) && + (node["name"].asString() != bar_.output->name)) { + continue; + } + output = node["name"].asString(); + } else if (node["type"].asString() == "workspace") { + // needs to be a string comparison, because filterWorkspace is the current_workspace + if (node["name"].asString() != immediateParent["current_workspace"].asString()) { + continue; + } + if (node["focused"].asBool()) { + std::pair all_leaf_nodes = leafNodesInWorkspace(node); + return {all_leaf_nodes.first, + all_leaf_nodes.second, + node["id"].asInt(), + (((all_leaf_nodes.first > 0) || (all_leaf_nodes.second > 0)) && + (config_["show-focused-workspace-name"].asBool())) + ? node["name"].asString() + : "", + "", + "", + "", + node["layout"].asString()}; + } + parentWorkspace = node; + } else if ((node["type"].asString() == "con" || node["type"].asString() == "floating_con") && + (node["focused"].asBool())) { + // 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() : ""; + int nb = node.size(); + int floating_count = 0; + std::string workspace_layout = ""; + if (!parentWorkspace.isNull()) { + std::pair all_leaf_nodes = leafNodesInWorkspace(parentWorkspace); + nb = all_leaf_nodes.first; + floating_count = all_leaf_nodes.second; + workspace_layout = parentWorkspace["layout"].asString(); + } + return {nb, + floating_count, + node["id"].asInt(), + Glib::Markup::escape_text(node["name"].asString()), + app_id, + app_class, + shell, + workspace_layout}; + } + + // iterate + auto [nb, f, id, name, app_id, app_class, shell, workspace_layout] = + gfnWithWorkspace(node["nodes"], output, config_, bar_, parentWorkspace, node); + auto [nb2, f2, id2, name2, app_id2, app_class2, shell2, workspace_layout2] = + gfnWithWorkspace(node["floating_nodes"], output, config_, bar_, parentWorkspace, node); + + // if ((id > 0 || ((id2 < 0 || name2.empty()) && id > -1)) && !name.empty()) { + if ((id > 0) || (id2 < 0 && id > -1)) { + return {nb, f, id, name, app_id, app_class, shell, workspace_layout}; + } else if (id2 > 0 && !name2.empty()) { + return {nb2, f2, id2, name2, app_id2, app_class, shell2, workspace_layout2}; + } + } + + // this only comes into effect when no focused children are present + if (config_["all-outputs"].asBool() && config_["offscreen-css"].asBool() && + immediateParent["type"].asString() == "workspace") { + std::pair all_leaf_nodes = leafNodesInWorkspace(immediateParent); + // using an empty string as default ensures that no window depending styles are set due to the + // checks above for !name.empty() + 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() + : "", + "", + "", + "", + immediateParent["layout"].asString()}; + } + + return {0, 0, -1, "", "", "", "", ""}; +} + +std::tuple Window::getFocusedNode(const Json::Value& nodes, std::string& output) { - Json::Value placeholder = 0; - return gfnWithWorkspace(nodes, output, config_, bar_, placeholder); + Json::Value placeholder = Json::Value::null; + return gfnWithWorkspace(nodes, output, config_, bar_, placeholder, placeholder); } void Window::getTree() { @@ -262,6 +361,7 @@ void Window::getTree() { ipc_.sendCmd(IPC_GET_TREE); } catch (const std::exception& e) { spdlog::error("Window: {}", e.what()); + spdlog::trace("Window::getTree exception"); } } From 120cba0f5e897d85cf634400442db5e083408fd2 Mon Sep 17 00:00:00 2001 From: Cyril LEVIS Date: Fri, 13 Jan 2023 09:29:49 +0100 Subject: [PATCH 033/217] fix: battery time remaining time is reported in second and should be divided by 3600 and not 1000. --- 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 97ff0a5..b3e51a6 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -505,12 +505,12 @@ const std::tuple waybar::modules::Battery::g float time_remaining{0.0f}; if (status == "Discharging" && time_to_empty_now_exists) { - if (time_to_empty_now != 0) time_remaining = (float)time_to_empty_now / 1000.0f; + if (time_to_empty_now != 0) time_remaining = (float)time_to_empty_now / 3600.0f; } else if (status == "Discharging" && total_power_exists && total_energy_exists) { if (total_power != 0) time_remaining = (float)total_energy / total_power; } else if (status == "Charging" && time_to_full_now_exists) { if (time_to_full_now_exists && (time_to_full_now != 0)) - time_remaining = -(float)time_to_full_now / 1000.0f; + time_remaining = -(float)time_to_full_now / 3600.0f; // If we've turned positive it means the battery is past 100% and so just report that as no // time remaining if (time_remaining > 0.0f) time_remaining = 0.0f; From 544c6deb885b9bcf27442b6869085740c32232a1 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Fri, 13 Jan 2023 15:08:59 +0100 Subject: [PATCH 034/217] sway/window: fix manpage --- man/waybar-sway-window.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 2ad1a2b..19e0cd2 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -136,7 +136,7 @@ Invalid expressions (e.g., mismatched parentheses) are skipped. # STYLE - *#window* -- *window#waybar.empty* When no windows are in the workspace, or screen is not focused and offscreen-text option is not set +- *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 - *window#waybar.stacked* When there is more than one window in the workspace and the workspace layout is stacked From a4b1b0a211ac3f601cdaafb1a560319246cd14b0 Mon Sep 17 00:00:00 2001 From: PolpOnline Date: Fri, 13 Jan 2023 22:39:59 +0100 Subject: [PATCH 035/217] modules/custom: Added percentage rounding --- src/modules/custom.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 23dba38..39d93d4 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -209,8 +209,8 @@ void waybar::modules::Custom::parseOutputJson() { class_.push_back(c.asString()); } } - if (!parsed["percentage"].asString().empty() && parsed["percentage"].isUInt()) { - percentage_ = parsed["percentage"].asUInt(); + if (!parsed["percentage"].asString().empty() && parsed["percentage"].isNumeric()) { + percentage_ = (int) lround(parsed["percentage"].asFloat()); } else { percentage_ = 0; } From 5649c3f552d9c788532ef0589a3bbaf52509f958 Mon Sep 17 00:00:00 2001 From: shironeko Date: Sun, 15 Jan 2023 12:16:30 -0500 Subject: [PATCH 036/217] river/tags: refactor to support special purpose tags adds the set-tags and toggle-tags setting so it's possible to have different tags set vs toggled. This enables the use of e.g. sticky tags Also clean-up the code a bit. --- src/modules/river/tags.cpp | 89 +++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index ccafc16..baa6b7e 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -53,7 +53,7 @@ static const zriver_command_callback_v1_listener command_callback_listener_impl{ static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (std::strcmp(interface, zriver_status_manager_v1_interface.name) == 0) { - version = std::min(version, 2); + version = std::min(version, 2u); if (version < ZRIVER_OUTPUT_STATUS_V1_URGENT_TAGS_SINCE_VERSION) { spdlog::warn("river server does not support urgent tags"); } @@ -62,13 +62,13 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam } if (std::strcmp(interface, zriver_control_v1_interface.name) == 0) { - version = std::min(version, 1); + version = std::min(version, 1u); static_cast(data)->control_ = static_cast( wl_registry_bind(registry, name, &zriver_control_v1_interface, version)); } if (std::strcmp(interface, wl_seat_interface.name) == 0) { - version = std::min(version, 1); + version = std::min(version, 1u); static_cast(data)->seat_ = static_cast( wl_registry_bind(registry, name, &wl_seat_interface, version)); } @@ -114,33 +114,39 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con event_box_.add(box_); // Default to 9 tags, cap at 32 - const uint32_t num_tags = - config["num-tags"].isUInt() ? std::min(32, config_["num-tags"].asUInt()) : 9; + const int num_tags = + config["num-tags"].isUInt() ? std::min(32, config_["num-tags"].asUInt()) : 9; - std::vector tag_labels(num_tags); - for (uint32_t tag = 0; tag < num_tags; ++tag) { - tag_labels[tag] = std::to_string(tag + 1); - } - const Json::Value custom_labels = config["tag-labels"]; - if (custom_labels.isArray() && !custom_labels.empty()) { - for (uint32_t tag = 0; tag < std::min(num_tags, custom_labels.size()); ++tag) { - tag_labels[tag] = custom_labels[tag].asString(); + const auto tag_labels = config["tag-labels"]; + const auto set_tags = config["set-tags"]; + const auto toggle_tags = config["toggle-tags"]; + for (int tag = 0; tag < num_tags; ++tag) { + if (tag_labels.isArray() && !tag_labels.empty()) { + buttons_.emplace_back(tag_labels[tag].asString()); + } else { + // default name is the tag value + buttons_.emplace_back(std::to_string(tag + 1)); } - } - uint32_t i = 1; - for (const auto &tag_label : tag_labels) { - Gtk::Button &button = buttons_.emplace_back(tag_label); + auto &button = buttons_[tag]; button.set_relief(Gtk::RELIEF_NONE); box_.pack_start(button, false, false, 0); + if (!config_["disable-click"].asBool()) { - button.signal_clicked().connect( - sigc::bind(sigc::mem_fun(*this, &Tags::handle_primary_clicked), i)); - button.signal_button_press_event().connect( - sigc::bind(sigc::mem_fun(*this, &Tags::handle_button_press), i)); + if (set_tags.isArray() && !set_tags.empty()) + button.signal_clicked().connect(sigc::bind( + sigc::mem_fun(*this, &Tags::handle_primary_clicked), set_tags[tag].asUInt())); + else + button.signal_clicked().connect( + sigc::bind(sigc::mem_fun(*this, &Tags::handle_primary_clicked), (1 << tag))); + if (toggle_tags.isArray() && !toggle_tags.empty()) + button.signal_button_press_event().connect(sigc::bind( + sigc::mem_fun(*this, &Tags::handle_button_press), toggle_tags[tag].asUInt())); + else + button.signal_button_press_event().connect( + sigc::bind(sigc::mem_fun(*this, &Tags::handle_button_press), (1 << tag))); } button.show(); - i <<= 1; } struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); @@ -182,45 +188,38 @@ bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) { } void Tags::handle_focused_tags(uint32_t tags) { - uint32_t i = 0; - for (auto &button : buttons_) { + for (size_t i = 0; i < buttons_.size(); ++i) { if ((1 << i) & tags) { - button.get_style_context()->add_class("focused"); + buttons_[i].get_style_context()->add_class("focused"); } else { - button.get_style_context()->remove_class("focused"); + buttons_[i].get_style_context()->remove_class("focused"); } - ++i; } } void Tags::handle_view_tags(struct wl_array *view_tags) { - // First clear all occupied state - for (auto &button : buttons_) { - button.get_style_context()->remove_class("occupied"); + uint32_t tags = 0; + auto view_tag = reinterpret_cast(view_tags->data); + auto end = view_tag + (view_tags->size / sizeof(uint32_t)); + for (; view_tag < end; ++view_tag) { + tags |= *view_tag; } - - // Set tags with a view to occupied - uint32_t *start = static_cast(view_tags->data); - for (uint32_t *tags = start; tags < start + view_tags->size / sizeof(uint32_t); ++tags) { - uint32_t i = 0; - for (auto &button : buttons_) { - if (*tags & (1 << i)) { - button.get_style_context()->add_class("occupied"); - } - ++i; + for (size_t i = 0; i < buttons_.size(); ++i) { + if ((1 << i) & tags) { + buttons_[i].get_style_context()->add_class("occupied"); + } else { + buttons_[i].get_style_context()->remove_class("occupied"); } } } void Tags::handle_urgent_tags(uint32_t tags) { - uint32_t i = 0; - for (auto &button : buttons_) { + for (size_t i = 0; i < buttons_.size(); ++i) { if ((1 << i) & tags) { - button.get_style_context()->add_class("urgent"); + buttons_[i].get_style_context()->add_class("urgent"); } else { - button.get_style_context()->remove_class("urgent"); + buttons_[i].get_style_context()->remove_class("urgent"); } - ++i; } } From 4e8ccf36b54cacf5281726d23ea14312a133f977 Mon Sep 17 00:00:00 2001 From: Sasha Moak Date: Thu, 12 Jan 2023 16:17:11 -0800 Subject: [PATCH 037/217] fix(wireplumber): waybar crashes when default node changes In order to fix the issue, the default node name is cached rather than the default node id. This is due to ids being unstable. So now when the object manager is installed (ie ready), the default node name is retrieved and stored for later. Now when the mixer changed signal is emitted, the id of the changed node is used to get the node from the object manager. The nodes name is grabbed off that node and compared against the default node name, if they match the volume is updated. Some safeguarding has been added such that if the node cannot be found off the object manager, it's ignored. Additionally, the "changed" signal on the default nodes api is now utilized to update the default node name if it has changed. This way if the default node changes, the module will be updated with the correct volume and node.nick. This adds additional debug logging for helping diagnose wireplumber issues. This also adds the wireplumber man page entry to the main waybar supported section. --- include/modules/wireplumber.hpp | 10 +- man/waybar.5.scd.in | 1 + src/modules/wireplumber.cpp | 184 ++++++++++++++++++++++++++------ 3 files changed, 157 insertions(+), 38 deletions(-) diff --git a/include/modules/wireplumber.hpp b/include/modules/wireplumber.hpp index c0ee7f0..fa988fc 100644 --- a/include/modules/wireplumber.hpp +++ b/include/modules/wireplumber.hpp @@ -20,15 +20,19 @@ class Wireplumber : public ALabel { void loadRequiredApiModules(); void prepare(); void activatePlugins(); - static void updateVolume(waybar::modules::Wireplumber* self); - static void updateNodeName(waybar::modules::Wireplumber* self); - static uint32_t getDefaultNodeId(waybar::modules::Wireplumber* self); + 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 onObjectManagerInstalled(waybar::modules::Wireplumber* self); + static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id); + static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self); WpCore* wp_core_; GPtrArray* apis_; WpObjectManager* om_; + WpPlugin* mixer_api_; + WpPlugin* def_nodes_api_; + gchar* default_node_name_; uint32_t pending_plugins_; bool muted_; double volume_; diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index b1ed4c5..7566dd0 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -277,6 +277,7 @@ A module group is defined by specifying a module named "group/some-group-name". - *waybar-sway-scratchpad(5)* - *waybar-sway-window(5)* - *waybar-sway-workspaces(5)* +- *waybar-wireplumber(5)* - *waybar-wlr-taskbar(5)* - *waybar-wlr-workspaces(5)* - *waybar-temperature(5)* diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index 9a12a9b..9652e1e 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -1,15 +1,22 @@ #include "modules/wireplumber.hpp" +#include + +bool isValidNodeId(uint32_t id) { return id > 0 && id < G_MAXUINT32; } + waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Value& config) : ALabel(config, "wireplumber", id, "{volume}%"), wp_core_(nullptr), apis_(nullptr), om_(nullptr), + mixer_api_(nullptr), + def_nodes_api_(nullptr), + default_node_name_(nullptr), pending_plugins_(0), muted_(false), volume_(0.0), node_id_(0) { - wp_init(WP_INIT_ALL); + wp_init(WP_INIT_PIPEWIRE); wp_core_ = wp_core_new(NULL, NULL); apis_ = g_ptr_array_new_with_free_func(g_object_unref); om_ = wp_object_manager_new(); @@ -18,10 +25,15 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val loadRequiredApiModules(); + spdlog::debug("[{}]: connecting to pipewire...", this->name_); + if (!wp_core_connect(wp_core_)) { + spdlog::error("[{}]: Could not connect to PipeWire", this->name_); throw std::runtime_error("Could not connect to PipeWire\n"); } + spdlog::debug("[{}]: connected!", this->name_); + g_signal_connect_swapped(om_, "installed", (GCallback)onObjectManagerInstalled, this); activatePlugins(); @@ -33,33 +45,26 @@ waybar::modules::Wireplumber::~Wireplumber() { g_clear_pointer(&apis_, g_ptr_array_unref); g_clear_object(&om_); g_clear_object(&wp_core_); + g_clear_object(&mixer_api_); + g_clear_object(&def_nodes_api_); + g_free(&default_node_name_); } -uint32_t waybar::modules::Wireplumber::getDefaultNodeId(waybar::modules::Wireplumber* self) { - uint32_t id; - g_autoptr(WpPlugin) def_nodes_api = wp_plugin_find(self->wp_core_, "default-nodes-api"); +void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* self, uint32_t id) { + spdlog::debug("[{}]: updating node name with node.id {}", self->name_, id); - if (!def_nodes_api) { - throw std::runtime_error("Default nodes API is not loaded\n"); + if (!isValidNodeId(id)) { + spdlog::warn("[{}]: '{}' is not a valid node ID. Ignoring node name update.", self->name_, id); + return; } - g_signal_emit_by_name(def_nodes_api, "get-default-node", "Audio/Sink", &id); - - if (id <= 0 || id >= G_MAXUINT32) { - auto err = fmt::format("'{}' is not a valid ID (returned by default-nodes-api)\n", id); - throw std::runtime_error(err); - } - - return id; -} - -void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* self) { - auto proxy = static_cast( - wp_object_manager_lookup(self->om_, WP_TYPE_GLOBAL_PROXY, WP_CONSTRAINT_TYPE_G_PROPERTY, - "bound-id", "=u", self->node_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, NULL)); if (!proxy) { - throw std::runtime_error(fmt::format("Object '{}' not found\n", self->node_id_)); + auto err = fmt::format("Object '{}' not found\n", id); + spdlog::error("[{}]: {}", self->name_, err); + throw std::runtime_error(err); } g_autoptr(WpProperties) properties = @@ -73,15 +78,24 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* auto description = wp_properties_get(properties, "node.description"); self->node_name_ = nick ? nick : description; + spdlog::debug("[{}]: Updating node name to: {}", self->name_, self->node_name_); } -void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self) { +void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self, uint32_t id) { + spdlog::debug("[{}]: updating volume", self->name_); double vol; GVariant* variant = NULL; - g_autoptr(WpPlugin) mixer_api = wp_plugin_find(self->wp_core_, "mixer-api"); - g_signal_emit_by_name(mixer_api, "get-volume", self->node_id_, &variant); + + if (!isValidNodeId(id)) { + spdlog::error("[{}]: '{}' is not a valid node ID. Ignoring volume update.", self->name_, id); + return; + } + + g_signal_emit_by_name(self->mixer_api_, "get-volume", id, &variant); + if (!variant) { - auto err = fmt::format("Node {} does not support volume\n", self->node_id_); + auto err = fmt::format("Node {} does not support volume\n", id); + spdlog::error("[{}]: {}", self->name_, err); throw std::runtime_error(err); } @@ -93,22 +107,121 @@ void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* se self->dp.emit(); } +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, NULL)); + + if (!node) { + spdlog::warn("[{}]: (onMixerChanged) - Object with id {} not found", self->name_, id); + return; + } + + const gchar* name = wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(node), "node.name"); + + if (g_strcmp0(self->default_node_name_, name) != 0) { + spdlog::debug( + "[{}]: (onMixerChanged) - ignoring mixer update for node: id: {}, name: {} as it is not " + "the default node: {}", + self->name_, id, name, self->default_node_name_); + return; + } + + spdlog::debug("[{}]: (onMixerChanged) - Need to update volume for node with id {} and name {}", + self->name_, id, name); + updateVolume(self, id); +} + +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); + + if (!isValidNodeId(default_node_id)) { + spdlog::warn("[{}]: '{}' is not a valid node ID. Ignoring node change.", self->name_, + default_node_id); + 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)); + + if (!node) { + spdlog::warn("[{}]: (onDefaultNodesApiChanged) - Object with id {} not found", self->name_, + default_node_id); + return; + } + + const gchar* default_node_name = + 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); + + if (g_strcmp0(self->default_node_name_, default_node_name) == 0) { + spdlog::debug( + "[{}]: (onDefaultNodesApiChanged) - Default node has not changed. Node(name: {}, id: {}). " + "Ignoring.", + self->name_, self->default_node_name_, default_node_id); + return; + } + + spdlog::debug( + "[{}]: (onDefaultNodesApiChanged) - Default node changed to -> Node(name: {}, id: {})", + self->name_, default_node_name, default_node_id); + + self->default_node_name_ = g_strdup(default_node_name); + updateVolume(self, default_node_id); + updateNodeName(self, default_node_id); +} + void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wireplumber* self) { - self->node_id_ = - self->config_["node-id"].isInt() ? self->config_["node-id"].asInt() : getDefaultNodeId(self); + spdlog::debug("[{}]: onObjectManagerInstalled", self->name_); - g_autoptr(WpPlugin) mixer_api = wp_plugin_find(self->wp_core_, "mixer-api"); + self->def_nodes_api_ = wp_plugin_find(self->wp_core_, "default-nodes-api"); - updateVolume(self); - updateNodeName(self); - g_signal_connect_swapped(mixer_api, "changed", (GCallback)updateVolume, self); + if (!self->def_nodes_api_) { + 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_) { + spdlog::error("[{}]: mixer api is not loaded.", self->name_); + throw std::runtime_error("Mixer api is not loaded\n"); + } + + uint32_t default_node_id; + g_signal_emit_by_name(self->def_nodes_api_, "get-default-configured-node-name", "Audio/Sink", + &self->default_node_name_); + g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &default_node_id); + + if (self->default_node_name_) { + spdlog::debug("[{}]: (onObjectManagerInstalled) - default configured node name: {} and id: {}", + self->name_, self->default_node_name_, default_node_id); + } + + updateVolume(self, default_node_id); + updateNodeName(self, default_node_id); + + g_signal_connect_swapped(self->mixer_api_, "changed", (GCallback)onMixerChanged, self); + g_signal_connect_swapped(self->def_nodes_api_, "changed", (GCallback)onDefaultNodesApiChanged, + self); } 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; if (!wp_object_activate_finish(p, res, &error)) { + spdlog::error("[{}]: error activating plugin: {}", self->name_, error->message); throw std::runtime_error(error->message); } @@ -118,6 +231,7 @@ void waybar::modules::Wireplumber::onPluginActivated(WpObject* p, GAsyncResult* } void waybar::modules::Wireplumber::activatePlugins() { + spdlog::debug("[{}]: activating plugins", name_); for (uint16_t i = 0; i < apis_->len; i++) { WpPlugin* plugin = static_cast(g_ptr_array_index(apis_, i)); pending_plugins_++; @@ -127,13 +241,13 @@ void waybar::modules::Wireplumber::activatePlugins() { } void waybar::modules::Wireplumber::prepare() { - wp_object_manager_add_interest(om_, WP_TYPE_NODE, NULL); - wp_object_manager_add_interest(om_, WP_TYPE_GLOBAL_PROXY, NULL); - wp_object_manager_request_object_features(om_, WP_TYPE_GLOBAL_PROXY, - WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL); + 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); } void waybar::modules::Wireplumber::loadRequiredApiModules() { + spdlog::debug("[{}]: loading required modules", name_); g_autoptr(GError) error = NULL; if (!wp_core_load_component(wp_core_, "libwireplumber-module-default-nodes-api", "module", NULL, From 6e9f21fc6bc4b57e53b3a5bbcd517097e1acd01f Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Fri, 20 Jan 2023 23:40:08 +0100 Subject: [PATCH 038/217] hyprland/submap: run initial render on startup --- src/modules/hyprland/submap.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 6eb0942..d61c8d4 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -19,6 +19,7 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) // register for hyprland ipc gIPC->registerForIPC("submap", this); + dp.emit(); } Submap::~Submap() { From ca9d237b00b4d01f341b0d7bc938afb10a4f8cad Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 16 Jan 2023 10:27:21 -0800 Subject: [PATCH 039/217] fix(sway): add missing includes for GCC 13 See also: https://gcc.gnu.org/gcc-13/porting_to.html --- include/modules/sway/ipc/client.hpp | 1 + src/modules/sway/ipc/client.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/modules/sway/ipc/client.hpp b/include/modules/sway/ipc/client.hpp index 77dab08..a6705ea 100644 --- a/include/modules/sway/ipc/client.hpp +++ b/include/modules/sway/ipc/client.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "ipc.hpp" #include "util/sleeper_thread.hpp" diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index 4d6495c..5c3df7b 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -2,6 +2,8 @@ #include +#include + namespace waybar::modules::sway { Ipc::Ipc() { From 43d52c59d99e7de50a28362780cc9068eb2fecc8 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 16 Jan 2023 11:04:56 -0800 Subject: [PATCH 040/217] test: fix build with Catch2 v2.x Use smaller includes for Catch2 v3. --- meson.build | 2 +- test/SafeSignal.cpp | 6 +++++- test/config.cpp | 6 +++++- test/main.cpp | 15 +++++++++++++-- test/waybar_time.cpp | 7 ++++++- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index ebf68d4..7b63181 100644 --- a/meson.build +++ b/meson.build @@ -447,7 +447,7 @@ endif catch2 = dependency( 'catch2', - version: '>=3.0.0', + version: '>=2.0.0', fallback: ['catch2', 'catch2_dep'], required: get_option('tests'), ) diff --git a/test/SafeSignal.cpp b/test/SafeSignal.cpp index 7ff6f2a..f496d7a 100644 --- a/test/SafeSignal.cpp +++ b/test/SafeSignal.cpp @@ -2,7 +2,11 @@ #include -#include +#if __has_include() +#include +#else +#include +#endif #include #include diff --git a/test/config.cpp b/test/config.cpp index cdc96b0..3d0f007 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -1,6 +1,10 @@ #include "config.hpp" -#include +#if __has_include() +#include +#else +#include +#endif TEST_CASE("Load simple config", "[config]") { waybar::Config conf; diff --git a/test/main.cpp b/test/main.cpp index 7970c26..daeee69 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -3,8 +3,13 @@ #include #include +#if __has_include() #include #include +#else +#include +#include +#endif #include int main(int argc, char* argv[]) { @@ -13,10 +18,16 @@ int main(int argc, char* argv[]) { session.applyCommandLine(argc, argv); const auto logger = spdlog::default_logger(); +#if CATCH_VERSION_MAJOR >= 3 for (const auto& spec : session.config().getReporterSpecs()) { - if (spec.name() == "tap") { + const auto& reporter_name = spec.name(); +#else + { + const auto& reporter_name = session.config().getReporterName(); +#endif + if (reporter_name == "tap") { spdlog::set_pattern("# [%l] %v"); - } else if (spec.name() == "compact") { + } else if (reporter_name == "compact") { logger->sinks().clear(); } else { logger->sinks().assign({std::make_shared()}); diff --git a/test/waybar_time.cpp b/test/waybar_time.cpp index 79469d4..9f9f5dc 100644 --- a/test/waybar_time.cpp +++ b/test/waybar_time.cpp @@ -3,7 +3,12 @@ #include #include -#include +#if __has_include() +#include +#include +#else +#include +#endif #include #include From ba498869c5a2a35c1b389c7067926be1c4617828 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 18 Jan 2023 06:17:55 -0800 Subject: [PATCH 041/217] fix(clock): delete outdated warning --- src/modules/clock.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 55f2c5b..9871024 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -41,12 +41,6 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) time_zones_.push_back(nullptr); } - if (!is_timezone_fixed()) { - spdlog::warn( - "As using a timezone, some format args may be missing as the date library haven't got a " - "release since 2018."); - } - // Check if a particular placeholder is present in the tooltip format, to know what to calculate // on update. if (config_["tooltip-format"].isString()) { From 67efe1af892cff2cc37f9aeb0fd7420b7d4eb621 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 16 Jan 2023 16:48:30 -0800 Subject: [PATCH 042/217] refactor(clock): remove struct waybar_time The structure was used to pass the locale instance to the date formatter. All the supported versions of `fmt` are passing the locale parameter via `FormatContext.locale()` so we can remove the struct and simplify the code. While we at it, drop `date::make_zoned` in favor of CTAD on a `date::zoned_time` constructor. --- include/modules/clock.hpp | 11 +-- include/util/{waybar_time.hpp => date.hpp} | 21 ++--- src/modules/clock.cpp | 27 +++--- test/date.cpp | 94 +++++++++++++++++++++ test/meson.build | 2 +- test/waybar_time.cpp | 95 ---------------------- 6 files changed, 117 insertions(+), 133 deletions(-) rename include/util/{waybar_time.hpp => date.hpp} (51%) create mode 100644 test/date.cpp delete mode 100644 test/waybar_time.cpp diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index ef129fb..c97565d 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -5,11 +5,7 @@ #include "ALabel.hpp" #include "util/sleeper_thread.hpp" -namespace waybar { - -struct waybar_time; - -namespace modules { +namespace waybar::modules { const std::string kCalendarPlaceholder = "calendar"; const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list"; @@ -36,12 +32,11 @@ class Clock : public ALabel { std::string fmt_str_weeks_; std::string fmt_str_calendar_; int fmt_weeks_left_pad_{0}; - auto calendar_text(const waybar_time& wtime) -> std::string; + auto calendar_text(const date::zoned_seconds& ztime) -> std::string; auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void; 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; }; -} // namespace modules -} // namespace waybar +} // namespace waybar::modules diff --git a/include/util/waybar_time.hpp b/include/util/date.hpp similarity index 51% rename from include/util/waybar_time.hpp rename to include/util/date.hpp index b9f9ea9..ec948bd 100644 --- a/include/util/waybar_time.hpp +++ b/include/util/date.hpp @@ -3,17 +3,8 @@ #include #include -namespace waybar { - -struct waybar_time { - std::locale locale; - date::zoned_seconds ztime; -}; - -} // namespace waybar - -template <> -struct fmt::formatter { +template +struct fmt::formatter> { std::string_view specs; template @@ -33,7 +24,11 @@ struct fmt::formatter { } template - auto format(const waybar::waybar_time& t, FormatContext& ctx) { - return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(specs), t.ztime)); + auto format(const date::zoned_time& ztime, FormatContext& ctx) { + if (ctx.locale()) { + const auto loc = ctx.locale().template get(); + return fmt::format_to(ctx.out(), "{}", date::format(loc, fmt::to_string(specs), ztime)); + } + return fmt::format_to(ctx.out(), "{}", date::format(fmt::to_string(specs), ztime)); } }; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 9871024..360b746 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -9,15 +9,13 @@ #include #include +#include "util/date.hpp" #include "util/ustring_clen.hpp" -#include "util/waybar_time.hpp" #ifdef HAVE_LANGINFO_1STDAY #include #include #endif -using waybar::waybar_time; - 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), @@ -110,15 +108,13 @@ bool waybar::modules::Clock::is_timezone_fixed() { } auto waybar::modules::Clock::update() -> void { - auto time_zone = current_timezone(); + const auto* time_zone = current_timezone(); auto now = std::chrono::system_clock::now(); - waybar_time wtime = {locale_, - date::make_zoned(time_zone, date::floor(now))}; + auto ztime = date::zoned_time{time_zone, date::floor(now)}; auto shifted_date = date::year_month_day{date::floor(now)} + calendar_shift_; auto now_shifted = date::sys_days{shifted_date} + (now - date::floor(now)); - waybar_time shifted_wtime = { - locale_, date::make_zoned(time_zone, date::floor(now_shifted))}; + auto shifted_ztime = date::zoned_time{time_zone, date::floor(now_shifted)}; std::string text = ""; if (!is_timezone_fixed()) { @@ -127,7 +123,7 @@ auto waybar::modules::Clock::update() -> void { auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); text = fmt::format(locale_, format_, localtime); } else { - text = fmt::format(format_, wtime); + text = fmt::format(locale_, format_, ztime); } label_.set_markup(text); @@ -136,13 +132,13 @@ auto waybar::modules::Clock::update() -> void { std::string calendar_lines{""}; std::string timezoned_time_lines{""}; if (is_calendar_in_tooltip_) { - calendar_lines = calendar_text(shifted_wtime); + calendar_lines = calendar_text(shifted_ztime); } if (is_timezoned_list_in_tooltip_) { timezoned_time_lines = timezones_text(&now); } auto tooltip_format = config_["tooltip-format"].asString(); - text = fmt::format(tooltip_format, shifted_wtime, + text = fmt::format(locale_, tooltip_format, shifted_ztime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines), fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines)); label_.set_tooltip_markup(text); @@ -190,8 +186,8 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) { return true; } -auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string { - const auto daypoint = date::floor(wtime.ztime.get_local_time()); +auto waybar::modules::Clock::calendar_text(const date::zoned_seconds& ztime) -> std::string { + const auto daypoint = date::floor(ztime.get_local_time()); const auto ymd{date::year_month_day{daypoint}}; if (calendar_cached_ymd_ == ymd) { @@ -318,7 +314,6 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin return ""; } std::stringstream os; - waybar_time wtime; 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; @@ -327,8 +322,8 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin if (!timezone) { timezone = date::current_zone(); } - wtime = {locale_, date::make_zoned(timezone, date::floor(*now))}; - os << fmt::format(format_, wtime) << '\n'; + auto ztime = date::zoned_time{timezone, date::floor(*now)}; + os << fmt::format(locale_, format_, ztime) << '\n'; } return os.str(); } diff --git a/test/date.cpp b/test/date.cpp new file mode 100644 index 0000000..704feb2 --- /dev/null +++ b/test/date.cpp @@ -0,0 +1,94 @@ +#include "util/date.hpp" + +#if __has_include() +#include +#include +#else +#include +#endif +#include +#include + +using namespace std::literals::chrono_literals; + +/* + * 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}; + +TEST_CASE("Format UTC time", "[clock][util]") { + const auto loc = std::locale("C"); + const auto tm = TEST_TIME; + + REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified + REQUIRE(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC"); + REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + + /* Test a few locales that are most likely to be present */ + SECTION("US locale") { + try { + const auto loc = std::locale("en_US"); + + REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM")); + REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 01:04:05 PM"); + REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } + SECTION("GB locale") { + try { + const auto loc = std::locale("en_GB"); + + REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05")); + REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 13:04:05"); + REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } +} + +TEST_CASE("Format zoned time", "[clock][util]") { + const auto loc = std::locale("C"); + const auto tm = date::zoned_time{"America/New_York", TEST_TIME}; + + REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified + REQUIRE(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST"); + REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + + /* Test a few locales that are most likely to be present */ + SECTION("US locale") { + try { + const auto loc = std::locale("en_US"); + + REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM")); + REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 08:04:05 AM"); + REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } + + SECTION("GB locale") { + try { + const auto loc = std::locale("en_GB"); + + REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05")); + REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 08:04:05"); + REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } +} diff --git a/test/meson.build b/test/meson.build index b1e1123..02cbb2a 100644 --- a/test/meson.build +++ b/test/meson.build @@ -15,7 +15,7 @@ test_src = files( if tz_dep.found() test_dep += tz_dep - test_src += files('waybar_time.cpp') + test_src += files('date.cpp') endif waybar_test = executable( diff --git a/test/waybar_time.cpp b/test/waybar_time.cpp deleted file mode 100644 index 9f9f5dc..0000000 --- a/test/waybar_time.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "util/waybar_time.hpp" - -#include -#include - -#if __has_include() -#include -#include -#else -#include -#endif -#include -#include - -using namespace std::literals::chrono_literals; - -/* - * Check that the date/time formatter with locale and timezone support is working as expected. - */ - -const date::zoned_time TEST_TIME = date::make_zoned( - "UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s); - -TEST_CASE("Format UTC time", "[clock][util]") { - waybar::waybar_time tm{std::locale("C"), TEST_TIME}; - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); - - /* Test a few locales that are most likely to be present */ - SECTION("US locale") { - try { - tm.locale = std::locale("en_US"); - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM")); - REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); - } catch (const std::runtime_error&) { - // locale not found; ignore - } - } - SECTION("GB locale") { - try { - tm.locale = std::locale("en_GB"); - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05")); - REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 13:04:05"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); - } catch (const std::runtime_error&) { - // locale not found; ignore - } - } -} - -TEST_CASE("Format zoned time", "[clock][util]") { - waybar::waybar_time tm{std::locale("C"), date::make_zoned("America/New_York", TEST_TIME)}; - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); - - /* Test a few locales that are most likely to be present */ - SECTION("US locale") { - try { - tm.locale = std::locale("en_US"); - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM")); - REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); - } catch (const std::runtime_error&) { - // locale not found; ignore - } - } - - SECTION("GB locale") { - try { - tm.locale = std::locale("en_GB"); - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05")); - REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 08:04:05"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); - } catch (const std::runtime_error&) { - // locale not found; ignore - } - } -} From ea17a66dfc46e2349416d1fa6ff89fe901e95e62 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 16 Jan 2023 13:24:55 -0800 Subject: [PATCH 043/217] fix: compilation errors with cpp_std=c++20 There were two main issues with fmtlib and C++20 mode: - `fmt::format` defaults to compile-time argument checking and requires using `fmt::runtime(format_string)` to bypass that. - `std::format` implementation introduces conflicting declarations and we have to specify the namespace for all `format`/`format_to` calls. --- include/modules/sway/workspaces.hpp | 5 ++++- include/util/format.hpp | 8 ++++---- src/modules/backlight.cpp | 9 +++++---- src/modules/battery.cpp | 11 ++++++----- src/modules/bluetooth.cpp | 7 ++++--- src/modules/clock.cpp | 23 ++++++++++++----------- src/modules/custom.cpp | 2 +- src/modules/disk.cpp | 20 ++++++++++---------- src/modules/gamemode.cpp | 8 ++++---- src/modules/hyprland/language.cpp | 8 ++++---- src/modules/hyprland/submap.cpp | 2 +- src/modules/hyprland/window.cpp | 4 ++-- src/modules/idle_inhibitor.cpp | 18 ++++++------------ src/modules/inhibitor.cpp | 2 +- src/modules/jack.cpp | 8 ++++---- src/modules/keyboard_state.cpp | 2 +- src/modules/memory/common.cpp | 7 ++++--- src/modules/mpd/mpd.cpp | 18 +++++++++--------- src/modules/mpris/mpris.cpp | 8 ++++---- src/modules/network.cpp | 6 +++--- src/modules/pulseaudio.cpp | 6 +++--- src/modules/river/mode.cpp | 2 +- src/modules/river/window.cpp | 2 +- src/modules/sndio.cpp | 3 ++- src/modules/sway/language.cpp | 4 ++-- src/modules/sway/mode.cpp | 2 +- src/modules/sway/scratchpad.cpp | 5 +++-- src/modules/sway/window.cpp | 7 ++++--- src/modules/sway/workspaces.cpp | 10 ++++------ src/modules/temperature.cpp | 8 ++++---- src/modules/upower/upower.cpp | 4 ++-- src/modules/user.cpp | 20 ++++++++++---------- src/modules/wireplumber.cpp | 8 ++++---- src/modules/wlr/taskbar.cpp | 21 ++++++++++++--------- src/modules/wlr/workspace_manager.cpp | 2 +- 35 files changed, 143 insertions(+), 137 deletions(-) diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index e6df067..f8a55fa 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "AModule.hpp" @@ -21,7 +22,9 @@ class Workspaces : public AModule, public sigc::trackable { auto update() -> void; private: - static inline const std::string workspace_switch_cmd_ = "workspace {} \"{}\""; + static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\""; + static constexpr std::string_view persistent_workspace_switch_cmd_ = + R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")"; static int convertWorkspaceNameToNum(std::string name); diff --git a/include/util/format.hpp b/include/util/format.hpp index fac0377..00b6a31 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -66,9 +66,9 @@ struct formatter { std::string string; switch (spec) { case '>': - return format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width); + return fmt::format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width); case '<': - return format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width); + return fmt::format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width); case '=': format = "{coefficient:<{number_width}.1f}{padding}{prefix}{unit}"; break; @@ -77,8 +77,8 @@ struct formatter { format = "{coefficient:.1f}{prefix}{unit}"; break; } - return format_to( - ctx.out(), format, fmt::arg("coefficient", fraction), + return fmt::format_to( + ctx.out(), fmt::runtime(format), fmt::arg("coefficient", fraction), fmt::arg("number_width", number_width), fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : "")), fmt::arg("unit", s.unit_), diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index aa734a4..77c1dc0 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -48,13 +48,13 @@ struct UdevMonitorDeleter { void check_eq(int rc, int expected, const char *message = "eq, rc was: ") { if (rc != expected) { - throw std::runtime_error(fmt::format(message, rc)); + 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(message, rc)); + throw std::runtime_error(fmt::format(fmt::runtime(message), rc)); } } @@ -62,7 +62,7 @@ void check0(int rc, const char *message = "rc wasn't 0") { check_eq(rc, 0, messa void check_gte(int rc, int gte, const char *message = "rc was: ") { if (rc < gte) { - throw std::runtime_error(fmt::format(message, rc)); + throw std::runtime_error(fmt::format(fmt::runtime(message), rc)); } } @@ -181,7 +181,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()); - label_.set_markup(fmt::format(format_, fmt::arg("percent", std::to_string(percent)), + label_.set_markup(fmt::format(fmt::runtime(format_), + fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent)))); getState(percent); } else { diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index b3e51a6..abd1240 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -604,7 +604,7 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai format = config_["format-time"].asString(); } std::string zero_pad_minutes = fmt::format("{:02d}", minutes); - return fmt::format(format, fmt::arg("H", full_hours), fmt::arg("M", minutes), + return fmt::format(fmt::runtime(format), fmt::arg("H", full_hours), fmt::arg("M", minutes), fmt::arg("m", zero_pad_minutes)); } @@ -644,7 +644,8 @@ auto waybar::modules::Battery::update() -> void { } else if (config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } - label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("timeTo", tooltip_text_default), + 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))); } @@ -665,9 +666,9 @@ auto waybar::modules::Battery::update() -> void { } else { event_box_.show(); auto icons = std::vector{status + "-" + state, status, state}; - label_.set_markup(fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), - fmt::arg("time", time_remaining_formatted))); + 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))); } // Call parent update ALabel::update(); diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index e6a1fe3..c3a2547 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -206,7 +206,8 @@ auto waybar::modules::Bluetooth::update() -> void { state_ = state; label_.set_markup(fmt::format( - format_, fmt::arg("status", state_), fmt::arg("num_connections", connected_devices_.size()), + 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), @@ -234,7 +235,7 @@ auto waybar::modules::Bluetooth::update() -> void { enumerate_format = config_["tooltip-format-enumerate-connected"].asString(); } ss << fmt::format( - enumerate_format, fmt::arg("device_address", dev.address), + fmt::runtime(enumerate_format), fmt::arg("device_address", dev.address), fmt::arg("device_address_type", dev.address_type), fmt::arg("device_alias", dev.alias), fmt::arg("icon", enumerate_icon), fmt::arg("device_battery_percentage", dev.battery_percentage.value_or(0))); @@ -247,7 +248,7 @@ auto waybar::modules::Bluetooth::update() -> void { } } label_.set_tooltip_text(fmt::format( - tooltip_format, fmt::arg("status", state_), + 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), diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 360b746..0dbd255 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -121,9 +121,9 @@ auto waybar::modules::Clock::update() -> void { // 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_, format_, localtime); + text = fmt::format(locale_, fmt::runtime(format_), localtime); } else { - text = fmt::format(locale_, format_, ztime); + text = fmt::format(locale_, fmt::runtime(format_), ztime); } label_.set_markup(text); @@ -138,7 +138,7 @@ auto waybar::modules::Clock::update() -> void { timezoned_time_lines = timezones_text(&now); } auto tooltip_format = config_["tooltip-format"].asString(); - text = fmt::format(locale_, tooltip_format, shifted_ztime, + 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); @@ -228,7 +228,7 @@ auto waybar::modules::Clock::calendar_text(const date::zoned_seconds& ztime) -> /* Print weeknumber on the left for the first row*/ if (weeks_pos == WeeksSide::LEFT) { - os << fmt::format(fmt_str_weeks_, print_wd) << ' '; + os << fmt::format(fmt::runtime(fmt_str_weeks_), print_wd) << ' '; } if (empty_days > 0) { @@ -242,7 +242,7 @@ auto waybar::modules::Clock::calendar_text(const date::zoned_seconds& ztime) -> os << ' '; } else if (unsigned(d) != 1) { if (weeks_pos == WeeksSide::RIGHT) { - os << ' ' << fmt::format(fmt_str_weeks_, print_wd); + os << ' ' << fmt::format(fmt::runtime(fmt_str_weeks_), print_wd); } os << '\n'; @@ -250,19 +250,19 @@ auto waybar::modules::Clock::calendar_text(const date::zoned_seconds& ztime) -> print_wd = (ym / d); if (weeks_pos == WeeksSide::LEFT) { - os << fmt::format(fmt_str_weeks_, print_wd) << ' '; + os << fmt::format(fmt::runtime(fmt_str_weeks_), print_wd) << ' '; } } if (d == curr_day) { if (config_["today-format"].isString()) { auto today_format = config_["today-format"].asString(); - os << fmt::format(today_format, date::format("%e", d)); + os << fmt::format(fmt::runtime(today_format), date::format("%e", d)); } else { os << "" << date::format("%e", d) << ""; } } else { - os << fmt::format(fmt_str_calendar_, date::format("%e", d)); + os << fmt::format(fmt::runtime(fmt_str_calendar_), date::format("%e", d)); } /*Print weeks on the right when the endings with spaces*/ if (weeks_pos == WeeksSide::RIGHT && d == last_day) { @@ -271,7 +271,7 @@ auto waybar::modules::Clock::calendar_text(const date::zoned_seconds& ztime) -> os << std::string(empty_days * 3, ' '); } - os << ' ' << fmt::format(fmt_str_weeks_, print_wd); + os << ' ' << fmt::format(fmt::runtime(fmt_str_weeks_), print_wd); } } @@ -303,7 +303,8 @@ auto waybar::modules::Clock::weekdays_header(const date::weekday& first_week_day res << '\n'; if (config_["format-calendar-weekdays"].isString()) { - os << fmt::format(config_["format-calendar-weekdays"].asString(), res.str()); + auto weekdays_format = config_["format-calendar-weekdays"].asString(); + os << fmt::format(fmt::runtime(weekdays_format), res.str()); } else os << res.str(); } @@ -323,7 +324,7 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin timezone = date::current_zone(); } auto ztime = date::zoned_time{timezone, date::floor(*now)}; - os << fmt::format(locale_, format_, ztime) << '\n'; + os << fmt::format(locale_, fmt::runtime(format_), ztime) << '\n'; } return os.str(); } diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 23dba38..3100adc 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -126,7 +126,7 @@ auto waybar::modules::Custom::update() -> void { } else { parseOutputRaw(); } - auto str = fmt::format(format_, text_, fmt::arg("alt", alt_), + 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()) { diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index 5578dc2..eb4d902 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -58,11 +58,11 @@ auto waybar::modules::Disk::update() -> void { event_box_.hide(); } else { event_box_.show(); - label_.set_markup( - fmt::format(format, stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), - fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), - fmt::arg("used", used), fmt::arg("percentage_used", percentage_used), - fmt::arg("total", total), fmt::arg("path", path_))); + label_.set_markup(fmt::format( + fmt::runtime(format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), + fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used), + fmt::arg("percentage_used", percentage_used), fmt::arg("total", total), + fmt::arg("path", path_))); } if (tooltipEnabled()) { @@ -70,11 +70,11 @@ auto waybar::modules::Disk::update() -> void { if (config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } - label_.set_tooltip_text( - fmt::format(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_))); + label_.set_tooltip_text(fmt::format( + 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_))); } // Call parent update ALabel::update(); diff --git a/src/modules/gamemode.cpp b/src/modules/gamemode.cpp index 7129297..1b8d7fc 100644 --- a/src/modules/gamemode.cpp +++ b/src/modules/gamemode.cpp @@ -213,14 +213,14 @@ auto Gamemode::update() -> void { // Tooltip if (tooltip) { - std::string text = fmt::format(tooltip_format, fmt::arg("count", gameCount)); + std::string text = fmt::format(fmt::runtime(tooltip_format), fmt::arg("count", gameCount)); box_.set_tooltip_text(text); } // Label format - std::string str = - fmt::format(showAltText ? format_alt : format, fmt::arg("glyph", useIcon ? "" : glyph), - fmt::arg("count", gameCount > 0 ? std::to_string(gameCount) : "")); + std::string str = fmt::format(fmt::runtime(showAltText ? format_alt : format), + fmt::arg("glyph", useIcon ? "" : glyph), + fmt::arg("count", gameCount > 0 ? std::to_string(gameCount) : "")); label_.set_markup(str); if (useIcon) { diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 6cadd62..d398b23 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -59,9 +59,9 @@ void Language::onEvent(const std::string& ev) { if (config_.isMember("format-" + briefName)) { const auto propName = "format-" + briefName; - layoutName = fmt::format(format_, config_[propName].asString()); + layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); } else { - layoutName = fmt::format(format_, layoutName); + layoutName = fmt::format(fmt::runtime(format_), layoutName); } layoutName = waybar::util::sanitize_string(layoutName); @@ -92,9 +92,9 @@ void Language::initLanguage() { if (config_.isMember("format-" + briefName)) { const auto propName = "format-" + briefName; - layoutName = fmt::format(format_, config_[propName].asString()); + layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); } else { - layoutName = fmt::format(format_, searcher); + layoutName = fmt::format(fmt::runtime(format_), searcher); } layoutName = waybar::util::sanitize_string(layoutName); diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index d61c8d4..22acbf3 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -34,7 +34,7 @@ auto Submap::update() -> void { if (submap_.empty()) { event_box_.hide(); } else { - label_.set_markup(fmt::format(format_, submap_)); + label_.set_markup(fmt::format(fmt::runtime(format_), submap_)); if (tooltipEnabled()) { label_.set_tooltip_text(submap_); } diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index d3d06cc..47daae9 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -40,8 +40,8 @@ auto Window::update() -> void { if (!format_.empty()) { label_.show(); - label_.set_markup( - fmt::format(format_, waybar::util::rewriteTitle(lastView, config_["rewrite"]))); + label_.set_markup(fmt::format(fmt::runtime(format_), + waybar::util::rewriteTitle(lastView, config_["rewrite"]))); } else { label_.hide(); } diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index c4109b0..a5fc9ac 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -63,21 +63,15 @@ auto waybar::modules::IdleInhibitor::update() -> void { } std::string status_text = status ? "activated" : "deactivated"; - label_.set_markup(fmt::format(format_, fmt::arg("status", status_text), + label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("status", status_text), fmt::arg("icon", getIcon(0, status_text)))); label_.get_style_context()->add_class(status_text); if (tooltipEnabled()) { - label_.set_tooltip_markup( - status ? fmt::format(config_["tooltip-format-activated"].isString() - ? config_["tooltip-format-activated"].asString() - : "{status}", - fmt::arg("status", status_text), - fmt::arg("icon", getIcon(0, status_text))) - : fmt::format(config_["tooltip-format-deactivated"].isString() - ? config_["tooltip-format-deactivated"].asString() - : "{status}", - fmt::arg("status", status_text), - fmt::arg("icon", getIcon(0, status_text)))); + auto config = config_[status ? "tooltip-format-activated" : "tooltip-format-deactivated"]; + auto tooltip_format = config.isString() ? config.asString() : "{status}"; + label_.set_tooltip_markup(fmt::format(fmt::runtime(tooltip_format), + fmt::arg("status", status_text), + fmt::arg("icon", getIcon(0, status_text)))); } // Call parent update ALabel::update(); diff --git a/src/modules/inhibitor.cpp b/src/modules/inhibitor.cpp index e4340b1..fe2a4be 100644 --- a/src/modules/inhibitor.cpp +++ b/src/modules/inhibitor.cpp @@ -118,7 +118,7 @@ auto Inhibitor::update() -> void { std::string status_text = activated() ? "activated" : "deactivated"; label_.get_style_context()->remove_class(activated() ? "deactivated" : "activated"); - label_.set_markup(fmt::format(format_, fmt::arg("status", status_text), + label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("status", status_text), fmt::arg("icon", getIcon(0, status_text)))); label_.get_style_context()->add_class(status_text); diff --git a/src/modules/jack.cpp b/src/modules/jack.cpp index 3a92110..9bd6fcd 100644 --- a/src/modules/jack.cpp +++ b/src/modules/jack.cpp @@ -72,7 +72,7 @@ auto JACK::update() -> void { } else format = "{load}%"; - label_.set_markup(fmt::format(format, fmt::arg("load", std::round(load_)), + label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("load", std::round(load_)), fmt::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_), fmt::arg("latency", fmt::format("{:.2f}", latency)), fmt::arg("xruns", xruns_))); @@ -81,9 +81,9 @@ auto JACK::update() -> void { std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms"; if (config_["tooltip-format"].isString()) tooltip_format = config_["tooltip-format"].asString(); label_.set_tooltip_text(fmt::format( - tooltip_format, fmt::arg("load", std::round(load_)), fmt::arg("bufsize", bufsize_), - fmt::arg("samplerate", samplerate_), fmt::arg("latency", fmt::format("{:.2f}", latency)), - fmt::arg("xruns", xruns_))); + fmt::runtime(tooltip_format), fmt::arg("load", std::round(load_)), + fmt::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_), + fmt::arg("latency", fmt::format("{:.2f}", latency)), fmt::arg("xruns", xruns_))); } // Call parent update diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index b2750b6..4c081d6 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -278,7 +278,7 @@ auto waybar::modules::KeyboardState::update() -> void { }; for (auto& label_state : label_states) { std::string text; - text = fmt::format(label_state.format, + text = fmt::format(fmt::runtime(label_state.format), fmt::arg("icon", label_state.state ? icon_locked_ : icon_unlocked_), fmt::arg("name", label_state.name)); label_state.label.set_markup(text); diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index 4a0e634..544d781 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -56,7 +56,8 @@ auto waybar::modules::Memory::update() -> void { event_box_.show(); auto icons = std::vector{state}; label_.set_markup(fmt::format( - format, used_ram_percentage, fmt::arg("icon", getIcon(used_ram_percentage, icons)), + fmt::runtime(format), used_ram_percentage, + 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("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes), @@ -68,8 +69,8 @@ auto waybar::modules::Memory::update() -> void { if (config_["tooltip-format"].isString()) { auto tooltip_format = config_["tooltip-format"].asString(); label_.set_tooltip_text(fmt::format( - tooltip_format, used_ram_percentage, fmt::arg("total", total_ram_gigabytes), - fmt::arg("swapTotal", total_swap_gigabytes), + 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("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes), fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes), diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 401b759..e728897 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -174,14 +174,14 @@ void waybar::modules::MPD::setLabel() { try { auto text = fmt::format( - format, fmt::arg("artist", artist.raw()), fmt::arg("albumArtist", album_artist.raw()), - fmt::arg("album", album.raw()), fmt::arg("title", title.raw()), fmt::arg("date", date), - fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime), - fmt::arg("totalTime", totalTime), fmt::arg("songPosition", song_pos), - fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon), - fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon), - fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon), - fmt::arg("filename", filename)); + fmt::runtime(format), fmt::arg("artist", artist.raw()), + fmt::arg("albumArtist", album_artist.raw()), fmt::arg("album", album.raw()), + fmt::arg("title", title.raw()), fmt::arg("date", date), fmt::arg("volume", volume), + fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), + fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length), + fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), + fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon), + fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename)); if (text.empty()) { label_.hide(); } else { @@ -198,7 +198,7 @@ void waybar::modules::MPD::setLabel() { : "MPD (connected)"; try { auto tooltip_text = - fmt::format(tooltip_format, fmt::arg("artist", artist.raw()), + fmt::format(fmt::runtime(tooltip_format), fmt::arg("artist", artist.raw()), fmt::arg("albumArtist", album_artist.raw()), fmt::arg("album", album.raw()), fmt::arg("title", title.raw()), fmt::arg("date", date), fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime), diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 651dfd5..f11821f 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -378,10 +378,10 @@ auto Mpris::update() -> void { break; } auto label_format = - fmt::format(formatstr, fmt::arg("player", info.name), fmt::arg("status", info.status_string), - fmt::arg("artist", *info.artist), fmt::arg("title", *info.title), - fmt::arg("album", *info.album), fmt::arg("length", *info.length), - fmt::arg("dynamic", dynamic.str()), + fmt::format(fmt::runtime(formatstr), fmt::arg("player", info.name), + fmt::arg("status", info.status_string), fmt::arg("artist", *info.artist), + fmt::arg("title", *info.title), fmt::arg("album", *info.album), + fmt::arg("length", *info.length), fmt::arg("dynamic", dynamic.str()), fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); label_.set_markup(label_format); diff --git a/src/modules/network.cpp b/src/modules/network.cpp index a4797ee..8409311 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -331,7 +331,7 @@ auto waybar::modules::Network::update() -> void { getState(signal_strength_); auto text = fmt::format( - format_, fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), + fmt::runtime(format_), fmt::arg("essid", essid_), 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_), @@ -363,8 +363,8 @@ auto waybar::modules::Network::update() -> void { } if (!tooltip_format.empty()) { auto tooltip_text = fmt::format( - tooltip_format, fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), - fmt::arg("signalStrength", signal_strength_), + fmt::runtime(tooltip_format), fmt::arg("essid", essid_), + 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_)), diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index c797997..0630710 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -294,9 +294,9 @@ auto waybar::modules::Pulseaudio::update() -> void { format_source = config_["format-source"].asString(); } } - format_source = fmt::format(format_source, fmt::arg("volume", source_volume_)); + format_source = fmt::format(fmt::runtime(format_source), fmt::arg("volume", source_volume_)); auto text = fmt::format( - format, fmt::arg("desc", desc_), fmt::arg("volume", volume_), + 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()))); if (text.empty()) { @@ -313,7 +313,7 @@ auto waybar::modules::Pulseaudio::update() -> void { } if (!tooltip_format.empty()) { label_.set_tooltip_text(fmt::format( - tooltip_format, fmt::arg("desc", desc_), fmt::arg("volume", volume_), + 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())))); diff --git a/src/modules/river/mode.cpp b/src/modules/river/mode.cpp index 4a51c83..1f788e0 100644 --- a/src/modules/river/mode.cpp +++ b/src/modules/river/mode.cpp @@ -103,7 +103,7 @@ void Mode::handle_mode(const char *mode) { } label_.get_style_context()->add_class(mode); - label_.set_markup(fmt::format(format_, Glib::Markup::escape_text(mode).raw())); + label_.set_markup(fmt::format(fmt::runtime(format_), Glib::Markup::escape_text(mode).raw())); label_.show(); } diff --git a/src/modules/river/window.cpp b/src/modules/river/window.cpp index d0f492f..d93938c 100644 --- a/src/modules/river/window.cpp +++ b/src/modules/river/window.cpp @@ -106,7 +106,7 @@ void Window::handle_focused_view(const char *title) { label_.hide(); // hide empty labels or labels with empty format } else { label_.show(); - label_.set_markup(fmt::format(format_, Glib::Markup::escape_text(title).raw())); + label_.set_markup(fmt::format(fmt::runtime(format_), Glib::Markup::escape_text(title).raw())); } ALabel::update(); diff --git a/src/modules/sndio.cpp b/src/modules/sndio.cpp index e6f1bd0..72e7207 100644 --- a/src/modules/sndio.cpp +++ b/src/modules/sndio.cpp @@ -110,7 +110,8 @@ auto Sndio::update() -> void { label_.get_style_context()->remove_class("muted"); } - auto text = fmt::format(format, fmt::arg("volume", vol), fmt::arg("raw_value", volume_)); + auto text = + fmt::format(fmt::runtime(format), fmt::arg("volume", vol), fmt::arg("raw_value", volume_)); if (text.empty()) { label_.hide(); } else { diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index d3730a1..a5860bd 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -96,14 +96,14 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { auto Language::update() -> void { std::lock_guard lock(mutex_); auto display_layout = trim(fmt::format( - format_, fmt::arg("short", layout_.short_name), + fmt::runtime(format_), fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), fmt::arg("variant", layout_.variant), fmt::arg("flag", layout_.country_flag()))); label_.set_markup(display_layout); if (tooltipEnabled()) { if (tooltip_format_ != "") { auto tooltip_display_layout = trim( - fmt::format(tooltip_format_, fmt::arg("short", layout_.short_name), + fmt::format(fmt::runtime(tooltip_format_), fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), fmt::arg("variant", layout_.variant), fmt::arg("flag", layout_.country_flag()))); diff --git a/src/modules/sway/mode.cpp b/src/modules/sway/mode.cpp index 7eaa523..b81735e 100644 --- a/src/modules/sway/mode.cpp +++ b/src/modules/sway/mode.cpp @@ -42,7 +42,7 @@ auto Mode::update() -> void { if (mode_.empty()) { event_box_.hide(); } else { - label_.set_markup(fmt::format(format_, mode_)); + label_.set_markup(fmt::format(fmt::runtime(format_), mode_)); if (tooltipEnabled()) { label_.set_tooltip_text(mode_); } diff --git a/src/modules/sway/scratchpad.cpp b/src/modules/sway/scratchpad.cpp index 59e3053..17dc270 100644 --- a/src/modules/sway/scratchpad.cpp +++ b/src/modules/sway/scratchpad.cpp @@ -32,7 +32,8 @@ auto Scratchpad::update() -> void { if (count_ || show_empty_) { event_box_.show(); label_.set_markup( - fmt::format(format_, fmt::arg("icon", getIcon(count_, "", config_["format-icons"].size())), + fmt::format(fmt::runtime(format_), + fmt::arg("icon", getIcon(count_, "", config_["format-icons"].size())), fmt::arg("count", count_))); if (tooltip_enabled_) { label_.set_tooltip_markup(tooltip_text_); @@ -64,7 +65,7 @@ auto Scratchpad::onCmd(const struct Ipc::ipc_response& res) -> void { if (tooltip_enabled_) { tooltip_text_.clear(); for (const auto& window : tree["nodes"][0]["nodes"][0]["floating_nodes"]) { - tooltip_text_.append(fmt::format(tooltip_format_ + '\n', + tooltip_text_.append(fmt::format(fmt::runtime(tooltip_format_ + '\n'), fmt::arg("app", window["app_id"].asString()), fmt::arg("title", window["name"].asString()))); } diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 0e74b76..7d60d29 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -204,9 +204,10 @@ auto Window::update() -> void { old_app_id_ = app_id_; } - label_.set_markup(fmt::format( - format_, fmt::arg("title", waybar::util::rewriteTitle(window_, config_["rewrite"])), - fmt::arg("app_id", app_id_), fmt::arg("shell", shell_))); + label_.set_markup( + fmt::format(fmt::runtime(format_), + fmt::arg("title", waybar::util::rewriteTitle(window_, config_["rewrite"])), + fmt::arg("app_id", app_id_), fmt::arg("shell", shell_))); if (tooltipEnabled()) { label_.set_tooltip_text(window_); } diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index b621b83..08742ae 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -233,7 +233,7 @@ auto Workspaces::update() -> void { std::string output = (*it)["name"].asString(); if (config_["format"].isString()) { auto format = config_["format"].asString(); - output = fmt::format(format, fmt::arg("icon", getIcon(output, *it)), + 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())); } @@ -259,11 +259,9 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { try { if (node["target_output"].isString()) { ipc_.sendCmd(IPC_COMMAND, - fmt::format(workspace_switch_cmd_ + "; move workspace to output \"{}\"; " + - workspace_switch_cmd_, - "--no-auto-back-and-forth", node["name"].asString(), - node["target_output"].asString(), "--no-auto-back-and-forth", - node["name"].asString())); + fmt::format(persistent_workspace_switch_cmd_, "--no-auto-back-and-forth", + node["name"].asString(), node["target_output"].asString(), + "--no-auto-back-and-forth", node["name"].asString())); } else { ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace {} \"{}\"", config_["disable-auto-back-and-forth"].asBool() diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index eca05a7..ff722d7 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -55,7 +55,7 @@ auto waybar::modules::Temperature::update() -> void { } auto max_temp = config_["critical-threshold"].isInt() ? config_["critical-threshold"].asInt() : 0; - label_.set_markup(fmt::format(format, fmt::arg("temperatureC", temperature_c), + label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("temperatureC", temperature_c), fmt::arg("temperatureF", temperature_f), fmt::arg("temperatureK", temperature_k), fmt::arg("icon", getIcon(temperature_c, "", max_temp)))); @@ -64,9 +64,9 @@ auto waybar::modules::Temperature::update() -> void { if (config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } - label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("temperatureC", temperature_c), - fmt::arg("temperatureF", temperature_f), - fmt::arg("temperatureK", temperature_k))); + label_.set_tooltip_text(fmt::format( + fmt::runtime(tooltip_format), fmt::arg("temperatureC", temperature_c), + fmt::arg("temperatureF", temperature_f), fmt::arg("temperatureK", temperature_k))); } // Call parent update ALabel::update(); diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index eb29913..38c1f7f 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -336,8 +336,8 @@ auto UPower::update() -> void { break; } std::string label_format = - fmt::format(showAltText ? format_alt : format, fmt::arg("percentage", percentString), - fmt::arg("time", time_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) { diff --git a/src/modules/user.cpp b/src/modules/user.cpp index 2f7c6e9..418fc58 100644 --- a/src/modules/user.cpp +++ b/src/modules/user.cpp @@ -127,16 +127,16 @@ auto User::update() -> void { auto startSystemTime = currentSystemTime - workSystemTimeSeconds; long workSystemDays = uptimeSeconds / 86400; - auto label = fmt::format(ALabel::format_, fmt::arg("up_H", fmt::format("{:%H}", startSystemTime)), - fmt::arg("up_M", fmt::format("{:%M}", startSystemTime)), - fmt::arg("up_d", fmt::format("{:%d}", startSystemTime)), - fmt::arg("up_m", fmt::format("{:%m}", startSystemTime)), - fmt::arg("up_Y", fmt::format("{:%Y}", startSystemTime)), - fmt::arg("work_d", workSystemDays), - fmt::arg("work_H", fmt::format("{:%H}", workSystemTimeSeconds)), - fmt::arg("work_M", fmt::format("{:%M}", workSystemTimeSeconds)), - fmt::arg("work_S", fmt::format("{:%S}", workSystemTimeSeconds)), - fmt::arg("user", systemUser)); + auto label = fmt::format( + fmt::runtime(ALabel::format_), fmt::arg("up_H", fmt::format("{:%H}", startSystemTime)), + fmt::arg("up_M", fmt::format("{:%M}", startSystemTime)), + fmt::arg("up_d", fmt::format("{:%d}", startSystemTime)), + fmt::arg("up_m", fmt::format("{:%m}", startSystemTime)), + fmt::arg("up_Y", fmt::format("{:%Y}", startSystemTime)), fmt::arg("work_d", workSystemDays), + fmt::arg("work_H", fmt::format("{:%H}", workSystemTimeSeconds)), + fmt::arg("work_M", fmt::format("{:%M}", workSystemTimeSeconds)), + fmt::arg("work_S", fmt::format("{:%S}", workSystemTimeSeconds)), + fmt::arg("user", systemUser)); ALabel::label_.set_markup(label); AIconLabel::update(); } diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index 9652e1e..fd1a0d3 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -279,7 +279,7 @@ auto waybar::modules::Wireplumber::update() -> void { label_.get_style_context()->remove_class("muted"); } - std::string markup = fmt::format(format, fmt::arg("node_name", node_name_), + std::string markup = fmt::format(fmt::runtime(format), fmt::arg("node_name", node_name_), fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_))); label_.set_markup(markup); @@ -291,9 +291,9 @@ auto waybar::modules::Wireplumber::update() -> void { } if (!tooltip_format.empty()) { - label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("node_name", node_name_), - fmt::arg("volume", volume_), - fmt::arg("icon", getIcon(volume_)))); + label_.set_tooltip_text( + fmt::format(fmt::runtime(tooltip_format), fmt::arg("node_name", node_name_), + fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_)))); } else { label_.set_tooltip_text(node_name_); } diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 5460244..427083b 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -618,9 +618,10 @@ void Task::update() { app_id = Glib::Markup::escape_text(app_id); } if (!format_before_.empty()) { - auto txt = fmt::format(format_before_, fmt::arg("title", title), fmt::arg("name", name), - fmt::arg("app_id", app_id), fmt::arg("state", state_string()), - fmt::arg("short_state", state_string(true))); + auto txt = + fmt::format(fmt::runtime(format_before_), fmt::arg("title", title), fmt::arg("name", name), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), + fmt::arg("short_state", state_string(true))); if (markup) text_before_.set_markup(txt); else @@ -628,9 +629,10 @@ void Task::update() { text_before_.show(); } if (!format_after_.empty()) { - auto txt = fmt::format(format_after_, fmt::arg("title", title), fmt::arg("name", name), - fmt::arg("app_id", app_id), fmt::arg("state", state_string()), - fmt::arg("short_state", state_string(true))); + auto txt = + fmt::format(fmt::runtime(format_after_), fmt::arg("title", title), fmt::arg("name", name), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), + fmt::arg("short_state", state_string(true))); if (markup) text_after_.set_markup(txt); else @@ -639,9 +641,10 @@ void Task::update() { } if (!format_tooltip_.empty()) { - auto txt = fmt::format(format_tooltip_, fmt::arg("title", title), fmt::arg("name", name), - fmt::arg("app_id", app_id), fmt::arg("state", state_string()), - fmt::arg("short_state", state_string(true))); + auto txt = + fmt::format(fmt::runtime(format_tooltip_), fmt::arg("title", title), fmt::arg("name", name), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), + fmt::arg("short_state", state_string(true))); if (markup) button_.set_tooltip_markup(txt); else diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index ade0269..c1b68c8 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -379,7 +379,7 @@ Workspace::~Workspace() { } auto Workspace::update() -> void { - label_.set_markup(fmt::format(format_, fmt::arg("name", name_), + label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("name", name_), fmt::arg("icon", with_icon_ ? get_icon() : ""))); } From 6225db0a4855badd0a3047b9aa13ec502d223fef Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 20 Jan 2023 22:46:16 -0800 Subject: [PATCH 044/217] test: refactor date formatter tests. - Add tests for global locale. - Warn about missing locales. - Downgrade REQUIRE to CHECK. - Skip tests if localized formatting does not work as expected. --- test/date.cpp | 150 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 109 insertions(+), 41 deletions(-) diff --git a/test/date.cpp b/test/date.cpp index 704feb2..aa6d79b 100644 --- a/test/date.cpp +++ b/test/date.cpp @@ -1,13 +1,23 @@ #include "util/date.hpp" +#include +#include +#include +#include +#include + #if __has_include() #include #include #else #include #endif -#include -#include + +#ifndef SKIP +#define SKIP(...) \ + WARN(__VA_ARGS__); \ + return +#endif using namespace std::literals::chrono_literals; @@ -18,39 +28,79 @@ using namespace std::literals::chrono_literals; const date::zoned_time TEST_TIME = date::zoned_time{ "UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s}; +/* + * Check if the date formatted with LC_TIME=en_US is within expectations. + * + * The check expects Glibc output style and will fail with FreeBSD (different implementation) + * or musl (no implementation). + */ +static const bool LC_TIME_is_sane = []() { + try { + std::stringstream ss; + ss.imbue(std::locale("en_US.UTF-8")); + + time_t t = 1641211200; + std::tm tm = *std::gmtime(&t); + + ss << std::put_time(&tm, "%x %X"); + return ss.str() == "01/03/2022 12:00:00 PM"; + } catch (std::exception &) { + return false; + } +}(); + TEST_CASE("Format UTC time", "[clock][util]") { const auto loc = std::locale("C"); const auto tm = TEST_TIME; - REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified - REQUIRE(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC"); - REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + 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 (!LC_TIME_is_sane) { + SKIP("Locale support check failed, skip tests"); + } /* Test a few locales that are most likely to be present */ SECTION("US locale") { try { - const auto loc = std::locale("en_US"); + const auto loc = std::locale("en_US.UTF-8"); - REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM")); - REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 01:04:05 PM"); - REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); - } catch (const std::runtime_error&) { - // locale not found; ignore + CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified + CHECK_THAT(fmt::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"); + } catch (const std::runtime_error &) { + WARN("Locale en_US not found, skip tests"); } } SECTION("GB locale") { try { - const auto loc = std::locale("en_GB"); + const auto loc = std::locale("en_GB.UTF-8"); - REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05")); - REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 13:04:05"); - REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); - } catch (const std::runtime_error&) { - // locale not found; ignore + CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified + CHECK_THAT(fmt::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"); + } catch (const std::runtime_error &) { + WARN("Locale en_GB not found, skip tests"); + } + } + SECTION("Global locale") { + 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 + 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"); + + std::locale::global(loc); + } catch (const std::runtime_error &) { + WARN("Locale en_US not found, skip tests"); } } } @@ -59,36 +109,54 @@ TEST_CASE("Format zoned time", "[clock][util]") { const auto loc = std::locale("C"); const auto tm = date::zoned_time{"America/New_York", TEST_TIME}; - REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified - REQUIRE(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST"); - REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + 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 (!LC_TIME_is_sane) { + SKIP("Locale support check failed, skip tests"); + } /* Test a few locales that are most likely to be present */ SECTION("US locale") { try { - const auto loc = std::locale("en_US"); + const auto loc = std::locale("en_US.UTF-8"); - REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM")); - REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 08:04:05 AM"); - REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); - } catch (const std::runtime_error&) { - // locale not found; ignore + CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified + CHECK_THAT(fmt::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"); + } catch (const std::runtime_error &) { + WARN("Locale en_US not found, skip tests"); } } - SECTION("GB locale") { try { - const auto loc = std::locale("en_GB"); + const auto loc = std::locale("en_GB.UTF-8"); - REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05")); - REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 08:04:05"); - REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); - } catch (const std::runtime_error&) { - // locale not found; ignore + CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified + CHECK_THAT(fmt::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"); + } catch (const std::runtime_error &) { + WARN("Locale en_GB not found, skip tests"); + } + } + SECTION("Global locale") { + 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 + 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"); + + std::locale::global(loc); + } catch (const std::runtime_error &) { + WARN("Locale en_US not found, skip tests"); } } } From 93e340a081bcf32ba775baf11a2aaa468af99239 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 16 Jan 2023 17:30:06 -0800 Subject: [PATCH 045/217] feat(clock): support chrono Time Zone extensions. Use chrono Calendars and Time Zones (P0355R7, P1466R3) when available instead of the `date` library. Verified with a patched build of a recent GCC 13 snapshot. --- include/factory.hpp | 2 +- include/modules/clock.hpp | 3 +-- include/util/date.hpp | 28 +++++++++++++++++++++++++++- meson.build | 22 ++++++++++++++++------ src/modules/clock.cpp | 1 - 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/include/factory.hpp b/include/factory.hpp index 21dc647..558a8d4 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -1,7 +1,7 @@ #pragma once #include -#ifdef HAVE_LIBDATE +#if defined(HAVE_CHRONO_TIMEZONES) || defined(HAVE_LIBDATE) #include "modules/clock.hpp" #else #include "modules/simpleclock.hpp" diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index c97565d..a7290f6 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -1,8 +1,7 @@ #pragma once -#include - #include "ALabel.hpp" +#include "util/date.hpp" #include "util/sleeper_thread.hpp" namespace waybar::modules { diff --git a/include/util/date.hpp b/include/util/date.hpp index ec948bd..380bb6e 100644 --- a/include/util/date.hpp +++ b/include/util/date.hpp @@ -1,8 +1,34 @@ #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 +#endif + template struct fmt::formatter> { std::string_view specs; diff --git a/meson.build b/meson.build index 7b63181..32bfd2b 100644 --- a/meson.build +++ b/meson.build @@ -123,11 +123,18 @@ gtk_layer_shell = dependency('gtk-layer-shell-0', required: get_option('gtk-layer-shell'), fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep']) systemd = dependency('systemd', required: get_option('systemd')) -tz_dep = dependency('date', - required: false, - default_options : [ 'use_system_tzdb=true' ], - modules : [ 'date::date', 'date::date-tz' ], - fallback: [ 'date', 'tz_dep' ]) + +cpp_lib_chrono = compiler.compute_int('__cpp_lib_chrono', prefix : '#include ') +have_chrono_timezones = cpp_lib_chrono >= 201907 +if have_chrono_timezones + tz_dep = declare_dependency() +else + tz_dep = dependency('date', + required: false, + default_options : [ 'use_system_tzdb=true' ], + modules : [ 'date::date', 'date::date-tz' ], + fallback: [ 'date', 'tz_dep' ]) +endif prefix = get_option('prefix') sysconfdir = get_option('sysconfdir') @@ -312,7 +319,10 @@ if get_option('rfkill').enabled() and is_linux ) endif -if tz_dep.found() +if have_chrono_timezones + add_project_arguments('-DHAVE_CHRONO_TIMEZONES', language: 'cpp') + src_files += 'src/modules/clock.cpp' +elif tz_dep.found() add_project_arguments('-DHAVE_LIBDATE', language: 'cpp') src_files += 'src/modules/clock.cpp' else diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 0dbd255..76ec73c 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -9,7 +9,6 @@ #include #include -#include "util/date.hpp" #include "util/ustring_clen.hpp" #ifdef HAVE_LANGINFO_1STDAY #include From 01cee153a44805f6b0a107ab8613a3586c1888dd Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 16 Jan 2023 23:56:26 -0800 Subject: [PATCH 046/217] ci: try to build with cpp_std=c++20 Add an extra job to build with `-std=c++20` on Fedora. Update actions/checkout to v3. --- .github/workflows/freebsd.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/linux.yml | 10 +++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index a6da7ef..550f945 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -9,7 +9,7 @@ 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@v2 + - uses: actions/checkout@v3 - name: Test in FreeBSD VM uses: vmactions/freebsd-vm@v0 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e9f1656..d11d2cc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,7 +6,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: DoozyX/clang-format-lint-action@v0.13 with: source: '.' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4c77c3a..c82af85 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -13,16 +13,20 @@ jobs: - fedora - opensuse - gentoo + cpp_std: [c++17] + include: + - distro: fedora + cpp_std: c++20 runs-on: ubuntu-latest container: image: alexays/waybar:${{ matrix.distro }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: configure - run: meson -Dman-pages=enabled build + run: meson -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build - name: build run: ninja -C build - name: test - run: meson test -C build --no-rebuild --print-errorlogs --suite waybar + run: meson test -C build --no-rebuild --verbose --suite waybar From 51b6c22cab5420f15bc1b59ff81fbbfc0bd0c5c8 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 21 Jan 2023 00:14:01 -0800 Subject: [PATCH 047/217] ci: add glibc locales for date formatting tests. Add some missing dependencies for Fedora. --- Dockerfiles/archlinux | 3 ++- Dockerfiles/fedora | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index e7cbba6..cab4146 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,4 +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 && \ + sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index e1abd44..5892159 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -2,12 +2,33 @@ FROM fedora:latest -RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ - 'pkgconfig(dbusmenu-gtk3-0.4)' 'pkgconfig(fmt)' 'pkgconfig(gdk-pixbuf-2.0)' \ - 'pkgconfig(gio-unix-2.0)' 'pkgconfig(gtk-layer-shell-0)' 'pkgconfig(gtkmm-3.0)' \ - 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ - 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ - 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ - 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' \ - 'pkgconfig(playerctl)' && \ +RUN dnf install -y @c-development \ + git-core glibc-langpack-en meson scdoc \ + 'pkgconfig(catch2)' \ + 'pkgconfig(date)' \ + 'pkgconfig(dbusmenu-gtk3-0.4)' \ + 'pkgconfig(fmt)' \ + 'pkgconfig(gdk-pixbuf-2.0)' \ + 'pkgconfig(gio-unix-2.0)' \ + 'pkgconfig(gtk-layer-shell-0)' \ + 'pkgconfig(gtkmm-3.0)' \ + 'pkgconfig(jack)' \ + 'pkgconfig(jsoncpp)' \ + 'pkgconfig(libevdev)' \ + 'pkgconfig(libinput)' \ + 'pkgconfig(libmpdclient)' \ + 'pkgconfig(libnl-3.0)' \ + 'pkgconfig(libnl-genl-3.0)' \ + 'pkgconfig(libpulse)' \ + 'pkgconfig(libudev)' \ + 'pkgconfig(playerctl)' \ + 'pkgconfig(pugixml)' \ + 'pkgconfig(sigc++-2.0)' \ + 'pkgconfig(spdlog)' \ + 'pkgconfig(upower-glib)' \ + 'pkgconfig(wayland-client)' \ + 'pkgconfig(wayland-cursor)' \ + 'pkgconfig(wayland-protocols)' \ + 'pkgconfig(wireplumber-0.4)' \ + 'pkgconfig(xkbregistry)' && \ dnf clean all -y From de77787b606bd930c22fce2746ed9fd25830e330 Mon Sep 17 00:00:00 2001 From: Kauan Decarli Date: Sat, 21 Jan 2023 21:57:28 -0300 Subject: [PATCH 048/217] Allow any module to implement signal handling --- include/AModule.hpp | 1 + src/bar.cpp | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 357f70e..c5f0ebc 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -15,6 +15,7 @@ class AModule : public IModule { bool enable_scroll = false); virtual ~AModule(); virtual auto update() -> void; + virtual auto refresh(int) -> void {}; virtual operator Gtk::Widget &(); Glib::Dispatcher dp; diff --git a/src/bar.cpp b/src/bar.cpp index f46b7d0..62ff80c 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -725,10 +725,7 @@ void waybar::Bar::setupAltFormatKeyForModuleList(const char* module_list_name) { void waybar::Bar::handleSignal(int signal) { for (auto& module : modules_all_) { - auto* custom = dynamic_cast(module.get()); - if (custom != nullptr) { - custom->refresh(signal); - } + module->refresh(signal); } } From 0ca1c3957a0b02528c03507cd80463c3bfe29902 Mon Sep 17 00:00:00 2001 From: asas1asas200 Date: Sun, 22 Jan 2023 20:16:46 +0800 Subject: [PATCH 049/217] docs(image): add image doc in meson and fix title --- man/waybar-image.5.scd | 4 ++-- man/waybar.5.scd.in | 1 + meson.build | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/man/waybar-image.5.scd b/man/waybar-image.5.scd index feff9f6..df7086f 100644 --- a/man/waybar-image.5.scd +++ b/man/waybar-image.5.scd @@ -1,4 +1,4 @@ -waybar-custom(5) +waybar-image(5) # NAME @@ -69,4 +69,4 @@ Addressed by *custom/* "interval": 5, "on-click": "mpc toggle" } -``` \ No newline at end of file +``` diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 7566dd0..704d666 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -263,6 +263,7 @@ A module group is defined by specifying a module named "group/some-group-name". - *waybar-custom(5)* - *waybar-disk(5)* - *waybar-idle-inhibitor(5)* +- *waybar-image(5)* - *waybar-keyboard-state(5)* - *waybar-memory(5)* - *waybar-mpd(5)* diff --git a/meson.build b/meson.build index ebf68d4..96ef1f3 100644 --- a/meson.build +++ b/meson.build @@ -395,6 +395,7 @@ if scdoc.found() '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', From f4cfafd2380607d1fd448bb93e29d43aa3cb8777 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 23 Jan 2023 09:25:02 +0100 Subject: [PATCH 050/217] fix: lint --- include/AModule.hpp | 2 +- src/modules/custom.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index c5f0ebc..35625cd 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -15,7 +15,7 @@ class AModule : public IModule { bool enable_scroll = false); virtual ~AModule(); virtual auto update() -> void; - virtual auto refresh(int) -> void {}; + virtual auto refresh(int) -> void{}; virtual operator Gtk::Widget &(); Glib::Dispatcher dp; diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 9818f5a..b7e1d2d 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -210,7 +210,7 @@ void waybar::modules::Custom::parseOutputJson() { } } if (!parsed["percentage"].asString().empty() && parsed["percentage"].isNumeric()) { - percentage_ = (int) lround(parsed["percentage"].asFloat()); + percentage_ = (int)lround(parsed["percentage"].asFloat()); } else { percentage_ = 0; } From 3c8ca009ff2630bdb2e9778448d73e9b3e37faee Mon Sep 17 00:00:00 2001 From: Enes Hecan Date: Fri, 13 Jan 2023 16:28:34 +0100 Subject: [PATCH 051/217] Sanitize hyprland language string only instead of the whole format. Fixes #1940 --- src/modules/hyprland/language.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index d398b23..f9ad091 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -55,6 +55,8 @@ void Language::onEvent(const std::string& ev) { if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString()) return; // ignore + layoutName = waybar::util::sanitize_string(layoutName); + const auto briefName = getShortFrom(layoutName); if (config_.isMember("format-" + briefName)) { @@ -64,8 +66,6 @@ void Language::onEvent(const std::string& ev) { layoutName = fmt::format(fmt::runtime(format_), layoutName); } - layoutName = waybar::util::sanitize_string(layoutName); - if (layoutName == layoutName_) return; layoutName_ = layoutName; @@ -87,6 +87,8 @@ void Language::initLanguage() { searcher = searcher.substr(searcher.find("keymap:") + 8); searcher = searcher.substr(0, searcher.find_first_of("\n\t")); + searcher = waybar::util::sanitize_string(searcher); + auto layoutName = std::string{}; const auto briefName = getShortFrom(searcher); @@ -97,8 +99,6 @@ void Language::initLanguage() { layoutName = fmt::format(fmt::runtime(format_), searcher); } - layoutName = waybar::util::sanitize_string(layoutName); - layoutName_ = layoutName; spdlog::debug("hyprland language initLanguage found {}", layoutName_); From ed31b20c26a4ee0dedd24f5ab5d0f8f40918e159 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Mon, 23 Jan 2023 18:42:32 +0300 Subject: [PATCH 052/217] Merge branch 'master' into YearCalendar Signed-off-by: Viktar Lukashonak --- .github/workflows/freebsd.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/linux.yml | 10 +- Dockerfiles/archlinux | 3 +- Dockerfiles/fedora | 37 +- include/AModule.hpp | 1 + include/factory.hpp | 2 +- include/modules/clock.hpp | 51 ++- include/modules/sway/ipc/client.hpp | 1 + include/modules/sway/workspaces.hpp | 5 +- include/util/date.hpp | 60 ++++ include/util/format.hpp | 8 +- include/util/waybar_time.hpp | 39 --- man/waybar-image.5.scd | 4 +- man/waybar.5.scd.in | 1 + meson.build | 25 +- src/bar.cpp | 5 +- src/modules/backlight.cpp | 9 +- src/modules/battery.cpp | 11 +- src/modules/bluetooth.cpp | 7 +- src/modules/clock.cpp | 480 +++++++++++++++++--------- src/modules/custom.cpp | 6 +- src/modules/disk.cpp | 20 +- src/modules/gamemode.cpp | 8 +- src/modules/hyprland/language.cpp | 16 +- src/modules/hyprland/submap.cpp | 3 +- src/modules/hyprland/window.cpp | 4 +- src/modules/idle_inhibitor.cpp | 18 +- src/modules/inhibitor.cpp | 2 +- src/modules/jack.cpp | 8 +- src/modules/keyboard_state.cpp | 2 +- src/modules/memory/common.cpp | 7 +- src/modules/mpd/mpd.cpp | 18 +- src/modules/mpris/mpris.cpp | 8 +- src/modules/network.cpp | 6 +- src/modules/pulseaudio.cpp | 6 +- src/modules/river/mode.cpp | 2 +- src/modules/river/window.cpp | 2 +- src/modules/sndio.cpp | 3 +- src/modules/sway/ipc/client.cpp | 2 + src/modules/sway/language.cpp | 4 +- src/modules/sway/mode.cpp | 2 +- src/modules/sway/scratchpad.cpp | 5 +- src/modules/sway/window.cpp | 7 +- src/modules/sway/workspaces.cpp | 10 +- src/modules/temperature.cpp | 8 +- src/modules/upower/upower.cpp | 4 +- src/modules/user.cpp | 20 +- src/modules/wireplumber.cpp | 8 +- src/modules/wlr/taskbar.cpp | 21 +- src/modules/wlr/workspace_manager.cpp | 2 +- test/SafeSignal.cpp | 6 +- test/config.cpp | 6 +- test/date.cpp | 162 +++++++++ test/main.cpp | 15 +- test/meson.build | 2 +- test/waybar_time.cpp | 90 ----- 57 files changed, 799 insertions(+), 477 deletions(-) create mode 100644 include/util/date.hpp delete mode 100644 include/util/waybar_time.hpp create mode 100644 test/date.cpp delete mode 100644 test/waybar_time.cpp diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index a6da7ef..550f945 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -9,7 +9,7 @@ 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@v2 + - uses: actions/checkout@v3 - name: Test in FreeBSD VM uses: vmactions/freebsd-vm@v0 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e9f1656..d11d2cc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,7 +6,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: DoozyX/clang-format-lint-action@v0.13 with: source: '.' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4c77c3a..c82af85 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -13,16 +13,20 @@ jobs: - fedora - opensuse - gentoo + cpp_std: [c++17] + include: + - distro: fedora + cpp_std: c++20 runs-on: ubuntu-latest container: image: alexays/waybar:${{ matrix.distro }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: configure - run: meson -Dman-pages=enabled build + run: meson -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build - name: build run: ninja -C build - name: test - run: meson test -C build --no-rebuild --print-errorlogs --suite waybar + run: meson test -C build --no-rebuild --verbose --suite waybar diff --git a/Dockerfiles/archlinux b/Dockerfiles/archlinux index e7cbba6..cab4146 100644 --- a/Dockerfiles/archlinux +++ b/Dockerfiles/archlinux @@ -3,4 +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 && \ + sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen diff --git a/Dockerfiles/fedora b/Dockerfiles/fedora index e1abd44..5892159 100644 --- a/Dockerfiles/fedora +++ b/Dockerfiles/fedora @@ -2,12 +2,33 @@ FROM fedora:latest -RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \ - 'pkgconfig(dbusmenu-gtk3-0.4)' 'pkgconfig(fmt)' 'pkgconfig(gdk-pixbuf-2.0)' \ - 'pkgconfig(gio-unix-2.0)' 'pkgconfig(gtk-layer-shell-0)' 'pkgconfig(gtkmm-3.0)' \ - 'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \ - 'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \ - 'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \ - 'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' \ - 'pkgconfig(playerctl)' && \ +RUN dnf install -y @c-development \ + git-core glibc-langpack-en meson scdoc \ + 'pkgconfig(catch2)' \ + 'pkgconfig(date)' \ + 'pkgconfig(dbusmenu-gtk3-0.4)' \ + 'pkgconfig(fmt)' \ + 'pkgconfig(gdk-pixbuf-2.0)' \ + 'pkgconfig(gio-unix-2.0)' \ + 'pkgconfig(gtk-layer-shell-0)' \ + 'pkgconfig(gtkmm-3.0)' \ + 'pkgconfig(jack)' \ + 'pkgconfig(jsoncpp)' \ + 'pkgconfig(libevdev)' \ + 'pkgconfig(libinput)' \ + 'pkgconfig(libmpdclient)' \ + 'pkgconfig(libnl-3.0)' \ + 'pkgconfig(libnl-genl-3.0)' \ + 'pkgconfig(libpulse)' \ + 'pkgconfig(libudev)' \ + 'pkgconfig(playerctl)' \ + 'pkgconfig(pugixml)' \ + 'pkgconfig(sigc++-2.0)' \ + 'pkgconfig(spdlog)' \ + 'pkgconfig(upower-glib)' \ + 'pkgconfig(wayland-client)' \ + 'pkgconfig(wayland-cursor)' \ + 'pkgconfig(wayland-protocols)' \ + 'pkgconfig(wireplumber-0.4)' \ + 'pkgconfig(xkbregistry)' && \ dnf clean all -y diff --git a/include/AModule.hpp b/include/AModule.hpp index 357f70e..35625cd 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -15,6 +15,7 @@ class AModule : public IModule { bool enable_scroll = false); virtual ~AModule(); virtual auto update() -> void; + virtual auto refresh(int) -> void{}; virtual operator Gtk::Widget &(); Glib::Dispatcher dp; diff --git a/include/factory.hpp b/include/factory.hpp index 21dc647..558a8d4 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -1,7 +1,7 @@ #pragma once #include -#ifdef HAVE_LIBDATE +#if defined(HAVE_CHRONO_TIMEZONES) || defined(HAVE_LIBDATE) #include "modules/clock.hpp" #else #include "modules/simpleclock.hpp" diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index ef129fb..9d615ae 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -1,19 +1,25 @@ #pragma once -#include - #include "ALabel.hpp" +#include "util/date.hpp" #include "util/sleeper_thread.hpp" -namespace waybar { - -struct waybar_time; - -namespace modules { +namespace waybar::modules { const std::string kCalendarPlaceholder = "calendar"; const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list"; +enum class WeeksSide { + LEFT, + RIGHT, + HIDDEN, +}; + +enum class CldMode { + MONTH, + YEAR +}; + class Clock : public ALabel { public: Clock(const std::string&, const Json::Value&); @@ -22,26 +28,37 @@ class Clock : public ALabel { private: util::SleeperThread thread_; + std::map, void (waybar::modules::Clock::*)()> eventMap_; std::locale locale_; std::vector time_zones_; int current_time_zone_idx_; - date::year_month_day calendar_cached_ymd_{date::January / 1 / 0}; - date::months calendar_shift_{0}, calendar_shift_init_{0}; - std::string calendar_cached_text_; bool is_calendar_in_tooltip_; bool is_timezoned_list_in_tooltip_; bool handleScroll(GdkEventScroll* e); + bool handleToggle(GdkEventButton* const& e); - std::string fmt_str_weeks_; - std::string fmt_str_calendar_; - int fmt_weeks_left_pad_{0}; - auto calendar_text(const waybar_time& wtime) -> std::string; - auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void; 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; + + /*Calendar properties*/ + WeeksSide cldWPos_{WeeksSide::HIDDEN}; + std::map fmtMap_; + CldMode cldMode_{CldMode::MONTH}; + uint cldMonCols_{3}; // Count of the month in the row + int cldMonColLen_{20}; // Length of the month column + int cldWnLen_{2}; // 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_{}; + /*Calendar functions*/ + auto get_calendar(const date::zoned_seconds& now, + const date::zoned_seconds& wtime) -> std::string; + void cldModeSwitch(); }; -} // namespace modules -} // namespace waybar +} // namespace waybar::modules diff --git a/include/modules/sway/ipc/client.hpp b/include/modules/sway/ipc/client.hpp index 77dab08..a6705ea 100644 --- a/include/modules/sway/ipc/client.hpp +++ b/include/modules/sway/ipc/client.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "ipc.hpp" #include "util/sleeper_thread.hpp" diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index e6df067..f8a55fa 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "AModule.hpp" @@ -21,7 +22,9 @@ class Workspaces : public AModule, public sigc::trackable { auto update() -> void; private: - static inline const std::string workspace_switch_cmd_ = "workspace {} \"{}\""; + static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\""; + static constexpr std::string_view persistent_workspace_switch_cmd_ = + R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")"; static int convertWorkspaceNameToNum(std::string name); diff --git a/include/util/date.hpp b/include/util/date.hpp new file mode 100644 index 0000000..380bb6e --- /dev/null +++ b/include/util/date.hpp @@ -0,0 +1,60 @@ +#pragma once + +#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 +#endif + +template +struct fmt::formatter> { + std::string_view specs; + + template + constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + if (it != ctx.end() && *it == ':') { + ++it; + } + auto end = it; + while (end != ctx.end() && *end != '}') { + ++end; + } + if (end != it) { + specs = {it, std::string_view::size_type(end - it)}; + } + return end; + } + + template + auto format(const date::zoned_time& ztime, FormatContext& ctx) { + if (ctx.locale()) { + const auto loc = ctx.locale().template get(); + return fmt::format_to(ctx.out(), "{}", date::format(loc, fmt::to_string(specs), ztime)); + } + return fmt::format_to(ctx.out(), "{}", date::format(fmt::to_string(specs), ztime)); + } +}; diff --git a/include/util/format.hpp b/include/util/format.hpp index fac0377..00b6a31 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -66,9 +66,9 @@ struct formatter { std::string string; switch (spec) { case '>': - return format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width); + return fmt::format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width); case '<': - return format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width); + return fmt::format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width); case '=': format = "{coefficient:<{number_width}.1f}{padding}{prefix}{unit}"; break; @@ -77,8 +77,8 @@ struct formatter { format = "{coefficient:.1f}{prefix}{unit}"; break; } - return format_to( - ctx.out(), format, fmt::arg("coefficient", fraction), + return fmt::format_to( + ctx.out(), fmt::runtime(format), fmt::arg("coefficient", fraction), fmt::arg("number_width", number_width), fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : "")), fmt::arg("unit", s.unit_), diff --git a/include/util/waybar_time.hpp b/include/util/waybar_time.hpp deleted file mode 100644 index b9f9ea9..0000000 --- a/include/util/waybar_time.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -namespace waybar { - -struct waybar_time { - std::locale locale; - date::zoned_seconds ztime; -}; - -} // namespace waybar - -template <> -struct fmt::formatter { - std::string_view specs; - - template - constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(); - if (it != ctx.end() && *it == ':') { - ++it; - } - auto end = it; - while (end != ctx.end() && *end != '}') { - ++end; - } - if (end != it) { - specs = {it, std::string_view::size_type(end - it)}; - } - return end; - } - - template - auto format(const waybar::waybar_time& t, FormatContext& ctx) { - return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(specs), t.ztime)); - } -}; diff --git a/man/waybar-image.5.scd b/man/waybar-image.5.scd index feff9f6..df7086f 100644 --- a/man/waybar-image.5.scd +++ b/man/waybar-image.5.scd @@ -1,4 +1,4 @@ -waybar-custom(5) +waybar-image(5) # NAME @@ -69,4 +69,4 @@ Addressed by *custom/* "interval": 5, "on-click": "mpc toggle" } -``` \ No newline at end of file +``` diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 7566dd0..704d666 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -263,6 +263,7 @@ A module group is defined by specifying a module named "group/some-group-name". - *waybar-custom(5)* - *waybar-disk(5)* - *waybar-idle-inhibitor(5)* +- *waybar-image(5)* - *waybar-keyboard-state(5)* - *waybar-memory(5)* - *waybar-mpd(5)* diff --git a/meson.build b/meson.build index ebf68d4..77b292a 100644 --- a/meson.build +++ b/meson.build @@ -123,11 +123,18 @@ gtk_layer_shell = dependency('gtk-layer-shell-0', required: get_option('gtk-layer-shell'), fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep']) systemd = dependency('systemd', required: get_option('systemd')) -tz_dep = dependency('date', - required: false, - default_options : [ 'use_system_tzdb=true' ], - modules : [ 'date::date', 'date::date-tz' ], - fallback: [ 'date', 'tz_dep' ]) + +cpp_lib_chrono = compiler.compute_int('__cpp_lib_chrono', prefix : '#include ') +have_chrono_timezones = cpp_lib_chrono >= 201907 +if have_chrono_timezones + tz_dep = declare_dependency() +else + tz_dep = dependency('date', + required: false, + default_options : [ 'use_system_tzdb=true' ], + modules : [ 'date::date', 'date::date-tz' ], + fallback: [ 'date', 'tz_dep' ]) +endif prefix = get_option('prefix') sysconfdir = get_option('sysconfdir') @@ -312,7 +319,10 @@ if get_option('rfkill').enabled() and is_linux ) endif -if tz_dep.found() +if have_chrono_timezones + add_project_arguments('-DHAVE_CHRONO_TIMEZONES', language: 'cpp') + src_files += 'src/modules/clock.cpp' +elif tz_dep.found() add_project_arguments('-DHAVE_LIBDATE', language: 'cpp') src_files += 'src/modules/clock.cpp' else @@ -395,6 +405,7 @@ if scdoc.found() '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', @@ -447,7 +458,7 @@ endif catch2 = dependency( 'catch2', - version: '>=3.0.0', + version: '>=2.0.0', fallback: ['catch2', 'catch2_dep'], required: get_option('tests'), ) diff --git a/src/bar.cpp b/src/bar.cpp index f46b7d0..62ff80c 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -725,10 +725,7 @@ void waybar::Bar::setupAltFormatKeyForModuleList(const char* module_list_name) { void waybar::Bar::handleSignal(int signal) { for (auto& module : modules_all_) { - auto* custom = dynamic_cast(module.get()); - if (custom != nullptr) { - custom->refresh(signal); - } + module->refresh(signal); } } diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index aa734a4..77c1dc0 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -48,13 +48,13 @@ struct UdevMonitorDeleter { void check_eq(int rc, int expected, const char *message = "eq, rc was: ") { if (rc != expected) { - throw std::runtime_error(fmt::format(message, rc)); + 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(message, rc)); + throw std::runtime_error(fmt::format(fmt::runtime(message), rc)); } } @@ -62,7 +62,7 @@ void check0(int rc, const char *message = "rc wasn't 0") { check_eq(rc, 0, messa void check_gte(int rc, int gte, const char *message = "rc was: ") { if (rc < gte) { - throw std::runtime_error(fmt::format(message, rc)); + throw std::runtime_error(fmt::format(fmt::runtime(message), rc)); } } @@ -181,7 +181,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()); - label_.set_markup(fmt::format(format_, fmt::arg("percent", std::to_string(percent)), + label_.set_markup(fmt::format(fmt::runtime(format_), + fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent)))); getState(percent); } else { diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index b3e51a6..abd1240 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -604,7 +604,7 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai format = config_["format-time"].asString(); } std::string zero_pad_minutes = fmt::format("{:02d}", minutes); - return fmt::format(format, fmt::arg("H", full_hours), fmt::arg("M", minutes), + return fmt::format(fmt::runtime(format), fmt::arg("H", full_hours), fmt::arg("M", minutes), fmt::arg("m", zero_pad_minutes)); } @@ -644,7 +644,8 @@ auto waybar::modules::Battery::update() -> void { } else if (config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } - label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("timeTo", tooltip_text_default), + 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))); } @@ -665,9 +666,9 @@ auto waybar::modules::Battery::update() -> void { } else { event_box_.show(); auto icons = std::vector{status + "-" + state, status, state}; - label_.set_markup(fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("power", power), - fmt::arg("icon", getIcon(capacity, icons)), - fmt::arg("time", time_remaining_formatted))); + 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))); } // Call parent update ALabel::update(); diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp index e6a1fe3..c3a2547 100644 --- a/src/modules/bluetooth.cpp +++ b/src/modules/bluetooth.cpp @@ -206,7 +206,8 @@ auto waybar::modules::Bluetooth::update() -> void { state_ = state; label_.set_markup(fmt::format( - format_, fmt::arg("status", state_), fmt::arg("num_connections", connected_devices_.size()), + 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), @@ -234,7 +235,7 @@ auto waybar::modules::Bluetooth::update() -> void { enumerate_format = config_["tooltip-format-enumerate-connected"].asString(); } ss << fmt::format( - enumerate_format, fmt::arg("device_address", dev.address), + fmt::runtime(enumerate_format), fmt::arg("device_address", dev.address), fmt::arg("device_address_type", dev.address_type), fmt::arg("device_alias", dev.alias), fmt::arg("icon", enumerate_icon), fmt::arg("device_battery_percentage", dev.battery_percentage.value_or(0))); @@ -247,7 +248,7 @@ auto waybar::modules::Bluetooth::update() -> void { } } label_.set_tooltip_text(fmt::format( - tooltip_format, fmt::arg("status", state_), + 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), diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 55f2c5b..37d5e30 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -10,14 +10,11 @@ #include #include "util/ustring_clen.hpp" -#include "util/waybar_time.hpp" #ifdef HAVE_LANGINFO_1STDAY #include #include #endif -using waybar::waybar_time; - 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), @@ -41,12 +38,6 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) time_zones_.push_back(nullptr); } - if (!is_timezone_fixed()) { - spdlog::warn( - "As using a timezone, some format args may be missing as the date library haven't got a " - "release since 2018."); - } - // Check if a particular placeholder is present in the tooltip format, to know what to calculate // on update. if (config_["tooltip-format"].isString()) { @@ -62,39 +53,98 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } } + // Calendar configuration if (is_calendar_in_tooltip_) { - if (config_["on-scroll"][kCalendarPlaceholder].isInt()) { - calendar_shift_init_ = - date::months{config_["on-scroll"].get(kCalendarPlaceholder, 0).asInt()}; + 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()) { + fmtMap_.insert( + {4, std::regex_replace(config_[kCalendarPlaceholder]["format"]["weeks"].asString(), + std::regex("\\{\\}"), + (first_day_of_week() == date::Monday) ? "{:%W}" : "{:%U}")}); + + if (cldWPos_ == WeeksSide::HIDDEN) + cldWnLen_ = 0; + else { + // tmp contains full length of the weeks including user characters + Glib::ustring tmp{std::regex_replace(fmtMap_[4], std::regex("]+>|\\{.*\\}"), "")}; + cldWnLen_ += (tmp.size() + 1); + cldMonColLen_ += cldWnLen_; + } + } else { + if (cldWPos_ != WeeksSide::HIDDEN) + fmtMap_.insert({4, (first_day_of_week() == date::Monday) ? "{:%W}" : "{:%U}"}); + } + 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()}); + 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}}; + 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", + cfgMode); + } + if (config_[kCalendarPlaceholder]["mode-mon-col"].isInt()) { + cldMonCols_ = config_[kCalendarPlaceholder]["mode-mon-col"].asInt(); + if (cldMonCols_ == 0u || 12 % cldMonCols_ != 0u) { + cldMonCols_ = 3u; + spdlog::warn( + "Clock calendar configuration \"mode-mon-col\" = {0} must be one of [1, 2, 3, 4, 6, " + "12]. Value 3 is using instead", + cldMonCols_); + } + } else + cldMonCols_ = 1; + if (config_[kCalendarPlaceholder]["on-scroll"].isInt()) { + cldShift_ = date::months{config_[kCalendarPlaceholder]["on-scroll"].asInt()}; event_box_.add_events(Gdk::LEAVE_NOTIFY_MASK); event_box_.signal_leave_notify_event().connect([this](GdkEventCrossing*) { - calendar_shift_ = date::months{0}; + cldCurrShift_ = date::months{0}; return false; }); } + if (config_[kCalendarPlaceholder]["on-click-left"].isString()) { + if (config_[kCalendarPlaceholder]["on-click-left"].asString() == "mode") + eventMap_.insert({std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), + &waybar::modules::Clock::cldModeSwitch}); + } + if (config_[kCalendarPlaceholder]["on-click-right"].isString()) { + if (config_[kCalendarPlaceholder]["on-click-right"].asString() == "mode") + eventMap_.insert({std::make_pair(3, GdkEventType::GDK_BUTTON_PRESS), + &waybar::modules::Clock::cldModeSwitch}); + } } - if (config_["locale"].isString()) { + if (config_["locale"].isString()) locale_ = std::locale(config_["locale"].asString()); - } else { + else locale_ = std::locale(""); - } - - if (config_["format-calendar-weeks"].isString()) { - fmt_str_weeks_ = - std::regex_replace(config_["format-calendar-weeks"].asString(), std::regex("\\{\\}"), - (first_day_of_week() == date::Monday) ? "{:%V}" : "{:%U}"); - fmt_weeks_left_pad_ = - std::regex_replace(fmt_str_weeks_, std::regex("]+>|\\{.*\\}"), "").length(); - } else { - fmt_str_weeks_ = ""; - } - - if (config_["format-calendar"].isString()) { - fmt_str_calendar_ = config_["format-calendar"].asString(); - } else { - fmt_str_calendar_ = "{}"; - } thread_ = [this] { dp.emit(); @@ -116,24 +166,22 @@ bool waybar::modules::Clock::is_timezone_fixed() { } auto waybar::modules::Clock::update() -> void { - auto time_zone = current_timezone(); + const auto* time_zone = current_timezone(); auto now = std::chrono::system_clock::now(); - waybar_time wtime = {locale_, - date::make_zoned(time_zone, date::floor(now))}; + auto ztime = date::zoned_time{time_zone, date::floor(now)}; - auto shifted_date = date::year_month_day{date::floor(now)} + calendar_shift_; + auto shifted_date = date::year_month_day{date::floor(now)} + cldCurrShift_; auto now_shifted = date::sys_days{shifted_date} + (now - date::floor(now)); - waybar_time shifted_wtime = { - locale_, date::make_zoned(time_zone, date::floor(now_shifted))}; + auto shifted_ztime = date::zoned_time{time_zone, date::floor(now_shifted)}; - std::string text = ""; + 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_, format_, localtime); + text = fmt::format(locale_, fmt::runtime(format_), localtime); } else { - text = fmt::format(format_, wtime); + text = fmt::format(locale_, fmt::runtime(format_), ztime); } label_.set_markup(text); @@ -142,13 +190,13 @@ auto waybar::modules::Clock::update() -> void { std::string calendar_lines{""}; std::string timezoned_time_lines{""}; if (is_calendar_in_tooltip_) { - calendar_lines = calendar_text(shifted_wtime); + 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(tooltip_format, shifted_wtime, + 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); @@ -159,6 +207,21 @@ auto waybar::modules::Clock::update() -> void { ALabel::update(); } +bool waybar::modules::Clock::handleToggle(GdkEventButton* const& e) { + const std::map, void (waybar::modules::Clock::*)()>::const_iterator& + rec{eventMap_.find(std::pair(e->button, e->type))}; + + const auto callMethod{(rec != eventMap_.cend()) ? rec->second : nullptr}; + + if (callMethod) { + (this->*callMethod)(); + } else + return AModule::handleToggle(e); + + update(); + return true; +} + bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) { // defer to user commands if set if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { @@ -168,11 +231,11 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) { auto dir = AModule::getScrollDir(e); // Shift calendar date - if (calendar_shift_init_.count() != 0) { + if (cldShift_.count() != 0) { if (dir == SCROLL_DIR::UP) - calendar_shift_ += calendar_shift_init_; + cldCurrShift_ += ((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; else - calendar_shift_ -= calendar_shift_init_; + cldCurrShift_ -= ((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; } else { // Change time zone if (dir != SCROLL_DIR::UP && dir != SCROLL_DIR::DOWN) { @@ -196,126 +259,212 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) { return true; } -auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string { - const auto daypoint = date::floor(wtime.ztime.get_local_time()); - const auto ymd{date::year_month_day{daypoint}}; - - if (calendar_cached_ymd_ == ymd) { - return calendar_cached_text_; - } - - const auto curr_day{(calendar_shift_init_.count() != 0 && calendar_shift_.count() != 0) - ? date::day{0} - : ymd.day()}; - const date::year_month ym{ymd.year(), ymd.month()}; - const auto first_dow = first_day_of_week(); - - std::stringstream os; - - enum class WeeksSide { - LEFT, - RIGHT, - HIDDEN, - }; - WeeksSide weeks_pos = WeeksSide::HIDDEN; - - if (config_["calendar-weeks-pos"].isString()) { - if (config_["calendar-weeks-pos"].asString() == "left") { - weeks_pos = WeeksSide::LEFT; - // Add paddings before the header - os << std::string(3 + fmt_weeks_left_pad_, ' '); - } else if (config_["calendar-weeks-pos"].asString() == "right") { - weeks_pos = WeeksSide::RIGHT; - } - } - - weekdays_header(first_dow, os); - - // First week day prefixed with spaces if needed. - date::sys_days print_wd{ym / 1}; - auto wd{date::weekday{print_wd}}; - auto empty_days = (wd - first_dow).count(); - - /* Print weeknumber on the left for the first row*/ - if (weeks_pos == WeeksSide::LEFT) { - os << fmt::format(fmt_str_weeks_, print_wd) << ' '; - } - - if (empty_days > 0) { - os << std::string(empty_days * 3 - 1, ' '); - } - - const auto last_day = (ym / date::literals::last).day(); - - for (auto d{date::day{1}}; d <= last_day; ++d, ++wd) { - if (wd != first_dow) { - os << ' '; - } else if (unsigned(d) != 1) { - if (weeks_pos == WeeksSide::RIGHT) { - os << ' ' << fmt::format(fmt_str_weeks_, print_wd); - } - - os << '\n'; - - print_wd = (ym / d); - - if (weeks_pos == WeeksSide::LEFT) { - os << fmt::format(fmt_str_weeks_, print_wd) << ' '; - } - } - - if (d == curr_day) { - if (config_["today-format"].isString()) { - auto today_format = config_["today-format"].asString(); - os << fmt::format(today_format, date::format("%e", d)); - } else { - os << "" << date::format("%e", d) << ""; - } - } else { - os << fmt::format(fmt_str_calendar_, date::format("%e", d)); - } - /*Print weeks on the right when the endings with spaces*/ - if (weeks_pos == WeeksSide::RIGHT && d == last_day) { - empty_days = 6 - (wd.c_encoding() - first_dow.c_encoding()); - if (empty_days > 0 && empty_days < 7) { - os << std::string(empty_days * 3, ' '); - } - - os << ' ' << fmt::format(fmt_str_weeks_, print_wd); - } - } - - auto result = os.str(); - calendar_cached_ymd_ = ymd; - calendar_cached_text_ = result; - return result; +// 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) { + using namespace date; + return static_cast( + ceil((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count()) + + 2; } -auto waybar::modules::Clock::weekdays_header(const date::weekday& first_week_day, std::ostream& os) - -> void { - std::stringstream res; - auto wd = first_week_day; - do { - if (wd != first_week_day) { - res << ' '; - } - Glib::ustring wd_ustring(date::format(locale_, "%a", wd)); - auto clen = ustring_clen(wd_ustring); - auto wd_len = wd_ustring.length(); - while (clen > 2) { - wd_ustring = wd_ustring.substr(0, wd_len - 1); - wd_len--; - clen = ustring_clen(wd_ustring); - } - const std::string pad(2 - clen, ' '); - res << pad << wd_ustring; - } while (++wd != first_week_day); - res << '\n'; +auto cldGetWeekForLine(date::year_month const ym, date::weekday const 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; +} - if (config_["format-calendar-weekdays"].isString()) { - os << fmt::format(config_["format-calendar-weekdays"].asString(), res.str()); - } else - os << res.str(); +auto getCalendarLine(date::year_month_day const currDate, date::year_month const ym, + unsigned const line, date::weekday const firstdow, int rowLen, + const std::locale* const locale_) -> std::string { + using namespace date::literals; + std::ostringstream res; + + switch (line) { + case 0: { + // Output month and year title + Glib::ustring wd_ustring{Glib::ustring::format( + std::left, std::setw(rowLen), date::format(*locale_, "%B %Y", ym), std::right)}; + res << wd_ustring; + break; + } + case 1: { + // Output weekday names title + auto wd{firstdow}; + do { + Glib::ustring wd_ustring{date::format(*locale_, "%a", wd)}; + auto clen{ustring_clen(wd_ustring)}; + auto wd_len{wd_ustring.length()}; + while (clen > 2) { + wd_ustring = wd_ustring.substr(0, wd_len - 1); + --wd_len; + clen = ustring_clen(wd_ustring); + } + const std::string pad(2 - clen, ' '); + + if (wd != firstdow) res << ' '; + + res << pad << wd_ustring; + } while (++wd != firstdow); + + break; + } + 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, ' '); + + if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / 1_d) + res << date::format("%e", 1_d); + else + res << "{today}"; + + auto d = 2_d; + + while (++wd != firstdow) { + if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / d) + res << date::format(" %e", d); + else + res << " {today}"; + + ++d; + } + break; + } + 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; + + if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / d) + res << date::format("%e", d); + else + res << "{today}"; + + while (++wd != firstdow && ++d <= e) { + if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / d) + res << date::format(" %e", d); + else + res << " {today}"; + } + // Append row with spaces if the week did not complete + res << std::string(static_cast((firstdow - wd).count()) * 3, ' '); + } else // Otherwise not a valid week, output a blank row + res << std::string(rowLen, ' '); + + break; + } + } + + 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}}; + const auto ym{ymd.year() / ymd.month()}; + const auto y{ymd.year()}; + const auto firstdow = first_day_of_week(); + 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_) + return cldYearCached_; + else + cldYearShift_ = y / date::month{1} / 1; + } + if (cldMode_ == CldMode::MONTH) { + if (ym == cldMonShift_) + return cldMonCached_; + else + cldMonShift_ = ym; + } + + // 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); + 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_)); + for (auto line{0u}; line < lines; ++line) { + for (auto col{0u}; col < cldMonCols_; ++col) { + const auto mon{date::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 << " "; + + // Week numbers on the left + if (cldWPos_ == WeeksSide::LEFT && line > 0) { + if (line == 1 && cldWnLen_ > 0) os << std::string(cldWnLen_, ' '); + + if (line > 1 && 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)}) + << ' '; + } + + os << getCalendarLine(currDate, ymTmp, line, firstdow, cldMonColLen_, &locale_); + + // Week numbers on the right + if (cldWPos_ == WeeksSide ::RIGHT && line > 0) { + if (line == 1 && cldWnLen_ > 0) os << std::string(cldWnLen_, ' '); + + if (line > 1 && 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)}); + } + } + } + + // Apply user formats to calendar + if (line < 2) + tmp << fmt::format(fmt::runtime(fmtMap_[line]), os.str()); + else + tmp << os.str(); + // Clear ostringstream + std::ostringstream().swap(os); + if (line + 1u != lines || (row + 1u != maxRows && cldMode_ == CldMode::YEAR)) tmp << '\n'; + } + 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", ymd.day())))); + + if (cldMode_ == CldMode::YEAR) + cldYearCached_ = os.str(); + else + cldMonCached_ = os.str(); + + return os.str(); +} + +void waybar::modules::Clock::cldModeSwitch() { + cldMode_ = (cldMode_ == CldMode::YEAR) ? CldMode::MONTH : CldMode::YEAR; } auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point* now) @@ -324,7 +473,6 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin return ""; } std::stringstream os; - waybar_time wtime; 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; @@ -333,8 +481,8 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin if (!timezone) { timezone = date::current_zone(); } - wtime = {locale_, date::make_zoned(timezone, date::floor(*now))}; - os << fmt::format(format_, wtime) << '\n'; + auto ztime = date::zoned_time{timezone, date::floor(*now)}; + os << fmt::format(locale_, fmt::runtime(format_), ztime) << '\n'; } return os.str(); } diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 23dba38..b7e1d2d 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -126,7 +126,7 @@ auto waybar::modules::Custom::update() -> void { } else { parseOutputRaw(); } - auto str = fmt::format(format_, text_, fmt::arg("alt", alt_), + 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()) { @@ -209,8 +209,8 @@ void waybar::modules::Custom::parseOutputJson() { class_.push_back(c.asString()); } } - if (!parsed["percentage"].asString().empty() && parsed["percentage"].isUInt()) { - percentage_ = parsed["percentage"].asUInt(); + if (!parsed["percentage"].asString().empty() && parsed["percentage"].isNumeric()) { + percentage_ = (int)lround(parsed["percentage"].asFloat()); } else { percentage_ = 0; } diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index 5578dc2..eb4d902 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -58,11 +58,11 @@ auto waybar::modules::Disk::update() -> void { event_box_.hide(); } else { event_box_.show(); - label_.set_markup( - fmt::format(format, stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), - fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), - fmt::arg("used", used), fmt::arg("percentage_used", percentage_used), - fmt::arg("total", total), fmt::arg("path", path_))); + label_.set_markup(fmt::format( + fmt::runtime(format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), + fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used), + fmt::arg("percentage_used", percentage_used), fmt::arg("total", total), + fmt::arg("path", path_))); } if (tooltipEnabled()) { @@ -70,11 +70,11 @@ auto waybar::modules::Disk::update() -> void { if (config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } - label_.set_tooltip_text( - fmt::format(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_))); + label_.set_tooltip_text(fmt::format( + 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_))); } // Call parent update ALabel::update(); diff --git a/src/modules/gamemode.cpp b/src/modules/gamemode.cpp index 7129297..1b8d7fc 100644 --- a/src/modules/gamemode.cpp +++ b/src/modules/gamemode.cpp @@ -213,14 +213,14 @@ auto Gamemode::update() -> void { // Tooltip if (tooltip) { - std::string text = fmt::format(tooltip_format, fmt::arg("count", gameCount)); + std::string text = fmt::format(fmt::runtime(tooltip_format), fmt::arg("count", gameCount)); box_.set_tooltip_text(text); } // Label format - std::string str = - fmt::format(showAltText ? format_alt : format, fmt::arg("glyph", useIcon ? "" : glyph), - fmt::arg("count", gameCount > 0 ? std::to_string(gameCount) : "")); + std::string str = fmt::format(fmt::runtime(showAltText ? format_alt : format), + fmt::arg("glyph", useIcon ? "" : glyph), + fmt::arg("count", gameCount > 0 ? std::to_string(gameCount) : "")); label_.set_markup(str); if (useIcon) { diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 6cadd62..f9ad091 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -55,17 +55,17 @@ void Language::onEvent(const std::string& ev) { if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString()) return; // ignore + layoutName = waybar::util::sanitize_string(layoutName); + const auto briefName = getShortFrom(layoutName); if (config_.isMember("format-" + briefName)) { const auto propName = "format-" + briefName; - layoutName = fmt::format(format_, config_[propName].asString()); + layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); } else { - layoutName = fmt::format(format_, layoutName); + layoutName = fmt::format(fmt::runtime(format_), layoutName); } - layoutName = waybar::util::sanitize_string(layoutName); - if (layoutName == layoutName_) return; layoutName_ = layoutName; @@ -87,18 +87,18 @@ void Language::initLanguage() { searcher = searcher.substr(searcher.find("keymap:") + 8); searcher = searcher.substr(0, searcher.find_first_of("\n\t")); + searcher = waybar::util::sanitize_string(searcher); + auto layoutName = std::string{}; const auto briefName = getShortFrom(searcher); if (config_.isMember("format-" + briefName)) { const auto propName = "format-" + briefName; - layoutName = fmt::format(format_, config_[propName].asString()); + layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); } else { - layoutName = fmt::format(format_, searcher); + layoutName = fmt::format(fmt::runtime(format_), searcher); } - layoutName = waybar::util::sanitize_string(layoutName); - layoutName_ = layoutName; spdlog::debug("hyprland language initLanguage found {}", layoutName_); diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 6eb0942..22acbf3 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -19,6 +19,7 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config) // register for hyprland ipc gIPC->registerForIPC("submap", this); + dp.emit(); } Submap::~Submap() { @@ -33,7 +34,7 @@ auto Submap::update() -> void { if (submap_.empty()) { event_box_.hide(); } else { - label_.set_markup(fmt::format(format_, submap_)); + label_.set_markup(fmt::format(fmt::runtime(format_), submap_)); if (tooltipEnabled()) { label_.set_tooltip_text(submap_); } diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index d3d06cc..47daae9 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -40,8 +40,8 @@ auto Window::update() -> void { if (!format_.empty()) { label_.show(); - label_.set_markup( - fmt::format(format_, waybar::util::rewriteTitle(lastView, config_["rewrite"]))); + label_.set_markup(fmt::format(fmt::runtime(format_), + waybar::util::rewriteTitle(lastView, config_["rewrite"]))); } else { label_.hide(); } diff --git a/src/modules/idle_inhibitor.cpp b/src/modules/idle_inhibitor.cpp index c4109b0..a5fc9ac 100644 --- a/src/modules/idle_inhibitor.cpp +++ b/src/modules/idle_inhibitor.cpp @@ -63,21 +63,15 @@ auto waybar::modules::IdleInhibitor::update() -> void { } std::string status_text = status ? "activated" : "deactivated"; - label_.set_markup(fmt::format(format_, fmt::arg("status", status_text), + label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("status", status_text), fmt::arg("icon", getIcon(0, status_text)))); label_.get_style_context()->add_class(status_text); if (tooltipEnabled()) { - label_.set_tooltip_markup( - status ? fmt::format(config_["tooltip-format-activated"].isString() - ? config_["tooltip-format-activated"].asString() - : "{status}", - fmt::arg("status", status_text), - fmt::arg("icon", getIcon(0, status_text))) - : fmt::format(config_["tooltip-format-deactivated"].isString() - ? config_["tooltip-format-deactivated"].asString() - : "{status}", - fmt::arg("status", status_text), - fmt::arg("icon", getIcon(0, status_text)))); + auto config = config_[status ? "tooltip-format-activated" : "tooltip-format-deactivated"]; + auto tooltip_format = config.isString() ? config.asString() : "{status}"; + label_.set_tooltip_markup(fmt::format(fmt::runtime(tooltip_format), + fmt::arg("status", status_text), + fmt::arg("icon", getIcon(0, status_text)))); } // Call parent update ALabel::update(); diff --git a/src/modules/inhibitor.cpp b/src/modules/inhibitor.cpp index e4340b1..fe2a4be 100644 --- a/src/modules/inhibitor.cpp +++ b/src/modules/inhibitor.cpp @@ -118,7 +118,7 @@ auto Inhibitor::update() -> void { std::string status_text = activated() ? "activated" : "deactivated"; label_.get_style_context()->remove_class(activated() ? "deactivated" : "activated"); - label_.set_markup(fmt::format(format_, fmt::arg("status", status_text), + label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("status", status_text), fmt::arg("icon", getIcon(0, status_text)))); label_.get_style_context()->add_class(status_text); diff --git a/src/modules/jack.cpp b/src/modules/jack.cpp index 3a92110..9bd6fcd 100644 --- a/src/modules/jack.cpp +++ b/src/modules/jack.cpp @@ -72,7 +72,7 @@ auto JACK::update() -> void { } else format = "{load}%"; - label_.set_markup(fmt::format(format, fmt::arg("load", std::round(load_)), + label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("load", std::round(load_)), fmt::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_), fmt::arg("latency", fmt::format("{:.2f}", latency)), fmt::arg("xruns", xruns_))); @@ -81,9 +81,9 @@ auto JACK::update() -> void { std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms"; if (config_["tooltip-format"].isString()) tooltip_format = config_["tooltip-format"].asString(); label_.set_tooltip_text(fmt::format( - tooltip_format, fmt::arg("load", std::round(load_)), fmt::arg("bufsize", bufsize_), - fmt::arg("samplerate", samplerate_), fmt::arg("latency", fmt::format("{:.2f}", latency)), - fmt::arg("xruns", xruns_))); + fmt::runtime(tooltip_format), fmt::arg("load", std::round(load_)), + fmt::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_), + fmt::arg("latency", fmt::format("{:.2f}", latency)), fmt::arg("xruns", xruns_))); } // Call parent update diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index b2750b6..4c081d6 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -278,7 +278,7 @@ auto waybar::modules::KeyboardState::update() -> void { }; for (auto& label_state : label_states) { std::string text; - text = fmt::format(label_state.format, + text = fmt::format(fmt::runtime(label_state.format), fmt::arg("icon", label_state.state ? icon_locked_ : icon_unlocked_), fmt::arg("name", label_state.name)); label_state.label.set_markup(text); diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index 4a0e634..544d781 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -56,7 +56,8 @@ auto waybar::modules::Memory::update() -> void { event_box_.show(); auto icons = std::vector{state}; label_.set_markup(fmt::format( - format, used_ram_percentage, fmt::arg("icon", getIcon(used_ram_percentage, icons)), + fmt::runtime(format), used_ram_percentage, + 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("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes), @@ -68,8 +69,8 @@ auto waybar::modules::Memory::update() -> void { if (config_["tooltip-format"].isString()) { auto tooltip_format = config_["tooltip-format"].asString(); label_.set_tooltip_text(fmt::format( - tooltip_format, used_ram_percentage, fmt::arg("total", total_ram_gigabytes), - fmt::arg("swapTotal", total_swap_gigabytes), + 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("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes), fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes), diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 401b759..e728897 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -174,14 +174,14 @@ void waybar::modules::MPD::setLabel() { try { auto text = fmt::format( - format, fmt::arg("artist", artist.raw()), fmt::arg("albumArtist", album_artist.raw()), - fmt::arg("album", album.raw()), fmt::arg("title", title.raw()), fmt::arg("date", date), - fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime), - fmt::arg("totalTime", totalTime), fmt::arg("songPosition", song_pos), - fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon), - fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon), - fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon), - fmt::arg("filename", filename)); + fmt::runtime(format), fmt::arg("artist", artist.raw()), + fmt::arg("albumArtist", album_artist.raw()), fmt::arg("album", album.raw()), + fmt::arg("title", title.raw()), fmt::arg("date", date), fmt::arg("volume", volume), + fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), + fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length), + fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), + fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon), + fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename)); if (text.empty()) { label_.hide(); } else { @@ -198,7 +198,7 @@ void waybar::modules::MPD::setLabel() { : "MPD (connected)"; try { auto tooltip_text = - fmt::format(tooltip_format, fmt::arg("artist", artist.raw()), + fmt::format(fmt::runtime(tooltip_format), fmt::arg("artist", artist.raw()), fmt::arg("albumArtist", album_artist.raw()), fmt::arg("album", album.raw()), fmt::arg("title", title.raw()), fmt::arg("date", date), fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime), diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 651dfd5..f11821f 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -378,10 +378,10 @@ auto Mpris::update() -> void { break; } auto label_format = - fmt::format(formatstr, fmt::arg("player", info.name), fmt::arg("status", info.status_string), - fmt::arg("artist", *info.artist), fmt::arg("title", *info.title), - fmt::arg("album", *info.album), fmt::arg("length", *info.length), - fmt::arg("dynamic", dynamic.str()), + fmt::format(fmt::runtime(formatstr), fmt::arg("player", info.name), + fmt::arg("status", info.status_string), fmt::arg("artist", *info.artist), + fmt::arg("title", *info.title), fmt::arg("album", *info.album), + fmt::arg("length", *info.length), fmt::arg("dynamic", dynamic.str()), fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); label_.set_markup(label_format); diff --git a/src/modules/network.cpp b/src/modules/network.cpp index a4797ee..8409311 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -331,7 +331,7 @@ auto waybar::modules::Network::update() -> void { getState(signal_strength_); auto text = fmt::format( - format_, fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), + fmt::runtime(format_), fmt::arg("essid", essid_), 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_), @@ -363,8 +363,8 @@ auto waybar::modules::Network::update() -> void { } if (!tooltip_format.empty()) { auto tooltip_text = fmt::format( - tooltip_format, fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), - fmt::arg("signalStrength", signal_strength_), + fmt::runtime(tooltip_format), fmt::arg("essid", essid_), + 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_)), diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index c797997..0630710 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -294,9 +294,9 @@ auto waybar::modules::Pulseaudio::update() -> void { format_source = config_["format-source"].asString(); } } - format_source = fmt::format(format_source, fmt::arg("volume", source_volume_)); + format_source = fmt::format(fmt::runtime(format_source), fmt::arg("volume", source_volume_)); auto text = fmt::format( - format, fmt::arg("desc", desc_), fmt::arg("volume", volume_), + 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()))); if (text.empty()) { @@ -313,7 +313,7 @@ auto waybar::modules::Pulseaudio::update() -> void { } if (!tooltip_format.empty()) { label_.set_tooltip_text(fmt::format( - tooltip_format, fmt::arg("desc", desc_), fmt::arg("volume", volume_), + 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())))); diff --git a/src/modules/river/mode.cpp b/src/modules/river/mode.cpp index 4a51c83..1f788e0 100644 --- a/src/modules/river/mode.cpp +++ b/src/modules/river/mode.cpp @@ -103,7 +103,7 @@ void Mode::handle_mode(const char *mode) { } label_.get_style_context()->add_class(mode); - label_.set_markup(fmt::format(format_, Glib::Markup::escape_text(mode).raw())); + label_.set_markup(fmt::format(fmt::runtime(format_), Glib::Markup::escape_text(mode).raw())); label_.show(); } diff --git a/src/modules/river/window.cpp b/src/modules/river/window.cpp index d0f492f..d93938c 100644 --- a/src/modules/river/window.cpp +++ b/src/modules/river/window.cpp @@ -106,7 +106,7 @@ void Window::handle_focused_view(const char *title) { label_.hide(); // hide empty labels or labels with empty format } else { label_.show(); - label_.set_markup(fmt::format(format_, Glib::Markup::escape_text(title).raw())); + label_.set_markup(fmt::format(fmt::runtime(format_), Glib::Markup::escape_text(title).raw())); } ALabel::update(); diff --git a/src/modules/sndio.cpp b/src/modules/sndio.cpp index e6f1bd0..72e7207 100644 --- a/src/modules/sndio.cpp +++ b/src/modules/sndio.cpp @@ -110,7 +110,8 @@ auto Sndio::update() -> void { label_.get_style_context()->remove_class("muted"); } - auto text = fmt::format(format, fmt::arg("volume", vol), fmt::arg("raw_value", volume_)); + auto text = + fmt::format(fmt::runtime(format), fmt::arg("volume", vol), fmt::arg("raw_value", volume_)); if (text.empty()) { label_.hide(); } else { diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index 4d6495c..5c3df7b 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -2,6 +2,8 @@ #include +#include + namespace waybar::modules::sway { Ipc::Ipc() { diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index d3730a1..a5860bd 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -96,14 +96,14 @@ void Language::onEvent(const struct Ipc::ipc_response& res) { auto Language::update() -> void { std::lock_guard lock(mutex_); auto display_layout = trim(fmt::format( - format_, fmt::arg("short", layout_.short_name), + fmt::runtime(format_), fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), fmt::arg("variant", layout_.variant), fmt::arg("flag", layout_.country_flag()))); label_.set_markup(display_layout); if (tooltipEnabled()) { if (tooltip_format_ != "") { auto tooltip_display_layout = trim( - fmt::format(tooltip_format_, fmt::arg("short", layout_.short_name), + fmt::format(fmt::runtime(tooltip_format_), fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), fmt::arg("variant", layout_.variant), fmt::arg("flag", layout_.country_flag()))); diff --git a/src/modules/sway/mode.cpp b/src/modules/sway/mode.cpp index 7eaa523..b81735e 100644 --- a/src/modules/sway/mode.cpp +++ b/src/modules/sway/mode.cpp @@ -42,7 +42,7 @@ auto Mode::update() -> void { if (mode_.empty()) { event_box_.hide(); } else { - label_.set_markup(fmt::format(format_, mode_)); + label_.set_markup(fmt::format(fmt::runtime(format_), mode_)); if (tooltipEnabled()) { label_.set_tooltip_text(mode_); } diff --git a/src/modules/sway/scratchpad.cpp b/src/modules/sway/scratchpad.cpp index 59e3053..17dc270 100644 --- a/src/modules/sway/scratchpad.cpp +++ b/src/modules/sway/scratchpad.cpp @@ -32,7 +32,8 @@ auto Scratchpad::update() -> void { if (count_ || show_empty_) { event_box_.show(); label_.set_markup( - fmt::format(format_, fmt::arg("icon", getIcon(count_, "", config_["format-icons"].size())), + fmt::format(fmt::runtime(format_), + fmt::arg("icon", getIcon(count_, "", config_["format-icons"].size())), fmt::arg("count", count_))); if (tooltip_enabled_) { label_.set_tooltip_markup(tooltip_text_); @@ -64,7 +65,7 @@ auto Scratchpad::onCmd(const struct Ipc::ipc_response& res) -> void { if (tooltip_enabled_) { tooltip_text_.clear(); for (const auto& window : tree["nodes"][0]["nodes"][0]["floating_nodes"]) { - tooltip_text_.append(fmt::format(tooltip_format_ + '\n', + tooltip_text_.append(fmt::format(fmt::runtime(tooltip_format_ + '\n'), fmt::arg("app", window["app_id"].asString()), fmt::arg("title", window["name"].asString()))); } diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 0e74b76..7d60d29 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -204,9 +204,10 @@ auto Window::update() -> void { old_app_id_ = app_id_; } - label_.set_markup(fmt::format( - format_, fmt::arg("title", waybar::util::rewriteTitle(window_, config_["rewrite"])), - fmt::arg("app_id", app_id_), fmt::arg("shell", shell_))); + label_.set_markup( + fmt::format(fmt::runtime(format_), + fmt::arg("title", waybar::util::rewriteTitle(window_, config_["rewrite"])), + fmt::arg("app_id", app_id_), fmt::arg("shell", shell_))); if (tooltipEnabled()) { label_.set_tooltip_text(window_); } diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index b621b83..08742ae 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -233,7 +233,7 @@ auto Workspaces::update() -> void { std::string output = (*it)["name"].asString(); if (config_["format"].isString()) { auto format = config_["format"].asString(); - output = fmt::format(format, fmt::arg("icon", getIcon(output, *it)), + 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())); } @@ -259,11 +259,9 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { try { if (node["target_output"].isString()) { ipc_.sendCmd(IPC_COMMAND, - fmt::format(workspace_switch_cmd_ + "; move workspace to output \"{}\"; " + - workspace_switch_cmd_, - "--no-auto-back-and-forth", node["name"].asString(), - node["target_output"].asString(), "--no-auto-back-and-forth", - node["name"].asString())); + fmt::format(persistent_workspace_switch_cmd_, "--no-auto-back-and-forth", + node["name"].asString(), node["target_output"].asString(), + "--no-auto-back-and-forth", node["name"].asString())); } else { ipc_.sendCmd(IPC_COMMAND, fmt::format("workspace {} \"{}\"", config_["disable-auto-back-and-forth"].asBool() diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index eca05a7..ff722d7 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -55,7 +55,7 @@ auto waybar::modules::Temperature::update() -> void { } auto max_temp = config_["critical-threshold"].isInt() ? config_["critical-threshold"].asInt() : 0; - label_.set_markup(fmt::format(format, fmt::arg("temperatureC", temperature_c), + label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("temperatureC", temperature_c), fmt::arg("temperatureF", temperature_f), fmt::arg("temperatureK", temperature_k), fmt::arg("icon", getIcon(temperature_c, "", max_temp)))); @@ -64,9 +64,9 @@ auto waybar::modules::Temperature::update() -> void { if (config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); } - label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("temperatureC", temperature_c), - fmt::arg("temperatureF", temperature_f), - fmt::arg("temperatureK", temperature_k))); + label_.set_tooltip_text(fmt::format( + fmt::runtime(tooltip_format), fmt::arg("temperatureC", temperature_c), + fmt::arg("temperatureF", temperature_f), fmt::arg("temperatureK", temperature_k))); } // Call parent update ALabel::update(); diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index eb29913..38c1f7f 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -336,8 +336,8 @@ auto UPower::update() -> void { break; } std::string label_format = - fmt::format(showAltText ? format_alt : format, fmt::arg("percentage", percentString), - fmt::arg("time", time_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) { diff --git a/src/modules/user.cpp b/src/modules/user.cpp index 2f7c6e9..418fc58 100644 --- a/src/modules/user.cpp +++ b/src/modules/user.cpp @@ -127,16 +127,16 @@ auto User::update() -> void { auto startSystemTime = currentSystemTime - workSystemTimeSeconds; long workSystemDays = uptimeSeconds / 86400; - auto label = fmt::format(ALabel::format_, fmt::arg("up_H", fmt::format("{:%H}", startSystemTime)), - fmt::arg("up_M", fmt::format("{:%M}", startSystemTime)), - fmt::arg("up_d", fmt::format("{:%d}", startSystemTime)), - fmt::arg("up_m", fmt::format("{:%m}", startSystemTime)), - fmt::arg("up_Y", fmt::format("{:%Y}", startSystemTime)), - fmt::arg("work_d", workSystemDays), - fmt::arg("work_H", fmt::format("{:%H}", workSystemTimeSeconds)), - fmt::arg("work_M", fmt::format("{:%M}", workSystemTimeSeconds)), - fmt::arg("work_S", fmt::format("{:%S}", workSystemTimeSeconds)), - fmt::arg("user", systemUser)); + auto label = fmt::format( + fmt::runtime(ALabel::format_), fmt::arg("up_H", fmt::format("{:%H}", startSystemTime)), + fmt::arg("up_M", fmt::format("{:%M}", startSystemTime)), + fmt::arg("up_d", fmt::format("{:%d}", startSystemTime)), + fmt::arg("up_m", fmt::format("{:%m}", startSystemTime)), + fmt::arg("up_Y", fmt::format("{:%Y}", startSystemTime)), fmt::arg("work_d", workSystemDays), + fmt::arg("work_H", fmt::format("{:%H}", workSystemTimeSeconds)), + fmt::arg("work_M", fmt::format("{:%M}", workSystemTimeSeconds)), + fmt::arg("work_S", fmt::format("{:%S}", workSystemTimeSeconds)), + fmt::arg("user", systemUser)); ALabel::label_.set_markup(label); AIconLabel::update(); } diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index 9652e1e..fd1a0d3 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -279,7 +279,7 @@ auto waybar::modules::Wireplumber::update() -> void { label_.get_style_context()->remove_class("muted"); } - std::string markup = fmt::format(format, fmt::arg("node_name", node_name_), + std::string markup = fmt::format(fmt::runtime(format), fmt::arg("node_name", node_name_), fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_))); label_.set_markup(markup); @@ -291,9 +291,9 @@ auto waybar::modules::Wireplumber::update() -> void { } if (!tooltip_format.empty()) { - label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("node_name", node_name_), - fmt::arg("volume", volume_), - fmt::arg("icon", getIcon(volume_)))); + label_.set_tooltip_text( + fmt::format(fmt::runtime(tooltip_format), fmt::arg("node_name", node_name_), + fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_)))); } else { label_.set_tooltip_text(node_name_); } diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 5460244..427083b 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -618,9 +618,10 @@ void Task::update() { app_id = Glib::Markup::escape_text(app_id); } if (!format_before_.empty()) { - auto txt = fmt::format(format_before_, fmt::arg("title", title), fmt::arg("name", name), - fmt::arg("app_id", app_id), fmt::arg("state", state_string()), - fmt::arg("short_state", state_string(true))); + auto txt = + fmt::format(fmt::runtime(format_before_), fmt::arg("title", title), fmt::arg("name", name), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), + fmt::arg("short_state", state_string(true))); if (markup) text_before_.set_markup(txt); else @@ -628,9 +629,10 @@ void Task::update() { text_before_.show(); } if (!format_after_.empty()) { - auto txt = fmt::format(format_after_, fmt::arg("title", title), fmt::arg("name", name), - fmt::arg("app_id", app_id), fmt::arg("state", state_string()), - fmt::arg("short_state", state_string(true))); + auto txt = + fmt::format(fmt::runtime(format_after_), fmt::arg("title", title), fmt::arg("name", name), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), + fmt::arg("short_state", state_string(true))); if (markup) text_after_.set_markup(txt); else @@ -639,9 +641,10 @@ void Task::update() { } if (!format_tooltip_.empty()) { - auto txt = fmt::format(format_tooltip_, fmt::arg("title", title), fmt::arg("name", name), - fmt::arg("app_id", app_id), fmt::arg("state", state_string()), - fmt::arg("short_state", state_string(true))); + auto txt = + fmt::format(fmt::runtime(format_tooltip_), fmt::arg("title", title), fmt::arg("name", name), + fmt::arg("app_id", app_id), fmt::arg("state", state_string()), + fmt::arg("short_state", state_string(true))); if (markup) button_.set_tooltip_markup(txt); else diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index ade0269..c1b68c8 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -379,7 +379,7 @@ Workspace::~Workspace() { } auto Workspace::update() -> void { - label_.set_markup(fmt::format(format_, fmt::arg("name", name_), + label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("name", name_), fmt::arg("icon", with_icon_ ? get_icon() : ""))); } diff --git a/test/SafeSignal.cpp b/test/SafeSignal.cpp index 7ff6f2a..f496d7a 100644 --- a/test/SafeSignal.cpp +++ b/test/SafeSignal.cpp @@ -2,7 +2,11 @@ #include -#include +#if __has_include() +#include +#else +#include +#endif #include #include diff --git a/test/config.cpp b/test/config.cpp index cdc96b0..3d0f007 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -1,6 +1,10 @@ #include "config.hpp" -#include +#if __has_include() +#include +#else +#include +#endif TEST_CASE("Load simple config", "[config]") { waybar::Config conf; diff --git a/test/date.cpp b/test/date.cpp new file mode 100644 index 0000000..aa6d79b --- /dev/null +++ b/test/date.cpp @@ -0,0 +1,162 @@ +#include "util/date.hpp" + +#include +#include +#include +#include +#include + +#if __has_include() +#include +#include +#else +#include +#endif + +#ifndef SKIP +#define SKIP(...) \ + WARN(__VA_ARGS__); \ + return +#endif + +using namespace std::literals::chrono_literals; + +/* + * 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}; + +/* + * Check if the date formatted with LC_TIME=en_US is within expectations. + * + * The check expects Glibc output style and will fail with FreeBSD (different implementation) + * or musl (no implementation). + */ +static const bool LC_TIME_is_sane = []() { + try { + std::stringstream ss; + ss.imbue(std::locale("en_US.UTF-8")); + + time_t t = 1641211200; + std::tm tm = *std::gmtime(&t); + + ss << std::put_time(&tm, "%x %X"); + return ss.str() == "01/03/2022 12:00:00 PM"; + } catch (std::exception &) { + return false; + } +}(); + +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 (!LC_TIME_is_sane) { + SKIP("Locale support check failed, skip tests"); + } + + /* Test a few locales that are most likely to be present */ + SECTION("US locale") { + 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 + 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"); + } catch (const std::runtime_error &) { + WARN("Locale en_US not found, skip tests"); + } + } + SECTION("GB locale") { + 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 + 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"); + } catch (const std::runtime_error &) { + WARN("Locale en_GB not found, skip tests"); + } + } + SECTION("Global locale") { + 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 + 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"); + + std::locale::global(loc); + } catch (const std::runtime_error &) { + WARN("Locale en_US not found, skip tests"); + } + } +} + +TEST_CASE("Format zoned time", "[clock][util]") { + const auto loc = std::locale("C"); + const auto tm = date::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 (!LC_TIME_is_sane) { + SKIP("Locale support check failed, skip tests"); + } + + /* Test a few locales that are most likely to be present */ + SECTION("US locale") { + 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 + 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"); + } catch (const std::runtime_error &) { + WARN("Locale en_US not found, skip tests"); + } + } + SECTION("GB locale") { + 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 + 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"); + } catch (const std::runtime_error &) { + WARN("Locale en_GB not found, skip tests"); + } + } + SECTION("Global locale") { + 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 + 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"); + + std::locale::global(loc); + } catch (const std::runtime_error &) { + WARN("Locale en_US not found, skip tests"); + } + } +} diff --git a/test/main.cpp b/test/main.cpp index 7970c26..daeee69 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -3,8 +3,13 @@ #include #include +#if __has_include() #include #include +#else +#include +#include +#endif #include int main(int argc, char* argv[]) { @@ -13,10 +18,16 @@ int main(int argc, char* argv[]) { session.applyCommandLine(argc, argv); const auto logger = spdlog::default_logger(); +#if CATCH_VERSION_MAJOR >= 3 for (const auto& spec : session.config().getReporterSpecs()) { - if (spec.name() == "tap") { + const auto& reporter_name = spec.name(); +#else + { + const auto& reporter_name = session.config().getReporterName(); +#endif + if (reporter_name == "tap") { spdlog::set_pattern("# [%l] %v"); - } else if (spec.name() == "compact") { + } else if (reporter_name == "compact") { logger->sinks().clear(); } else { logger->sinks().assign({std::make_shared()}); diff --git a/test/meson.build b/test/meson.build index b1e1123..02cbb2a 100644 --- a/test/meson.build +++ b/test/meson.build @@ -15,7 +15,7 @@ test_src = files( if tz_dep.found() test_dep += tz_dep - test_src += files('waybar_time.cpp') + test_src += files('date.cpp') endif waybar_test = executable( diff --git a/test/waybar_time.cpp b/test/waybar_time.cpp deleted file mode 100644 index 79469d4..0000000 --- a/test/waybar_time.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "util/waybar_time.hpp" - -#include -#include - -#include -#include -#include - -using namespace std::literals::chrono_literals; - -/* - * Check that the date/time formatter with locale and timezone support is working as expected. - */ - -const date::zoned_time TEST_TIME = date::make_zoned( - "UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s); - -TEST_CASE("Format UTC time", "[clock][util]") { - waybar::waybar_time tm{std::locale("C"), TEST_TIME}; - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); - - /* Test a few locales that are most likely to be present */ - SECTION("US locale") { - try { - tm.locale = std::locale("en_US"); - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM")); - REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); - } catch (const std::runtime_error&) { - // locale not found; ignore - } - } - SECTION("GB locale") { - try { - tm.locale = std::locale("en_GB"); - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05")); - REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 13:04:05"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); - } catch (const std::runtime_error&) { - // locale not found; ignore - } - } -} - -TEST_CASE("Format zoned time", "[clock][util]") { - waybar::waybar_time tm{std::locale("C"), date::make_zoned("America/New_York", TEST_TIME)}; - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); - - /* Test a few locales that are most likely to be present */ - SECTION("US locale") { - try { - tm.locale = std::locale("en_US"); - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM")); - REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); - } catch (const std::runtime_error&) { - // locale not found; ignore - } - } - - SECTION("GB locale") { - try { - tm.locale = std::locale("en_GB"); - - REQUIRE(fmt::format("{}", tm).empty()); // no format specified - REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 - Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05")); - REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 08:04:05"); - REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); - } catch (const std::runtime_error&) { - // locale not found; ignore - } - } -} From a9613892bb628525ddcc38d38104bb08ba8b5da5 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 24 Jan 2023 09:33:38 +0100 Subject: [PATCH 053/217] Rename .envrc to .envrc.sample --- .envrc => .envrc.sample | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .envrc => .envrc.sample (100%) diff --git a/.envrc b/.envrc.sample similarity index 100% rename from .envrc rename to .envrc.sample From e128c48573fc1cb2db3814fcdaa708e186755a59 Mon Sep 17 00:00:00 2001 From: Brent George Date: Tue, 24 Jan 2023 14:34:02 -0500 Subject: [PATCH 054/217] image module CSS fix --- include/modules/image.hpp | 4 +++- man/waybar-image.5.scd | 12 +++++------- src/factory.cpp | 5 +++-- src/modules/image.cpp | 15 ++++++++++----- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/modules/image.hpp b/include/modules/image.hpp index 06b61ee..b2c8946 100644 --- a/include/modules/image.hpp +++ b/include/modules/image.hpp @@ -7,6 +7,7 @@ #include #include "ALabel.hpp" +#include "gtkmm/box.h" #include "util/command.hpp" #include "util/json.hpp" #include "util/sleeper_thread.hpp" @@ -15,7 +16,7 @@ namespace waybar::modules { class Image : public AModule { public: - Image(const std::string&, const std::string&, const Json::Value&); + Image(const std::string&, const Json::Value&); auto update() -> void; void refresh(int /*signal*/); @@ -23,6 +24,7 @@ class Image : public AModule { void delayWorker(); void handleEvent(); + Gtk::Box box_; Gtk::Image image_; std::string path_; int size_; diff --git a/man/waybar-image.5.scd b/man/waybar-image.5.scd index df7086f..2d09f66 100644 --- a/man/waybar-image.5.scd +++ b/man/waybar-image.5.scd @@ -10,8 +10,6 @@ The *image* module displays an image from a path. # CONFIGURATION -Addressed by *custom/* - *path*: ++ typeof: string ++ The path to the image. @@ -58,15 +56,15 @@ Addressed by *custom/* # EXAMPLES -## Spotify: - -## mpd: - ``` -"image/album-art": { +"image#album-art": { "path": "/tmp/mpd_art", "size": 32, "interval": 5, "on-click": "mpc toggle" } ``` + +# STYLE + +- *#image* diff --git a/src/factory.cpp b/src/factory.cpp index c5b70a9..028d69e 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -97,6 +97,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { } if (ref == "disk") { return new waybar::modules::Disk(id, config_[name]); + } + if (ref == "image") { + return new waybar::modules::Image(id, config_[name]); } #ifdef HAVE_DBUSMENU if (ref == "tray") { @@ -156,8 +159,6 @@ 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]); - } else if (ref.compare(0, 6, "image/") == 0 && ref.size() > 6) { - return new waybar::modules::Image(ref.substr(6), id, config_[name]); } } catch (const std::exception& e) { auto err = fmt::format("Disabling module \"{}\", {}", name, e.what()); diff --git a/src/modules/image.cpp b/src/modules/image.cpp index eed19ae..769c16a 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -1,11 +1,16 @@ #include "modules/image.hpp" -#include - -waybar::modules::Image::Image(const std::string& name, const std::string& id, +waybar::modules::Image::Image(const std::string& id, const Json::Value& config) - : AModule(config, "image-" + name, id, "{}") { - event_box_.add(image_); + : AModule(config, "image", id), + box_(Gtk::ORIENTATION_HORIZONTAL, 0) + { + box_.pack_start(image_); + box_.set_name("image"); + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + event_box_.add(box_); dp.emit(); From c1ceb7ac42b78a2ede260e6f9ce05e536e7501a3 Mon Sep 17 00:00:00 2001 From: Brent George Date: Tue, 24 Jan 2023 14:53:49 -0500 Subject: [PATCH 055/217] fix linting issues --- src/factory.cpp | 2 +- src/modules/image.cpp | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/factory.cpp b/src/factory.cpp index 028d69e..4f196f5 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -97,7 +97,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { } if (ref == "disk") { return new waybar::modules::Disk(id, config_[name]); - } + } if (ref == "image") { return new waybar::modules::Image(id, config_[name]); } diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 769c16a..130fe79 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -1,10 +1,7 @@ #include "modules/image.hpp" -waybar::modules::Image::Image(const std::string& id, - const Json::Value& config) - : AModule(config, "image", id), - box_(Gtk::ORIENTATION_HORIZONTAL, 0) - { +waybar::modules::Image::Image(const std::string& id, const Json::Value& config) + : AModule(config, "image", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0) { box_.pack_start(image_); box_.set_name("image"); if (!id.empty()) { From f3f0b008c6e0590761aff208b5e0ec84af3354be Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Wed, 25 Jan 2023 16:56:45 +0300 Subject: [PATCH 056/217] Clock. Proper handletoggle propagation 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 37d5e30..012d3d6 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -216,7 +216,7 @@ bool waybar::modules::Clock::handleToggle(GdkEventButton* const& e) { if (callMethod) { (this->*callMethod)(); } else - return AModule::handleToggle(e); + return ALabel::handleToggle(e); update(); return true; From 1495b957f18cb9b01e0915e901fcf98e59ca4a29 Mon Sep 17 00:00:00 2001 From: Anuragh K P Date: Wed, 25 Jan 2023 22:28:07 +0530 Subject: [PATCH 057/217] for image module get path from executable file --- include/modules/image.hpp | 1 + src/modules/image.cpp | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/include/modules/image.hpp b/include/modules/image.hpp index 06b61ee..391922c 100644 --- a/include/modules/image.hpp +++ b/include/modules/image.hpp @@ -27,6 +27,7 @@ class Image : public AModule { std::string path_; int size_; int interval_; + util::command::res output_; util::SleeperThread thread_; }; diff --git a/src/modules/image.cpp b/src/modules/image.cpp index eed19ae..01f1375 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -9,7 +9,7 @@ waybar::modules::Image::Image(const std::string& name, const std::string& id, dp.emit(); - path_ = config["path"].asString(); + //path_ = config["path"].asString(); size_ = config["size"].asInt(); interval_ = config_["interval"].asInt(); @@ -41,7 +41,15 @@ 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 + { + output_ = util::command::exec(config_["exec"].asString()); + path_ =output_.out; + } if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) pixbuf = Gdk::Pixbuf::create_from_file(path_, size_, size_); else From 9068b7548fad9d78085ceffb3c641879326fc04c Mon Sep 17 00:00:00 2001 From: Anuragh K P Date: Wed, 25 Jan 2023 22:29:33 +0530 Subject: [PATCH 058/217] for image module get path from executable file --- src/modules/image.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 01f1375..2388d60 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -9,7 +9,6 @@ waybar::modules::Image::Image(const std::string& name, const std::string& id, dp.emit(); - //path_ = config["path"].asString(); size_ = config["size"].asInt(); interval_ = config_["interval"].asInt(); From 5d8f9a8273d8ca34ac6642037c0446facfe58f9d Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 27 Jan 2023 15:55:27 +0300 Subject: [PATCH 059/217] Clock. Formating simplify Signed-off-by: Viktar Lukashonak --- include/modules/clock.hpp | 2 +- src/modules/clock.cpp | 68 +++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 9d615ae..10012ab 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -49,7 +49,7 @@ class Clock : public ALabel { CldMode cldMode_{CldMode::MONTH}; uint cldMonCols_{3}; // Count of the month in the row int cldMonColLen_{20}; // Length of the month column - int cldWnLen_{2}; // Length of the week number + int cldWnLen_{3}; // Length of the week number date::year_month_day cldYearShift_; date::year_month cldMonShift_; date::months cldCurrShift_{0}; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 012d3d6..0e1ea60 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -70,23 +70,19 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) fmtMap_.insert({2, config_[kCalendarPlaceholder]["format"]["days"].asString()}); else fmtMap_.insert({2, "{}"}); - if (config_[kCalendarPlaceholder]["format"]["weeks"].isString()) { + 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}")}); - - if (cldWPos_ == WeeksSide::HIDDEN) - cldWnLen_ = 0; - else { - // tmp contains full length of the weeks including user characters - Glib::ustring tmp{std::regex_replace(fmtMap_[4], std::regex("]+>|\\{.*\\}"), "")}; - cldWnLen_ += (tmp.size() + 1); - cldMonColLen_ += cldWnLen_; - } + 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()}); @@ -277,7 +273,7 @@ auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow, } auto getCalendarLine(date::year_month_day const currDate, date::year_month const ym, - unsigned const line, date::weekday const firstdow, int rowLen, + unsigned const line, date::weekday const firstdow, const std::locale* const locale_) -> std::string { using namespace date::literals; std::ostringstream res; @@ -285,8 +281,8 @@ auto getCalendarLine(date::year_month_day const currDate, date::year_month const switch (line) { case 0: { // Output month and year title - Glib::ustring wd_ustring{Glib::ustring::format( - std::left, std::setw(rowLen), date::format(*locale_, "%B %Y", ym), std::right)}; + Glib::ustring wd_ustring{ + Glib::ustring::format(std::left, date::format(*locale_, "%B %Y", ym), std::right)}; res << wd_ustring; break; } @@ -308,7 +304,6 @@ auto getCalendarLine(date::year_month_day const currDate, date::year_month const res << pad << wd_ustring; } while (++wd != firstdow); - break; } case 2: { @@ -354,9 +349,7 @@ auto getCalendarLine(date::year_month_day const currDate, date::year_month const } // Append row with spaces if the week did not complete res << std::string(static_cast((firstdow - wd).count()) * 3, ' '); - } else // Otherwise not a valid week, output a blank row - res << std::string(rowLen, ' '); - + } break; } } @@ -412,28 +405,35 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, // Week numbers on the left if (cldWPos_ == WeeksSide::LEFT && line > 0) { - if (line == 1 && cldWnLen_ > 0) os << std::string(cldWnLen_, ' '); - - if (line > 1 && 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)}) - << ' '; + if (line > 1) { + 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)}) + << ' '; + else + os << std::string(cldWnLen_, ' '); + } } - os << getCalendarLine(currDate, ymTmp, line, firstdow, cldMonColLen_, &locale_); + os << fmt::format( + fmt::runtime((cldWPos_ == WeeksSide::RIGHT || line == 0) ? "{:<{}}" : "{:>{}}"), + getCalendarLine(currDate, ymTmp, line, firstdow, &locale_), + (cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0))); // Week numbers on the right if (cldWPos_ == WeeksSide ::RIGHT && line > 0) { - if (line == 1 && cldWnLen_ > 0) os << std::string(cldWnLen_, ' '); - - if (line > 1 && 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)}); + if (line > 1) { + 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)}); + else + os << std::string(cldWnLen_, ' '); + } } } } From 012baadaca2a6dea6d4e9f2b23ff99920735a4d5 Mon Sep 17 00:00:00 2001 From: Anuragh K P Date: Sun, 29 Jan 2023 10:41:24 +0530 Subject: [PATCH 060/217] added exec condition check on image module --- src/modules/image.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 2388d60..069f723 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -44,11 +44,15 @@ auto waybar::modules::Image::update() -> void { { path_ = config_["path"].asString(); } - else + else if(config_['exec'].isString()) { output_ = util::command::exec(config_["exec"].asString()); path_ =output_.out; } + else + { + path_=""; + } if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) pixbuf = Gdk::Pixbuf::create_from_file(path_, size_, size_); else From 220b859948b905da6c16d810afa10f74f0d15007 Mon Sep 17 00:00:00 2001 From: Oleksandr Kulkov Date: Sun, 29 Jan 2023 13:14:05 +0100 Subject: [PATCH 061/217] Fix kbName initialization Second argument of substr is the length of the substring, _not_ the position. With positions, it's better to do like this. Example: ```sh [2023-01-29 13:08:00.927] [debug] hyprland IPC received activelayout>>ITE Tech. Inc. ITE Device(8910) Keyboard,Russian (with Ukrainian-Belorussian layout) [2023-01-29 13:08:00.927] [debug] kbName is ITE Tech. Inc. ITE Device(8910) Keyboard,Russian (with ``` After the fix it's correct: ```sh [2023-01-29 13:11:11.408] [debug] hyprland IPC received activelayout>>ITE Tech. Inc. ITE Device(8910) Keyboard,Russian (with Ukrainian-Belorussian layout) [2023-01-29 13:11:11.408] [debug] kbName is ITE Tech. Inc. ITE Device(8910) Keyboard ``` --- 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 f9ad091..622c28d 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -49,7 +49,7 @@ auto Language::update() -> void { void Language::onEvent(const std::string& ev) { std::lock_guard lg(mutex_); - auto kbName = ev.substr(ev.find_last_of('>') + 1, ev.find_first_of(',')); + 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); if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString()) From 7554d7f07135fc0005124cc5cb78ae588d3d7bb5 Mon Sep 17 00:00:00 2001 From: Anuragh K P Date: Sun, 29 Jan 2023 19:13:01 +0530 Subject: [PATCH 062/217] Fixing build issue in image module --- src/modules/image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 069f723..d2eb779 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -44,7 +44,7 @@ auto waybar::modules::Image::update() -> void { { path_ = config_["path"].asString(); } - else if(config_['exec'].isString()) + else if(config_["exec"].isString()) { output_ = util::command::exec(config_["exec"].asString()); path_ =output_.out; From 7b3a6fbaa70f44963cb09008716b46deef7cf57d Mon Sep 17 00:00:00 2001 From: Anuragh K P Date: Mon, 30 Jan 2023 18:35:06 +0530 Subject: [PATCH 063/217] created local variable for _output --- include/modules/image.hpp | 1 - src/modules/image.cpp | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/modules/image.hpp b/include/modules/image.hpp index 391922c..06b61ee 100644 --- a/include/modules/image.hpp +++ b/include/modules/image.hpp @@ -27,7 +27,6 @@ class Image : public AModule { std::string path_; int size_; int interval_; - util::command::res output_; util::SleeperThread thread_; }; diff --git a/src/modules/image.cpp b/src/modules/image.cpp index d2eb779..b153523 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -39,6 +39,8 @@ void waybar::modules::Image::refresh(int sig) { } auto waybar::modules::Image::update() -> void { + util::command::res output_; + Glib::RefPtr pixbuf; if(config_["path"].isString()) { From 757a450324169ede368310c93000c7e184254438 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Tue, 31 Jan 2023 16:34:38 +1100 Subject: [PATCH 064/217] add river/layout --- include/factory.hpp | 1 + include/modules/river/layout.hpp | 33 +++++ man/waybar-river-layout.5.scd | 67 ++++++++++ man/waybar.5.scd.in | 1 + meson.build | 2 + protocol/river-status-unstable-v1.xml | 19 ++- src/factory.cpp | 3 + src/modules/river/layout.cpp | 171 ++++++++++++++++++++++++++ 8 files changed, 295 insertions(+), 2 deletions(-) create mode 100644 include/modules/river/layout.hpp create mode 100644 man/waybar-river-layout.5.scd create mode 100644 src/modules/river/layout.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 21dc647..f2ddf18 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -21,6 +21,7 @@ #include "modules/river/mode.hpp" #include "modules/river/tags.hpp" #include "modules/river/window.hpp" +#include "modules/river/layout.hpp" #endif #ifdef HAVE_HYPRLAND #include "modules/hyprland/backend.hpp" diff --git a/include/modules/river/layout.hpp b/include/modules/river/layout.hpp new file mode 100644 index 0000000..ffa5e21 --- /dev/null +++ b/include/modules/river/layout.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include "ALabel.hpp" +#include "bar.hpp" +#include "river-status-unstable-v1-client-protocol.h" + +namespace waybar::modules::river { + +class Layout : public waybar::ALabel { + public: + Layout(const std::string &, const waybar::Bar &, const Json::Value &); + ~Layout(); + + // Handlers for wayland events + void handle_name(const char *name); + void handle_clear(); + void handle_focused_output(struct wl_output *output); + void handle_unfocused_output(struct wl_output *output); + + struct zriver_status_manager_v1 *status_manager_; + struct wl_seat *seat_; + + private: + const waybar::Bar &bar_; + struct wl_output *output_; // stores the output this module belongs to + struct wl_output *focused_output_; // stores the currently focused output + struct zriver_output_status_v1 *output_status_; + struct zriver_seat_status_v1 *seat_status_; +}; + +} /* namespace waybar::modules::river */ diff --git a/man/waybar-river-layout.5.scd b/man/waybar-river-layout.5.scd new file mode 100644 index 0000000..5b18eee --- /dev/null +++ b/man/waybar-river-layout.5.scd @@ -0,0 +1,67 @@ +waybar-river-layout(5) + +# NAME + +waybar - river layout module + +# DESCRIPTION + +The *layout* module displays the current layout in river. + +It may not be set until a layout is first applied. + +# CONFIGURATION + +Addressed by *river/layout* + +*format*: ++ + typeof: string ++ + default: {} ++ + The format, how information should be displayed. On {} data gets inserted. + +*rotate*: ++ + typeof: integer ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*on-click*: ++ + typeof: string ++ + Command to execute when clicked on the module. + +*on-click-middle*: ++ + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. + +*on-click-right*: ++ + typeof: string ++ + Command to execute when you right clicked on the module. + +# EXAMPLE + +``` +"river/layout": { + "format": "{}", + "min-length": 4, + "align": "right" +} +``` + +# STYLE + +- *#layout* +- *#layout.focused* Applied when the output this module's bar belongs to is focused. + +# SEE ALSO + +waybar(5), river(1) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index b1ed4c5..74407bd 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -272,6 +272,7 @@ A module group is defined by specifying a module named "group/some-group-name". - *waybar-river-mode(5)* - *waybar-river-tags(5)* - *waybar-river-window(5)* +- *waybar-river-layout(5)* - *waybar-states(5)* - *waybar-sway-mode(5)* - *waybar-sway-scratchpad(5)* diff --git a/meson.build b/meson.build index 5b3c475..a4b4c7a 100644 --- a/meson.build +++ b/meson.build @@ -217,6 +217,7 @@ if true 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' endif if true @@ -404,6 +405,7 @@ if scdoc.found() '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', diff --git a/protocol/river-status-unstable-v1.xml b/protocol/river-status-unstable-v1.xml index 6a74256..e9629dd 100644 --- a/protocol/river-status-unstable-v1.xml +++ b/protocol/river-status-unstable-v1.xml @@ -16,7 +16,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - + A global factory for objects that receive status information specific to river. It could be used to implement, for example, a status bar. @@ -47,7 +47,7 @@ - + This interface allows clients to receive information about the current windowing state of an output. @@ -83,6 +83,21 @@ + + + + Sent once on binding the interface should a layout name exist and again + whenever the name changes. + + + + + + + Sent when the current layout name has been removed without a new one + being set, for example when the active layout generator disconnects. + + diff --git a/src/factory.cpp b/src/factory.cpp index c5b70a9..afe5c61 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -64,6 +64,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "river/window") { return new waybar::modules::river::Window(id, bar_, config_[name]); } + if (ref == "river/layout") { + return new waybar::modules::river::Layout(id, bar_, config_[name]); + } #endif #ifdef HAVE_HYPRLAND if (ref == "hyprland/window") { diff --git a/src/modules/river/layout.cpp b/src/modules/river/layout.cpp new file mode 100644 index 0000000..5b0222c --- /dev/null +++ b/src/modules/river/layout.cpp @@ -0,0 +1,171 @@ +#include "modules/river/layout.hpp" + +#include +#include + +#include "client.hpp" + +namespace waybar::modules::river { + +static void listen_focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + uint32_t tags) { + // Intentionally empty +} + +static void listen_view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + struct wl_array *tags) { + // Intentionally empty +} + +static void listen_urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + uint32_t tags) { + // Intentionally empty +} + +static void listen_layout_name(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, const char *layout) { + static_cast(data)->handle_name(layout); +} + +static void listen_layout_name_clear(void *data, struct zriver_output_status_v1 *zriver_output_status_v1) { + static_cast(data)->handle_clear(); +} + +static void listen_focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, + struct wl_output *output) { + static_cast(data)->handle_focused_output(output); +} + +static void listen_unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, + struct wl_output *output) { + static_cast(data)->handle_unfocused_output(output); +} + +static void listen_focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, + const char *title) { + // Intentionally empty +} + +static void listen_mode(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, + const char *mode) { + // Intentionally empty +} + +static const zriver_output_status_v1_listener output_status_listener_impl{ + .focused_tags = listen_focused_tags, + .view_tags = listen_view_tags, + .urgent_tags = listen_urgent_tags, + .layout_name = listen_layout_name, + .layout_name_clear = listen_layout_name_clear, +}; + +static const zriver_seat_status_v1_listener seat_status_listener_impl{ + .focused_output = listen_focused_output, + .unfocused_output = listen_unfocused_output, + .focused_view = listen_focused_view, + .mode = listen_mode, +}; + +static void handle_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) { + if (std::strcmp(interface, zriver_status_manager_v1_interface.name) == 0) { + version = std::min(version, 4); + + // implies ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_CLEAR_SINCE_VERSION + if (version < ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_SINCE_VERSION) { + spdlog::error( + "river server does not support the \"layout_name\" and \"layout_clear\" events; the module will be disabled" + std::to_string(version)); + return; + } + static_cast(data)->status_manager_ = static_cast( + wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, version)); + } + + if (std::strcmp(interface, wl_seat_interface.name) == 0) { + version = std::min(version, 1); + static_cast(data)->seat_ = static_cast( + wl_registry_bind(registry, name, &wl_seat_interface, version)); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + // Nobody cares +} + +static const wl_registry_listener registry_listener_impl = {.global = handle_global, + .global_remove = handle_global_remove}; + +Layout::Layout(const std::string &id, const waybar::Bar &bar, const Json::Value &config) + : waybar::ALabel(config, "layout", id, "{}"), + status_manager_{nullptr}, + seat_{nullptr}, + bar_(bar), + 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); + + output_ = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + + if (!status_manager_) { + spdlog::error("river_status_manager_v1 not advertised"); + return; + } + + if (!seat_) { + spdlog::error("wl_seat not advertised"); + } + + label_.hide(); + label_.set_markup(""); + ALabel::update(); + + seat_status_ = zriver_status_manager_v1_get_river_seat_status(status_manager_, seat_); + zriver_seat_status_v1_add_listener(seat_status_, &seat_status_listener_impl, this); + + output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output_); + zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this); + + zriver_status_manager_v1_destroy(status_manager_); +} + +Layout::~Layout() { + if (output_status_) { + zriver_output_status_v1_destroy(output_status_); + } + if (seat_status_) { + zriver_seat_status_v1_destroy(seat_status_); + } +} + +void Layout::handle_name(const char *name) { + if (std::strcmp(name, "") == 0 || format_.empty()) { + label_.hide(); // hide empty labels or labels with empty format + } else { + label_.show(); + label_.set_markup(fmt::format(format_, Glib::Markup::escape_text(name).raw())); + } + ALabel::update(); +} + +void Layout::handle_clear() { + label_.hide(); + ALabel::update(); +} + +void Layout::handle_focused_output(struct wl_output *output) { + if (output_ == output) { // if we focused the output this bar belongs to + label_.get_style_context()->add_class("focused"); + ALabel::update(); + } + focused_output_ = output; +} + +void Layout::handle_unfocused_output(struct wl_output *output) { + if (output_ == output) { // if we unfocused the output this bar belongs to + label_.get_style_context()->remove_class("focused"); + ALabel::update(); + } +} + +} /* namespace waybar::modules::river */ From 301b106c48cbdd23660a5960820d1e75c1467e51 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Tue, 31 Jan 2023 16:41:36 +1100 Subject: [PATCH 065/217] add river/layout clang-format --- src/modules/river/layout.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/modules/river/layout.cpp b/src/modules/river/layout.cpp index 5b0222c..945efa6 100644 --- a/src/modules/river/layout.cpp +++ b/src/modules/river/layout.cpp @@ -22,11 +22,13 @@ static void listen_urgent_tags(void *data, struct zriver_output_status_v1 *zrive // Intentionally empty } -static void listen_layout_name(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, const char *layout) { +static void listen_layout_name(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + const char *layout) { static_cast(data)->handle_name(layout); } -static void listen_layout_name_clear(void *data, struct zriver_output_status_v1 *zriver_output_status_v1) { +static void listen_layout_name_clear(void *data, + struct zriver_output_status_v1 *zriver_output_status_v1) { static_cast(data)->handle_clear(); } @@ -51,11 +53,11 @@ static void listen_mode(void *data, struct zriver_seat_status_v1 *zriver_seat_st } static const zriver_output_status_v1_listener output_status_listener_impl{ - .focused_tags = listen_focused_tags, - .view_tags = listen_view_tags, - .urgent_tags = listen_urgent_tags, - .layout_name = listen_layout_name, - .layout_name_clear = listen_layout_name_clear, + .focused_tags = listen_focused_tags, + .view_tags = listen_view_tags, + .urgent_tags = listen_urgent_tags, + .layout_name = listen_layout_name, + .layout_name_clear = listen_layout_name_clear, }; static const zriver_seat_status_v1_listener seat_status_listener_impl{ @@ -73,7 +75,9 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam // implies ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_CLEAR_SINCE_VERSION if (version < ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_SINCE_VERSION) { spdlog::error( - "river server does not support the \"layout_name\" and \"layout_clear\" events; the module will be disabled" + std::to_string(version)); + "river server does not support the \"layout_name\" and \"layout_clear\" events; the " + "module will be disabled" + + std::to_string(version)); return; } static_cast(data)->status_manager_ = static_cast( @@ -117,7 +121,6 @@ Layout::Layout(const std::string &id, const waybar::Bar &bar, const Json::Value } label_.hide(); - label_.set_markup(""); ALabel::update(); seat_status_ = zriver_status_manager_v1_get_river_seat_status(status_manager_, seat_); From a9c9f1d705991c7f6ff9de7eac3430a219011978 Mon Sep 17 00:00:00 2001 From: Sasha Moak Date: Tue, 31 Jan 2023 17:56:58 -0800 Subject: [PATCH 066/217] fix(wireplumber): free(): invalid pointer When freeing the `default_node_name_` pointer using `free`, the `&` operator was used to try to free the reference rather than the pointer. This caused a core dump. In order to fix this, the pointer is freed instead (ie the `&` operator is no longer used). --- 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 fd1a0d3..4c7a2d0 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -47,7 +47,7 @@ waybar::modules::Wireplumber::~Wireplumber() { g_clear_object(&wp_core_); g_clear_object(&mixer_api_); g_clear_object(&def_nodes_api_); - g_free(&default_node_name_); + g_free(default_node_name_); } void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* self, uint32_t id) { From a78f0124d2c483c7f34dcc521bcc55ca21a5c5ad Mon Sep 17 00:00:00 2001 From: Kuruyia <8174691+Kuruyia@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:40:36 +0100 Subject: [PATCH 067/217] feat(backlight): add brightness control --- include/modules/backlight.hpp | 2 + man/waybar-backlight.5.scd | 13 ++++++- src/modules/backlight.cpp | 72 +++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/include/modules/backlight.hpp b/include/modules/backlight.hpp index 81e2516..a2ce878 100644 --- a/include/modules/backlight.hpp +++ b/include/modules/backlight.hpp @@ -50,6 +50,8 @@ class Backlight : public ALabel { template static void enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, udev *udev); + bool handleScroll(GdkEventScroll* e); + const std::string preferred_device_; static constexpr int EPOLL_MAX_EVENTS = 16; diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index 9c8ba79..ca3d922 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -58,16 +58,25 @@ The *backlight* module displays the current backlight level. *on-scroll-up*: ++ typeof: string ++ - Command to execute when performing a scroll up on the module. + Command to execute when performing a scroll up on the module. This replaces the default behaviour of brightness control. *on-scroll-down*: ++ typeof: string - Command to execute when performing a scroll down on the module. + Command to execute when performing a scroll down on the module. This replaces the default behaviour of brightness control. *smooth-scrolling-threshold*: ++ typeof: double Threshold to be used when scrolling. +*reverse-scrolling*: ++ + typeof: bool ++ + Option to reverse the scroll direction. + +*scroll-step*: ++ + typeof: float ++ + default: 1.0 ++ + The speed in which to change the brightness when scrolling. + # EXAMPLE: ``` diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 77c1dc0..65eb11a 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -106,6 +106,10 @@ waybar::modules::Backlight::Backlight(const std::string &id, const Json::Value & 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)); + udev_thread_ = [this] { std::unique_ptr udev{udev_new()}; check_nn(udev.get(), "Udev new failed"); @@ -264,3 +268,71 @@ void waybar::modules::Backlight::enumerate_devices(ForwardIt first, ForwardIt la 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()) { + return AModule::handleScroll(e); + } + + // Check scroll direction + auto dir = AModule::getScrollDir(e); + if (dir == SCROLL_DIR::NONE) { + return true; + } + + if (config_["reverse-scrolling"].asBool()) { + if (dir == SCROLL_DIR::UP) { + dir = SCROLL_DIR::DOWN; + } else if (dir == SCROLL_DIR::DOWN) { + dir = SCROLL_DIR::UP; + } + } + + // Get scroll step + double step = 1; + + if (config_["scroll-step"].isDouble()) { + 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()); + + // Get a udev instance + std::unique_ptr udev{udev_new()}; + check_nn(udev.get(), "Udev new failed"); + + // Get the udev device + std::unique_ptr dev{udev_device_new_from_subsystem_sysname(udev.get(), "backlight", std::string(best->name()).c_str())}; + check_nn(dev.get(), "Udev device new failed"); + + // Set the new value + udev_device_set_sysattr_value(dev.get(), "brightness", std::to_string(new_value).c_str()); + + return true; +} From 74a8464c09cf84375954213b49d32875b907bb12 Mon Sep 17 00:00:00 2001 From: Anuragh K P Date: Wed, 1 Feb 2023 21:22:10 +0530 Subject: [PATCH 068/217] updating man page --- man/waybar-image.5.scd | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/man/waybar-image.5.scd b/man/waybar-image.5.scd index df7086f..ff0e34c 100644 --- a/man/waybar-image.5.scd +++ b/man/waybar-image.5.scd @@ -15,7 +15,10 @@ Addressed by *custom/* *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 *size*: ++ typeof: integer ++ The width/height to render the image. From b5ad77ea8cb521660d612cdebfbb2bda50724dd9 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Thu, 2 Feb 2023 13:26:05 +0300 Subject: [PATCH 069/217] Avoid of unnecessary string transformation Signed-off-by: Viktar Lukashonak --- src/modules/clock.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 0e1ea60..6a06193 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -281,9 +281,7 @@ auto getCalendarLine(date::year_month_day const currDate, date::year_month const switch (line) { case 0: { // Output month and year title - Glib::ustring wd_ustring{ - Glib::ustring::format(std::left, date::format(*locale_, "%B %Y", ym), std::right)}; - res << wd_ustring; + res << date::format(*locale_, "%B %Y", ym); break; } case 1: { @@ -418,7 +416,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, } os << fmt::format( - fmt::runtime((cldWPos_ == WeeksSide::RIGHT || line == 0) ? "{:<{}}" : "{:>{}}"), + fmt::runtime((cldWPos_ != WeeksSide::LEFT || line == 0) ? "{:<{}}" : "{:>{}}"), getCalendarLine(currDate, ymTmp, line, firstdow, &locale_), (cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0))); From e8c4b85328f6fb6b8a250db6709595af6cca3bfb Mon Sep 17 00:00:00 2001 From: Kuruyia <8174691+Kuruyia@users.noreply.github.com> Date: Fri, 3 Feb 2023 12:58:52 +0100 Subject: [PATCH 070/217] feat(backlight): use dbus to set the brightness --- include/modules/backlight.hpp | 3 +++ src/modules/backlight.cpp | 21 ++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/modules/backlight.hpp b/include/modules/backlight.hpp index a2ce878..1882a83 100644 --- a/include/modules/backlight.hpp +++ b/include/modules/backlight.hpp @@ -6,6 +6,7 @@ #include #include "ALabel.hpp" +#include "giomm/dbusproxy.h" #include "util/json.hpp" #include "util/sleeper_thread.hpp" @@ -62,5 +63,7 @@ class Backlight : public ALabel { std::vector devices_; // thread must destruct before shared data util::SleeperThread udev_thread_; + + Glib::RefPtr login_proxy_; }; } // namespace waybar::modules diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 65eb11a..7aa5889 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -110,6 +110,11 @@ waybar::modules::Backlight::Backlight(const std::string &id, const Json::Value & 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"); @@ -275,6 +280,11 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { return AModule::handleScroll(e); } + // Fail fast if the proxy could not be initialized + if (!login_proxy_) { + return true; + } + // Check scroll direction auto dir = AModule::getScrollDir(e); if (dir == SCROLL_DIR::NONE) { @@ -323,16 +333,9 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { // Clamp the value new_value = std::clamp(new_value, 0, best->get_max()); - // Get a udev instance - std::unique_ptr udev{udev_new()}; - check_nn(udev.get(), "Udev new failed"); - - // Get the udev device - std::unique_ptr dev{udev_device_new_from_subsystem_sysname(udev.get(), "backlight", std::string(best->name()).c_str())}; - check_nn(dev.get(), "Udev device new failed"); - // Set the new value - udev_device_set_sysattr_value(dev.get(), "brightness", std::to_string(new_value).c_str()); + 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); return true; } From 973aa09f8bfbb24f630698329a51343b5e604d36 Mon Sep 17 00:00:00 2001 From: Kuruyia <8174691+Kuruyia@users.noreply.github.com> Date: Fri, 3 Feb 2023 18:18:44 +0100 Subject: [PATCH 071/217] refactor(backlight): fix linter --- src/modules/backlight.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 7aa5889..a46b296 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -325,16 +325,18 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { int new_value = best->get_actual(); if (dir == SCROLL_DIR::UP) { - new_value += abs_step; + new_value += abs_step; } else if (dir == SCROLL_DIR::DOWN) { - new_value -= abs_step; + 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)); + 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); return true; From 677cbb3384a7e30e03f19b5fc6bf3d6c19b8806b Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 4 Feb 2023 23:47:44 +0300 Subject: [PATCH 072/217] When no one timezone is provided use system's TZ 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 0e1ea60..2c22721 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -32,10 +32,10 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) time_zones_.push_back(date::locate_zone(config_["timezone"].asString())); } - // If all timezones are parsed and no one is good, add nullptr to the timezones vector, to mark - // that local time should be shown. + // 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 (!time_zones_.size()) { - 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 From 67ab2697068fb0ea55fd19feb8ab3ef0fb7f138d Mon Sep 17 00:00:00 2001 From: Bryan Waite Date: Wed, 8 Feb 2023 17:19:51 +1100 Subject: [PATCH 073/217] Fixing memory leak on update UPower tooltip --- src/main.cpp | 6 ++++++ src/modules/upower/upower_tooltip.cpp | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index e06774b..ff446ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -85,6 +85,12 @@ int main(int argc, char* argv[]) { 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) { diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp index bec55c7..7dd5d10 100644 --- a/src/modules/upower/upower_tooltip.cpp +++ b/src/modules/upower/upower_tooltip.cpp @@ -29,7 +29,7 @@ UPowerTooltip::~UPowerTooltip() {} uint UPowerTooltip::updateTooltip(Devices& devices) { // Removes all old devices for (auto child : contentBox->get_children()) { - child->~Widget(); + delete child; } uint deviceCount = 0; From ea38eec2afd249481df737cc38e055974f83967c Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 9 Feb 2023 13:25:39 +0100 Subject: [PATCH 074/217] fix: lint --- include/modules/backlight.hpp | 2 +- include/modules/clock.hpp | 15 ++++++--------- src/modules/image.cpp | 15 +++++---------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/include/modules/backlight.hpp b/include/modules/backlight.hpp index 1882a83..6597d39 100644 --- a/include/modules/backlight.hpp +++ b/include/modules/backlight.hpp @@ -51,7 +51,7 @@ class Backlight : public ALabel { template static void enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, udev *udev); - bool handleScroll(GdkEventScroll* e); + bool handleScroll(GdkEventScroll *e); const std::string preferred_device_; static constexpr int EPOLL_MAX_EVENTS = 16; diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 10012ab..c318e8b 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -15,10 +15,7 @@ enum class WeeksSide { HIDDEN, }; -enum class CldMode { - MONTH, - YEAR -}; +enum class CldMode { MONTH, YEAR }; class Clock : public ALabel { public: @@ -47,9 +44,9 @@ class Clock : public ALabel { WeeksSide cldWPos_{WeeksSide::HIDDEN}; std::map fmtMap_; 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 + 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}; @@ -57,8 +54,8 @@ class Clock : public ALabel { std::string cldYearCached_{}; std::string cldMonCached_{}; /*Calendar functions*/ - auto get_calendar(const date::zoned_seconds& now, - const date::zoned_seconds& wtime) -> std::string; + auto get_calendar(const date::zoned_seconds& now, const date::zoned_seconds& wtime) + -> std::string; void cldModeSwitch(); }; } // namespace waybar::modules diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 22f4982..a938617 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -44,18 +44,13 @@ auto waybar::modules::Image::update() -> void { util::command::res output_; Glib::RefPtr pixbuf; - if(config_["path"].isString()) - { + if (config_["path"].isString()) { path_ = config_["path"].asString(); - } - else if(config_["exec"].isString()) - { + } else if (config_["exec"].isString()) { output_ = util::command::exec(config_["exec"].asString()); - path_ =output_.out; - } - else - { - path_=""; + path_ = output_.out; + } else { + path_ = ""; } if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) pixbuf = Gdk::Pixbuf::create_from_file(path_, size_, size_); From 43958ef5606b4375a7a751d16d370badcec27916 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 11 Feb 2023 13:26:03 +1100 Subject: [PATCH 075/217] fix: compilation errors with cpp_std=c++20 --- src/modules/river/layout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/river/layout.cpp b/src/modules/river/layout.cpp index 945efa6..e938400 100644 --- a/src/modules/river/layout.cpp +++ b/src/modules/river/layout.cpp @@ -146,7 +146,7 @@ void Layout::handle_name(const char *name) { label_.hide(); // hide empty labels or labels with empty format } else { label_.show(); - label_.set_markup(fmt::format(format_, Glib::Markup::escape_text(name).raw())); + label_.set_markup(fmt::format(fmt::runtime(format_), Glib::Markup::escape_text(name).raw())); } ALabel::update(); } From 4dcce810d2bfd132d5364cb76f308e3bdc95f8fd Mon Sep 17 00:00:00 2001 From: chayleaf Date: Thu, 16 Feb 2023 18:36:28 +0700 Subject: [PATCH 076/217] improve nix dev shell; override the nixpkgs waybar for example, the update from 0.9.16 to 0.9.17 broke this flake, after this change the derivation will be the same as the nixpkgs one. This is the better option since the flake is unmaintained in this repo (although it may still break inbetween releases) --- flake.nix | 43 ++++++++++++++-- nix/default.nix | 127 ++---------------------------------------------- 2 files changed, 44 insertions(+), 126 deletions(-) diff --git a/flake.nix b/flake.nix index d909186..66bc397 100644 --- a/flake.nix +++ b/flake.nix @@ -26,9 +26,9 @@ ]); in { - overlays.default = _: prev: rec { + overlays.default = _: prev: { waybar = prev.callPackage ./nix/default.nix { - version = "0.9.16" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); + version = prev.waybar.version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); }; }; packages = genSystems @@ -55,10 +55,47 @@ } ]; devshell.packages = with pkgs; [ + # from nativeBuildInputs clang-tools gdb - ]; + meson + ninja + pkg-config + scdoc + ] ++ (map lib.getDev [ + # from buildInputs + wayland wlroots gtkmm3 libsigcxx jsoncpp spdlog gtk-layer-shell howard-hinnant-date libxkbcommon + # opttional 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 + ]); language.c.libraries = with pkgs; [ + # runtime dependencies (normally handled by patchElf) + bzip2 celt dbus expat flac fontconfig freetype fribidi glibc + graphite2 gtk3 howard-hinnant-date icu json-glib libGL + libasyncns libcap libdatrie libepoxy libffi libgcrypt + libgpg-error libgudev libjpeg libogg libopus libpng + libselinux libsndfile libthai libvorbis libwacom libxml2 lz4 + mtdev pcre pcre2 pipewire pixman pulseaudio sndio sqlite + tracker util-linux xorg.libX11 xorg.libXau + xorg.libXcomposite xorg.libXcursor xorg.libXdmcp + xorg.libXext xorg.libXfixes xorg.libXi xorg.libXinerama + xorg.libXrandr xorg.libXrender xorg.libxcb xz zlib zstd + ]; + 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"; } + { name = "LD_LIBRARY_PATH"; prefix = "${lib.getLib pulseaudio}/lib/pulseaudio"; } ]; }; }); diff --git a/nix/default.nix b/nix/default.nix index 2665446..fc77225 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,60 +1,11 @@ { lib -, stdenv -, fetchFromGitHub -, meson -, pkg-config -, ninja -, wrapGAppsHook -, wayland -, wlroots -, gtkmm3 -, libsigcxx -, jsoncpp -, scdoc -, spdlog -, gtk-layer-shell -, howard-hinnant-date -, libinotify-kqueue -, libxkbcommon -, evdevSupport ? true -, libevdev -, inputSupport ? true -, libinput -, jackSupport ? true -, libjack2 -, mpdSupport ? true -, libmpdclient -, nlSupport ? true -, libnl -, pulseSupport ? true -, libpulseaudio -, rfkillSupport ? true -, runTests ? true -, catch2_3 -, sndioSupport ? true -, sndio -, swaySupport ? true -, sway -, traySupport ? true -, libdbusmenu-gtk3 -, udevSupport ? true -, udev -, upowerSupport ? true -, upower -, wireplumberSupport ? true -, wireplumber -, withMediaPlayer ? false -, glib -, gobject-introspection -, python3 -, playerctl +, waybar , version }: -stdenv.mkDerivation rec { - pname = "waybar"; +waybar.overrideAttrs (prev: { inherit version; - # version = "0.9.16"; + # version = "0.9.17"; src = lib.cleanSourceWith { filter = name: type: @@ -66,74 +17,4 @@ stdenv.mkDerivation rec { ); src = lib.cleanSource ../.; }; - - nativeBuildInputs = [ - meson - ninja - pkg-config - scdoc - wrapGAppsHook - ] ++ lib.optional withMediaPlayer gobject-introspection; - - propagatedBuildInputs = lib.optionals withMediaPlayer [ - glib - playerctl - python3.pkgs.pygobject3 - ]; - strictDeps = false; - - buildInputs = with lib; - [ wayland wlroots gtkmm3 libsigcxx jsoncpp spdlog gtk-layer-shell howard-hinnant-date libxkbcommon ] - ++ optional (!stdenv.isLinux) libinotify-kqueue - ++ optional evdevSupport libevdev - ++ optional inputSupport libinput - ++ optional jackSupport libjack2 - ++ optional mpdSupport libmpdclient - ++ optional nlSupport libnl - ++ optional pulseSupport libpulseaudio - ++ optional sndioSupport sndio - ++ optional swaySupport sway - ++ optional traySupport libdbusmenu-gtk3 - ++ optional udevSupport udev - ++ optional upowerSupport upower - ++ optional wireplumberSupport wireplumber; - - checkInputs = [ catch2_3 ]; - doCheck = runTests; - - mesonFlags = (lib.mapAttrsToList - (option: enable: "-D${option}=${if enable then "enabled" else "disabled"}") - { - dbusmenu-gtk = traySupport; - jack = jackSupport; - libinput = inputSupport; - libnl = nlSupport; - libudev = udevSupport; - mpd = mpdSupport; - pulseaudio = pulseSupport; - rfkill = rfkillSupport; - sndio = sndioSupport; - tests = runTests; - upower_glib = upowerSupport; - wireplumber = wireplumberSupport; - } - ) ++ [ - "-Dsystemd=disabled" - "-Dgtk-layer-shell=enabled" - "-Dman-pages=enabled" - ]; - - preFixup = lib.optionalString withMediaPlayer '' - cp $src/resources/custom_modules/mediaplayer.py $out/bin/waybar-mediaplayer.py - wrapProgram $out/bin/waybar-mediaplayer.py \ - --prefix PYTHONPATH : "$PYTHONPATH:$out/${python3.sitePackages}" - ''; - - meta = with lib; { - description = "Highly customizable Wayland bar for Sway and Wlroots based compositors"; - license = licenses.mit; - maintainers = with maintainers; [ FlorianFranzen minijackson synthetica lovesegfault ]; - platforms = platforms.unix; - homepage = "https://github.com/alexays/waybar"; - }; -} +}) From a7dbab79e5156de793c656006c53cef8b4566361 Mon Sep 17 00:00:00 2001 From: chayleaf Date: Thu, 16 Feb 2023 20:02:39 +0700 Subject: [PATCH 077/217] add default.nix for compatibility; update flake.lock --- default.nix | 13 +++++++++++++ flake.lock | 18 +++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 default.nix diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..5cf1b78 --- /dev/null +++ b/default.nix @@ -0,0 +1,13 @@ +( + import + ( + builtins.fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz"; + sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; + } + ) + { + src = ./.; + } +) +.defaultNix diff --git a/flake.lock b/flake.lock index 6d3b6b8..7920067 100644 --- a/flake.lock +++ b/flake.lock @@ -6,11 +6,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1667210711, - "narHash": "sha256-IoErjXZAkzYWHEpQqwu/DeRNJGFdR7X2OGbkhMqMrpw=", + "lastModified": 1676293499, + "narHash": "sha256-uIOTlTxvrXxpKeTvwBI1JGDGtCxMXE3BI0LFwoQMhiQ=", "owner": "numtide", "repo": "devshell", - "rev": "96a9dd12b8a447840cc246e17a47b81a4268bba7", + "rev": "71e3022e3ab20bbf1342640547ef5bc14fb43bf4", "type": "github" }, "original": { @@ -36,11 +36,11 @@ }, "flake-utils_2": { "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "lastModified": 1676283394, + "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", "owner": "numtide", "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073", "type": "github" }, "original": { @@ -67,11 +67,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1670152712, - "narHash": "sha256-LJttwIvJqsZIj8u1LxVRv82vwUtkzVqQVi7Wb8gxPS4=", + "lastModified": 1676300157, + "narHash": "sha256-1HjRzfp6LOLfcj/HJHdVKWAkX9QRAouoh6AjzJiIerU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "14ddeaebcbe9a25748221d1d7ecdf98e20e2325e", + "rev": "545c7a31e5dedea4a6d372712a18e00ce097d462", "type": "github" }, "original": { From f51894614d2694d7f44ef66dec2a199d0d6b3d3f Mon Sep 17 00:00:00 2001 From: chayleaf Date: Thu, 16 Feb 2023 20:25:07 +0700 Subject: [PATCH 078/217] after updating nix.lock all this soup isn't needed --- flake.nix | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/flake.nix b/flake.nix index 66bc397..0905931 100644 --- a/flake.nix +++ b/flake.nix @@ -55,9 +55,10 @@ } ]; devshell.packages = with pkgs; [ - # from nativeBuildInputs clang-tools gdb + # from nativeBuildInputs + gnumake meson ninja pkg-config @@ -74,19 +75,6 @@ at-spi2-atk atkmm cairo cairomm catch2 fmt_8 fontconfig gdk-pixbuf glibmm gtk3 harfbuzz pango pangomm wayland-protocols ]); - language.c.libraries = with pkgs; [ - # runtime dependencies (normally handled by patchElf) - bzip2 celt dbus expat flac fontconfig freetype fribidi glibc - graphite2 gtk3 howard-hinnant-date icu json-glib libGL - libasyncns libcap libdatrie libepoxy libffi libgcrypt - libgpg-error libgudev libjpeg libogg libopus libpng - libselinux libsndfile libthai libvorbis libwacom libxml2 lz4 - mtdev pcre pcre2 pipewire pixman pulseaudio sndio sqlite - tracker util-linux xorg.libX11 xorg.libXau - xorg.libXcomposite xorg.libXcursor xorg.libXdmcp - xorg.libXext xorg.libXfixes xorg.libXi xorg.libXinerama - xorg.libXrandr xorg.libXrender xorg.libxcb xz zlib zstd - ]; env = with pkgs; [ { name = "CPLUS_INCLUDE_PATH"; prefix = "$DEVSHELL_DIR/include"; } { name = "PKG_CONFIG_PATH"; prefix = "$DEVSHELL_DIR/lib/pkgconfig"; } @@ -95,7 +83,6 @@ { 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"; } - { name = "LD_LIBRARY_PATH"; prefix = "${lib.getLib pulseaudio}/lib/pulseaudio"; } ]; }; }); From 529031f44facf1a28c0b632024d2c0b9d468e6cb Mon Sep 17 00:00:00 2001 From: chayleaf Date: Thu, 16 Feb 2023 20:26:57 +0700 Subject: [PATCH 079/217] fix typo --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 0905931..ca27d59 100644 --- a/flake.nix +++ b/flake.nix @@ -66,7 +66,7 @@ ] ++ (map lib.getDev [ # from buildInputs wayland wlroots gtkmm3 libsigcxx jsoncpp spdlog gtk-layer-shell howard-hinnant-date libxkbcommon - # opttional dependencies + # optional dependencies gobject-introspection glib playerctl python3.pkgs.pygobject3 libevdev libinput libjack2 libmpdclient playerctl libnl libpulseaudio sndio sway libdbusmenu-gtk3 udev upower wireplumber From 3f23792df0ac565d702c757cf95c21642d1d7d0b Mon Sep 17 00:00:00 2001 From: chayleaf Date: Thu, 16 Feb 2023 20:31:02 +0700 Subject: [PATCH 080/217] use the recommended way of using flake-compat --- default.nix | 17 +++++++---------- flake.lock | 17 +++++++++++++++++ flake.nix | 6 +++++- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/default.nix b/default.nix index 5cf1b78..2cccff2 100644 --- a/default.nix +++ b/default.nix @@ -1,13 +1,10 @@ -( - import +(import ( - builtins.fetchTarball { - url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz"; - sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; + 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 + { src = ./.; } +).defaultNix diff --git a/flake.lock b/flake.lock index 7920067..b10c9bf 100644 --- a/flake.lock +++ b/flake.lock @@ -19,6 +19,22 @@ "type": "github" } }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "locked": { "lastModified": 1642700792, @@ -84,6 +100,7 @@ "root": { "inputs": { "devshell": "devshell", + "flake-compat": "flake-compat", "flake-utils": "flake-utils_2", "nixpkgs": "nixpkgs_2" } diff --git a/flake.nix b/flake.nix index ca27d59..97f4ed5 100644 --- a/flake.nix +++ b/flake.nix @@ -5,9 +5,13 @@ 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 }: + outputs = { self, flake-utils, devshell, nixpkgs, flake-compat }: let inherit (nixpkgs) lib; genSystems = lib.genAttrs [ From a10464d9bb67ba75d3c9159bd027b67a565318e1 Mon Sep 17 00:00:00 2001 From: Pascal Huber Date: Sat, 18 Feb 2023 19:57:09 +0100 Subject: [PATCH 081/217] feat(sway): add workspace css class --- src/modules/sway/workspaces.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 08742ae..c0d92b6 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -231,6 +231,7 @@ auto Workspaces::update() -> void { box_.reorder_child(button, it - workspaces_.begin()); } std::string output = (*it)["name"].asString(); + button.get_style_context()->add_class("workspace-" + trimWorkspaceName(output)); if (config_["format"].isString()) { auto format = config_["format"].asString(); output = fmt::format(fmt::runtime(format), fmt::arg("icon", getIcon(output, *it)), From 0d99d3808952b58fee324970c864c3210b6f99e6 Mon Sep 17 00:00:00 2001 From: Elyes Haouas Date: Sat, 25 Feb 2023 09:51:40 +0100 Subject: [PATCH 082/217] modules/network.cpp: Remove repeated "the" Signed-off-by: Elyes Haouas --- 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 8409311..9e9a93a 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -98,7 +98,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf // Start with some "text" in the module's label_. update() will then // update it. Since the text should be different, update() will be able // to show or hide the event_box_. This is to work around the case where - // the module start with no text, but the the event_box_ is shown. + // the module start with no text, but the event_box_ is shown. label_.set_markup(""); auto bandwidth = readBandwidthUsage(); From 1af25094b71b8a4dba1fbad698009b4891e3dc79 Mon Sep 17 00:00:00 2001 From: Elyes Haouas Date: Sat, 25 Feb 2023 09:55:04 +0100 Subject: [PATCH 083/217] Fix some typos Signed-off-by: Elyes Haouas --- src/modules/gamemode.cpp | 2 +- src/modules/sway/workspaces.cpp | 2 +- src/util/sanitize_str.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/gamemode.cpp b/src/modules/gamemode.cpp index 1b8d7fc..3b2213e 100644 --- a/src/modules/gamemode.cpp +++ b/src/modules/gamemode.cpp @@ -55,7 +55,7 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config) } box_.set_spacing(iconSpacing); - // Wether to use icon or not + // Whether to use icon or not if (config_["use-icon"].isBool()) { useIcon = config_["use-icon"].asBool(); } diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 08742ae..2bf0247 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 (l == r || config_["alphabetical_sort"].asBool()) { // In case both integers are the same, lexicographical // sort. The code above already ensure that this will only - // happend in case of explicitly numbered workspaces. + // happened in case of explicitly numbered workspaces. // // Additionally, if the config specifies to sort workspaces // alphabetically do this here. diff --git a/src/util/sanitize_str.cpp b/src/util/sanitize_str.cpp index 72c72f8..131b9f2 100644 --- a/src/util/sanitize_str.cpp +++ b/src/util/sanitize_str.cpp @@ -6,7 +6,7 @@ namespace waybar::util { // replaces ``<>&"'`` with their encoded counterparts std::string sanitize_string(std::string str) { - // note: it's important that '&' is replaced first; therefor we *can't* use std::map + // note: it's important that '&' is replaced first; therefore we *can't* use std::map const std::pair replacement_table[] = { {'&', "&"}, {'<', "<"}, {'>', ">"}, {'"', """}, {'\'', "'"}}; size_t startpoint; From e25a7c9719dbc7e81f6397b07bd32092fbd1df1e Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 28 Feb 2023 15:32:28 +0300 Subject: [PATCH 084/217] ISSUE#1977. AModule implements module actions call Signed-off-by: Viktar Lukashonak --- include/AModule.hpp | 9 +++- include/IModule.hpp | 1 + include/modules/clock.hpp | 26 +++++++++--- src/AModule.cpp | 48 ++++++++++++++++++---- src/modules/clock.cpp | 86 ++++++++++++--------------------------- 5 files changed, 96 insertions(+), 74 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 35625cd..d221cac 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -11,16 +11,20 @@ namespace waybar { class AModule : public IModule { public: - AModule(const Json::Value &, const std::string &, const std::string &, bool enable_click = false, - bool enable_scroll = false); virtual ~AModule(); virtual auto update() -> void; virtual auto refresh(int) -> void{}; virtual operator Gtk::Widget &(); + virtual auto doAction(const std::string& name) -> void; Glib::Dispatcher dp; protected: + // Don't need to make an object directly + // Derived classes are able to use it + AModule(const Json::Value &, const std::string &, const std::string &, bool enable_click = false, + bool enable_scroll = false); + enum SCROLL_DIR { NONE, UP, DOWN, LEFT, RIGHT }; SCROLL_DIR getScrollDir(GdkEventScroll *e); @@ -37,6 +41,7 @@ class AModule : public IModule { std::vector pid_; gdouble distance_scrolled_y_; gdouble distance_scrolled_x_; + 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_2BUTTON_PRESS), "on-double-click"}, diff --git a/include/IModule.hpp b/include/IModule.hpp index 961a461..b76c33e 100644 --- a/include/IModule.hpp +++ b/include/IModule.hpp @@ -9,6 +9,7 @@ class IModule { virtual ~IModule() = default; virtual auto update() -> void = 0; virtual operator Gtk::Widget&() = 0; + virtual auto doAction(const std::string& name) -> void = 0; }; } // namespace waybar diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index c318e8b..8d9f79f 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -15,26 +15,26 @@ enum class WeeksSide { HIDDEN, }; -enum class CldMode { MONTH, YEAR }; +enum class CldMode { + MONTH, + YEAR +}; -class Clock : public ALabel { +class Clock final : public ALabel { public: Clock(const std::string&, const Json::Value&); ~Clock() = default; auto update() -> void; + auto doAction(const std::string& name) -> void override; private: util::SleeperThread thread_; - std::map, void (waybar::modules::Clock::*)()> eventMap_; std::locale locale_; std::vector time_zones_; int current_time_zone_idx_; bool is_calendar_in_tooltip_; bool is_timezoned_list_in_tooltip_; - bool handleScroll(GdkEventScroll* e); - bool handleToggle(GdkEventButton* const& e); - auto first_day_of_week() -> date::weekday; const date::time_zone* current_timezone(); bool is_timezone_fixed(); @@ -56,6 +56,20 @@ class Clock : public ALabel { /*Calendar functions*/ auto get_calendar(const date::zoned_seconds& now, const date::zoned_seconds& wtime) -> std::string; + /*Clock actions*/ void cldModeSwitch(); + void cldShift_up(); + void cldShift_down(); + void tz_up(); + void tz_down(); + + // ModuleActionMap + static inline std::map actionMap_{ + {"mode", &waybar::modules::Clock::cldModeSwitch}, + {"shift_up", &waybar::modules::Clock::cldShift_up}, + {"shift_down", &waybar::modules::Clock::cldShift_down}, + {"tz_up", &waybar::modules::Clock::tz_up}, + {"tz_down", &waybar::modules::Clock::tz_down} + }; }; } // namespace waybar::modules diff --git a/src/AModule.cpp b/src/AModule.cpp index b19594a..1566e88 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -32,6 +32,18 @@ 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)); } + + // 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) + eventActionMap_.insert({it.key().asString(), it->asString()}); + else + spdlog::warn("Dublicate action is ignored: {0}", it.key().asString()); + else + spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); + } } AModule::~AModule() { @@ -48,19 +60,33 @@ auto AModule::update() -> void { pid_.push_back(util::command::forkExec(config_["on-update"].asString())); } } +// Get mapping between event name and module action name +// Then call overrided 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 + if (recA != eventActionMap_.cend() && name != recA->second) this->doAction(recA->second); + } +} bool AModule::handleToggle(GdkEventButton* const& e) { + std::string format{}; const std::map, std::string>::const_iterator& rec{ eventMap_.find(std::pair(e->button, e->type))}; - std::string format{(rec != eventMap_.cend()) ? rec->second : std::string{""}}; + if (rec != eventMap_.cend()) { + // First call module actions + this->AModule::doAction(rec->second); + format = rec->second; + } + // Second call user scripts if (!format.empty()) { if (config_[format].isString()) format = config_[format].asString(); else format.clear(); } - if (!format.empty()) { pid_.push_back(util::command::forkExec(format)); } @@ -122,11 +148,19 @@ AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { bool AModule::handleScroll(GdkEventScroll* e) { auto dir = getScrollDir(e); - if (dir == SCROLL_DIR::UP && config_["on-scroll-up"].isString()) { - pid_.push_back(util::command::forkExec(config_["on-scroll-up"].asString())); - } else if (dir == SCROLL_DIR::DOWN && config_["on-scroll-down"].isString()) { - pid_.push_back(util::command::forkExec(config_["on-scroll-down"].asString())); - } + std::string eventName{}; + + if (dir == SCROLL_DIR::UP) + eventName = "on-scroll-up"; + else if (dir == SCROLL_DIR::DOWN) + eventName = "on-scroll-down"; + + // First call module actions + this->AModule::doAction(eventName); + // Second call user scripts + if (config_[eventName].isString()) + pid_.push_back(util::command::forkExec(config_[eventName].asString())); + dp.emit(); return true; } diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 414ee33..af161d9 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -125,16 +125,6 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) return false; }); } - if (config_[kCalendarPlaceholder]["on-click-left"].isString()) { - if (config_[kCalendarPlaceholder]["on-click-left"].asString() == "mode") - eventMap_.insert({std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), - &waybar::modules::Clock::cldModeSwitch}); - } - if (config_[kCalendarPlaceholder]["on-click-right"].isString()) { - if (config_[kCalendarPlaceholder]["on-click-right"].asString() == "mode") - eventMap_.insert({std::make_pair(3, GdkEventType::GDK_BUTTON_PRESS), - &waybar::modules::Clock::cldModeSwitch}); - } } if (config_["locale"].isString()) @@ -203,56 +193,12 @@ auto waybar::modules::Clock::update() -> void { ALabel::update(); } -bool waybar::modules::Clock::handleToggle(GdkEventButton* const& e) { - const std::map, void (waybar::modules::Clock::*)()>::const_iterator& - rec{eventMap_.find(std::pair(e->button, e->type))}; - - const auto callMethod{(rec != eventMap_.cend()) ? rec->second : nullptr}; - - if (callMethod) { - (this->*callMethod)(); +auto waybar::modules::Clock::doAction(const std::string& name) -> void { + if ((actionMap_[name])) { + (this->*actionMap_[name])(); + update(); } else - return ALabel::handleToggle(e); - - update(); - return true; -} - -bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) { - // defer to user commands if set - if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { - return AModule::handleScroll(e); - } - - auto dir = AModule::getScrollDir(e); - - // Shift calendar date - if (cldShift_.count() != 0) { - if (dir == SCROLL_DIR::UP) - cldCurrShift_ += ((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; - else - cldCurrShift_ -= ((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; - } else { - // Change time zone - if (dir != SCROLL_DIR::UP && dir != SCROLL_DIR::DOWN) { - return true; - } - if (time_zones_.size() == 1) { - return true; - } - - auto nr_zones = time_zones_.size(); - if (dir == SCROLL_DIR::UP) { - size_t new_idx = current_time_zone_idx_ + 1; - current_time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx; - } else { - current_time_zone_idx_ = - current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1; - } - } - - update(); - return true; + spdlog::error("Clock. Unsupported action \"{0}\"", name); } // The number of weeks in calendar month layout plus 1 more for calendar titles @@ -461,9 +407,31 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, return os.str(); } +/*Clock 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_; +} +void waybar::modules::Clock::cldShift_down() { + cldCurrShift_ -= ((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_; +} +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; +} +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 { From 23f39938fb7bd84172597ea7e16d90a6b352a891 Mon Sep 17 00:00:00 2001 From: Pascal Huber Date: Wed, 1 Mar 2023 10:55:15 +0100 Subject: [PATCH 085/217] Revert "feat(sway): add workspace css class" This reverts commit a10464d9bb67ba75d3c9159bd027b67a565318e1. --- 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 4f551b5..2bf0247 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -231,7 +231,6 @@ auto Workspaces::update() -> void { box_.reorder_child(button, it - workspaces_.begin()); } std::string output = (*it)["name"].asString(); - button.get_style_context()->add_class("workspace-" + trimWorkspaceName(output)); if (config_["format"].isString()) { auto format = config_["format"].asString(); output = fmt::format(fmt::runtime(format), fmt::arg("icon", getIcon(output, *it)), From 79eb2f5bc102b421c3b8e8dcfcf56b0a630daa27 Mon Sep 17 00:00:00 2001 From: Louis DeLosSantos Date: Tue, 10 Jan 2023 17:32:10 +0000 Subject: [PATCH 086/217] wlr-taskbar: allow sorting by app_id some users (maybe only myself) may want to sort the task bar by app_id which then places occurrences of the same task next to each other. Signed-off-by: Louis DeLosSantos --- include/modules/wlr/taskbar.hpp | 3 +- man/waybar-wlr-taskbar.5.scd | 5 ++ src/modules/wlr/taskbar.cpp | 83 +++++++++++++++++++-------------- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 7a1f17a..4465dd0 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -40,6 +40,8 @@ class Task { FULLSCREEN = (1 << 3), INVALID = (1 << 4) }; + // made public so TaskBar can reorder based on configuration. + Gtk::Button button; private: static uint32_t global_id; @@ -53,7 +55,6 @@ class Task { uint32_t id_; - Gtk::Button button_; Gtk::Box content_; Gtk::Image icon_; Gtk::Label text_before_; diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index b2946ac..8737196 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -52,6 +52,11 @@ Addressed by *wlr/taskbar* default: false ++ If set to true, always reorder the tasks in the taskbar so that the currently active one is first. Otherwise don't reorder. +*sort-by-app-id*: ++ + typeof: bool ++ + default: false ++ + If set to true, group tasks by their app_id. Cannot be used with 'active-first'. + *on-click*: ++ typeof: string ++ The action which should be triggered when clicking on the application button with the left mouse button. diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 427083b..74b8be5 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -268,14 +268,14 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} { zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this); - button_.set_relief(Gtk::RELIEF_NONE); + button.set_relief(Gtk::RELIEF_NONE); content_.add(text_before_); content_.add(icon_); content_.add(text_after_); content_.show(); - button_.add(content_); + button.add(content_); format_before_.clear(); format_after_.clear(); @@ -317,20 +317,20 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, config_["on-click-right"].isString()) { } - 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_motion_notify_event().connect(sigc::mem_fun(*this, &Task::handle_motion_notify), + 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_.drag_source_set(target_entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE); - button_.drag_dest_set(target_entries, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_MOVE); + button.signal_motion_notify_event().connect(sigc::mem_fun(*this, &Task::handle_motion_notify), + false); - button_.signal_drag_data_get().connect(sigc::mem_fun(*this, &Task::handle_drag_data_get), false); - button_.signal_drag_data_received().connect( - sigc::mem_fun(*this, &Task::handle_drag_data_received), false); + button.drag_source_set(target_entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE); + button.drag_dest_set(target_entries, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_MOVE); + + button.signal_drag_data_get().connect(sigc::mem_fun(*this, &Task::handle_drag_data_get), false); + button.signal_drag_data_received().connect(sigc::mem_fun(*this, &Task::handle_drag_data_received), + false); } Task::~Task() { @@ -339,7 +339,7 @@ Task::~Task() { handle_ = nullptr; } if (button_visible_) { - tbar_->remove_button(button_); + tbar_->remove_button(button); button_visible_ = false; } } @@ -438,8 +438,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 */ - tbar_->add_button(button_); - button_.show(); + tbar_->add_button(button); + button.show(); button_visible_ = true; spdlog::debug("{} now visible on {}", repr(), bar_.output->name); } @@ -450,8 +450,8 @@ void Task::handle_output_leave(struct wl_output *output) { if (button_visible_ && !tbar_->all_outputs() && tbar_->show_output(output)) { /* The task left the output of the current bar, make the button invisible */ - tbar_->remove_button(button_); - button_.hide(); + tbar_->remove_button(button); + button.hide(); button_visible_ = false; spdlog::debug("{} now invisible on {}", repr(), bar_.output->name); } @@ -473,31 +473,31 @@ void Task::handle_done() { spdlog::debug("{} changed", repr()); if (state_ & MAXIMIZED) { - button_.get_style_context()->add_class("maximized"); + button.get_style_context()->add_class("maximized"); } else if (!(state_ & MAXIMIZED)) { - button_.get_style_context()->remove_class("maximized"); + button.get_style_context()->remove_class("maximized"); } if (state_ & MINIMIZED) { - button_.get_style_context()->add_class("minimized"); + button.get_style_context()->add_class("minimized"); } else if (!(state_ & MINIMIZED)) { - button_.get_style_context()->remove_class("minimized"); + button.get_style_context()->remove_class("minimized"); } if (state_ & ACTIVE) { - button_.get_style_context()->add_class("active"); + button.get_style_context()->add_class("active"); } else if (!(state_ & ACTIVE)) { - button_.get_style_context()->remove_class("active"); + button.get_style_context()->remove_class("active"); } if (state_ & FULLSCREEN) { - button_.get_style_context()->add_class("fullscreen"); + button.get_style_context()->add_class("fullscreen"); } else if (!(state_ & FULLSCREEN)) { - button_.get_style_context()->remove_class("fullscreen"); + button.get_style_context()->remove_class("fullscreen"); } if (config_["active-first"].isBool() && config_["active-first"].asBool() && active()) - tbar_->move_button(button_, 0); + tbar_->move_button(button, 0); tbar_->dp.emit(); } @@ -507,7 +507,7 @@ void Task::handle_closed() { zwlr_foreign_toplevel_handle_v1_destroy(handle_); handle_ = nullptr; if (button_visible_) { - tbar_->remove_button(button_); + tbar_->remove_button(button); button_visible_ = false; } tbar_->remove_task(id_); @@ -563,12 +563,12 @@ bool Task::handle_button_release(GdkEventButton *bt) { bool Task::handle_motion_notify(GdkEventMotion *mn) { if (drag_start_button == -1) return false; - if (button_.drag_check_threshold(drag_start_x, drag_start_y, mn->x, mn->y)) { + if (button.drag_check_threshold(drag_start_x, drag_start_y, mn->x, mn->y)) { /* start drag in addition to other assigned action */ auto target_list = Gtk::TargetList::create(target_entries); auto refptr = Glib::RefPtr(target_list); auto drag_context = - button_.drag_begin(refptr, Gdk::DragAction::ACTION_MOVE, drag_start_button, (GdkEvent *)mn); + button.drag_begin(refptr, Gdk::DragAction::ACTION_MOVE, drag_start_button, (GdkEvent *)mn); } return false; @@ -577,7 +577,7 @@ bool Task::handle_motion_notify(GdkEventMotion *mn) { void Task::handle_drag_data_get(const Glib::RefPtr &context, Gtk::SelectionData &selection_data, guint info, guint time) { spdlog::debug("drag_data_get"); - void *button_addr = (void *)&this->button_; + void *button_addr = (void *)&this->button; selection_data.set("WAYBAR_TOPLEVEL", 32, (const guchar *)&button_addr, sizeof(gpointer)); } @@ -588,16 +588,16 @@ void Task::handle_drag_data_received(const Glib::RefPtr &conte gpointer handle = *(gpointer *)selection_data.get_data(); auto dragged_button = (Gtk::Button *)handle; - if (dragged_button == &this->button_) return; + if (dragged_button == &this->button) return; auto parent_of_dragged = dragged_button->get_parent(); - auto parent_of_dest = this->button_.get_parent(); + auto parent_of_dest = this->button.get_parent(); if (parent_of_dragged != parent_of_dest) return; auto box = (Gtk::Box *)parent_of_dragged; - auto position_prop = box->child_property_position(this->button_); + auto position_prop = box->child_property_position(this->button); auto position = position_prop.get_value(); box->reorder_child(*dragged_button, position); @@ -646,9 +646,9 @@ void Task::update() { fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true))); if (markup) - button_.set_tooltip_markup(txt); + button.set_tooltip_markup(txt); else - button_.set_tooltip_text(txt); + button.set_tooltip_text(txt); } } @@ -791,6 +791,17 @@ void Taskbar::update() { t->update(); } + if (config_["sort-by-app-id"].asBool()) { + std::stable_sort(tasks_.begin(), tasks_.end(), + [](const std::unique_ptr &a, const std::unique_ptr &b) { + return a->app_id() < b->app_id(); + }); + + for (unsigned long i = 0; i < tasks_.size(); i++) { + move_button(tasks_[i]->button, i); + } + } + AModule::update(); } From ce16e029ed4db207894bc334eb1a00438739d8d2 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Wed, 1 Mar 2023 23:39:36 +0300 Subject: [PATCH 087/217] Issue #2030. AModule enable_click & enable_scroll Signed-off-by: Viktar Lukashonak --- src/AModule.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 1566e88..459e562 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -12,6 +12,20 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std:: config_(std::move(config)), distance_scrolled_y_(0.0), 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) { + eventActionMap_.insert({it.key().asString(), it->asString()}); + enable_click = true; + enable_scroll = true; + } else + spdlog::warn("Dublicate action is ignored: {0}", it.key().asString()); + else + spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); + } + // configure events' user commands if (enable_click) { event_box_.add_events(Gdk::BUTTON_PRESS_MASK); @@ -32,18 +46,6 @@ 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)); } - - // 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) - eventActionMap_.insert({it.key().asString(), it->asString()}); - else - spdlog::warn("Dublicate action is ignored: {0}", it.key().asString()); - else - spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index()); - } } AModule::~AModule() { From 43862ceb345dc1fadac6b1e55245a049249213d3 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Thu, 2 Mar 2023 10:07:10 +0300 Subject: [PATCH 088/217] ISSUE #878. Try catch Clock timezones --- src/modules/clock.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index af161d9..ab3a4e0 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -22,14 +22,20 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) is_timezoned_list_in_tooltip_(false) { if (config_["timezones"].isArray() && !config_["timezones"].empty()) { for (const auto& zone_name : config_["timezones"]) { - if (!zone_name.isString() || zone_name.asString().empty()) { - time_zones_.push_back(nullptr); + if (!zone_name.isString() || zone_name.asString().empty()) continue; + try { + time_zones_.push_back(date::locate_zone(zone_name.asString())); + } catch(const std::exception& e) { + spdlog::warn("Timezone: {0}. {1}", zone_name.asString(), e.what()); } - time_zones_.push_back(date::locate_zone(zone_name.asString())); } } else if (config_["timezone"].isString() && !config_["timezone"].asString().empty()) { - time_zones_.push_back(date::locate_zone(config_["timezone"].asString())); + try { + time_zones_.push_back(date::locate_zone(config_["timezone"].asString())); + } catch (const std::exception& e) { + spdlog::warn("Timezone: {0}. {1}", config_["timezone"].asString(), e.what()); + } } // If all timezones are parsed and no one is good, add current time zone. nullptr in timezones From 4cb7e55a915ba04039444a2fa71d1768cb0b3f39 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Thu, 2 Mar 2023 10:10:34 +0300 Subject: [PATCH 089/217] ISSUE #878. Try catch Clock timezones 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 ab3a4e0..fb12d2b 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -22,11 +22,10 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) is_timezoned_list_in_tooltip_(false) { if (config_["timezones"].isArray() && !config_["timezones"].empty()) { for (const auto& zone_name : config_["timezones"]) { - if (!zone_name.isString() || zone_name.asString().empty()) - continue; + if (!zone_name.isString() || zone_name.asString().empty()) continue; try { time_zones_.push_back(date::locate_zone(zone_name.asString())); - } catch(const std::exception& e) { + } catch (const std::exception& e) { spdlog::warn("Timezone: {0}. {1}", zone_name.asString(), e.what()); } } From 61a6c00c0295db99f32e37b2ad406900bb8ad562 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Thu, 2 Mar 2023 16:57:07 +0300 Subject: [PATCH 090/217] Happy clang --- include/AIconLabel.hpp | 2 +- include/ALabel.hpp | 4 ++-- include/AModule.hpp | 6 +++--- include/group.hpp | 4 ++-- include/modules/backlight.hpp | 6 +++--- include/modules/battery.hpp | 4 ++-- include/modules/bluetooth.hpp | 4 ++-- include/modules/clock.hpp | 4 ++-- include/modules/cpu.hpp | 4 ++-- include/modules/custom.hpp | 10 +++++----- include/modules/disk.hpp | 4 ++-- include/modules/gamemode.hpp | 6 +++--- include/modules/hyprland/language.hpp | 6 +++--- include/modules/hyprland/submap.hpp | 6 +++--- include/modules/hyprland/window.hpp | 6 +++--- include/modules/idle_inhibitor.hpp | 6 +++--- include/modules/image.hpp | 5 +++-- include/modules/inhibitor.hpp | 6 +++--- include/modules/jack.hpp | 4 ++-- include/modules/keyboard_state.hpp | 4 ++-- include/modules/memory.hpp | 4 ++-- include/modules/mpd/mpd.hpp | 2 +- include/modules/mpris/mpris.hpp | 6 +++--- include/modules/network.hpp | 4 ++-- include/modules/pulseaudio.hpp | 6 +++--- include/modules/river/layout.hpp | 2 +- include/modules/river/mode.hpp | 2 +- include/modules/river/tags.hpp | 2 +- include/modules/river/window.hpp | 2 +- include/modules/simpleclock.hpp | 4 ++-- include/modules/sndio.hpp | 8 ++++---- include/modules/sni/tray.hpp | 4 ++-- include/modules/sway/language.hpp | 4 ++-- include/modules/sway/mode.hpp | 4 ++-- include/modules/sway/scratchpad.hpp | 6 +++--- include/modules/sway/window.hpp | 4 ++-- include/modules/sway/workspaces.hpp | 6 +++--- include/modules/temperature.hpp | 4 ++-- include/modules/upower/upower.hpp | 6 +++--- include/modules/upower/upower_tooltip.hpp | 2 +- include/modules/user.hpp | 4 ++-- include/modules/wireplumber.hpp | 4 ++-- 42 files changed, 96 insertions(+), 95 deletions(-) diff --git a/include/AIconLabel.hpp b/include/AIconLabel.hpp index aeeba6c..054d031 100644 --- a/include/AIconLabel.hpp +++ b/include/AIconLabel.hpp @@ -13,7 +13,7 @@ class AIconLabel : public ALabel { const std::string &format, uint16_t interval = 0, bool ellipsize = false, bool enable_click = false, bool enable_scroll = false); virtual ~AIconLabel() = default; - virtual auto update() -> void; + auto update() -> void override; protected: Gtk::Image image_; diff --git a/include/ALabel.hpp b/include/ALabel.hpp index 14f8224..888c65a 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -14,7 +14,7 @@ class ALabel : public AModule { uint16_t interval = 0, bool ellipsize = false, bool enable_click = false, bool enable_scroll = false); virtual ~ALabel() = default; - virtual auto update() -> void; + auto update() -> void override; virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0); virtual std::string getIcon(uint16_t, const std::vector &alts, uint16_t max = 0); @@ -25,7 +25,7 @@ class ALabel : public AModule { bool alt_ = false; std::string default_format_; - virtual bool handleToggle(GdkEventButton *const &e); + bool handleToggle(GdkEventButton *const &e) override; virtual std::string getState(uint8_t value, bool lesser = false); }; diff --git a/include/AModule.hpp b/include/AModule.hpp index d221cac..6f724cb 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -12,10 +12,10 @@ namespace waybar { class AModule : public IModule { public: virtual ~AModule(); - virtual auto update() -> void; + auto update() -> void override; virtual auto refresh(int) -> void{}; - virtual operator Gtk::Widget &(); - virtual auto doAction(const std::string& name) -> void; + operator Gtk::Widget &() override; + auto doAction(const std::string& name) -> void override; Glib::Dispatcher dp; diff --git a/include/group.hpp b/include/group.hpp index 5e82867..21aef3c 100644 --- a/include/group.hpp +++ b/include/group.hpp @@ -14,8 +14,8 @@ class Group : public AModule { public: Group(const std::string&, const Bar&, const Json::Value&); ~Group() = default; - auto update() -> void; - operator Gtk::Widget&(); + auto update() -> void override; + operator Gtk::Widget&() override; Gtk::Box box; }; diff --git a/include/modules/backlight.hpp b/include/modules/backlight.hpp index 6597d39..ade4bc7 100644 --- a/include/modules/backlight.hpp +++ b/include/modules/backlight.hpp @@ -40,8 +40,8 @@ class Backlight : public ALabel { public: Backlight(const std::string &, const Json::Value &); - ~Backlight(); - auto update() -> void; + virtual ~Backlight(); + auto update() -> void override; private: template @@ -51,7 +51,7 @@ class Backlight : public ALabel { template static void enumerate_devices(ForwardIt first, ForwardIt last, Inserter inserter, udev *udev); - bool handleScroll(GdkEventScroll *e); + bool handleScroll(GdkEventScroll *e) override; const std::string preferred_device_; static constexpr int EPOLL_MAX_EVENTS = 16; diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 54a7dd3..017b0e4 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -29,8 +29,8 @@ namespace fs = std::filesystem; class Battery : public ALabel { public: Battery(const std::string&, const Json::Value&); - ~Battery(); - auto update() -> void; + virtual ~Battery(); + auto update() -> void override; private: static inline const fs::path data_dir_ = "/sys/class/power_supply/"; diff --git a/include/modules/bluetooth.hpp b/include/modules/bluetooth.hpp index bd9737b..18481e3 100644 --- a/include/modules/bluetooth.hpp +++ b/include/modules/bluetooth.hpp @@ -45,8 +45,8 @@ class Bluetooth : public ALabel { public: Bluetooth(const std::string&, const Json::Value&); - ~Bluetooth() = default; - auto update() -> void; + virtual ~Bluetooth() = default; + auto update() -> void override; private: static auto onInterfaceAddedOrRemoved(GDBusObjectManager*, GDBusObject*, GDBusInterface*, diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 8d9f79f..07f2284 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -23,8 +23,8 @@ enum class CldMode { class Clock final : public ALabel { public: Clock(const std::string&, const Json::Value&); - ~Clock() = default; - auto update() -> void; + virtual ~Clock() = default; + auto update() -> void override; auto doAction(const std::string& name) -> void override; private: diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 539f926..a523548 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -17,8 +17,8 @@ namespace waybar::modules { class Cpu : public ALabel { public: Cpu(const std::string&, const Json::Value&); - ~Cpu() = default; - auto update() -> void; + virtual ~Cpu() = default; + auto update() -> void override; private: double getCpuLoad(); diff --git a/include/modules/custom.hpp b/include/modules/custom.hpp index 711d07e..a6024a8 100644 --- a/include/modules/custom.hpp +++ b/include/modules/custom.hpp @@ -15,9 +15,9 @@ namespace waybar::modules { class Custom : public ALabel { public: Custom(const std::string&, const std::string&, const Json::Value&); - ~Custom(); - auto update() -> void; - void refresh(int /*signal*/); + virtual ~Custom(); + auto update() -> void override; + void refresh(int /*signal*/) override; private: void delayWorker(); @@ -25,8 +25,8 @@ class Custom : public ALabel { void parseOutputRaw(); void parseOutputJson(); void handleEvent(); - bool handleScroll(GdkEventScroll* e); - bool handleToggle(GdkEventButton* const& e); + bool handleScroll(GdkEventScroll* e) override; + bool handleToggle(GdkEventButton* const& e) override; const std::string name_; std::string text_; diff --git a/include/modules/disk.hpp b/include/modules/disk.hpp index ec386b2..2a307c9 100644 --- a/include/modules/disk.hpp +++ b/include/modules/disk.hpp @@ -14,8 +14,8 @@ namespace waybar::modules { class Disk : public ALabel { public: Disk(const std::string&, const Json::Value&); - ~Disk() = default; - auto update() -> void; + virtual ~Disk() = default; + auto update() -> void override; private: util::SleeperThread thread_; diff --git a/include/modules/gamemode.hpp b/include/modules/gamemode.hpp index b027393..69c0c3a 100644 --- a/include/modules/gamemode.hpp +++ b/include/modules/gamemode.hpp @@ -18,8 +18,8 @@ namespace waybar::modules { class Gamemode : public AModule { public: Gamemode(const std::string &, const Json::Value &); - ~Gamemode(); - auto update() -> void; + virtual ~Gamemode(); + auto update() -> void override; private: const std::string DEFAULT_ICON_NAME = "input-gaming-symbolic"; @@ -39,7 +39,7 @@ class Gamemode : public AModule { const Glib::VariantContainerBase &arguments); void getData(); - bool handleToggle(GdkEventButton *const &); + bool handleToggle(GdkEventButton *const &) override; // Config std::string format = DEFAULT_FORMAT; diff --git a/include/modules/hyprland/language.hpp b/include/modules/hyprland/language.hpp index a07cfc0..64290f8 100644 --- a/include/modules/hyprland/language.hpp +++ b/include/modules/hyprland/language.hpp @@ -10,12 +10,12 @@ namespace waybar::modules::hyprland { class Language : public waybar::ALabel, public EventHandler { public: Language(const std::string&, const waybar::Bar&, const Json::Value&); - ~Language(); + virtual ~Language(); - auto update() -> void; + auto update() -> void override; private: - void onEvent(const std::string&); + void onEvent(const std::string&) override; void initLanguage(); std::string getShortFrom(const std::string&); diff --git a/include/modules/hyprland/submap.hpp b/include/modules/hyprland/submap.hpp index c36578c..e2a8498 100644 --- a/include/modules/hyprland/submap.hpp +++ b/include/modules/hyprland/submap.hpp @@ -10,12 +10,12 @@ namespace waybar::modules::hyprland { class Submap : public waybar::ALabel, public EventHandler { public: Submap(const std::string&, const waybar::Bar&, const Json::Value&); - ~Submap(); + virtual ~Submap(); - auto update() -> void; + auto update() -> void override; private: - void onEvent(const std::string&); + void onEvent(const std::string&) override; std::mutex mutex_; const Bar& bar_; diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index 35438cd..b5ab6fb 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -12,14 +12,14 @@ namespace waybar::modules::hyprland { class Window : public waybar::ALabel, public EventHandler { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); - ~Window(); + virtual ~Window(); - auto update() -> void; + auto update() -> void override; private: int getActiveWorkspaceID(std::string); std::string getLastWindowTitle(int); - void onEvent(const std::string&); + void onEvent(const std::string&) override; bool separate_outputs; std::mutex mutex_; diff --git a/include/modules/idle_inhibitor.hpp b/include/modules/idle_inhibitor.hpp index 8378e58..22bd808 100644 --- a/include/modules/idle_inhibitor.hpp +++ b/include/modules/idle_inhibitor.hpp @@ -13,13 +13,13 @@ class IdleInhibitor : public ALabel { public: IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&); - ~IdleInhibitor(); - auto update() -> void; + virtual ~IdleInhibitor(); + auto update() -> void override; static std::list modules; static bool status; private: - bool handleToggle(GdkEventButton* const& e); + bool handleToggle(GdkEventButton* const& e) override; void toggleStatus(); const Bar& bar_; diff --git a/include/modules/image.hpp b/include/modules/image.hpp index b2c8946..c15270d 100644 --- a/include/modules/image.hpp +++ b/include/modules/image.hpp @@ -17,8 +17,9 @@ namespace waybar::modules { class Image : public AModule { public: Image(const std::string&, const Json::Value&); - auto update() -> void; - void refresh(int /*signal*/); + virtual ~Image() = default; + auto update() -> void override; + void refresh(int /*signal*/) override; private: void delayWorker(); diff --git a/include/modules/inhibitor.hpp b/include/modules/inhibitor.hpp index a5f300d..43cb6ca 100644 --- a/include/modules/inhibitor.hpp +++ b/include/modules/inhibitor.hpp @@ -12,12 +12,12 @@ namespace waybar::modules { class Inhibitor : public ALabel { public: Inhibitor(const std::string&, const waybar::Bar&, const Json::Value&); - ~Inhibitor() override; - auto update() -> void; + virtual ~Inhibitor(); + auto update() -> void override; auto activated() -> bool; private: - auto handleToggle(::GdkEventButton* const& e) -> bool; + auto handleToggle(::GdkEventButton* const& e) -> bool override; const std::unique_ptr<::GDBusConnection, void (*)(::GDBusConnection*)> dbus_; const std::string inhibitors_; diff --git a/include/modules/jack.hpp b/include/modules/jack.hpp index fbab062..452c43a 100644 --- a/include/modules/jack.hpp +++ b/include/modules/jack.hpp @@ -14,8 +14,8 @@ namespace waybar::modules { class JACK : public ALabel { public: JACK(const std::string &, const Json::Value &); - ~JACK() = default; - auto update() -> void; + virtual ~JACK() = default; + auto update() -> void override; int bufSize(jack_nframes_t size); int sampleRate(jack_nframes_t rate); diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index ce9faba..deb577e 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -19,8 +19,8 @@ namespace waybar::modules { class KeyboardState : public AModule { public: KeyboardState(const std::string&, const waybar::Bar&, const Json::Value&); - ~KeyboardState(); - auto update() -> void; + virtual ~KeyboardState(); + auto update() -> void override; private: auto tryAddDevice(const std::string&) -> void; diff --git a/include/modules/memory.hpp b/include/modules/memory.hpp index e23ed84..3b6342b 100644 --- a/include/modules/memory.hpp +++ b/include/modules/memory.hpp @@ -13,8 +13,8 @@ namespace waybar::modules { class Memory : public ALabel { public: Memory(const std::string&, const Json::Value&); - ~Memory() = default; - auto update() -> void; + virtual ~Memory() = default; + auto update() -> void override; private: void parseMeminfo(); diff --git a/include/modules/mpd/mpd.hpp b/include/modules/mpd/mpd.hpp index ae3f915..32d526e 100644 --- a/include/modules/mpd/mpd.hpp +++ b/include/modules/mpd/mpd.hpp @@ -37,7 +37,7 @@ class MPD : public ALabel { public: MPD(const std::string&, const Json::Value&); virtual ~MPD() noexcept = default; - auto update() -> void; + auto update() -> void override; private: std::string getTag(mpd_tag_type type, unsigned idx = 0) const; diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index 040401f..e82c929 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -19,9 +19,9 @@ namespace waybar::modules::mpris { class Mpris : public AModule { public: Mpris(const std::string&, const Json::Value&); - ~Mpris(); - auto update() -> void; - bool handleToggle(GdkEventButton* const&); + virtual ~Mpris(); + auto update() -> void override; + bool handleToggle(GdkEventButton* const&) override; private: static auto onPlayerNameAppeared(PlayerctlPlayerManager*, PlayerctlPlayerName*, gpointer) -> void; diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 8ec6b6d..4958591 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -21,8 +21,8 @@ namespace waybar::modules { class Network : public ALabel { public: Network(const std::string&, const Json::Value&); - ~Network(); - auto update() -> void; + virtual ~Network(); + auto update() -> void override; private: static const uint8_t MAX_RETRY = 5; diff --git a/include/modules/pulseaudio.hpp b/include/modules/pulseaudio.hpp index a8e4890..d0b17e4 100644 --- a/include/modules/pulseaudio.hpp +++ b/include/modules/pulseaudio.hpp @@ -14,8 +14,8 @@ namespace waybar::modules { class Pulseaudio : public ALabel { public: Pulseaudio(const std::string&, const Json::Value&); - ~Pulseaudio(); - auto update() -> void; + virtual ~Pulseaudio(); + auto update() -> void override; private: static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*); @@ -25,7 +25,7 @@ class Pulseaudio : public ALabel { static void serverInfoCb(pa_context*, const pa_server_info*, void*); static void volumeModifyCb(pa_context*, int, void*); - bool handleScroll(GdkEventScroll* e); + bool handleScroll(GdkEventScroll* e) override; const std::vector getPulseIcon() const; pa_threaded_mainloop* mainloop_; diff --git a/include/modules/river/layout.hpp b/include/modules/river/layout.hpp index ffa5e21..dc78ee2 100644 --- a/include/modules/river/layout.hpp +++ b/include/modules/river/layout.hpp @@ -11,7 +11,7 @@ namespace waybar::modules::river { class Layout : public waybar::ALabel { public: Layout(const std::string &, const waybar::Bar &, const Json::Value &); - ~Layout(); + virtual ~Layout(); // Handlers for wayland events void handle_name(const char *name); diff --git a/include/modules/river/mode.hpp b/include/modules/river/mode.hpp index 2aff495..246ceca 100644 --- a/include/modules/river/mode.hpp +++ b/include/modules/river/mode.hpp @@ -11,7 +11,7 @@ namespace waybar::modules::river { class Mode : public waybar::ALabel { public: Mode(const std::string &, const waybar::Bar &, const Json::Value &); - ~Mode(); + virtual ~Mode(); // Handlers for wayland events void handle_mode(const char *mode); diff --git a/include/modules/river/tags.hpp b/include/modules/river/tags.hpp index c2b1a11..fb3eefa 100644 --- a/include/modules/river/tags.hpp +++ b/include/modules/river/tags.hpp @@ -14,7 +14,7 @@ namespace waybar::modules::river { class Tags : public waybar::AModule { public: Tags(const std::string &, const waybar::Bar &, const Json::Value &); - ~Tags(); + virtual ~Tags(); // Handlers for wayland events void handle_focused_tags(uint32_t tags); diff --git a/include/modules/river/window.hpp b/include/modules/river/window.hpp index 1f6c525..bf29ebb 100644 --- a/include/modules/river/window.hpp +++ b/include/modules/river/window.hpp @@ -13,7 +13,7 @@ namespace waybar::modules::river { class Window : public waybar::ALabel { public: Window(const std::string &, const waybar::Bar &, const Json::Value &); - ~Window(); + virtual ~Window(); // Handlers for wayland events void handle_focused_view(const char *title); diff --git a/include/modules/simpleclock.hpp b/include/modules/simpleclock.hpp index 5cbee4c..43472ef 100644 --- a/include/modules/simpleclock.hpp +++ b/include/modules/simpleclock.hpp @@ -10,8 +10,8 @@ namespace waybar::modules { class Clock : public ALabel { public: Clock(const std::string&, const Json::Value&); - ~Clock() = default; - auto update() -> void; + virtual ~Clock() = default; + auto update() -> void override; private: util::SleeperThread thread_; diff --git a/include/modules/sndio.hpp b/include/modules/sndio.hpp index eb9b218..3fe36fa 100644 --- a/include/modules/sndio.hpp +++ b/include/modules/sndio.hpp @@ -12,12 +12,12 @@ namespace waybar::modules { class Sndio : public ALabel { public: Sndio(const std::string &, const Json::Value &); - ~Sndio(); - auto update() -> void; + virtual ~Sndio(); + auto update() -> void override; auto set_desc(struct sioctl_desc *, unsigned int) -> void; auto put_val(unsigned int, unsigned int) -> void; - bool handleScroll(GdkEventScroll *); - bool handleToggle(GdkEventButton *const &); + bool handleScroll(GdkEventScroll *) override; + bool handleToggle(GdkEventButton *const &) override; private: auto connect_to_sndio() -> void; diff --git a/include/modules/sni/tray.hpp b/include/modules/sni/tray.hpp index c1499b9..6cda35d 100644 --- a/include/modules/sni/tray.hpp +++ b/include/modules/sni/tray.hpp @@ -13,8 +13,8 @@ namespace waybar::modules::SNI { class Tray : public AModule { public: Tray(const std::string&, const Bar&, const Json::Value&); - ~Tray() = default; - auto update() -> void; + virtual ~Tray() = default; + auto update() -> void override; private: void onAdd(std::unique_ptr& item); diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index f760276..3e9519f 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -17,8 +17,8 @@ namespace waybar::modules::sway { class Language : public ALabel, public sigc::trackable { public: Language(const std::string& id, const Json::Value& config); - ~Language() = default; - auto update() -> void; + virtual ~Language() = default; + auto update() -> void override; private: enum class DispayedShortFlag { None = 0, ShortName = 1, ShortDescription = 1 << 1 }; diff --git a/include/modules/sway/mode.hpp b/include/modules/sway/mode.hpp index 5543c4e..4458535 100644 --- a/include/modules/sway/mode.hpp +++ b/include/modules/sway/mode.hpp @@ -13,8 +13,8 @@ namespace waybar::modules::sway { class Mode : public ALabel, public sigc::trackable { public: Mode(const std::string&, const Json::Value&); - ~Mode() = default; - auto update() -> void; + virtual ~Mode() = default; + auto update() -> void override; private: void onEvent(const struct Ipc::ipc_response&); diff --git a/include/modules/sway/scratchpad.hpp b/include/modules/sway/scratchpad.hpp index e68e772..551cc8c 100644 --- a/include/modules/sway/scratchpad.hpp +++ b/include/modules/sway/scratchpad.hpp @@ -15,8 +15,8 @@ namespace waybar::modules::sway { class Scratchpad : public ALabel { public: Scratchpad(const std::string&, const Json::Value&); - ~Scratchpad() = default; - auto update() -> void; + virtual ~Scratchpad() = default; + auto update() -> void override; private: auto getTree() -> void; @@ -32,4 +32,4 @@ class Scratchpad : public ALabel { Ipc ipc_; util::JsonParser parser_; }; -} // namespace waybar::modules::sway \ No newline at end of file +} // namespace waybar::modules::sway diff --git a/include/modules/sway/window.hpp b/include/modules/sway/window.hpp index e99e94f..22f5a59 100644 --- a/include/modules/sway/window.hpp +++ b/include/modules/sway/window.hpp @@ -15,8 +15,8 @@ namespace waybar::modules::sway { class Window : public AIconLabel, public sigc::trackable { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); - ~Window() = default; - auto update() -> void; + virtual ~Window() = default; + auto update() -> void override; private: void setClass(std::string classname, bool enable); diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index f8a55fa..d07edb4 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -18,8 +18,8 @@ namespace waybar::modules::sway { class Workspaces : public AModule, public sigc::trackable { public: Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); - ~Workspaces() = default; - auto update() -> void; + virtual ~Workspaces() = default; + auto update() -> void override; private: static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\""; @@ -37,7 +37,7 @@ class Workspaces : public AModule, public sigc::trackable { 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*); + bool handleScroll(GdkEventScroll*) override; const Bar& bar_; std::vector workspaces_; diff --git a/include/modules/temperature.hpp b/include/modules/temperature.hpp index 04caafc..5440df7 100644 --- a/include/modules/temperature.hpp +++ b/include/modules/temperature.hpp @@ -12,8 +12,8 @@ namespace waybar::modules { class Temperature : public ALabel { public: Temperature(const std::string&, const Json::Value&); - ~Temperature() = default; - auto update() -> void; + virtual ~Temperature() = default; + auto update() -> void override; private: float getTemperature(); diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index 2724443..51fee0a 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -19,8 +19,8 @@ namespace waybar::modules::upower { class UPower : public AModule { public: UPower(const std::string &, const Json::Value &); - ~UPower(); - auto update() -> void; + virtual ~UPower(); + auto update() -> void override; private: typedef std::unordered_map Devices; @@ -45,7 +45,7 @@ class UPower : public AModule { void resetDevices(); void removeDevices(); bool show_tooltip_callback(int, int, bool, const Glib::RefPtr &tooltip); - bool handleToggle(GdkEventButton *const &); + bool handleToggle(GdkEventButton *const &) override; std::string timeToString(gint64 time); const std::string getDeviceStatus(UpDeviceState &state); diff --git a/include/modules/upower/upower_tooltip.hpp b/include/modules/upower/upower_tooltip.hpp index 730ee93..05e9dcb 100644 --- a/include/modules/upower/upower_tooltip.hpp +++ b/include/modules/upower/upower_tooltip.hpp @@ -24,7 +24,7 @@ class UPowerTooltip : public Gtk::Window { public: UPowerTooltip(uint iconSize, uint tooltipSpacing, uint tooltipPadding); - ~UPowerTooltip(); + virtual ~UPowerTooltip(); uint updateTooltip(Devices& devices); }; diff --git a/include/modules/user.hpp b/include/modules/user.hpp index ac14d25..bcb03da 100644 --- a/include/modules/user.hpp +++ b/include/modules/user.hpp @@ -11,8 +11,8 @@ namespace waybar::modules { class User : public AIconLabel { public: User(const std::string&, const Json::Value&); - ~User() = default; - auto update() -> void; + virtual ~User() = default; + auto update() -> void override; bool handleToggle(GdkEventButton* const& e) override; diff --git a/include/modules/wireplumber.hpp b/include/modules/wireplumber.hpp index fa988fc..5cf462e 100644 --- a/include/modules/wireplumber.hpp +++ b/include/modules/wireplumber.hpp @@ -13,8 +13,8 @@ namespace waybar::modules { class Wireplumber : public ALabel { public: Wireplumber(const std::string&, const Json::Value&); - ~Wireplumber(); - auto update() -> void; + virtual ~Wireplumber(); + auto update() -> void override; private: void loadRequiredApiModules(); From 3c96881a59a61fe3c7859738217f411dc91d08f5 Mon Sep 17 00:00:00 2001 From: John Maximilian <2e0byo@gmail.com> Date: Tue, 7 Mar 2023 16:48:05 +0000 Subject: [PATCH 091/217] fix: mpd bug paused with no song. --- src/modules/mpd/mpd.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index e728897..008462a 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -122,7 +122,10 @@ void waybar::modules::MPD::setLabel() { std::chrono::seconds elapsedTime, totalTime; std::string stateIcon = ""; - if (stopped()) { + bool no_song = song_.get() == nullptr; + if (stopped() || no_song ) { + if (no_song) + spdlog::warn("Bug in mpd: no current song but state is not stopped."); format = config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped"; label_.get_style_context()->add_class("stopped"); From 8ccf00f0fe6aaefd99455dd9a2a865ce9e8896bf Mon Sep 17 00:00:00 2001 From: Lucas Alber Date: Wed, 8 Mar 2023 15:21:52 +0100 Subject: [PATCH 092/217] Wireplumber: Free the default node name The `default_node_name_` is reassigned without calling `g_free` on the old string. --- 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 4c7a2d0..150acb4 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -173,7 +173,8 @@ void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wir spdlog::debug( "[{}]: (onDefaultNodesApiChanged) - Default node changed to -> Node(name: {}, id: {})", self->name_, default_node_name, default_node_id); - + + g_free(self->default_node_name_); self->default_node_name_ = g_strdup(default_node_name); updateVolume(self, default_node_id); updateNodeName(self, default_node_id); From a9015c7c98cfc03fe462155783eac7d032eb743e Mon Sep 17 00:00:00 2001 From: John Maximilian <2e0byo@gmail.com> Date: Wed, 8 Mar 2023 14:31:53 +0000 Subject: [PATCH 093/217] refactor: make linter happy. --- src/modules/mpd/mpd.cpp | 5 ++--- src/modules/wireplumber.cpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 008462a..73062c7 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -123,9 +123,8 @@ void waybar::modules::MPD::setLabel() { std::string stateIcon = ""; bool no_song = song_.get() == nullptr; - if (stopped() || no_song ) { - if (no_song) - spdlog::warn("Bug in mpd: no current song but state is not stopped."); + if (stopped() || no_song) { + if (no_song) spdlog::warn("Bug in mpd: no current song but state is not stopped."); format = config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped"; label_.get_style_context()->add_class("stopped"); diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index 150acb4..7d9689c 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -173,7 +173,7 @@ void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wir spdlog::debug( "[{}]: (onDefaultNodesApiChanged) - Default node changed to -> Node(name: {}, id: {})", self->name_, default_node_name, default_node_id); - + g_free(self->default_node_name_); self->default_node_name_ = g_strdup(default_node_name); updateVolume(self, default_node_id); From 90206f55be01c12be9c1b239b54bc3b346821742 Mon Sep 17 00:00:00 2001 From: Paul Riou Date: Mon, 13 Mar 2023 00:44:07 +0000 Subject: [PATCH 094/217] config:output:Allow multiple exclusions & wildcard Covers the use case where needing to exclude more than 1 output but still include all other displays. e.g. I have 3 monitors: laptop + HD + 4K; and 3 bar types: - The main bar is on the laptop. `output: "laptop-monitor-id"` - The 4K has a specific waybar bar-1 configuration. `output: "4K-monitor-id"` - I want all other displays (3rd HD monitor / any HDMI output when presenting) to have a plain bar: `output: ["!laptop-monitor-id", "!4k-monitor-id", "*"]` --- man/waybar.5.scd.in | 1 + src/config.cpp | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index d366e3e..7061bc6 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -30,6 +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. + In an array, star '*\**' can be used at the end to accept all outputs, in case all previous entries are exclusions. *position* ++ typeof: string ++ diff --git a/src/config.cpp b/src/config.cpp index a4d9090..45f5ee3 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -124,9 +124,21 @@ bool isValidOutput(const Json::Value &config, const std::string &name, const std::string &identifier) { if (config["output"].isArray()) { for (auto const &output_conf : config["output"]) { - if (output_conf.isString() && - (output_conf.asString() == name || output_conf.asString() == identifier)) { - return true; + if (output_conf.isString()) { + auto config_output = output_conf.asString(); + if (config_output.substr(0, 1) == "!") { + if (config_output.substr(1) == name || config_output.substr(1) == identifier) { + return false; + } else { + continue; + } + } + if (config_output == name || config_output == identifier) { + return true; + } + if (config_output.substr(0, 1) == "*") { + return true; + } } } return false; From c26849217e23b1698e889fd91186158ecbd8dad2 Mon Sep 17 00:00:00 2001 From: Paul Riou Date: Mon, 13 Mar 2023 00:49:18 +0000 Subject: [PATCH 095/217] test:Add test for output config with multiple exclusion/wildcard --- test/config.cpp | 3 ++- test/config/multi.json | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/test/config.cpp b/test/config.cpp index 3d0f007..ad3df06 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -31,7 +31,7 @@ TEST_CASE("Load config with multiple bars", "[config]") { SECTION("select multiple configs #1") { auto data = conf.getOutputConfigs("DP-0", "Fake DisplayPort output #0"); - REQUIRE(data.size() == 3); + REQUIRE(data.size() == 4); REQUIRE(data[0]["layer"].asString() == "bottom"); REQUIRE(data[0]["height"].asInt() == 20); REQUIRE(data[1]["layer"].asString() == "top"); @@ -40,6 +40,7 @@ TEST_CASE("Load config with multiple bars", "[config]") { REQUIRE(data[2]["layer"].asString() == "overlay"); REQUIRE(data[2]["position"].asString() == "right"); REQUIRE(data[2]["height"].asInt() == 23); + REQUIRE(data[3]["height"].asInt() == 24); } SECTION("select multiple configs #2") { auto data = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); diff --git a/test/config/multi.json b/test/config/multi.json index ed43a39..06f6ae2 100644 --- a/test/config/multi.json +++ b/test/config/multi.json @@ -21,5 +21,9 @@ "layer": "overlay", "height": 23, "output": "!HDMI-1" + }, + { + "height": 24, + "output": ["!HDMI-0", "!HDMI-1", "*"] } ] From fa9f3a6dc52ca3739b97ed8b92ec6538a9b43e54 Mon Sep 17 00:00:00 2001 From: Paul Riou Date: Mon, 13 Mar 2023 00:50:19 +0000 Subject: [PATCH 096/217] make:Add phony 'test' rule --- .github/workflows/linux.yml | 2 +- Makefile | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c82af85..1c00a5e 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -29,4 +29,4 @@ jobs: - name: build run: ninja -C build - name: test - run: meson test -C build --no-rebuild --verbose --suite waybar + run: make test diff --git a/Makefile b/Makefile index 94f8ee6..b1dbfc6 100644 --- a/Makefile +++ b/Makefile @@ -19,5 +19,9 @@ run: build debug-run: build-debug ./build/waybar --log-level debug +test: + meson test -C build --no-rebuild --verbose --suite waybar +.PHONY: test + clean: rm -rf build From 4ab4ff790e94efc6d3ff1064361e77587c12ac00 Mon Sep 17 00:00:00 2001 From: en3wton Date: Sun, 19 Mar 2023 15:12:11 +0000 Subject: [PATCH 097/217] add more format replacements for hyprland/language --- include/modules/hyprland/language.hpp | 13 +++++- man/waybar-hyprland-language.5.scd | 14 ++++++- src/modules/hyprland/language.cpp | 59 +++++++++++++-------------- 3 files changed, 51 insertions(+), 35 deletions(-) diff --git a/include/modules/hyprland/language.hpp b/include/modules/hyprland/language.hpp index 64290f8..b12504c 100644 --- a/include/modules/hyprland/language.hpp +++ b/include/modules/hyprland/language.hpp @@ -18,12 +18,21 @@ class Language : public waybar::ALabel, public EventHandler { void onEvent(const std::string&) override; void initLanguage(); - std::string getShortFrom(const std::string&); + + struct Layout { + std::string full_name; + std::string short_name; + std::string variant; + std::string short_description; + }; + + auto getLayout(const std::string&) -> Layout*; std::mutex mutex_; const Bar& bar_; util::JsonParser parser_; - std::string layoutName_; + + Layout layout_; }; } // namespace waybar::modules::hyprland diff --git a/man/waybar-hyprland-language.5.scd b/man/waybar-hyprland-language.5.scd index 4a4e175..3e92def 100644 --- a/man/waybar-hyprland-language.5.scd +++ b/man/waybar-hyprland-language.5.scd @@ -15,7 +15,7 @@ Addressed by *hyprland/language* *format*: ++ typeof: string ++ default: {} ++ - The format, how information should be displayed. On {} the currently selected language is displayed. + The format, how information should be displayed. *format-* ++ typeof: string++ @@ -26,12 +26,22 @@ Addressed by *hyprland/language* Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended. +# 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 ``` "hyprland/language": { - "format": "Lang: {}" + "format": "Lang: {long}" "format-en": "AMERICA, HELL YEAH!" "format-tr": "As bayrakları" "keyboard-name": "at-translated-set-2-keyboard" diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 622c28d..832516a 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -6,7 +6,7 @@ #include -#include "modules/hyprland/backend.hpp" +#include "util/string.hpp" namespace waybar::modules::hyprland { @@ -37,9 +37,20 @@ Language::~Language() { auto Language::update() -> void { std::lock_guard lg(mutex_); + std::string layoutName = std::string{}; + 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))); + } + if (!format_.empty()) { label_.show(); - label_.set_markup(layoutName_); + label_.set_markup(layoutName); } else { label_.hide(); } @@ -57,18 +68,7 @@ void Language::onEvent(const std::string& ev) { layoutName = waybar::util::sanitize_string(layoutName); - const auto briefName = getShortFrom(layoutName); - - if (config_.isMember("format-" + briefName)) { - const auto propName = "format-" + briefName; - layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); - } else { - layoutName = fmt::format(fmt::runtime(format_), layoutName); - } - - if (layoutName == layoutName_) return; - - layoutName_ = layoutName; + layout_ = *getLayout(layoutName); spdlog::debug("hyprland language onevent with {}", layoutName); @@ -89,19 +89,9 @@ void Language::initLanguage() { searcher = waybar::util::sanitize_string(searcher); - auto layoutName = std::string{}; - const auto briefName = getShortFrom(searcher); + layout_ = *getLayout(searcher); - if (config_.isMember("format-" + briefName)) { - const auto propName = "format-" + briefName; - layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString()); - } else { - layoutName = fmt::format(fmt::runtime(format_), searcher); - } - - layoutName_ = layoutName; - - spdlog::debug("hyprland language initLanguage found {}", layoutName_); + spdlog::debug("hyprland language initLanguage found {}", layout_.full_name); dp.emit(); @@ -110,11 +100,10 @@ void Language::initLanguage() { } } -std::string Language::getShortFrom(const std::string& fullName) { +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); - std::string foundName = ""; rxkb_layout* layout = rxkb_layout_first(CONTEXT); while (layout) { std::string nameOfLayout = rxkb_layout_get_description(layout); @@ -124,16 +113,24 @@ std::string Language::getShortFrom(const std::string& fullName) { continue; } - std::string briefName = rxkb_layout_get_brief(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_); + + auto short_description_ = rxkb_layout_get_brief(layout); + std::string short_description = + short_description_ == nullptr ? "" : std::string(short_description_); + + Layout* info = new Layout{nameOfLayout, name, variant, short_description}; rxkb_context_unref(CONTEXT); - return briefName; + return info; } rxkb_context_unref(CONTEXT); - return ""; + return NULL; } } // namespace waybar::modules::hyprland From bbcfb5e138f8a3f7e180ee2684016513ccee5d8a Mon Sep 17 00:00:00 2001 From: en3wton Date: Tue, 21 Mar 2023 19:30:35 +0000 Subject: [PATCH 098/217] remove use of new on Layout --- include/modules/hyprland/language.hpp | 2 +- src/modules/hyprland/language.cpp | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/language.hpp b/include/modules/hyprland/language.hpp index b12504c..30789d0 100644 --- a/include/modules/hyprland/language.hpp +++ b/include/modules/hyprland/language.hpp @@ -26,7 +26,7 @@ class Language : public waybar::ALabel, public EventHandler { std::string short_description; }; - auto getLayout(const std::string&) -> Layout*; + auto getLayout(const std::string&) -> Layout; std::mutex mutex_; const Bar& bar_; diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 832516a..cfc24f8 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -68,7 +68,7 @@ void Language::onEvent(const std::string& ev) { layoutName = waybar::util::sanitize_string(layoutName); - layout_ = *getLayout(layoutName); + layout_ = getLayout(layoutName); spdlog::debug("hyprland language onevent with {}", layoutName); @@ -89,7 +89,7 @@ void Language::initLanguage() { searcher = waybar::util::sanitize_string(searcher); - layout_ = *getLayout(searcher); + layout_ = getLayout(searcher); spdlog::debug("hyprland language initLanguage found {}", layout_.full_name); @@ -100,7 +100,7 @@ void Language::initLanguage() { } } -auto Language::getLayout(const std::string& fullName) -> Layout* { +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); @@ -121,7 +121,7 @@ auto Language::getLayout(const std::string& fullName) -> Layout* { std::string short_description = short_description_ == nullptr ? "" : std::string(short_description_); - Layout* info = new Layout{nameOfLayout, name, variant, short_description}; + Layout info = Layout{nameOfLayout, name, variant, short_description}; rxkb_context_unref(CONTEXT); @@ -130,7 +130,9 @@ auto Language::getLayout(const std::string& fullName) -> Layout* { rxkb_context_unref(CONTEXT); - return NULL; + spdlog::debug("hyprland language didn't find matching layout"); + + return Layout {"", "", "", ""}; } } // namespace waybar::modules::hyprland From 5791a6abc631a350d1312846b512f9c789b951c2 Mon Sep 17 00:00:00 2001 From: MonstrousOgre Date: Fri, 24 Mar 2023 23:11:47 +0530 Subject: [PATCH 099/217] Reading persistent workspaces --- include/modules/wlr/workspace_manager.hpp | 11 ++- src/modules/wlr/workspace_manager.cpp | 81 +++++++++++++++++++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index 963d610..1fdb992 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -22,7 +22,7 @@ class WorkspaceGroup; class Workspace { public: Workspace(const waybar::Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, - zext_workspace_handle_v1 *workspace, uint32_t id); + zext_workspace_handle_v1 *workspace, uint32_t id, std::string name); ~Workspace(); auto update() -> void; @@ -30,11 +30,14 @@ class Workspace { auto is_active() const -> bool { return state_ & static_cast(State::ACTIVE); } auto is_urgent() const -> bool { return state_ & static_cast(State::URGENT); } auto is_hidden() const -> bool { return state_ & static_cast(State::HIDDEN); } + auto is_persistent() const -> bool { return persistent_; } // wlr stuff auto handle_name(const std::string &name) -> void; auto handle_coordinates(const std::vector &coordinates) -> void; auto handle_state(const std::vector &state) -> void; auto handle_remove() -> void; + auto make_persistent() -> void; + auto handle_duplicate() -> void; auto handle_done() -> void; auto handle_clicked(GdkEventButton *bt) -> bool; @@ -67,6 +70,7 @@ class Workspace { static std::map icons_map_; std::string format_; bool with_icon_ = false; + bool persistent_ = false; Gtk::Button button_; Gtk::Box content_; @@ -87,11 +91,14 @@ class WorkspaceGroup { auto active_only() const -> bool; auto creation_delayed() const -> bool; auto workspaces() -> std::vector> & { return workspaces_; } + auto persistent_workspaces() -> std::vector & { return persistent_workspaces_; } auto sort_workspaces() -> void; auto set_need_to_sort() -> void { need_to_sort = true; } auto add_button(Gtk::Button &button) -> void; auto remove_button(Gtk::Button &button) -> void; + auto fill_persistent_workspaces() -> void; + auto create_persistent_workspaces() -> void; // wlr stuff auto handle_workspace_create(zext_workspace_handle_v1 *workspace_handle) -> void; @@ -115,6 +122,8 @@ class WorkspaceGroup { uint32_t id_; std::vector> workspaces_; bool need_to_sort = false; + std::vector persistent_workspaces_; + bool persistent_created_ = false; }; class WorkspaceManager : public AModule { diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index c1b68c8..75a0c0b 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -208,6 +208,38 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value add_workspace_group_listener(workspace_group_handle, this); } +auto WorkspaceGroup::fill_persistent_workspaces() -> void { + 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) { + const Json::Value &p_w = p_workspaces[p_w_name]; + if (p_w.isArray() && !p_w.empty()) { + // Adding to target outputs + for (const Json::Value &output : p_w) { + if (output.asString() == bar_.output->name) { + persistent_workspaces_.push_back(p_w_name); + break; + } + } + } else { + // Adding to all outputs + persistent_workspaces_.push_back(p_w_name); + } + } + } +} + +auto WorkspaceGroup::create_persistent_workspaces() -> void { + for (const std::string &p_w_name : persistent_workspaces_) { + auto new_id = ++workspace_global_id; + workspaces_.push_back( + std::make_unique(bar_, config_, *this, nullptr, new_id, p_w_name)); + spdlog::info("Workspace {} created", new_id); + } +} + auto WorkspaceGroup::active_only() const -> bool { return workspace_manager_.active_only(); } auto WorkspaceGroup::creation_delayed() const -> bool { return workspace_manager_.creation_delayed(); @@ -228,8 +260,13 @@ WorkspaceGroup::~WorkspaceGroup() { auto WorkspaceGroup::handle_workspace_create(zext_workspace_handle_v1 *workspace) -> void { auto new_id = ++workspace_global_id; - workspaces_.push_back(std::make_unique(bar_, config_, *this, workspace, new_id)); + workspaces_.push_back(std::make_unique(bar_, config_, *this, workspace, new_id, "")); spdlog::debug("Workspace {} created", new_id); + if (!persistent_created_) { + fill_persistent_workspaces(); + create_persistent_workspaces(); + persistent_created_ = true; + } } auto WorkspaceGroup::handle_remove() -> void { @@ -328,13 +365,16 @@ auto WorkspaceGroup::sort_workspaces() -> void { auto WorkspaceGroup::remove_button(Gtk::Button &button) -> void { box_.remove(button); } Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup &workspace_group, - zext_workspace_handle_v1 *workspace, uint32_t id) + zext_workspace_handle_v1 *workspace, uint32_t id, std::string name) : bar_(bar), config_(config), workspace_group_(workspace_group), workspace_handle_(workspace), - id_(id) { - add_workspace_listener(workspace, this); + id_(id), + name_(name) { + if (workspace) { + add_workspace_listener(workspace, this); + } auto config_format = config["format"]; @@ -401,9 +441,13 @@ auto Workspace::handle_state(const std::vector &state) -> void { } auto Workspace::handle_remove() -> void { - zext_workspace_handle_v1_destroy(workspace_handle_); - workspace_handle_ = nullptr; - workspace_group_.remove_workspace(id_); + if (workspace_handle_) { + zext_workspace_handle_v1_destroy(workspace_handle_); + workspace_handle_ = nullptr; + } + if (!persistent_) { + workspace_group_.remove_workspace(id_); + } } auto add_or_remove_class(Glib::RefPtr context, bool condition, @@ -487,6 +531,29 @@ auto Workspace::handle_name(const std::string &name) -> void { workspace_group_.set_need_to_sort(); } name_ = name; + spdlog::info("Workspace {} added to group {}", name, workspace_group_.id()); + + make_persistent(); + handle_duplicate(); +} + +auto Workspace::make_persistent() -> void { + auto p_workspaces = workspace_group_.persistent_workspaces(); + + if (std::find(p_workspaces.begin(), p_workspaces.end(), name_) != p_workspaces.end()) { + persistent_ = true; + } +} + +auto Workspace::handle_duplicate() -> void { + auto duplicate = + std::find_if(workspace_group_.workspaces().begin(), workspace_group_.workspaces().end(), + [this](const std::unique_ptr &g) { + return g->get_name() == name_ && g->id() != id_; + }); + if (duplicate != workspace_group_.workspaces().end()) { + workspace_group_.remove_workspace(duplicate->get()->id()); + } } auto Workspace::handle_coordinates(const std::vector &coordinates) -> void { From 63525ec95628a0720af457f38f6e266a723db9ac Mon Sep 17 00:00:00 2001 From: TheRealLorenz Date: Sat, 25 Mar 2023 17:33:01 +0100 Subject: [PATCH 100/217] Rename function name --- include/util/{rewrite_title.hpp => rewrite_string.hpp} | 2 +- meson.build | 2 +- src/modules/hyprland/window.cpp | 4 ++-- src/modules/sway/window.cpp | 4 ++-- src/util/{rewrite_title.cpp => rewrite_string.cpp} | 10 +++++----- 5 files changed, 11 insertions(+), 11 deletions(-) rename include/util/{rewrite_title.hpp => rewrite_string.hpp} (55%) rename src/util/{rewrite_title.cpp => rewrite_string.cpp} (75%) diff --git a/include/util/rewrite_title.hpp b/include/util/rewrite_string.hpp similarity index 55% rename from include/util/rewrite_title.hpp rename to include/util/rewrite_string.hpp index c477339..2ab39ad 100644 --- a/include/util/rewrite_title.hpp +++ b/include/util/rewrite_string.hpp @@ -4,5 +4,5 @@ #include namespace waybar::util { -std::string rewriteTitle(const std::string&, const Json::Value&); +std::string rewriteString(const std::string&, const Json::Value&); } diff --git a/meson.build b/meson.build index ad6541e..58e1c67 100644 --- a/meson.build +++ b/meson.build @@ -172,7 +172,7 @@ src_files = files( 'src/group.cpp', 'src/util/ustring_clen.cpp', 'src/util/sanitize_str.cpp', - 'src/util/rewrite_title.cpp' + 'src/util/rewrite_string.cpp' ) if is_linux diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 47daae9..40b6620 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -8,7 +8,7 @@ #include "modules/hyprland/backend.hpp" #include "util/command.hpp" #include "util/json.hpp" -#include "util/rewrite_title.hpp" +#include "util/rewrite_string.hpp" namespace waybar::modules::hyprland { @@ -41,7 +41,7 @@ auto Window::update() -> void { if (!format_.empty()) { label_.show(); label_.set_markup(fmt::format(fmt::runtime(format_), - waybar::util::rewriteTitle(lastView, config_["rewrite"]))); + waybar::util::rewriteString(lastView, config_["rewrite"]))); } else { label_.hide(); } diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 7d60d29..c3c19f5 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -12,7 +12,7 @@ #include #include -#include "util/rewrite_title.hpp" +#include "util/rewrite_string.hpp" namespace waybar::modules::sway { @@ -206,7 +206,7 @@ auto Window::update() -> void { label_.set_markup( fmt::format(fmt::runtime(format_), - fmt::arg("title", waybar::util::rewriteTitle(window_, config_["rewrite"])), + fmt::arg("title", waybar::util::rewriteString(window_, config_["rewrite"])), fmt::arg("app_id", app_id_), fmt::arg("shell", shell_))); if (tooltipEnabled()) { label_.set_tooltip_text(window_); diff --git a/src/util/rewrite_title.cpp b/src/util/rewrite_string.cpp similarity index 75% rename from src/util/rewrite_title.cpp rename to src/util/rewrite_string.cpp index fae59bb..40c71e9 100644 --- a/src/util/rewrite_title.cpp +++ b/src/util/rewrite_string.cpp @@ -1,16 +1,16 @@ -#include "util/rewrite_title.hpp" +#include "util/rewrite_string.hpp" #include #include namespace waybar::util { -std::string rewriteTitle(const std::string& title, const Json::Value& rules) { +std::string rewriteString(const std::string& value, const Json::Value& rules) { if (!rules.isObject()) { - return title; + return value; } - std::string res = title; + std::string res = value; for (auto it = rules.begin(); it != rules.end(); ++it) { if (it.key().isString() && it->isString()) { @@ -18,7 +18,7 @@ std::string rewriteTitle(const std::string& title, const Json::Value& rules) { // malformated regexes will cause an exception. // in this case, log error and try the next rule. const std::regex rule{it.key().asString()}; - if (std::regex_match(title, rule)) { + if (std::regex_match(value, rule)) { res = std::regex_replace(res, rule, it->asString()); } } catch (const std::regex_error& e) { From f557697e92b4fb07386ffe8fbc0450903019882c Mon Sep 17 00:00:00 2001 From: TheRealLorenz Date: Sat, 25 Mar 2023 17:36:00 +0100 Subject: [PATCH 101/217] Rewrite entire label in sway/window module Closes #1742 --- src/modules/sway/window.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index c3c19f5..3c528e0 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -204,10 +204,10 @@ auto Window::update() -> void { old_app_id_ = app_id_; } - label_.set_markup( - fmt::format(fmt::runtime(format_), - fmt::arg("title", waybar::util::rewriteString(window_, config_["rewrite"])), - fmt::arg("app_id", app_id_), fmt::arg("shell", shell_))); + label_.set_markup(waybar::util::rewriteString( + fmt::format(fmt::runtime(format_), fmt::arg("title", window_), fmt::arg("app_id", app_id_), + fmt::arg("shell", shell_)), + config_["rewrite"])); if (tooltipEnabled()) { label_.set_tooltip_text(window_); } From 4b27385da2838f69c72eb8a921b6aeeca7c4b266 Mon Sep 17 00:00:00 2001 From: TheRealLorenz Date: Sat, 25 Mar 2023 17:41:09 +0100 Subject: [PATCH 102/217] Update manpage for sway/window module --- man/waybar-sway-window.5.scd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 19e0cd2..0dd1629 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -87,7 +87,7 @@ Addressed by *sway/window* *rewrite*: ++ typeof: object ++ - Rules to rewrite window title. See *rewrite rules*. + Rules to rewrite the module format output. See *rewrite rules*. *icon*: ++ typeof: bool ++ @@ -116,7 +116,7 @@ captures of the expression. Regular expression and replacement follow ECMA-script rules. -If no expression matches, the title is left unchanged. +If no expression matches, the format output is left unchanged. Invalid expressions (e.g., mismatched parentheses) are skipped. From 918de599886ae3a7161d6ed3de12862fabaa86b3 Mon Sep 17 00:00:00 2001 From: lilydjwg Date: Wed, 29 Mar 2023 21:18:13 +0800 Subject: [PATCH 103/217] custom module: free memory returned by libc's getline --- src/modules/custom.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index b7e1d2d..5a246af 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -88,6 +88,7 @@ void waybar::modules::Custom::continuousWorker() { output_ = {0, output}; dp.emit(); } + free(buff); }; } From a53c97f7f6c34dadf4ba360c6baad6efe8227f8f Mon Sep 17 00:00:00 2001 From: chayleaf Date: Thu, 16 Feb 2023 19:41:24 +0700 Subject: [PATCH 104/217] mpris: add tooltip and length limits --- include/modules/mpris/mpris.hpp | 18 +++ man/waybar-mpd.5.scd | 24 ++-- man/waybar-mpris.5.scd | 46 +++++++- src/modules/mpris/mpris.cpp | 198 ++++++++++++++++++++++++++++---- 4 files changed, 251 insertions(+), 35 deletions(-) diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index e82c929..30c747a 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -44,6 +44,11 @@ class Mpris : public AModule { auto getPlayerInfo() -> std::optional; auto getIcon(const Json::Value&, const std::string&) -> std::string; + auto getArtistStr(const PlayerInfo&, bool) -> std::string; + auto getAlbumStr(const PlayerInfo&, bool) -> std::string; + auto getTitleStr(const PlayerInfo&, bool) -> std::string; + auto getLengthStr(const PlayerInfo&, bool) -> std::string; + auto getDynamicStr(const PlayerInfo&, bool, bool) -> std::string; Gtk::Box box_; Gtk::Label label_; @@ -53,6 +58,19 @@ class Mpris : public AModule { std::string format_playing_; std::string format_paused_; std::string format_stopped_; + + std::string tooltip_; + std::string tooltip_playing_; + std::string tooltip_paused_; + std::string tooltip_stopped_; + + int artist_len_; + int album_len_; + int title_len_; + int dynamic_len_; + std::vector dynamic_prio_; + bool tooltip_len_limits_; + std::chrono::seconds interval_; std::string player_; std::vector ignored_players_; diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index 044af98..1dde8f7 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -74,20 +74,20 @@ Addressed by *mpd* Tooltip information displayed when the MPD server can't be reached. *artist-len*: ++ - typeof: integer ++ - Maximum length of the Artist tag. + typeof: integer ++ + Maximum length of the Artist tag. *album-len*: ++ - typeof: integer ++ - Maximum length of the Album tag. + typeof: integer ++ + Maximum length of the Album tag. *album-artist-len*: ++ - typeof: integer ++ - Maximum length of the Album Artist tag. + typeof: integer ++ + Maximum length of the Album Artist tag. *title-len*: ++ - typeof: integer ++ - Maximum length of the Title tag. + typeof: integer ++ + Maximum length of the Title tag. *rotate*: ++ typeof: integer ++ @@ -98,12 +98,12 @@ Addressed by *mpd* 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 d2a72d9..b2d4097 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -33,6 +33,48 @@ The *mpris* module displays currently playing media via libplayerctl. typeof: string ++ The status-specific text format. +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to disable tooltip on hover. + +*tooltip-format*: ++ + typeof: string ++ + default: {player} ({status}) {dynamic} ++ + The tooltip text format. + +*tooltip-format-[status]*: ++ + typeof: string ++ + default: "MPD (disconnected)" ++ + The status-specif tooltip format. + +*artist-len*: ++ + typeof: integer ++ + Maximum length of the Artist tag. + +*album-len*: ++ + typeof: integer ++ + Maximum length of the Album tag. + +*title-len*: ++ + typeof: integer ++ + Maximum length of the Title tag. + +*dynamic-len*: ++ + typeof: integer ++ + Maximum length of the Dynamic tag. + +*dynamic-priority* ++ + typeof: []string ++ + default: ["title", "length", "artist", "album"] + Priority of the tags when truncating the Dynamic tag (absence in this + list means force inclusion). + +*enable-tooltip-len-limits*: ++ + typeof: bool ++ + default: false ++ + Option to enable the limits for the tooltip as well + *on-click*: ++ typeof: string ++ default: play-pause ++ @@ -83,8 +125,8 @@ The *mpris* module displays currently playing media via libplayerctl. ``` "mpris": { - "format": "DEFAULT: {player_icon} {dynamic}", - "format-paused": "DEFAULT: {status_icon} {dynamic}", + "format": "{player_icon} {dynamic}", + "format-paused": "{status_icon} {dynamic}", "player-icons": { "default": "▶", "mpv": "🎵" diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index f11821f..d088345 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -21,6 +21,13 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) box_(Gtk::ORIENTATION_HORIZONTAL, 0), label_(), format_(DEFAULT_FORMAT), + tooltip_(DEFAULT_FORMAT), + artist_len_(-1), + album_len_(-1), + title_len_(-1), + dynamic_len_(-1), + dynamic_prio_({"title", "length", "artist", "album"}), + tooltip_len_limits_(false), interval_(0), player_("playerctld"), manager(), @@ -42,6 +49,46 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) if (config_["format-stopped"].isString()) { format_stopped_ = config_["format-stopped"].asString(); } + if (tooltipEnabled()) { + if (config_["tooltip-format"].isString()) { + tooltip_ = config_["tooltip-format"].asString(); + } + if (config_["tooltip-format-playing"].isString()) { + tooltip_playing_ = config_["tooltip-format-playing"].asString(); + } + if (config_["tooltip-format-paused"].isString()) { + tooltip_paused_ = config_["tooltip-format-paused"].asString(); + } + if (config_["tooltip-format-stopped"].isString()) { + tooltip_stopped_ = config_["tooltip-format-stopped"].asString(); + } + if (config_["enable-tooltip-len-limits"].isBool()) { + tooltip_len_limits_ = config["enable-tooltip-len-limits"].asBool(); + } + } + + if (config["artist-len"].isUInt()) { + artist_len_ = config["artist-len"].asUInt(); + } + if (config["album-len"].isUInt()) { + album_len_ = config["album-len"].asUInt(); + } + if (config["title-len"].isUInt()) { + title_len_ = config["title-len"].asUInt(); + } + if (config["dynamic-len"].isUInt()) { + dynamic_len_ = config["dynamic-len"].asUInt(); + } + if (config_["dynamic-priority"].isArray()) { + dynamic_prio_.clear(); + for (auto it = config_["dynamic-priority"].begin(); it != config_["dynamic-priority"].end(); + ++it) { + if (it->isString()) { + dynamic_prio_.push_back(it->asString()); + } + } + } + if (config_["interval"].isUInt()) { interval_ = std::chrono::seconds(config_["interval"].asUInt()); } @@ -51,7 +98,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) if (config_["ignored-players"].isArray()) { for (auto it = config_["ignored-players"].begin(); it != config_["ignored-players"].end(); ++it) { - ignored_players_.push_back(it->asString()); + if (it->isString()) { + ignored_players_.push_back(it->asString()); + } } } @@ -129,6 +178,95 @@ auto Mpris::getIcon(const Json::Value& icons, const std::string& key) -> std::st return ""; } +auto Mpris::getArtistStr(const PlayerInfo &info, bool truncated) -> std::string { + std::string artist = info.artist.value_or(std::string()); + return (truncated && artist_len_ >= 0) ? artist.substr(0, artist_len_) : artist; +} + +auto Mpris::getAlbumStr(const PlayerInfo &info, bool truncated) -> std::string { + std::string album = info.album.value_or(std::string()); + return (truncated && album_len_ >= 0) ? album.substr(0, album_len_) : album; +} + +auto Mpris::getTitleStr(const PlayerInfo &info, bool truncated) -> std::string { + std::string title = info.title.value_or(std::string()); + return (truncated && title_len_ >= 0) ? title.substr(0, title_len_) : title; +} + +auto Mpris::getLengthStr(const PlayerInfo &info, bool from_dynamic) -> std::string { + if (info.length.has_value()) { + return from_dynamic ? ("[" + info.length.value() + "]") : info.length.value(); + } + return std::string(); +} + +auto Mpris::getDynamicStr(const PlayerInfo &info, bool truncated, bool html) -> std::string { + std::string artist = getArtistStr(info, truncated); + std::string album = getAlbumStr(info, truncated); + std::string title = getTitleStr(info, truncated); + std::string length = getLengthStr(info, true); + + std::stringstream dynamic; + bool showArtist, showAlbum, showTitle, showLength; + showArtist = artist.length() != 0; + showAlbum = album.length() != 0; + showTitle = title.length() != 0; + showLength = length.length() != 0; + + if (truncated && dynamic_len_ >= 0) { + size_t dynamicLen = dynamic_len_; + size_t artistLen = showArtist ? artist.length() + 3 : 0; + size_t albumLen = showAlbum ? album.length() + 3 : 0; + size_t titleLen = title.length(); + size_t lengthLen = showLength ? length.length() + 1 : 0; + + size_t totalLen = 0; + + for (auto it = dynamic_prio_.begin(); it != dynamic_prio_.end(); ++it) { + if (*it == "artist") { + if (totalLen + artistLen > dynamicLen) { + showArtist = false; + } else { + totalLen += artistLen; + } + } else if (*it == "album") { + if (totalLen + albumLen > dynamicLen) { + showAlbum = false; + } else { + totalLen += albumLen; + } + } else if (*it == "title") { + if (totalLen + titleLen > dynamicLen) { + showTitle = false; + } else { + totalLen += titleLen; + } + } else if (*it == "length") { + if (totalLen + lengthLen > dynamicLen) { + showLength = false; + } else { + totalLen += lengthLen; + } + } + } + } + + if (showArtist) dynamic << artist << " - "; + if (showAlbum) dynamic << album << " - "; + if (showTitle) dynamic << title; + if (showLength) { + dynamic << " "; + if (html) { + dynamic << ""; + } + dynamic << length; + if (html) { + dynamic << ""; + } + } + return dynamic.str(); +} + auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name, gpointer data) -> void { Mpris* mpris = static_cast(data); @@ -335,18 +473,6 @@ auto Mpris::update() -> void { spdlog::debug("mpris[{}]: running update", info.name); - // dynamic is the auto-formatted string containing a nice out-of-the-box - // format text - std::stringstream dynamic; - if (info.artist) dynamic << *info.artist << " - "; - if (info.album) dynamic << *info.album << " - "; - if (info.title) dynamic << *info.title; - if (info.length) - dynamic << " " - << "" - << "[" << *info.length << "]" - << ""; - // set css class for player status if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) { box_.get_style_context()->remove_class(lastStatus); @@ -366,25 +492,55 @@ auto Mpris::update() -> void { lastPlayer = info.name; auto formatstr = format_; + auto tooltipstr = tooltip_; switch (info.status) { case PLAYERCTL_PLAYBACK_STATUS_PLAYING: if (!format_playing_.empty()) formatstr = format_playing_; + if (!tooltip_playing_.empty()) tooltipstr = tooltip_playing_; break; case PLAYERCTL_PLAYBACK_STATUS_PAUSED: if (!format_paused_.empty()) formatstr = format_paused_; + if (!tooltip_paused_.empty()) tooltipstr = tooltip_paused_; break; case PLAYERCTL_PLAYBACK_STATUS_STOPPED: if (!format_stopped_.empty()) formatstr = format_stopped_; + if (!tooltip_stopped_.empty()) tooltipstr = tooltip_stopped_; break; } - auto label_format = - fmt::format(fmt::runtime(formatstr), fmt::arg("player", info.name), - fmt::arg("status", info.status_string), fmt::arg("artist", *info.artist), - fmt::arg("title", *info.title), fmt::arg("album", *info.album), - fmt::arg("length", *info.length), fmt::arg("dynamic", dynamic.str()), - fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), - fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); - label_.set_markup(label_format); + + try { + auto label_format = fmt::format( + fmt::runtime(formatstr), fmt::arg("player", info.name), + fmt::arg("status", info.status_string), fmt::arg("artist", getArtistStr(info, true)), + fmt::arg("title", getTitleStr(info, true)), fmt::arg("album", getAlbumStr(info, true)), + fmt::arg("length", getLengthStr(info, false)), + fmt::arg("dynamic", getDynamicStr(info, true, true)), + fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), + fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); + + label_.set_markup(label_format); + } catch (fmt::format_error const& e) { + spdlog::warn("mpris: format error: {}", e.what()); + } + + if (tooltipEnabled()) { + try { + auto tooltip_text = fmt::format( + fmt::runtime(tooltipstr), fmt::arg("player", info.name), + fmt::arg("status", info.status_string), + fmt::arg("artist", getArtistStr(info, tooltip_len_limits_)), + fmt::arg("title", getTitleStr(info, tooltip_len_limits_)), + fmt::arg("album", getAlbumStr(info, tooltip_len_limits_)), + fmt::arg("length", getLengthStr(info, false)), + fmt::arg("dynamic", getDynamicStr(info, tooltip_len_limits_, false)), + fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), + fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); + + label_.set_tooltip_text(tooltip_text); + } catch (fmt::format_error const& e) { + spdlog::warn("mpris: format error (tooltip): {}", e.what()); + } + } event_box_.set_visible(true); // call parent update From 5383f7bd567fbb02dd86c37907e6bf2a652d41ab Mon Sep 17 00:00:00 2001 From: chayleaf Date: Fri, 17 Feb 2023 03:41:38 +0700 Subject: [PATCH 105/217] mpris: add unicode support; add position tag --- include/modules/mpris/mpris.hpp | 6 +- man/waybar-mpris.5.scd | 30 +++++- src/modules/mpris/mpris.cpp | 178 ++++++++++++++++++++++++++------ 3 files changed, 176 insertions(+), 38 deletions(-) diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index 30c747a..ddb0cb6 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -39,7 +39,8 @@ class Mpris : public AModule { std::optional artist; std::optional album; std::optional title; - std::optional length; // as HH:MM:SS + std::optional length; // as HH:MM:SS + std::optional position; // same format }; auto getPlayerInfo() -> std::optional; @@ -48,6 +49,7 @@ class Mpris : public AModule { auto getAlbumStr(const PlayerInfo&, bool) -> std::string; auto getTitleStr(const PlayerInfo&, bool) -> std::string; auto getLengthStr(const PlayerInfo&, bool) -> std::string; + auto getPositionStr(const PlayerInfo&, bool) -> std::string; auto getDynamicStr(const PlayerInfo&, bool, bool) -> std::string; Gtk::Box box_; @@ -69,7 +71,9 @@ class Mpris : public AModule { int title_len_; int dynamic_len_; std::vector dynamic_prio_; + bool truncate_hours_; bool tooltip_len_limits_; + std::string ellipsis_; std::chrono::seconds interval_; std::string player_; diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index b2d4097..bc735b9 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -50,19 +50,23 @@ The *mpris* module displays currently playing media via libplayerctl. *artist-len*: ++ typeof: integer ++ - Maximum length of the Artist tag. + Maximum length of the Artist tag (Wide/Fullwidth Unicode characters + count as two). *album-len*: ++ typeof: integer ++ - Maximum length of the Album tag. + Maximum length of the Album tag (Wide/Fullwidth Unicode characters count + as two). *title-len*: ++ typeof: integer ++ - Maximum length of the Title tag. + Maximum length of the Title tag (Wide/Fullwidth Unicode characters count + as two). *dynamic-len*: ++ typeof: integer ++ - Maximum length of the Dynamic tag. + Maximum length of the Dynamic tag (Wide/Fullwidth Unicode characters + count as two). *dynamic-priority* ++ typeof: []string ++ @@ -70,10 +74,26 @@ The *mpris* module displays currently playing media via libplayerctl. Priority of the tags when truncating the Dynamic tag (absence in this list means force inclusion). +*truncate-hours*: ++ + typeof: bool ++ + default: true ++ + Whether to truncate hours when media duration is less than an hour long + *enable-tooltip-len-limits*: ++ typeof: bool ++ default: false ++ - Option to enable the limits for the tooltip as well + Option to enable the length limits for the tooltip as well + +*ellipsis*: ++ + typeof: string ++ + default: "…" ++ + Override the default ellipsis (set to empty string to simply truncate + the tags when needed instead). + +*on-click*: ++ + typeof: string ++ + default: play-pause ++ + Overwrite default action toggles. *on-click*: ++ typeof: string ++ diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index d088345..6e8004d 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -1,15 +1,16 @@ -#include "modules/mpris/mpris.hpp" - #include #include #include #include +#include "modules/mpris/mpris.hpp" + extern "C" { #include } +#include #include namespace waybar::modules::mpris { @@ -26,8 +27,11 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) album_len_(-1), title_len_(-1), dynamic_len_(-1), - dynamic_prio_({"title", "length", "artist", "album"}), + dynamic_prio_({"title", "length", "position", "artist", "album"}), + truncate_hours_(true), tooltip_len_limits_(false), + // this character is used in Gnome so it's fine to use it here + ellipsis_(u8"\u2026"), interval_(0), player_("playerctld"), manager(), @@ -49,6 +53,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) if (config_["format-stopped"].isString()) { format_stopped_ = config_["format-stopped"].asString(); } + if (config_["ellipsis"].isString()) { + ellipsis_ = config_["ellipsis"].asString(); + } if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { tooltip_ = config_["tooltip-format"].asString(); @@ -89,6 +96,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) } } + if (config_["truncate-hours"].isBool()) { + truncate_hours_ = config["truncate-hours"].asBool(); + } if (config_["interval"].isUInt()) { interval_ = std::chrono::seconds(config_["interval"].asUInt()); } @@ -178,47 +188,116 @@ auto Mpris::getIcon(const Json::Value& icons, const std::string& key) -> std::st return ""; } -auto Mpris::getArtistStr(const PlayerInfo &info, bool truncated) -> std::string { +// Wide characters count as two, zero-width characters count as zero +// Modifies str in-place (unless width = std::string::npos) +// Returns the total width of the string pre-truncating +size_t utf8_truncate(std::string& str, size_t width = std::string::npos) { + if (str.length() == 0) return 0; + + const gchar* trunc_end = nullptr; + + size_t total_width = 0; + + for (gchar *data = str.data(), *end = data + str.size(); data;) { + gunichar c = g_utf8_get_char_validated(data, end - data); + if (c == -1 || c == -2) { + // invalid unicode, treat string as ascii + if (width != std::string::npos && str.length() > width) str.resize(width); + return str.length(); + } else if (g_unichar_iswide(c)) { + total_width += 2; + } else if (!g_unichar_iszerowidth(c)) { + total_width += 1; + } + + data = g_utf8_find_next_char(data, end); + if (width != std::string::npos && total_width <= width) trunc_end = data; + } + + if (trunc_end) str.resize(trunc_end - str.data()); + + return total_width; +} + +size_t utf8_width(const std::string& str) { return utf8_truncate(const_cast(str)); } + +void truncate(std::string& s, const std::string& ellipsis, size_t max_len) { + if (max_len == 0) { + s.resize(0); + return; + } + size_t len = utf8_truncate(s, max_len); + if (len > max_len) { + size_t ellipsis_len = utf8_width(ellipsis); + if (max_len >= ellipsis_len) { + if (ellipsis_len) utf8_truncate(s, max_len - ellipsis_len); + s += ellipsis; + } else { + s.resize(0); + } + } +} + +auto Mpris::getArtistStr(const PlayerInfo& info, bool truncated) -> std::string { std::string artist = info.artist.value_or(std::string()); - return (truncated && artist_len_ >= 0) ? artist.substr(0, artist_len_) : artist; + if (truncated && artist_len_ >= 0) truncate(artist, ellipsis_, artist_len_); + return artist; } -auto Mpris::getAlbumStr(const PlayerInfo &info, bool truncated) -> std::string { +auto Mpris::getAlbumStr(const PlayerInfo& info, bool truncated) -> std::string { std::string album = info.album.value_or(std::string()); - return (truncated && album_len_ >= 0) ? album.substr(0, album_len_) : album; + if (truncated && album_len_ >= 0) truncate(album, ellipsis_, album_len_); + return album; } -auto Mpris::getTitleStr(const PlayerInfo &info, bool truncated) -> std::string { +auto Mpris::getTitleStr(const PlayerInfo& info, bool truncated) -> std::string { std::string title = info.title.value_or(std::string()); - return (truncated && title_len_ >= 0) ? title.substr(0, title_len_) : title; + if (truncated && title_len_ >= 0) truncate(title, ellipsis_, title_len_); + return title; } -auto Mpris::getLengthStr(const PlayerInfo &info, bool from_dynamic) -> std::string { +auto Mpris::getLengthStr(const PlayerInfo& info, bool truncated) -> std::string { if (info.length.has_value()) { - return from_dynamic ? ("[" + info.length.value() + "]") : info.length.value(); + std::string length = info.length.value(); + return (truncated && length.substr(0, 3) == "00:") ? length.substr(3) : length; } return std::string(); } -auto Mpris::getDynamicStr(const PlayerInfo &info, bool truncated, bool html) -> std::string { +auto Mpris::getPositionStr(const PlayerInfo& info, bool truncated) -> std::string { + if (info.position.has_value()) { + std::string position = info.position.value(); + return (truncated && position.substr(0, 3) == "00:") ? position.substr(3) : position; + } + return std::string(); +} + +auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> std::string { std::string artist = getArtistStr(info, truncated); std::string album = getAlbumStr(info, truncated); std::string title = getTitleStr(info, truncated); - std::string length = getLengthStr(info, true); + std::string length = getLengthStr(info, truncated && truncate_hours_); + // keep position format same as length format + std::string position = getPositionStr(info, truncated && truncate_hours_ && length.length() < 6); - std::stringstream dynamic; - bool showArtist, showAlbum, showTitle, showLength; - showArtist = artist.length() != 0; - showAlbum = album.length() != 0; - showTitle = title.length() != 0; - showLength = length.length() != 0; + size_t artistLen = utf8_width(artist); + size_t albumLen = utf8_width(album); + size_t titleLen = utf8_width(title); + size_t lengthLen = length.length(); + size_t posLen = position.length(); + + bool showArtist = artistLen != 0; + bool showAlbum = albumLen != 0; + bool showTitle = titleLen != 0; + bool showLength = lengthLen != 0; + bool showPos = posLen != 0; if (truncated && dynamic_len_ >= 0) { size_t dynamicLen = dynamic_len_; - size_t artistLen = showArtist ? artist.length() + 3 : 0; - size_t albumLen = showAlbum ? album.length() + 3 : 0; - size_t titleLen = title.length(); - size_t lengthLen = showLength ? length.length() + 1 : 0; + if (showArtist) artistLen += 3; + if (showAlbum) albumLen += 3; + if (showLength) lengthLen += 3; + if (showPos) posLen += 3; size_t totalLen = 0; @@ -246,23 +325,34 @@ auto Mpris::getDynamicStr(const PlayerInfo &info, bool truncated, bool html) -> showLength = false; } else { totalLen += lengthLen; + posLen = std::max((size_t)2, posLen) - 2; + } + } else if (*it == "position") { + if (totalLen + posLen > dynamicLen) { + showPos = false; + } else { + totalLen += posLen; + lengthLen = std::max((size_t)2, lengthLen) - 2; } } } } + std::stringstream dynamic; if (showArtist) dynamic << artist << " - "; if (showAlbum) dynamic << album << " - "; if (showTitle) dynamic << title; - if (showLength) { + if (showLength || showPos) { dynamic << " "; - if (html) { - dynamic << ""; - } - dynamic << length; - if (html) { - dynamic << ""; + if (html) dynamic << ""; + dynamic << '['; + if (showPos) { + dynamic << position; + if (showLength) dynamic << '/'; } + if (showLength) dynamic << length; + dynamic << ']'; + if (html) dynamic << ""; } return dynamic.str(); } @@ -412,6 +502,22 @@ auto Mpris::getPlayerInfo() -> std::optional { } if (error) goto errorexit; + { + auto position_ = playerctl_player_get_position(player, &error); + if (error) { + // it's fine to have an error here because not all players report a position + g_error_free(error); + error = nullptr; + } else { + spdlog::debug("mpris[{}]: position = {}", info.name, position_); + std::chrono::microseconds 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_m); + info.position = fmt::format("{:02}:{:02}:{:02}", len_h.count(), len_m.count(), len_s.count()); + } + } + return info; errorexit: @@ -508,12 +614,20 @@ auto Mpris::update() -> void { break; } + std::string length = getLengthStr(info, truncate_hours_); + std::string tooltipLength = + (tooltip_len_limits_ || length.length() > 5) ? length : getLengthStr(info, false); + // keep position format same as length format + std::string position = getPositionStr(info, truncate_hours_ && length.length() < 6); + std::string tooltipPosition = + (tooltip_len_limits_ || position.length() > 5) ? position : getPositionStr(info, false); + try { auto label_format = fmt::format( fmt::runtime(formatstr), fmt::arg("player", info.name), fmt::arg("status", info.status_string), fmt::arg("artist", getArtistStr(info, true)), fmt::arg("title", getTitleStr(info, true)), fmt::arg("album", getAlbumStr(info, true)), - fmt::arg("length", getLengthStr(info, false)), + fmt::arg("length", length), fmt::arg("position", position), fmt::arg("dynamic", getDynamicStr(info, true, true)), fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); @@ -531,7 +645,7 @@ auto Mpris::update() -> void { fmt::arg("artist", getArtistStr(info, tooltip_len_limits_)), fmt::arg("title", getTitleStr(info, tooltip_len_limits_)), fmt::arg("album", getAlbumStr(info, tooltip_len_limits_)), - fmt::arg("length", getLengthStr(info, false)), + fmt::arg("length", tooltipLength), fmt::arg("position", tooltipPosition), fmt::arg("dynamic", getDynamicStr(info, tooltip_len_limits_, false)), fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); From a301b8c4cd561a77dc5b93eb1577a7879804da87 Mon Sep 17 00:00:00 2001 From: chayleaf Date: Fri, 17 Feb 2023 15:50:07 +0700 Subject: [PATCH 106/217] mpris: update docs --- man/waybar-mpris.5.scd | 25 ++++++++++++------------- src/modules/mpris/mpris.cpp | 13 +++++++++---- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index bc735b9..0abe4cc 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -45,8 +45,7 @@ The *mpris* module displays currently playing media via libplayerctl. *tooltip-format-[status]*: ++ typeof: string ++ - default: "MPD (disconnected)" ++ - The status-specif tooltip format. + The status-specific tooltip format. *artist-len*: ++ typeof: integer ++ @@ -66,34 +65,34 @@ The *mpris* module displays currently playing media via libplayerctl. *dynamic-len*: ++ typeof: integer ++ Maximum length of the Dynamic tag (Wide/Fullwidth Unicode characters - count as two). + count as two). The dynamic tag will not truncate any tags beyond their + set length limits, instead, it will attempt to fit as much of the + available tags as possible. It is recommended you set title-len to + something less than or equal to this value, so the title will always be + displayed. *dynamic-priority* ++ typeof: []string ++ - default: ["title", "length", "artist", "album"] + default: ["title", "length", "position", "artist", "album"] Priority of the tags when truncating the Dynamic tag (absence in this list means force inclusion). *truncate-hours*: ++ typeof: bool ++ default: true ++ - Whether to truncate hours when media duration is less than an hour long + Whether to hide hours when media duration is less than an hour long. *enable-tooltip-len-limits*: ++ typeof: bool ++ default: false ++ - Option to enable the length limits for the tooltip as well + Option to enable the length limits for the tooltip as well. By default + the tooltip ignores all length limits. *ellipsis*: ++ typeof: string ++ default: "…" ++ - Override the default ellipsis (set to empty string to simply truncate - the tags when needed instead). - -*on-click*: ++ - typeof: string ++ - default: play-pause ++ - Overwrite default action toggles. + 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. *on-click*: ++ typeof: string ++ diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 6e8004d..d283773 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -206,7 +206,7 @@ size_t utf8_truncate(std::string& str, size_t width = std::string::npos) { return str.length(); } else if (g_unichar_iswide(c)) { total_width += 2; - } else if (!g_unichar_iszerowidth(c)) { + } else if (!g_unichar_iszerowidth(c) && c != 0xAD) { // neither zero-width nor soft hyphen total_width += 1; } @@ -339,6 +339,11 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> } std::stringstream dynamic; + if (html) { + artist = Glib::Markup::escape_text(artist); + album = Glib::Markup::escape_text(album); + title = Glib::Markup::escape_text(title); + } if (showArtist) dynamic << artist << " - "; if (showAlbum) dynamic << album << " - "; if (showTitle) dynamic << title; @@ -472,21 +477,21 @@ auto Mpris::getPlayerInfo() -> std::optional { if (auto artist_ = playerctl_player_get_artist(player, &error)) { spdlog::debug("mpris[{}]: artist = {}", info.name, artist_); - info.artist = Glib::Markup::escape_text(artist_); + info.artist = artist_; g_free(artist_); } if (error) goto errorexit; if (auto album_ = playerctl_player_get_album(player, &error)) { spdlog::debug("mpris[{}]: album = {}", info.name, album_); - info.album = Glib::Markup::escape_text(album_); + info.album = album_; g_free(album_); } if (error) goto errorexit; if (auto title_ = playerctl_player_get_title(player, &error)) { spdlog::debug("mpris[{}]: title = {}", info.name, title_); - info.title = Glib::Markup::escape_text(title_); + info.title = title_; g_free(title_); } if (error) goto errorexit; From 774d8880cdf7c3bb555eaa980fce4e9eeca1a34a Mon Sep 17 00:00:00 2001 From: chayleaf Date: Mon, 20 Feb 2023 22:48:21 +0700 Subject: [PATCH 107/217] mpris: fix potentially invalid seconds count on >1h media --- src/modules/mpris/mpris.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index d283773..32896bb 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -348,7 +348,7 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> if (showAlbum) dynamic << album << " - "; if (showTitle) dynamic << title; if (showLength || showPos) { - dynamic << " "; + dynamic << ' '; if (html) dynamic << ""; dynamic << '['; if (showPos) { @@ -501,7 +501,7 @@ auto Mpris::getPlayerInfo() -> std::optional { std::chrono::microseconds 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_m); + auto len_s = std::chrono::duration_cast(len - len_h - len_m); info.length = fmt::format("{:02}:{:02}:{:02}", len_h.count(), len_m.count(), len_s.count()); g_free(length_); } @@ -518,7 +518,7 @@ auto Mpris::getPlayerInfo() -> std::optional { std::chrono::microseconds 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_m); + auto len_s = std::chrono::duration_cast(len - len_h - len_m); info.position = fmt::format("{:02}:{:02}:{:02}", len_h.count(), len_m.count(), len_s.count()); } } From e5524d505995ec857bdf23bc112bbaa24cfa4a69 Mon Sep 17 00:00:00 2001 From: chayleaf Date: Tue, 21 Feb 2023 02:37:49 +0700 Subject: [PATCH 108/217] mpris: switch base class from AModule to ALabel --- include/modules/mpris/mpris.hpp | 9 +--- man/waybar-mpris.5.scd | 16 +++++++ src/modules/mpris/mpris.cpp | 82 ++++++++++++++------------------- 3 files changed, 52 insertions(+), 55 deletions(-) diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index ddb0cb6..0563cee 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -16,7 +16,7 @@ extern "C" { namespace waybar::modules::mpris { -class Mpris : public AModule { +class Mpris : public ALabel { public: Mpris(const std::string&, const Json::Value&); virtual ~Mpris(); @@ -44,7 +44,7 @@ class Mpris : public AModule { }; auto getPlayerInfo() -> std::optional; - auto getIcon(const Json::Value&, const std::string&) -> std::string; + auto getIconFromJson(const Json::Value&, const std::string&) -> std::string; auto getArtistStr(const PlayerInfo&, bool) -> std::string; auto getAlbumStr(const PlayerInfo&, bool) -> std::string; auto getTitleStr(const PlayerInfo&, bool) -> std::string; @@ -52,11 +52,7 @@ class Mpris : public AModule { auto getPositionStr(const PlayerInfo&, bool) -> std::string; auto getDynamicStr(const PlayerInfo&, bool, bool) -> std::string; - Gtk::Box box_; - Gtk::Label label_; - // config - std::string format_; std::string format_playing_; std::string format_paused_; std::string format_stopped_; @@ -75,7 +71,6 @@ class Mpris : public AModule { bool tooltip_len_limits_; std::string ellipsis_; - std::chrono::seconds interval_; std::string player_; std::vector ignored_players_; diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 0abe4cc..e889411 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -94,6 +94,22 @@ The *mpris* module displays currently playing media via libplayerctl. 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 ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ default: play-pause ++ diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 32896bb..01f74c2 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -18,10 +18,7 @@ namespace waybar::modules::mpris { const std::string DEFAULT_FORMAT = "{player} ({status}): {dynamic}"; Mpris::Mpris(const std::string& id, const Json::Value& config) - : AModule(config, "mpris", id), - box_(Gtk::ORIENTATION_HORIZONTAL, 0), - label_(), - format_(DEFAULT_FORMAT), + : ALabel(config, "mpris", id, DEFAULT_FORMAT, 5, false, true), tooltip_(DEFAULT_FORMAT), artist_len_(-1), album_len_(-1), @@ -32,18 +29,10 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) tooltip_len_limits_(false), // this character is used in Gnome so it's fine to use it here ellipsis_(u8"\u2026"), - interval_(0), player_("playerctld"), manager(), player() { - box_.pack_start(label_); - box_.set_name(name_); - event_box_.add(box_); - event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &Mpris::handleToggle)); - if (config_["format"].isString()) { - format_ = config_["format"].asString(); - } if (config_["format-playing"].isString()) { format_playing_ = config_["format-playing"].asString(); } @@ -99,9 +88,6 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) if (config_["truncate-hours"].isBool()) { truncate_hours_ = config["truncate-hours"].asBool(); } - if (config_["interval"].isUInt()) { - interval_ = std::chrono::seconds(config_["interval"].asUInt()); - } if (config_["player"].isString()) { player_ = config_["player"].asString(); } @@ -177,7 +163,7 @@ Mpris::~Mpris() { if (player != NULL) g_object_unref(player); } -auto Mpris::getIcon(const Json::Value& icons, const std::string& key) -> std::string { +auto Mpris::getIconFromJson(const Json::Value& icons, const std::string& key) -> std::string { if (icons.isObject()) { if (icons[key].isString()) { return icons[key].asString(); @@ -239,26 +225,26 @@ void truncate(std::string& s, const std::string& ellipsis, size_t max_len) { } auto Mpris::getArtistStr(const PlayerInfo& info, bool truncated) -> std::string { - std::string artist = info.artist.value_or(std::string()); + auto artist = info.artist.value_or(std::string()); if (truncated && artist_len_ >= 0) truncate(artist, ellipsis_, artist_len_); return artist; } auto Mpris::getAlbumStr(const PlayerInfo& info, bool truncated) -> std::string { - std::string album = info.album.value_or(std::string()); + auto album = info.album.value_or(std::string()); if (truncated && album_len_ >= 0) truncate(album, ellipsis_, album_len_); return album; } auto Mpris::getTitleStr(const PlayerInfo& info, bool truncated) -> std::string { - std::string title = info.title.value_or(std::string()); + auto title = info.title.value_or(std::string()); if (truncated && title_len_ >= 0) truncate(title, ellipsis_, title_len_); return title; } auto Mpris::getLengthStr(const PlayerInfo& info, bool truncated) -> std::string { if (info.length.has_value()) { - std::string length = info.length.value(); + auto length = info.length.value(); return (truncated && length.substr(0, 3) == "00:") ? length.substr(3) : length; } return std::string(); @@ -266,19 +252,19 @@ auto Mpris::getLengthStr(const PlayerInfo& info, bool truncated) -> std::string auto Mpris::getPositionStr(const PlayerInfo& info, bool truncated) -> std::string { if (info.position.has_value()) { - std::string position = info.position.value(); + auto position = info.position.value(); return (truncated && position.substr(0, 3) == "00:") ? position.substr(3) : position; } return std::string(); } auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> std::string { - std::string artist = getArtistStr(info, truncated); - std::string album = getAlbumStr(info, truncated); - std::string title = getTitleStr(info, truncated); - std::string length = getLengthStr(info, truncated && truncate_hours_); + auto artist = getArtistStr(info, truncated); + auto album = getAlbumStr(info, truncated); + auto title = getTitleStr(info, truncated); + auto length = getLengthStr(info, truncated && truncate_hours_); // keep position format same as length format - std::string position = getPositionStr(info, truncated && truncate_hours_ && length.length() < 6); + auto position = getPositionStr(info, truncated && truncate_hours_ && length.length() < 6); size_t artistLen = utf8_width(artist); size_t albumLen = utf8_width(album); @@ -305,32 +291,32 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> if (*it == "artist") { if (totalLen + artistLen > dynamicLen) { showArtist = false; - } else { + } else if (showArtist) { totalLen += artistLen; } } else if (*it == "album") { if (totalLen + albumLen > dynamicLen) { showAlbum = false; - } else { + } else if (showAlbum) { totalLen += albumLen; } } else if (*it == "title") { if (totalLen + titleLen > dynamicLen) { showTitle = false; - } else { + } else if (showTitle) { totalLen += titleLen; } } else if (*it == "length") { if (totalLen + lengthLen > dynamicLen) { showLength = false; - } else { + } else if (showLength) { totalLen += lengthLen; posLen = std::max((size_t)2, posLen) - 2; } } else if (*it == "position") { if (totalLen + posLen > dynamicLen) { showPos = false; - } else { + } else if (showPos) { totalLen += posLen; lengthLen = std::max((size_t)2, lengthLen) - 2; } @@ -541,19 +527,19 @@ bool Mpris::handleToggle(GdkEventButton* const& e) { switch (e->button) { case 1: // left-click if (config_["on-click"].isString()) { - return AModule::handleToggle(e); + return ALabel::handleToggle(e); } playerctl_player_play_pause(player, &error); break; case 2: // middle-click if (config_["on-middle-click"].isString()) { - return AModule::handleToggle(e); + return ALabel::handleToggle(e); } playerctl_player_previous(player, &error); break; case 3: // right-click if (config_["on-right-click"].isString()) { - return AModule::handleToggle(e); + return ALabel::handleToggle(e); } playerctl_player_next(player, &error); break; @@ -572,7 +558,7 @@ auto Mpris::update() -> void { auto opt = getPlayerInfo(); if (!opt) { event_box_.set_visible(false); - AModule::update(); + ALabel::update(); return; } auto info = *opt; @@ -585,20 +571,20 @@ auto Mpris::update() -> void { spdlog::debug("mpris[{}]: running update", info.name); // set css class for player status - if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) { - box_.get_style_context()->remove_class(lastStatus); + if (!lastStatus.empty() && event_box_.get_style_context()->has_class(lastStatus)) { + event_box_.get_style_context()->remove_class(lastStatus); } - if (!box_.get_style_context()->has_class(info.status_string)) { - box_.get_style_context()->add_class(info.status_string); + if (!event_box_.get_style_context()->has_class(info.status_string)) { + event_box_.get_style_context()->add_class(info.status_string); } lastStatus = info.status_string; // set css class for player name - if (!lastPlayer.empty() && box_.get_style_context()->has_class(lastPlayer)) { - box_.get_style_context()->remove_class(lastPlayer); + if (!lastPlayer.empty() && event_box_.get_style_context()->has_class(lastPlayer)) { + event_box_.get_style_context()->remove_class(lastPlayer); } - if (!box_.get_style_context()->has_class(info.name)) { - box_.get_style_context()->add_class(info.name); + if (!event_box_.get_style_context()->has_class(info.name)) { + event_box_.get_style_context()->add_class(info.name); } lastPlayer = info.name; @@ -634,8 +620,8 @@ auto Mpris::update() -> void { fmt::arg("title", getTitleStr(info, true)), fmt::arg("album", getAlbumStr(info, true)), fmt::arg("length", length), fmt::arg("position", position), fmt::arg("dynamic", getDynamicStr(info, true, true)), - fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), - fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); + fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)), + fmt::arg("status_icon", getIconFromJson(config_["status-icons"], info.status_string))); label_.set_markup(label_format); } catch (fmt::format_error const& e) { @@ -652,8 +638,8 @@ auto Mpris::update() -> void { fmt::arg("album", getAlbumStr(info, tooltip_len_limits_)), fmt::arg("length", tooltipLength), fmt::arg("position", tooltipPosition), fmt::arg("dynamic", getDynamicStr(info, tooltip_len_limits_, false)), - fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), - fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); + fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)), + fmt::arg("status_icon", getIconFromJson(config_["status-icons"], info.status_string))); label_.set_tooltip_text(tooltip_text); } catch (fmt::format_error const& e) { @@ -663,7 +649,7 @@ auto Mpris::update() -> void { event_box_.set_visible(true); // call parent update - AModule::update(); + ALabel::update(); } } // namespace waybar::modules::mpris From 0cfd1c7094fca18082b166a2dd4f43e761903fed Mon Sep 17 00:00:00 2001 From: chayleaf Date: Tue, 21 Feb 2023 03:38:26 +0700 Subject: [PATCH 109/217] mpris: don't put ellipsis after whitespace --- include/modules/mpris/mpris.hpp | 4 ++-- man/waybar-mpris.5.scd | 27 ++++++++++++++------------- src/modules/mpris/mpris.cpp | 6 +++--- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index 0563cee..ea5f7de 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -39,8 +39,8 @@ class Mpris : public ALabel { std::optional artist; std::optional album; std::optional title; - std::optional length; // as HH:MM:SS - std::optional position; // same format + std::optional length; // as HH:MM:SS + std::optional position; // same format }; auto getPlayerInfo() -> std::optional; diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index e889411..40f132d 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -50,30 +50,30 @@ 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). + 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). + 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). + as two). Set to zero to hide the title in `{dynamic}` tag. *dynamic-len*: ++ typeof: integer ++ - Maximum length of the Dynamic tag (Wide/Fullwidth Unicode characters - count as two). The dynamic tag will not truncate any tags beyond their - set length limits, instead, it will attempt to fit as much of the - available tags as possible. It is recommended you set title-len to - something less than or equal to this value, so the title will always be + Maximum length of the Dynamic tag (Wide/Fullwidth Unicode characters ++ + count as two). The dynamic tag will not truncate any tags beyond their ++ + set length limits, instead, it will attempt to fit as much of the ++ + available tags as possible. It is recommended you set title-len to ++ + something less than or equal to this value, so the title will always be ++ displayed. -*dynamic-priority* ++ +*dynamic-priority*: ++ typeof: []string ++ - default: ["title", "length", "position", "artist", "album"] + default: ["title", "length", "position", "artist", "album"] ++ Priority of the tags when truncating the Dynamic tag (absence in this list means force inclusion). @@ -108,7 +108,8 @@ 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 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 ++ @@ -126,11 +127,11 @@ The *mpris* module displays currently playing media via libplayerctl. Overwrite default action toggles. *player-icons*: ++ - typeof: map[string]string + typeof: map[string]string ++ Allows setting _{player-icon}_ based on player-name property. *status-icons*: ++ - typeof: map[string]string + typeof: map[string]string ++ Allows setting _{status-icon}_ based on player status (playing, paused, stopped). diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 01f74c2..d1c94ec 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -32,7 +32,6 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) player_("playerctld"), manager(), player() { - if (config_["format-playing"].isString()) { format_playing_ = config_["format-playing"].asString(); } @@ -192,12 +191,13 @@ size_t utf8_truncate(std::string& str, size_t width = std::string::npos) { return str.length(); } else if (g_unichar_iswide(c)) { total_width += 2; - } else if (!g_unichar_iszerowidth(c) && c != 0xAD) { // neither zero-width nor soft hyphen + } else if (!g_unichar_iszerowidth(c) && c != 0xAD) { // neither zero-width nor soft hyphen total_width += 1; } data = g_utf8_find_next_char(data, end); - if (width != std::string::npos && total_width <= width) trunc_end = data; + if (width != std::string::npos && total_width <= width && !g_unichar_isspace(c)) + trunc_end = data; } if (trunc_end) str.resize(trunc_end - str.data()); From ffeef7f40fdd37b7273782016313eb64047b368c Mon Sep 17 00:00:00 2001 From: Maxim Andreev Date: Fri, 31 Mar 2023 20:07:59 +0300 Subject: [PATCH 110/217] modules/clock: fix calendar shift in months with 31 days --- src/modules/clock.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index fb12d2b..2edd21b 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -162,6 +162,9 @@ auto waybar::modules::Clock::update() -> void { auto ztime = date::zoned_time{time_zone, date::floor(now)}; 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)}; From 02b3c125a111f879d6de3aa1090e1a9900632b20 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 3 Apr 2023 09:38:12 +0200 Subject: [PATCH 111/217] fix: init optional to null --- src/modules/mpris/mpris.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index f11821f..7665e13 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -240,6 +240,10 @@ auto Mpris::getPlayerInfo() -> std::optional { .name = player_name, .status = player_playback_status, .status_string = player_status, + .artist = std::nullopt, + .album = std::nullopt, + .title = std::nullopt, + .length = std::nullopt, }; if (auto artist_ = playerctl_player_get_artist(player, &error)) { From 60e0584d1635170d1220c8f891bf580dec029632 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 3 Apr 2023 09:41:24 +0200 Subject: [PATCH 112/217] fix: lint --- include/AModule.hpp | 2 +- include/factory.hpp | 2 +- include/modules/clock.hpp | 18 +++++++----------- include/util/clara.hpp | 2 +- src/modules/mpris/mpris.cpp | 4 ++-- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/include/AModule.hpp b/include/AModule.hpp index 6f724cb..9b16076 100644 --- a/include/AModule.hpp +++ b/include/AModule.hpp @@ -15,7 +15,7 @@ class AModule : public IModule { auto update() -> void override; virtual auto refresh(int) -> void{}; operator Gtk::Widget &() override; - auto doAction(const std::string& name) -> void override; + auto doAction(const std::string &name) -> void override; Glib::Dispatcher dp; diff --git a/include/factory.hpp b/include/factory.hpp index b2b242f..89eea0e 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -18,10 +18,10 @@ #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" -#include "modules/river/layout.hpp" #endif #ifdef HAVE_HYPRLAND #include "modules/hyprland/backend.hpp" diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 07f2284..999c843 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -15,10 +15,7 @@ enum class WeeksSide { HIDDEN, }; -enum class CldMode { - MONTH, - YEAR -}; +enum class CldMode { MONTH, YEAR }; class Clock final : public ALabel { public: @@ -64,12 +61,11 @@ class Clock final : public ALabel { void tz_down(); // ModuleActionMap - static inline std::map actionMap_{ - {"mode", &waybar::modules::Clock::cldModeSwitch}, - {"shift_up", &waybar::modules::Clock::cldShift_up}, - {"shift_down", &waybar::modules::Clock::cldShift_down}, - {"tz_up", &waybar::modules::Clock::tz_up}, - {"tz_down", &waybar::modules::Clock::tz_down} - }; + static inline std::map actionMap_{ + {"mode", &waybar::modules::Clock::cldModeSwitch}, + {"shift_up", &waybar::modules::Clock::cldShift_up}, + {"shift_down", &waybar::modules::Clock::cldShift_down}, + {"tz_up", &waybar::modules::Clock::tz_up}, + {"tz_down", &waybar::modules::Clock::tz_down}}; }; } // namespace waybar::modules diff --git a/include/util/clara.hpp b/include/util/clara.hpp index 4cc6f48..73fa541 100644 --- a/include/util/clara.hpp +++ b/include/util/clara.hpp @@ -989,7 +989,7 @@ struct Help : Opt { showHelpFlag = flag; return ParserResult::ok(ParseResultType::ShortCircuitAll); }) { - static_cast (*this)("display usage information")["-?"]["-h"]["--help"].optional(); + static_cast(*this)("display usage information")["-?"]["-h"]["--help"].optional(); } }; diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 68bb7fe..e1194bb 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -1,11 +1,11 @@ +#include "modules/mpris/mpris.hpp" + #include #include #include #include -#include "modules/mpris/mpris.hpp" - extern "C" { #include } From 1445dc41513e62c4a7148516798046de15dc8143 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 3 Apr 2023 10:06:01 +0200 Subject: [PATCH 113/217] fix: lint --- 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 cfc24f8..3dfdb83 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -132,7 +132,7 @@ auto Language::getLayout(const std::string& fullName) -> Layout { spdlog::debug("hyprland language didn't find matching layout"); - return Layout {"", "", "", ""}; + return Layout{"", "", "", ""}; } } // namespace waybar::modules::hyprland From 626a1f58befc13994f19fc0a58a01fb0a7e4f3cc Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 3 Apr 2023 10:51:36 +0200 Subject: [PATCH 114/217] fix: build --- src/modules/mpris/mpris.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index e1194bb..f9527f1 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -185,7 +185,7 @@ size_t utf8_truncate(std::string& str, size_t width = std::string::npos) { for (gchar *data = str.data(), *end = data + str.size(); data;) { gunichar c = g_utf8_get_char_validated(data, end - data); - if (c == -1 || c == -2) { + if (c == -1U || c == -2U) { // invalid unicode, treat string as ascii if (width != std::string::npos && str.length() > width) str.resize(width); return str.length(); From 3d980f92a399a3656256d36c586940f3db928d24 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 3 Apr 2023 10:59:29 +0200 Subject: [PATCH 115/217] fix: build --- src/modules/mpris/mpris.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index f9527f1..d7e5136 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -28,7 +28,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) truncate_hours_(true), tooltip_len_limits_(false), // this character is used in Gnome so it's fine to use it here - ellipsis_(u8"\u2026"), + ellipsis_("\u2026"), player_("playerctld"), manager(), player() { From 33c49641877324a7474d9128c887be5bd745546f Mon Sep 17 00:00:00 2001 From: MonstrousOgre Date: Thu, 6 Apr 2023 00:34:52 +0530 Subject: [PATCH 116/217] replacing spdlog::info with spdlog::debug --- 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 75a0c0b..6673c28 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -236,7 +236,7 @@ auto WorkspaceGroup::create_persistent_workspaces() -> void { auto new_id = ++workspace_global_id; workspaces_.push_back( std::make_unique(bar_, config_, *this, nullptr, new_id, p_w_name)); - spdlog::info("Workspace {} created", new_id); + spdlog::debug("Workspace {} created", new_id); } } @@ -531,7 +531,7 @@ auto Workspace::handle_name(const std::string &name) -> void { workspace_group_.set_need_to_sort(); } name_ = name; - spdlog::info("Workspace {} added to group {}", name, workspace_group_.id()); + spdlog::debug("Workspace {} added to group {}", name, workspace_group_.id()); make_persistent(); handle_duplicate(); From cb82326b22d26773a42dd9c0ada0ee411071fee1 Mon Sep 17 00:00:00 2001 From: chayleaf Date: Thu, 6 Apr 2023 04:50:32 +0700 Subject: [PATCH 117/217] mpris: fix css class application apply styles to label rather than event_box (fixes play/pause state and per-player selectors) --- src/modules/mpris/mpris.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index d7e5136..e6fedff 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -575,20 +575,20 @@ auto Mpris::update() -> void { spdlog::debug("mpris[{}]: running update", info.name); // set css class for player status - if (!lastStatus.empty() && event_box_.get_style_context()->has_class(lastStatus)) { - event_box_.get_style_context()->remove_class(lastStatus); + if (!lastStatus.empty() && label_.get_style_context()->has_class(lastStatus)) { + label_.get_style_context()->remove_class(lastStatus); } - if (!event_box_.get_style_context()->has_class(info.status_string)) { - event_box_.get_style_context()->add_class(info.status_string); + if (!label_.get_style_context()->has_class(info.status_string)) { + label_.get_style_context()->add_class(info.status_string); } lastStatus = info.status_string; // set css class for player name - if (!lastPlayer.empty() && event_box_.get_style_context()->has_class(lastPlayer)) { - event_box_.get_style_context()->remove_class(lastPlayer); + if (!lastPlayer.empty() && label_.get_style_context()->has_class(lastPlayer)) { + label_.get_style_context()->remove_class(lastPlayer); } - if (!event_box_.get_style_context()->has_class(info.name)) { - event_box_.get_style_context()->add_class(info.name); + if (!label_.get_style_context()->has_class(info.name)) { + label_.get_style_context()->add_class(info.name); } lastPlayer = info.name; From 659fea07eeeb4854f3273e37779e0cc81c7f299b Mon Sep 17 00:00:00 2001 From: chayleaf Date: Thu, 6 Apr 2023 13:17:43 +0700 Subject: [PATCH 118/217] mpris: fix non-dynamic tags not being html escaped --- src/modules/mpris/mpris.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index e6fedff..e89b8ef 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -619,9 +619,12 @@ auto Mpris::update() -> void { try { auto label_format = fmt::format( - fmt::runtime(formatstr), fmt::arg("player", info.name), - fmt::arg("status", info.status_string), fmt::arg("artist", getArtistStr(info, true)), - fmt::arg("title", getTitleStr(info, true)), fmt::arg("album", getAlbumStr(info, true)), + fmt::runtime(formatstr), + fmt::arg("player", std::string(Glib::Markup::escape_text(info.name))), + fmt::arg("status", info.status_string), + fmt::arg("artist", std::string(Glib::Markup::escape_text(getArtistStr(info, true)))), + fmt::arg("title", std::string(Glib::Markup::escape_text(getTitleStr(info, true)))), + fmt::arg("album", std::string(Glib::Markup::escape_text(getAlbumStr(info, true)))), fmt::arg("length", length), fmt::arg("position", position), fmt::arg("dynamic", getDynamicStr(info, true, true)), fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)), From 6f3a27905dda5e94b19c0180ff9b42757ddce19a Mon Sep 17 00:00:00 2001 From: MonstrousOgre Date: Thu, 6 Apr 2023 12:14:44 +0530 Subject: [PATCH 119/217] preventing persistent_workspaces and all-outputs from being used together --- src/modules/wlr/workspace_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 6673c28..6a496e6 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -209,7 +209,7 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value } auto WorkspaceGroup::fill_persistent_workspaces() -> void { - if (config_["persistent_workspaces"].isObject()) { + 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(); From 51f0fc6b72b72aa28aa8513c4be36ab3e190d713 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Fri, 7 Apr 2023 14:19:45 +1000 Subject: [PATCH 120/217] #2094 pulseaudio apply states --- src/modules/pulseaudio.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 0630710..586b6cc 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -279,7 +279,12 @@ auto waybar::modules::Pulseaudio::update() -> void { label_.get_style_context()->remove_class("muted"); label_.get_style_context()->remove_class("sink-muted"); } - format = config_[format_name].isString() ? config_[format_name].asString() : format; + auto state = getState(volume_, true); + if (!state.empty() && config_[format_name + "-" + state].isString()) { + format = config_[format_name + "-" + state].asString(); + } else if (config_[format_name].isString()) { + format = config_[format_name].asString(); + } } // TODO: find a better way to split source/sink std::string format_source = "{volume}%"; @@ -305,7 +310,6 @@ auto waybar::modules::Pulseaudio::update() -> void { label_.set_markup(text); label_.show(); } - getState(volume_); if (tooltipEnabled()) { if (tooltip_format.empty() && config_["tooltip-format"].isString()) { From 60cdf10e64130a38fdca88e731b8a57dca938529 Mon Sep 17 00:00:00 2001 From: David Delarosa Date: Tue, 11 Apr 2023 04:50:21 +0300 Subject: [PATCH 121/217] Add DWL tags module --- README.md | 1 + include/factory.hpp | 3 + include/modules/dwl/tags.hpp | 34 ++++ man/waybar-dwl-tags.5.scd | 49 ++++++ meson.build | 5 + protocol/dwl-bar-ipc-unstable-v1.xml | 167 ++++++++++++++++++++ protocol/meson.build | 1 + src/factory.cpp | 5 + src/modules/dwl/tags.cpp | 224 +++++++++++++++++++++++++++ 9 files changed, 489 insertions(+) create mode 100644 include/modules/dwl/tags.hpp create mode 100644 man/waybar-dwl-tags.5.scd create mode 100644 protocol/dwl-bar-ipc-unstable-v1.xml create mode 100644 src/modules/dwl/tags.cpp diff --git a/README.md b/README.md index 2b57b13..b6ecd96 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ - Sway (Workspaces, Binding mode, Focused window name) - River (Mapping mode, Tags, Focused window name) - Hyprland (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 - Battery diff --git a/include/factory.hpp b/include/factory.hpp index 89eea0e..a33eac3 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -23,6 +23,9 @@ #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" diff --git a/include/modules/dwl/tags.hpp b/include/modules/dwl/tags.hpp new file mode 100644 index 0000000..6e6d086 --- /dev/null +++ b/include/modules/dwl/tags.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "dwl-bar-ipc-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" + +namespace waybar::modules::dwl { + +class Tags : public waybar::AModule { + public: + Tags(const std::string &, const waybar::Bar &, const Json::Value &); + virtual ~Tags(); + + // Handlers for wayland events + void handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused); + + void handle_primary_clicked(uint32_t tag); + bool handle_button_press(GdkEventButton *event_button, uint32_t tag); + + struct zdwl_manager_v1 *status_manager_; + struct wl_seat *seat_; + + private: + const waybar::Bar &bar_; + Gtk::Box box_; + std::vector buttons_; + struct zdwl_output_v1 *output_status_; +}; + +} /* namespace waybar::modules::dwl */ diff --git a/man/waybar-dwl-tags.5.scd b/man/waybar-dwl-tags.5.scd new file mode 100644 index 0000000..06fb577 --- /dev/null +++ b/man/waybar-dwl-tags.5.scd @@ -0,0 +1,49 @@ +waybar-dwl-tags(5) + +# NAME + +waybar - dwl tags module + +# DESCRIPTION + +The *tags* module displays the current state of tags in dwl. + +# CONFIGURATION + +Addressed by *dwl/tags* + +*num-tags*: ++ + 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. + +*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. + +# EXAMPLE + +``` +"dwl/tags": { + "num-tags": 5 +} +``` + +# STYLE + +- *#tags button* +- *#tags button.occupied* +- *#tags button.focused* +- *#tags button.urgent* + +Note that occupied/focused/urgent status may overlap. That is, a tag may be +both occupied and focused at the same time. + +# SEE ALSO + +waybar(5), dwl(1) diff --git a/meson.build b/meson.build index 58e1c67..3ab1962 100644 --- a/meson.build +++ b/meson.build @@ -227,6 +227,11 @@ if true src_files += 'src/modules/river/layout.cpp' endif +if true + add_project_arguments('-DHAVE_DWL', language: 'cpp') + src_files += 'src/modules/dwl/tags.cpp' +endif + if true add_project_arguments('-DHAVE_HYPRLAND', language: 'cpp') src_files += 'src/modules/hyprland/backend.cpp' diff --git a/protocol/dwl-bar-ipc-unstable-v1.xml b/protocol/dwl-bar-ipc-unstable-v1.xml new file mode 100644 index 0000000..0dcec4f --- /dev/null +++ b/protocol/dwl-bar-ipc-unstable-v1.xml @@ -0,0 +1,167 @@ + + + + + This protocol allows clients to get updates from dwl and vice versa. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible + changes may be added together with the corresponding interface + version bump. + Backward incompatible changes are done by bumping the version + number in the protocol and interface names and resetting the + interface version. Once the protocol is to be declared stable, + the 'z' prefix and the version number in the protocol and + interface names are removed and the interface version number is + reset. + + + + + This interface is exposed as a global in wl_registry. + + Clients can use this interface to get a dwl_output. + After binding the client will revieve dwl_manager.tag and dwl_manager.layout events. + The dwl_manager.tag and dwl_manager.layout events expose tags and layouts to the client. + + + + + Indicates that the client will not the dwl_manager object anymore. + Objects created through this instance are not affected. + + + + + + Get a dwl_output for the specified wl_output. + + + + + + + + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all tags. + + + + + + + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all layouts. + + + + + + + + Observe and control a dwl output. + + Events are double-buffered: + Clients should cache events and redraw when a dwl_output.done event is sent. + + Request are not double-buffered: + The compositor will update immediately upon request. + + + + + + + + + + + Indicates to that the client no longer needs this dwl_output. + + + + + + Indicates the client should hide or show themselves. + If the client is visible then hide, if hidden then show. + + + + + + Indicates if the output is active. Zero is invalid, nonzero is valid. + + + + + + + Indicates that a tag has been updated. + + + + + + + + + + Indicates a new layout is selected. + + + + + + + Indicates the title has changed. + + + + + + + Indicates the appid has changed. + + + + + + + Indicates the layout has changed. Since layout symbols are now dynamic. + As opposed to the zdwl_manager_v1.layout event, this should take precendence when displaying. + This also means ignoring the zdwl_output_v1.layout event. + + + + + + + + Indicates that a sequence of status updates have finished and the client should redraw. + + + + + + + + + + + + + + + + + The tags are updated as follows: + new_tags = (current_tags AND and_tags) XOR xor_tags + + + + + + diff --git a/protocol/meson.build b/protocol/meson.build index 6e82d63..9c1b019 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -30,6 +30,7 @@ client_protocols = [ ['ext-workspace-unstable-v1.xml'], ['river-status-unstable-v1.xml'], ['river-control-unstable-v1.xml'], + ['dwl-bar-ipc-unstable-v1.xml'], ] client_protos_src = [] diff --git a/src/factory.cpp b/src/factory.cpp index dd5c142..8102fee 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -68,6 +68,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { return new waybar::modules::river::Layout(id, bar_, config_[name]); } #endif +#ifdef HAVE_DWL + if (ref == "dwl/tags") { + return new waybar::modules::dwl::Tags(id, bar_, config_[name]); + } +#endif #ifdef HAVE_HYPRLAND if (ref == "hyprland/window") { return new waybar::modules::hyprland::Window(id, bar_, config_[name]); diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp new file mode 100644 index 0000000..a38e709 --- /dev/null +++ b/src/modules/dwl/tags.cpp @@ -0,0 +1,224 @@ +#include "modules/dwl/tags.hpp" + +#include +#include +#include +#include + +#include + +#include "client.hpp" +#include "dwl-bar-ipc-unstable-v1-client-protocol.h" + +#define TAG_INACTIVE 0 +#define TAG_ACTIVE 1 +#define TAG_URGENT 2 + +namespace waybar::modules::dwl { + +/* dwl stuff */ +wl_array tags, layouts; + +static uint num_tags = 0; + +void toggle_visibility(void* data, zdwl_output_v1* zdwl_output_v1) { + // Intentionally empty +} + +void active(void* data, zdwl_output_v1* zdwl_output_v1, uint32_t active) { + // Intentionally empty +} + +static void set_tag(void* data, zdwl_output_v1* zdwl_output_v1, uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) { + static_cast(data)->handle_view_tags(tag, state, clients, focused); + + num_tags = (state & ZDWL_OUTPUT_V1_TAG_STATE_ACTIVE) ? num_tags | (1 << tag) : num_tags & ~(1 << tag); +} + +void set_layout_symbol(void* data, zdwl_output_v1* zdwl_output_v1, const char *layout) { + // Intentionally empty +} + +void title(void* data, zdwl_output_v1* zdwl_output_v1, const char* title) { + // Intentionally empty +} + +void dwl_frame(void* data, zdwl_output_v1* zdwl_output_v1) { + // Intentionally empty +} + +static void set_layout(void* data, zdwl_output_v1* zdwl_output_v1, uint32_t layout) { + // Intentionally empty +} + +static void appid(void *data, zdwl_output_v1 *zdwl_output_v1, const char *appid) { + // Intentionally empty +}; + +static const zdwl_output_v1_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, +}; + +void add_layout(void* data, zdwl_manager_v1* zdwl_manager_v1, const char* name) { + void* temp = wl_array_add(&layouts, sizeof(char**)); + if (!temp) + return; + + char* dup = strdup(name); + + memcpy(temp, &dup, sizeof(char**)); +} + +void add_tag(void* data, zdwl_manager_v1* zdwl_manager_v1, const char* name) { + void* temp = wl_array_add(&tags, sizeof(char**)); + if (!temp) + return; + + char* dup = strdup(name); /* Gain ownership of name */ + + memcpy(temp, &dup, sizeof(char**)); /* Copy a pointer of it into the array */; +} + +static const struct zdwl_manager_v1_listener dwl_listener = { + .tag = add_tag, + .layout = add_layout, +}; + +static void handle_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) { + if (std::strcmp(interface, zdwl_manager_v1_interface.name) == 0) { + static_cast(data)->status_manager_ = static_cast( + (zdwl_manager_v1*)wl_registry_bind(registry, name, &zdwl_manager_v1_interface, 3)); + zdwl_manager_v1_add_listener(static_cast(data)->status_manager_, &dwl_listener, NULL); + } + if (std::strcmp(interface, wl_seat_interface.name) == 0) { + version = std::min(version, 1); + static_cast(data)->seat_ = static_cast( + wl_registry_bind(registry, name, &wl_seat_interface, version)); + } +} + +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}; + +Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &config) + : waybar::AModule(config, "tags", id, false, false), + status_manager_{nullptr}, + seat_{nullptr}, + bar_(bar), + box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0}, + 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); + + if (!status_manager_) { + spdlog::error("dwl_status_manager_v1 not advertised"); + return; + } + + if (!seat_) { + spdlog::error("wl_seat not advertised"); + } + + box_.set_name("tags"); + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + event_box_.add(box_); + + // Default to 9 tags, cap at 32 + const uint32_t num_tags = + config["num-tags"].isUInt() ? std::min(32, config_["num-tags"].asUInt()) : 9; + + std::vector tag_labels(num_tags); + for (uint32_t tag = 0; tag < num_tags; ++tag) { + tag_labels[tag] = std::to_string(tag + 1); + } + const Json::Value custom_labels = config["tag-labels"]; + if (custom_labels.isArray() && !custom_labels.empty()) { + for (uint32_t tag = 0; tag < std::min(num_tags, custom_labels.size()); ++tag) { + tag_labels[tag] = custom_labels[tag].asString(); + } + } + + uint32_t i = 1; + for (const auto &tag_label : tag_labels) { + Gtk::Button &button = buttons_.emplace_back(tag_label); + button.set_relief(Gtk::RELIEF_NONE); + box_.pack_start(button, false, false, 0); + if (!config_["disable-click"].asBool()) { + button.signal_clicked().connect( + sigc::bind(sigc::mem_fun(*this, &Tags::handle_primary_clicked), i)); + button.signal_button_press_event().connect( + sigc::bind(sigc::mem_fun(*this, &Tags::handle_button_press), i)); + } + button.show(); + i <<= 1; + } + + struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + output_status_ = zdwl_manager_v1_get_output(status_manager_, output); + zdwl_output_v1_add_listener(output_status_, &output_status_listener_impl, this); + + zdwl_manager_v1_destroy(status_manager_); +} + +Tags::~Tags() { + if (output_status_) { + zdwl_manager_v1_destroy(status_manager_); + } +} + +void Tags::handle_primary_clicked(uint32_t tag) { + if (!output_status_) return; + + zdwl_output_v1_set_tags(output_status_, tag, 1); +} + +bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) { + if (event_button->type == GDK_BUTTON_PRESS && event_button->button == 3) { + + if (!output_status_) return true; + zdwl_output_v1_set_tags(output_status_, num_tags ^ tag, 0); + } + return true; +} + + +void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) { + // First clear all occupied state + auto &button = buttons_[tag]; + if (clients) { + button.get_style_context()->add_class("occupied"); + } else { + button.get_style_context()->remove_class("occupied"); + } + + if (state & TAG_ACTIVE) { + button.get_style_context()->add_class("focused"); + } else { + button.get_style_context()->remove_class("focused"); + } + + if (state & TAG_URGENT) { + button.get_style_context()->add_class("urgent"); + } else { + button.get_style_context()->remove_class("urgent"); + } + +} + +} /* namespace waybar::modules::dwl */ From 18a2b90fc0cb027464c68f41819bf1c9a48a409e Mon Sep 17 00:00:00 2001 From: Denis Drakhnia Date: Tue, 11 Apr 2023 08:09:01 +0300 Subject: [PATCH 122/217] fix: generate wireplumber module man page --- man/waybar-wireplumber.5.scd | 2 +- meson.build | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/man/waybar-wireplumber.5.scd b/man/waybar-wireplumber.5.scd index 3cf5694..6088377 100644 --- a/man/waybar-wireplumber.5.scd +++ b/man/waybar-wireplumber.5.scd @@ -15,7 +15,7 @@ The *wireplumber* module displays the current volume reported by WirePlumber. default: *{volume}%* ++ The format, how information should be displayed. This format is used when other formats aren't specified. - *format-muted*: ++ +*format-muted*: ++ typeof: string ++ This format is used when the sound is muted. diff --git a/meson.build b/meson.build index 58e1c67..f3b7c6e 100644 --- a/meson.build +++ b/meson.build @@ -430,6 +430,7 @@ if scdoc.found() 'waybar-bluetooth.5.scd', 'waybar-sndio.5.scd', 'waybar-upower.5.scd', + 'waybar-wireplumber.5.scd', ] if (giounix.found() and not get_option('logind').disabled()) From 3b2dfeec010dd0165a30266796e3afc87df14b09 Mon Sep 17 00:00:00 2001 From: skylar779 <89084411+skylar779@users.noreply.github.com> Date: Tue, 11 Apr 2023 10:51:25 +0200 Subject: [PATCH 123/217] Made use of node_id_ Checking against names for volume changes seems a bit weird to me and also didn't really work, so I've made use of node_id_ to check against this instead and also fixed an issue, where the volume update would refuse to do its thing despite it being the same id that was used on launch. --- src/modules/wireplumber.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index 7d9689c..0c4848a 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -120,11 +120,11 @@ void waybar::modules::Wireplumber::onMixerChanged(waybar::modules::Wireplumber* const gchar* name = wp_pipewire_object_get_property(WP_PIPEWIRE_OBJECT(node), "node.name"); - if (g_strcmp0(self->default_node_name_, name) != 0) { + if (self->node_id_ != id) { spdlog::debug( "[{}]: (onMixerChanged) - ignoring mixer update for node: id: {}, name: {} as it is not " - "the default node: {}", - self->name_, id, name, self->default_node_name_); + "the default node: {} with id: {}", + self->name_, id, name, self->default_node_name_, self->node_id_); return; } @@ -176,6 +176,7 @@ void waybar::modules::Wireplumber::onDefaultNodesApiChanged(waybar::modules::Wir 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); } @@ -197,18 +198,17 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir throw std::runtime_error("Mixer api is not loaded\n"); } - uint32_t default_node_id; g_signal_emit_by_name(self->def_nodes_api_, "get-default-configured-node-name", "Audio/Sink", &self->default_node_name_); - g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &default_node_id); + g_signal_emit_by_name(self->def_nodes_api_, "get-default-node", "Audio/Sink", &self->node_id_); if (self->default_node_name_) { spdlog::debug("[{}]: (onObjectManagerInstalled) - default configured node name: {} and id: {}", - self->name_, self->default_node_name_, default_node_id); + self->name_, self->default_node_name_, self->node_id_); } - updateVolume(self, default_node_id); - updateNodeName(self, default_node_id); + updateVolume(self, self->node_id_); + updateNodeName(self, self->node_id_); g_signal_connect_swapped(self->mixer_api_, "changed", (GCallback)onMixerChanged, self); g_signal_connect_swapped(self->def_nodes_api_, "changed", (GCallback)onDefaultNodesApiChanged, From 022c9abad4d5e505ac177541c092dd8a281f538d Mon Sep 17 00:00:00 2001 From: yeheng282 Date: Tue, 11 Apr 2023 21:01:02 +0800 Subject: [PATCH 124/217] backlight: support tooltip --- src/modules/backlight.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index a46b296..dc0ef92 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -190,10 +190,25 @@ 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()); - label_.set_markup(fmt::format(fmt::runtime(format_), + std::string desc = fmt::format(fmt::runtime(format_), fmt::arg("percent", std::to_string(percent)), - fmt::arg("icon", getIcon(percent)))); - getState(percent); + fmt::arg("icon", getIcon(percent))); + label_.set_markup(desc); + getState(percent); + 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("percent", std::to_string(percent)), + fmt::arg("icon", getIcon(percent)))); + } else { + label_.set_tooltip_text(desc); + } + } } else { event_box_.hide(); } From 731eaeb0be09fd85c739790a03d1128b1aed05f8 Mon Sep 17 00:00:00 2001 From: David Delarosa Date: Thu, 13 Apr 2023 03:58:57 +0300 Subject: [PATCH 125/217] Addapt to DWL new tag system - tagscount is being used instead of tag array --- protocol/dwl-bar-ipc-unstable-v1.xml | 2 +- src/modules/dwl/tags.cpp | 26 -------------------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/protocol/dwl-bar-ipc-unstable-v1.xml b/protocol/dwl-bar-ipc-unstable-v1.xml index 0dcec4f..1b614b2 100644 --- a/protocol/dwl-bar-ipc-unstable-v1.xml +++ b/protocol/dwl-bar-ipc-unstable-v1.xml @@ -48,7 +48,7 @@ I would probably just submit raphi's patchset but I don't think that would be po This event is sent after binding. A roundtrip after binding guarantees the client recieved all tags. - + diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index a38e709..7d40882 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -66,37 +66,11 @@ static const zdwl_output_v1_listener output_status_listener_impl { .frame = dwl_frame, }; -void add_layout(void* data, zdwl_manager_v1* zdwl_manager_v1, const char* name) { - void* temp = wl_array_add(&layouts, sizeof(char**)); - if (!temp) - return; - - char* dup = strdup(name); - - memcpy(temp, &dup, sizeof(char**)); -} - -void add_tag(void* data, zdwl_manager_v1* zdwl_manager_v1, const char* name) { - void* temp = wl_array_add(&tags, sizeof(char**)); - if (!temp) - return; - - char* dup = strdup(name); /* Gain ownership of name */ - - memcpy(temp, &dup, sizeof(char**)); /* Copy a pointer of it into the array */; -} - -static const struct zdwl_manager_v1_listener dwl_listener = { - .tag = add_tag, - .layout = add_layout, -}; - static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (std::strcmp(interface, zdwl_manager_v1_interface.name) == 0) { static_cast(data)->status_manager_ = static_cast( (zdwl_manager_v1*)wl_registry_bind(registry, name, &zdwl_manager_v1_interface, 3)); - zdwl_manager_v1_add_listener(static_cast(data)->status_manager_, &dwl_listener, NULL); } if (std::strcmp(interface, wl_seat_interface.name) == 0) { version = std::min(version, 1); From 6de3b58d6aa7b17271da9c637a7ac6cbf92c49ba Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Thu, 13 Apr 2023 17:16:03 +0300 Subject: [PATCH 126/217] ISSUE#2073 Signed-off-by: Viktar Lukashonak --- include/modules/clock.hpp | 1 + src/modules/clock.cpp | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 999c843..fab3811 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -50,6 +50,7 @@ class Clock final : public ALabel { date::months cldShift_{0}; std::string cldYearCached_{}; std::string cldMonCached_{}; + date::day cldBaseDay_{0}; /*Calendar functions*/ auto get_calendar(const date::zoned_seconds& now, const date::zoned_seconds& wtime) -> std::string; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 2edd21b..7e351d4 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -93,9 +93,11 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) fmtMap_.insert({1, config_[kCalendarPlaceholder]["format"]["weekdays"].asString()}); else fmtMap_.insert({1, "{}"}); - if (config_[kCalendarPlaceholder]["format"]["today"].isString()) + if (config_[kCalendarPlaceholder]["format"]["today"].isString()) { fmtMap_.insert({3, config_[kCalendarPlaceholder]["format"]["today"].asString()}); - else + 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()) @@ -315,6 +317,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, const auto ymd{date::year_month_day{daypoint}}; 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; @@ -325,13 +328,19 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, if (cldMode_ == CldMode::YEAR) { if (y / date::month{1} / 1 == cldYearShift_) - return cldYearCached_; + if (d == cldBaseDay_ || (uint)cldBaseDay_ == 0u) + return cldYearCached_; + else + cldBaseDay_ = d; else cldYearShift_ = y / date::month{1} / 1; } if (cldMode_ == CldMode::MONTH) { if (ym == cldMonShift_) - return cldMonCached_; + if (d == cldBaseDay_ || (uint)cldBaseDay_ == 0u) + return cldMonCached_; + else + cldBaseDay_ = d; else cldMonShift_ = ym; } From 1368ba64b587ae392b8119b31cb76bb62baecd6b Mon Sep 17 00:00:00 2001 From: David Delarosa Date: Thu, 13 Apr 2023 17:16:37 +0300 Subject: [PATCH 127/217] Fix segfault in destructor --- src/modules/dwl/tags.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index 7d40882..88d932a 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -148,10 +148,11 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con zdwl_output_v1_add_listener(output_status_, &output_status_listener_impl, this); zdwl_manager_v1_destroy(status_manager_); + status_manager_ = nullptr; } Tags::~Tags() { - if (output_status_) { + if (status_manager_) { zdwl_manager_v1_destroy(status_manager_); } } From a5607b66ca64bbe4526ec3baad72c9e564a17ddb Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Sat, 15 Apr 2023 02:36:15 +0300 Subject: [PATCH 128/217] Cava module Signed-off-by: Viktar Lukashonak --- include/factory.hpp | 3 + include/modules/cava.hpp | 47 +++++++++ meson.build | 22 ++++- meson_options.txt | 3 +- src/factory.cpp | 5 + src/modules/cava.cpp | 203 +++++++++++++++++++++++++++++++++++++++ subprojects/cava.wrap | 7 ++ 7 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 include/modules/cava.hpp create mode 100644 src/modules/cava.cpp create mode 100644 subprojects/cava.wrap diff --git a/include/factory.hpp b/include/factory.hpp index 89eea0e..1848f6a 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -80,6 +80,9 @@ #ifdef HAVE_LIBWIREPLUMBER #include "modules/wireplumber.hpp" #endif +#ifdef HAVE_LIBCAVA +#include "modules/cava.hpp" +#endif #include "bar.hpp" #include "modules/custom.hpp" #include "modules/image.hpp" diff --git a/include/modules/cava.hpp b/include/modules/cava.hpp new file mode 100644 index 0000000..b31711d --- /dev/null +++ b/include/modules/cava.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +extern "C" { +#include +} + +namespace waybar::modules { +using namespace std::literals::chrono_literals; + +class Cava final: public ALabel { + public: + Cava(const std::string&, const Json::Value&); + virtual ~Cava(); + auto update() -> void override; + auto doAction(const std::string& name) -> void override; + + private: + 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{}}; + // Cava API to read audio source + ptr input_source_; + // Delay to handle audio source + std::chrono::milliseconds frame_time_milsec_{1s}; + // Text to display + std::string text_{""}; + int rePaint_{1}; + std::chrono::seconds fetch_input_delay_{4}; + std::chrono::seconds suspend_silence_delay_{0}; + bool silence_{false}; + int sleep_counter_{0}; + // Cava method + void pause_resume(); + // ModuleActionMap + static inline std::map actionMap_{ + {"mode", &waybar::modules::Cava::pause_resume} + }; +}; +} diff --git a/meson.build b/meson.build index 58e1c67..2b018ac 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project( 'waybar', 'cpp', 'c', version: '0.9.17', license: 'MIT', - meson_version: '>= 0.49.0', + meson_version: '>= 0.50.0', default_options : [ 'cpp_std=c++17', 'buildtype=release', @@ -175,6 +175,8 @@ src_files = files( 'src/util/rewrite_string.cpp' ) +inc_dirs = ['include'] + if is_linux add_project_arguments('-DHAVE_CPU_LINUX', language: 'cpp') add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp') @@ -334,6 +336,19 @@ if get_option('experimental') add_project_arguments('-DUSE_EXPERIMENTAL', language: 'cpp') endif +cava = compiler.find_library('cava', + required: get_option('cava')) +if not cava.found() and not get_option('cava').disabled() + cava = dependency('cava', + required: false, + fallback: [ 'cava', 'cava_dep' ]) +endif + +if cava.found() + add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp') + src_files += 'src/modules/cava.cpp' +endif + subdir('protocol') executable( @@ -367,9 +382,10 @@ executable( gtk_layer_shell, libsndio, tz_dep, - xkbregistry + xkbregistry, + cava ], - include_directories: [include_directories('include')], + include_directories: inc_dirs, install: true, ) diff --git a/meson_options.txt b/meson_options.txt index 98cd494..7dacf08 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -17,4 +17,5 @@ option('logind', type: 'feature', value: 'auto', description: 'Enable support fo option('tests', type: 'feature', value: 'auto', description: 'Enable tests') option('experimental', type : 'boolean', value : false, description: 'Enable experimental features') option('jack', type: 'feature', value: 'auto', description: 'Enable support for JACK') -option('wireplumber', type: 'feature', value: 'auto', description: 'Enable support for WirePlumber') \ No newline at end of file +option('wireplumber', type: 'feature', value: 'auto', description: 'Enable support for WirePlumber') +option('cava', type: 'feature', value: 'auto', description: 'Enable support for Cava') diff --git a/src/factory.cpp b/src/factory.cpp index dd5c142..618483f 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -156,6 +156,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "wireplumber") { return new waybar::modules::Wireplumber(id, config_[name]); } +#endif +#ifdef HAVE_LIBCAVA + if (ref == "cava") { + return new waybar::modules::Cava(id, config_[name]); + } #endif if (ref == "temperature") { return new waybar::modules::Temperature(id, config_[name]); diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp new file mode 100644 index 0000000..8e788f5 --- /dev/null +++ b/src/modules/cava.cpp @@ -0,0 +1,203 @@ +#include "modules/cava.hpp" + +#include + +waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) + : ALabel(config, "cava", id, "{}", 60, false, false, false) { + // Load waybar module 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) strPath.replace(npos, fnd.length() + 1, getenv(fnd.c_str())); + strcpy(cfgPath, strPath.data()); + } + // Load cava config + error_.length = 0; + + if (!load_config(cfgPath, &prm_, false, &error_)) { + spdlog::error("Error loading config. {0}", error_.message); + exit(EXIT_FAILURE); + } + + // Override cava parameters by the user config + prm_.inAtty = 0; + prm_.output = output_method::OUTPUT_RAW; + strcpy(prm_.data_format, "ascii"); + strcpy(prm_.raw_target, "/dev/stdout"); + prm_.ascii_range = config_["format-icons"].size() - 1; + + prm_.bar_width = 2; + 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_.autobars = 0; + prm_.gravity = 0; + prm_.integral = 1; + + if (config_["framerate"].isInt()) prm_.framerate = config_["framerate"].asInt(); + if (config_["autosens"].isInt()) prm_.autosens = config_["autosens"].asInt(); + if (config_["sensitivity"].isInt()) prm_.sens = config_["sensitivity"].asInt(); + if (config_["bars"].isInt()) prm_.fixedbars = config_["bars"].asInt(); + if (config_["lower_cutoff_freq"].isNumeric()) + prm_.lower_cut_off = config_["lower_cutoff_freq"].asLargestInt(); + if (config_["higher_cutoff_freq"].isNumeric()) + 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()); + 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_["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(); + if (config_["monstercat"].isBool()) prm_.monstercat = config_["monstercat"].asBool(); + if (config_["waves"].isBool()) prm_.waves = config_["waves"].asBool(); + if (config_["noise_reduction"].isDouble()) + prm_.noise_reduction = config_["noise_reduction"].asDouble(); + if (config_["input_delay"].isInt()) + fetch_input_delay_ = std::chrono::seconds(config_["input_delay"].asInt()); + // Make cava parameters configuration + plan_ = new cava_plan{}; + + audio_raw_.height = prm_.ascii_range; + audio_data_.format = -1; + audio_data_.source = new char[1 + strlen(prm_.audio_source)]; + audio_data_.source[0] = '\0'; + strcpy(audio_data_.source, prm_.audio_source); + + audio_data_.rate = 0; + audio_data_.samples_counter = 0; + audio_data_.channels = 2; + audio_data_.IEEE_FLOAT = 0; + + audio_data_.input_buffer_size = BUFFER_SIZE * audio_data_.channels; + audio_data_.cava_buffer_size = audio_data_.input_buffer_size * 8; + + audio_data_.cava_in = new double[audio_data_.cava_buffer_size]{0.0}; + + audio_data_.terminate = 0; + audio_data_.suspendFlag = false; + input_source_ = get_input(&audio_data_, &prm_); + + if (!input_source_) { + spdlog::error("cava API didn't provide input audio source method"); + exit(EXIT_FAILURE); + } + // Calculate delay for Update() thread + frame_time_milsec_ = std::chrono::milliseconds((int)(1e3 / prm_.framerate)); + + // Init cava plan, audio_raw structure + audio_raw_init(&audio_data_, &audio_raw_, &prm_, plan_); + if (!plan_) spdlog::error("cava plan is not provided"); + audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message + // Read audio source trough cava API. Cava orginizes this process via infinity loop + thread_fetch_input_ = [this] { + thread_fetch_input_.sleep_for(fetch_input_delay_); + input_source_(&audio_data_); + dp.emit(); + }; + + thread_ = [this] { + dp.emit(); + thread_.sleep_for(frame_time_milsec_); + }; +} + +waybar::modules::Cava::~Cava() { + thread_fetch_input_.stop(); + thread_.stop(); + delete plan_; + plan_ = nullptr; +} + +void upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) { + if (delta == std::chrono::seconds{0}) { + delta += std::chrono::seconds{1}; + delay += delta; + } +} + +void downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) { + if (delta > std::chrono::seconds{0}) { + delay -= delta; + delta -= std::chrono::seconds{1}; + } +} + +auto waybar::modules::Cava::update() -> void { + if (audio_data_.suspendFlag) return; + silence_ = true; + + for (int i{0}; i < audio_data_.input_buffer_size; ++i) { + if (audio_data_.cava_in[i]) { + silence_ = false; + sleep_counter_ = 0; + break; + } + } + + if (silence_ && prm_.sleep_timer) { + if (sleep_counter_ <= + (int)(std::chrono::milliseconds(prm_.sleep_timer * 1s) / frame_time_milsec_)) { + ++sleep_counter_; + silence_ = false; + } + } + + if (!silence_) { + 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_); + 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_); + + if (rePaint_ == 1) { + text_.clear(); + + for (int i{0}; i < audio_raw_.number_of_bars; ++i) { + audio_raw_.previous_frame[i] = audio_raw_.bars[i]; + text_.append( + getIcon((audio_raw_.bars[i] > prm_.ascii_range) ? prm_.ascii_range : audio_raw_.bars[i], + "", prm_.ascii_range + 1)); + if (prm_.bar_delim != 0) text_.push_back(prm_.bar_delim); + } + + label_.set_markup(text_); + ALabel::update(); + } + } else + upThreadDelay(frame_time_milsec_, suspend_silence_delay_); +} + +auto waybar::modules::Cava::doAction(const std::string& name) -> void { + if ((actionMap_[name])) { + (this->*actionMap_[name])(); + } else + spdlog::error("Cava. Unsupported action \"{0}\"", name); +} + +// Cava actions +void waybar::modules::Cava::pause_resume() { + pthread_mutex_lock(&audio_data_.lock); + if (audio_data_.suspendFlag) { + audio_data_.suspendFlag = false; + pthread_cond_broadcast(&audio_data_.resumeCond); + downThreadDelay(frame_time_milsec_, suspend_silence_delay_); + } else { + audio_data_.suspendFlag = true; + upThreadDelay(frame_time_milsec_, suspend_silence_delay_); + } + pthread_mutex_unlock(&audio_data_.lock); +} diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap new file mode 100644 index 0000000..95a9c8f --- /dev/null +++ b/subprojects/cava.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = cava-0.8.3 +source_url = https://github.com/LukashonakV/cava/archive/0.8.3.tar.gz +source_filename = cava-0.8.3.tar.gz +source_hash = 10f9ec910682102c47dc39d684fd3fc90d38a4d1c2e5a310f132f70ad0e00850 +[provide] +cava = cava_dep From ede1d254400ebeb994bfb62c8469c97b977819a8 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 17 Apr 2023 09:01:14 +0200 Subject: [PATCH 129/217] fix: lint --- include/modules/cava.hpp | 23 +++++++++++------------ src/modules/backlight.cpp | 15 +++++++-------- src/modules/dwl/tags.cpp | 37 ++++++++++++++++++------------------- 3 files changed, 36 insertions(+), 39 deletions(-) diff --git a/include/modules/cava.hpp b/include/modules/cava.hpp index b31711d..d4da2b7 100644 --- a/include/modules/cava.hpp +++ b/include/modules/cava.hpp @@ -10,22 +10,22 @@ extern "C" { namespace waybar::modules { using namespace std::literals::chrono_literals; -class Cava final: public ALabel { - public: +class Cava final : public ALabel { + public: Cava(const std::string&, const Json::Value&); virtual ~Cava(); auto update() -> void override; auto doAction(const std::string& name) -> void override; - private: + private: 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 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{}}; // Cava API to read audio source ptr input_source_; // Delay to handle audio source @@ -40,8 +40,7 @@ class Cava final: public ALabel { // Cava method void pause_resume(); // ModuleActionMap - static inline std::map actionMap_{ - {"mode", &waybar::modules::Cava::pause_resume} - }; + static inline std::map actionMap_{ + {"mode", &waybar::modules::Cava::pause_resume}}; }; -} +} // namespace waybar::modules diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index dc0ef92..eb7a7e9 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -190,21 +190,20 @@ 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", std::to_string(percent)), - fmt::arg("icon", getIcon(percent))); + std::string desc = + fmt::format(fmt::runtime(format_), fmt::arg("percent", std::to_string(percent)), + fmt::arg("icon", getIcon(percent))); label_.set_markup(desc); - getState(percent); + getState(percent); 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("percent", std::to_string(percent)), - fmt::arg("icon", getIcon(percent)))); + label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), + fmt::arg("percent", std::to_string(percent)), + fmt::arg("icon", getIcon(percent)))); } else { label_.set_tooltip_text(desc); } diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index 88d932a..fa64099 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -21,41 +21,43 @@ wl_array tags, layouts; static uint num_tags = 0; -void toggle_visibility(void* data, zdwl_output_v1* zdwl_output_v1) { +void toggle_visibility(void *data, zdwl_output_v1 *zdwl_output_v1) { // Intentionally empty } -void active(void* data, zdwl_output_v1* zdwl_output_v1, uint32_t active) { +void active(void *data, zdwl_output_v1 *zdwl_output_v1, uint32_t active) { // Intentionally empty } -static void set_tag(void* data, zdwl_output_v1* zdwl_output_v1, uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) { +static void set_tag(void *data, zdwl_output_v1 *zdwl_output_v1, uint32_t tag, uint32_t state, + uint32_t clients, uint32_t focused) { static_cast(data)->handle_view_tags(tag, state, clients, focused); - num_tags = (state & ZDWL_OUTPUT_V1_TAG_STATE_ACTIVE) ? num_tags | (1 << tag) : num_tags & ~(1 << tag); + num_tags = + (state & ZDWL_OUTPUT_V1_TAG_STATE_ACTIVE) ? num_tags | (1 << tag) : num_tags & ~(1 << tag); } -void set_layout_symbol(void* data, zdwl_output_v1* zdwl_output_v1, const char *layout) { +void set_layout_symbol(void *data, zdwl_output_v1 *zdwl_output_v1, const char *layout) { // Intentionally empty } -void title(void* data, zdwl_output_v1* zdwl_output_v1, const char* title) { +void title(void *data, zdwl_output_v1 *zdwl_output_v1, const char *title) { // Intentionally empty } -void dwl_frame(void* data, zdwl_output_v1* zdwl_output_v1) { +void dwl_frame(void *data, zdwl_output_v1 *zdwl_output_v1) { // Intentionally empty } -static void set_layout(void* data, zdwl_output_v1* zdwl_output_v1, uint32_t layout) { +static void set_layout(void *data, zdwl_output_v1 *zdwl_output_v1, uint32_t layout) { // Intentionally empty } -static void appid(void *data, zdwl_output_v1 *zdwl_output_v1, const char *appid) { - // Intentionally empty +static void appid(void *data, zdwl_output_v1 *zdwl_output_v1, const char *appid){ + // Intentionally empty }; -static const zdwl_output_v1_listener output_status_listener_impl { +static const zdwl_output_v1_listener output_status_listener_impl{ .toggle_visibility = toggle_visibility, .active = active, .tag = set_tag, @@ -70,7 +72,7 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam const char *interface, uint32_t version) { if (std::strcmp(interface, zdwl_manager_v1_interface.name) == 0) { static_cast(data)->status_manager_ = static_cast( - (zdwl_manager_v1*)wl_registry_bind(registry, name, &zdwl_manager_v1_interface, 3)); + (zdwl_manager_v1 *)wl_registry_bind(registry, name, &zdwl_manager_v1_interface, 3)); } if (std::strcmp(interface, wl_seat_interface.name) == 0) { version = std::min(version, 1); @@ -165,14 +167,12 @@ void Tags::handle_primary_clicked(uint32_t tag) { bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) { if (event_button->type == GDK_BUTTON_PRESS && event_button->button == 3) { - if (!output_status_) return true; zdwl_output_v1_set_tags(output_status_, num_tags ^ tag, 0); } return true; } - void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) { // First clear all occupied state auto &button = buttons_[tag]; @@ -183,17 +183,16 @@ void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint } if (state & TAG_ACTIVE) { - button.get_style_context()->add_class("focused"); + button.get_style_context()->add_class("focused"); } else { - button.get_style_context()->remove_class("focused"); + button.get_style_context()->remove_class("focused"); } if (state & TAG_URGENT) { - button.get_style_context()->add_class("urgent"); + button.get_style_context()->add_class("urgent"); } else { - button.get_style_context()->remove_class("urgent"); + button.get_style_context()->remove_class("urgent"); } - } } /* namespace waybar::modules::dwl */ From 272d6729539803d7213a89895b3c8868404f8eef Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 17 Apr 2023 09:03:01 +0200 Subject: [PATCH 130/217] fix: add missing man --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 470b210..0bd7c62 100644 --- a/meson.build +++ b/meson.build @@ -452,6 +452,7 @@ if scdoc.found() 'waybar-sndio.5.scd', 'waybar-upower.5.scd', 'waybar-wireplumber.5.scd', + 'waybar-dwl-tags.5.scd, ] if (giounix.found() and not get_option('logind').disabled()) From 33d1ee413f4f08daae43c49a306fc7576e8dd518 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 17 Apr 2023 09:07:59 +0200 Subject: [PATCH 131/217] fix: typo --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 0bd7c62..fd8ca79 100644 --- a/meson.build +++ b/meson.build @@ -452,7 +452,7 @@ if scdoc.found() 'waybar-sndio.5.scd', 'waybar-upower.5.scd', 'waybar-wireplumber.5.scd', - 'waybar-dwl-tags.5.scd, + 'waybar-dwl-tags.5.scd', ] if (giounix.found() and not get_option('logind').disabled()) From a9a222346990b46e4c928a1c57cf0678120721de Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 17 Apr 2023 09:09:13 +0200 Subject: [PATCH 132/217] fix: lint --- src/modules/hyprland/backend.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 33212c7..f0f2554 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -132,16 +132,16 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) { std::string IPC::getSocket1Reply(const std::string& rq) { // basically hyprctl - + struct addrinfo ai_hints; - struct addrinfo *ai_res = NULL; + struct addrinfo* ai_res = NULL; const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 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; From 6a1713942312a9475292e8b4dde65ccb321cb9c0 Mon Sep 17 00:00:00 2001 From: Alan-Kuan Date: Fri, 21 Apr 2023 16:38:21 +0800 Subject: [PATCH 133/217] feat: tooltip for image module --- include/modules/image.hpp | 3 +++ src/modules/image.cpp | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/include/modules/image.hpp b/include/modules/image.hpp index c15270d..7c0d014 100644 --- a/include/modules/image.hpp +++ b/include/modules/image.hpp @@ -24,12 +24,15 @@ class Image : public AModule { private: void delayWorker(); void handleEvent(); + void parseOutputRaw(); Gtk::Box box_; Gtk::Image image_; std::string path_; + std::string tooltip_; int size_; int interval_; + util::command::res output_; util::SleeperThread thread_; }; diff --git a/src/modules/image.cpp b/src/modules/image.cpp index a938617..843cd95 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -41,14 +41,12 @@ void waybar::modules::Image::refresh(int sig) { } auto waybar::modules::Image::update() -> void { - util::command::res output_; - Glib::RefPtr pixbuf; if (config_["path"].isString()) { path_ = config_["path"].asString(); } else if (config_["exec"].isString()) { output_ = util::command::exec(config_["exec"].asString()); - path_ = output_.out; + parseOutputRaw(); } else { path_ = ""; } @@ -58,6 +56,11 @@ auto waybar::modules::Image::update() -> void { pixbuf = {}; if (pixbuf) { + if (tooltipEnabled() && !tooltip_.empty()) { + if (box_.get_tooltip_markup() != tooltip_) { + box_.set_tooltip_markup(tooltip_); + } + } image_.set(pixbuf); image_.show(); } else { @@ -67,3 +70,19 @@ auto waybar::modules::Image::update() -> void { AModule::update(); } + +void waybar::modules::Image::parseOutputRaw() { + std::istringstream output(output_.out); + std::string line; + int i = 0; + while (getline(output, line)) { + if (i == 0) { + path_ = line; + } else if (i == 1) { + tooltip_ = line; + } else { + break; + } + i++; + } +} From 9389c8d85495c632af76862710e7d194934d544f Mon Sep 17 00:00:00 2001 From: Alan-Kuan Date: Fri, 21 Apr 2023 16:51:04 +0800 Subject: [PATCH 134/217] chore: update manual of the image modle --- man/waybar-image.5.scd | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/man/waybar-image.5.scd b/man/waybar-image.5.scd index 09e871e..d47dba3 100644 --- a/man/waybar-image.5.scd +++ b/man/waybar-image.5.scd @@ -57,6 +57,20 @@ The *image* module displays an image from a path. typeof: double ++ Threshold to be used when scrolling. +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to enable tooltip on hover. + +# SCRIPT OUTPUT + +Similar to the *custom* module, output values of the script is *newline* separated. +The following is the output format: + +``` +$path\\n$tooltip +``` + # EXAMPLES ``` From f00602058cdd82aad6643df3b97b161da37e8cbf Mon Sep 17 00:00:00 2001 From: Enes Hecan Date: Sun, 23 Apr 2023 13:31:02 +0200 Subject: [PATCH 135/217] Fix misinterpreting return value of getaddrinfo call which caused an early return for Hyprland language --- src/modules/hyprland/backend.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index f0f2554..1e684d9 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -145,9 +145,8 @@ std::string IPC::getSocket1Reply(const std::string& rq) { memset(&ai_hints, 0, sizeof(struct addrinfo)); ai_hints.ai_family = AF_UNSPEC; ai_hints.ai_socktype = SOCK_STREAM; - const auto SERVER = getaddrinfo("localhost", NULL, &ai_hints, &ai_res); - if (!SERVER) { + if (getaddrinfo("localhost", NULL, &ai_hints, &ai_res) != 0) { spdlog::error("Hyprland IPC: Couldn't get host (2)"); return ""; } From 3da3732cc9315ffe38ef2c627b216248756aacba Mon Sep 17 00:00:00 2001 From: Paymon Date: Wed, 26 Apr 2023 13:49:17 -0400 Subject: [PATCH 136/217] network: detect if link is p2p and use local addr without this we endup with peer's addr Signed-off-by: Paymon MARANDI --- include/modules/network.hpp | 1 + src/modules/network.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 4958591..8568bf9 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -62,6 +62,7 @@ class Network : public ALabel { bool want_link_dump_; bool want_addr_dump_; bool dump_in_progress_; + bool is_p2p_; unsigned long long bandwidth_down_total_; unsigned long long bandwidth_up_total_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 9e9a93a..aaff59e 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -87,6 +87,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf want_link_dump_(false), want_addr_dump_(false), dump_in_progress_(false), + is_p2p_(false), cidr_(0), signal_strength_dbm_(0), signal_strength_(0), @@ -456,6 +457,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { case IFLA_IFNAME: ifname = static_cast(RTA_DATA(ifla)); ifname_len = RTA_PAYLOAD(ifla) - 1; // minus \0 + if (ifi->ifi_flags & IFF_POINTOPOINT && net->checkInterface(ifname)) + net->is_p2p_ = true; break; case IFLA_CARRIER: { carrier = *(char *)RTA_DATA(ifla) == 1; @@ -494,6 +497,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifname_ = new_ifname; net->ifid_ = ifi->ifi_index; + if (ifi->ifi_flags & IFF_POINTOPOINT) + net->is_p2p_ = true; if (carrier.has_value()) { net->carrier_ = carrier.value(); } @@ -537,7 +542,10 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { for (; RTA_OK(ifa_rta, attrlen); ifa_rta = RTA_NEXT(ifa_rta, attrlen)) { switch (ifa_rta->rta_type) { - case IFA_ADDRESS: { + case IFA_ADDRESS: + if (net->is_p2p_) + continue; + 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)); @@ -570,7 +578,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } net->dp.emit(); break; - } } } break; From e253c34cd4f26db0959f8b0f2e08be42716c275a Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 28 Apr 2023 13:38:35 +0300 Subject: [PATCH 137/217] cava bump Signed-off-by: Viktar Lukashonak --- meson.build | 12 +++++------- subprojects/cava.wrap | 8 ++++---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/meson.build b/meson.build index fd8ca79..c874e19 100644 --- a/meson.build +++ b/meson.build @@ -341,13 +341,11 @@ if get_option('experimental') add_project_arguments('-DUSE_EXPERIMENTAL', language: 'cpp') endif -cava = compiler.find_library('cava', - required: get_option('cava')) -if not cava.found() and not get_option('cava').disabled() - cava = dependency('cava', - required: false, - fallback: [ 'cava', 'cava_dep' ]) -endif +cava = dependency('cava', + version : '>=0.8.4', + required: get_option('cava'), + fallback : ['cava', 'cava_dep'], + not_found_message: 'cava is not found. Building waybar without cava') if cava.found() add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp') diff --git a/subprojects/cava.wrap b/subprojects/cava.wrap index 95a9c8f..59044b1 100644 --- a/subprojects/cava.wrap +++ b/subprojects/cava.wrap @@ -1,7 +1,7 @@ [wrap-file] -directory = cava-0.8.3 -source_url = https://github.com/LukashonakV/cava/archive/0.8.3.tar.gz -source_filename = cava-0.8.3.tar.gz -source_hash = 10f9ec910682102c47dc39d684fd3fc90d38a4d1c2e5a310f132f70ad0e00850 +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 [provide] cava = cava_dep From 8c83af75a1f2c5291fd49d089631210dbf3978db Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 28 Apr 2023 15:38:34 +0300 Subject: [PATCH 138/217] happy linter Signed-off-by: Viktar Lukashonak --- src/modules/network.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index aaff59e..a61edd5 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -497,8 +497,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifname_ = new_ifname; net->ifid_ = ifi->ifi_index; - if (ifi->ifi_flags & IFF_POINTOPOINT) - net->is_p2p_ = true; + if (ifi->ifi_flags & IFF_POINTOPOINT) net->is_p2p_ = true; if (carrier.has_value()) { net->carrier_ = carrier.value(); } @@ -543,8 +542,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { for (; RTA_OK(ifa_rta, attrlen); ifa_rta = RTA_NEXT(ifa_rta, attrlen)) { switch (ifa_rta->rta_type) { case IFA_ADDRESS: - if (net->is_p2p_) - continue; + if (net->is_p2p_) continue; case IFA_LOCAL: char ipaddr[INET6_ADDRSTRLEN]; if (!is_del_event) { From f5ef13c7f1a10b580d34b4c557bfbdc8253ce4a0 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 28 Apr 2023 15:46:51 +0300 Subject: [PATCH 139/217] Gentoo docker. Add iniparser Signed-off-by: Viktar Lukashonak --- Dockerfiles/gentoo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/gentoo b/Dockerfiles/gentoo index 2b68398..406f523 100644 --- a/Dockerfiles/gentoo +++ b/Dockerfiles/gentoo @@ -8,4 +8,4 @@ RUN export FEATURES="-ipc-sandbox -network-sandbox -pid-sandbox -sandbox -usersa emerge --verbose --update --deep --with-bdeps=y --backtrack=30 --newuse @world && \ USE="wayland gtk3 gtk -doc X" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols =dev-cpp/gtkmm-3.24.6 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 + media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl dev-libs/iniparser From c04485a5d0152ae165fd0b34f050b6bc3c8e7b74 Mon Sep 17 00:00:00 2001 From: MonstrousOgre Date: Fri, 5 May 2023 00:09:32 +0530 Subject: [PATCH 140/217] separate css class for empty workspaces --- include/modules/wlr/workspace_manager.hpp | 2 ++ src/modules/wlr/workspace_manager.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index 1fdb992..f7cc759 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -30,6 +30,7 @@ class Workspace { auto is_active() const -> bool { return state_ & static_cast(State::ACTIVE); } auto is_urgent() const -> bool { return state_ & static_cast(State::URGENT); } auto is_hidden() const -> bool { return state_ & static_cast(State::HIDDEN); } + auto is_empty() const -> bool { return state_ & static_cast(State::EMPTY); } auto is_persistent() const -> bool { return persistent_; } // wlr stuff auto handle_name(const std::string &name) -> void; @@ -51,6 +52,7 @@ class Workspace { ACTIVE = (1 << 0), URGENT = (1 << 1), HIDDEN = (1 << 2), + EMPTY = (1 << 3), }; private: diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 6a496e6..c940b86 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -374,6 +374,8 @@ Workspace::Workspace(const Bar &bar, const Json::Value &config, WorkspaceGroup & name_(name) { if (workspace) { add_workspace_listener(workspace, this); + } else { + state_ = (uint32_t)State::EMPTY; } auto config_format = config["format"]; @@ -447,6 +449,8 @@ auto Workspace::handle_remove() -> void { } if (!persistent_) { workspace_group_.remove_workspace(id_); + } else { + state_ = (uint32_t)State::EMPTY; } } @@ -465,6 +469,7 @@ auto Workspace::handle_done() -> void { add_or_remove_class(style_context, is_active(), "active"); add_or_remove_class(style_context, is_urgent(), "urgent"); add_or_remove_class(style_context, is_hidden(), "hidden"); + add_or_remove_class(style_context, is_empty(), "persistent"); if (workspace_group_.creation_delayed()) { return; From 5df43be11fe8bfd13b4f335d64900324e74ce302 Mon Sep 17 00:00:00 2001 From: theRealCarneiro Date: Fri, 5 May 2023 08:52:37 -0300 Subject: [PATCH 141/217] add persistent format-icon --- src/modules/wlr/workspace_manager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index c940b86..0b1dc72 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -495,6 +495,13 @@ auto Workspace::get_icon() -> std::string { return named_icon_it->second; } + if (is_empty()) { + 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; From 4bc30e040f7c459c6378f6fbdcaba2dcfd55fd0c Mon Sep 17 00:00:00 2001 From: Finlay Davidson Date: Wed, 10 May 2023 13:43:43 +0200 Subject: [PATCH 142/217] mpris: Hide label if empty --- src/modules/mpris/mpris.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index e89b8ef..e434694 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -630,7 +630,12 @@ auto Mpris::update() -> void { fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)), fmt::arg("status_icon", getIconFromJson(config_["status-icons"], info.status_string))); - label_.set_markup(label_format); + if (label_format.empty()) { + label_.hide(); + } else { + label_.set_markup(label_format); + label_.show(); + } } catch (fmt::format_error const& e) { spdlog::warn("mpris: format error: {}", e.what()); } From 5748e56aeb46202a8d984ff3d9efedd97aa360b1 Mon Sep 17 00:00:00 2001 From: Finlay Davidson Date: Sat, 13 May 2023 21:45:29 +0200 Subject: [PATCH 143/217] mpris: Fix incorrect 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 40f132d..145e6da 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -153,9 +153,9 @@ The *mpris* module displays currently playing media via libplayerctl. *{dynamic}*: Use _{artist}_, _{album}_, _{title}_ and _{length}_, automatically omit++ empty values -*{player-icon}*: Chooses an icon from _player-icons_ based on _{player}_ +*{player_icon}*: Chooses an icon from _player-icons_ based on _{player}_ -*{status-icon}*: Chooses an icon from _status-icons_ based on _{status}_ +*{status_icon}*: Chooses an icon from _status-icons_ based on _{status}_ # EXAMPLES From 339bea1213cb9184b120ece4c7d210f6069f2843 Mon Sep 17 00:00:00 2001 From: maqrrr Date: Mon, 15 May 2023 07:01:00 -0400 Subject: [PATCH 144/217] Add a new start_hidden bool --- src/bar.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bar.cpp b/src/bar.cpp index b529fcd..60104f0 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -593,6 +593,10 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) setMode(MODE_DEFAULT); } + if (config["start_hidden"].asBool()) { + setVisible(false); + } + window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap)); #if HAVE_SWAY From df65cab17a2257cd12ab70b33a3f7d210bc00b71 Mon Sep 17 00:00:00 2001 From: cptpcrd <31829097+cptpcrd@users.noreply.github.com> Date: Sun, 21 May 2023 12:10:44 -0400 Subject: [PATCH 145/217] Open command pipes as close-on-exec Avoids a race where the pipe could be inherited by another process spawning at about the same time. If the other process didn't exit quickly (e.g. if it was a custom script that did its own looping), it would keep the write end of the pipe open, and so reading from the pipe to try to get the command's output would block. This bug manifested as some custom modules randomly not appearing in the bar, requiring a reload to fix. The custom script had run and exited, but the pipe had been inherited by another process, and the thread that updated the module's output was blocked trying to read from it. --- include/util/command.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/util/command.hpp b/include/util/command.hpp index c9f238c..eff9581 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef __linux__ #include @@ -68,7 +69,11 @@ inline int close(FILE* fp, pid_t pid) { inline FILE* open(const std::string& cmd, int& pid) { if (cmd == "") return nullptr; int fd[2]; - if (pipe(fd) != 0) { + // Open the pipe with the close-on-exec flag set, so it will not be inherited + // by any other subprocesses launched by other threads (which could result in + // the pipe staying open after this child dies, causing us to hang when trying + // to read from it) + if (pipe2(fd, O_CLOEXEC) != 0) { spdlog::error("Unable to pipe fd"); return nullptr; } From 6163be687d00496c6144f25da290dc59c24bd58f Mon Sep 17 00:00:00 2001 From: cptpcrd <31829097+cptpcrd@users.noreply.github.com> Date: Sun, 21 May 2023 12:13:17 -0400 Subject: [PATCH 146/217] Open network module eventfd as close-on-exec Ensures that it is not leaked to child processes. --- 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 a61edd5..ef8b2bd 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -188,7 +188,7 @@ void waybar::modules::Network::createEventSocket() { throw std::runtime_error("Can't create epoll"); } { - ev_fd_ = eventfd(0, EFD_NONBLOCK); + ev_fd_ = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC); struct epoll_event event; memset(&event, 0, sizeof(event)); event.events = EPOLLIN | EPOLLET; From 5cbbfd5c8a4737411c57db61073aa919d884e37d Mon Sep 17 00:00:00 2001 From: cptpcrd <31829097+cptpcrd@users.noreply.github.com> Date: Sun, 21 May 2023 12:15:49 -0400 Subject: [PATCH 147/217] Close pipe if fork() fails when spawning processes Prevents potential file descriptor leakage, albeit in a bit of an edge case. --- include/util/command.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/util/command.hpp b/include/util/command.hpp index eff9581..fe0543d 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -82,6 +82,8 @@ inline FILE* open(const std::string& cmd, int& pid) { if (child_pid < 0) { spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno)); + ::close(fd[0]); + ::close(fd[1]); return nullptr; } From 75eacb95ef8448b094a1b89d4ba39bf8fdbd72de Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Mon, 22 May 2023 10:02:14 -0400 Subject: [PATCH 148/217] Fix SEGFAULT in battery module In waybar::modules::Battery::~Battery(), store a copy of the batteries_ iterator before calling erase(), as erase() invalidates the iterator. Prior to this change, disconnecting outputs resulted in a SEGFAULT when using the battery module; e.g., [debug] Received SIGCHLD in signalThread [debug] Cmd exited with code 0 [debug] Received SIGCHLD in signalThread [debug] Cmd exited with code 0 [debug] Received SIGCHLD in signalThread [debug] Cmd exited with code 0 [debug] Output removed: AU Optronics 0x2336 [info] Bar configured (width: 1280, height: 25) for output: eDP-1 [info] Bar configured (width: 1280, height: 25) for output: eDP-1 zsh: segmentation fault (core dumped) ./build/waybar -l trace Signed-off-by: Lukas Fleischer --- 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 abd1240..c0f433a 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -36,7 +36,8 @@ waybar::modules::Battery::~Battery() { } close(global_watch_fd_); - for (auto it = batteries_.cbegin(); it != batteries_.cend(); it++) { + for (auto it = batteries_.cbegin(), next_it = it; it != batteries_.cend(); it = next_it) { + ++next_it; auto watch_id = (*it).second; if (watch_id >= 0) { inotify_rm_watch(battery_watch_fd_, watch_id); From 7e9bfc504ca2f49f29f545b6efd57a30af6ddddd Mon Sep 17 00:00:00 2001 From: David Delarosa Date: Thu, 25 May 2023 00:27:50 +0300 Subject: [PATCH 149/217] Update to new ipc version --- include/modules/dwl/tags.hpp | 6 +- ...nstable-v1.xml => dwl-ipc-unstable-v2.xml} | 57 +++++++++---------- protocol/meson.build | 2 +- src/modules/dwl/tags.cpp | 47 ++++++++------- 4 files changed, 55 insertions(+), 57 deletions(-) rename protocol/{dwl-bar-ipc-unstable-v1.xml => dwl-ipc-unstable-v2.xml} (78%) diff --git a/include/modules/dwl/tags.hpp b/include/modules/dwl/tags.hpp index 6e6d086..53dff98 100644 --- a/include/modules/dwl/tags.hpp +++ b/include/modules/dwl/tags.hpp @@ -5,7 +5,7 @@ #include "AModule.hpp" #include "bar.hpp" -#include "dwl-bar-ipc-unstable-v1-client-protocol.h" +#include "dwl-ipc-unstable-v2-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" namespace waybar::modules::dwl { @@ -21,14 +21,14 @@ class Tags : public waybar::AModule { void handle_primary_clicked(uint32_t tag); bool handle_button_press(GdkEventButton *event_button, uint32_t tag); - struct zdwl_manager_v1 *status_manager_; + struct zdwl_ipc_manager_v2 *status_manager_; struct wl_seat *seat_; private: const waybar::Bar &bar_; Gtk::Box box_; std::vector buttons_; - struct zdwl_output_v1 *output_status_; + struct zdwl_ipc_output_v2 *output_status_; }; } /* namespace waybar::modules::dwl */ diff --git a/protocol/dwl-bar-ipc-unstable-v1.xml b/protocol/dwl-ipc-unstable-v2.xml similarity index 78% rename from protocol/dwl-bar-ipc-unstable-v1.xml rename to protocol/dwl-ipc-unstable-v2.xml index 1b614b2..74a212f 100644 --- a/protocol/dwl-bar-ipc-unstable-v1.xml +++ b/protocol/dwl-ipc-unstable-v2.xml @@ -3,9 +3,9 @@ This is largely ripped from somebar's ipc patchset; just with some personal modifications. I would probably just submit raphi's patchset but I don't think that would be polite. --> - + - This protocol allows clients to get updates from dwl and vice versa. + This protocol allows clients to update and get updates from dwl. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible @@ -19,36 +19,36 @@ I would probably just submit raphi's patchset but I don't think that would be po reset. - + This interface is exposed as a global in wl_registry. - Clients can use this interface to get a dwl_output. - After binding the client will revieve dwl_manager.tag and dwl_manager.layout events. - The dwl_manager.tag and dwl_manager.layout events expose tags and layouts to the client. + Clients can use this interface to get a dwl_ipc_output. + After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. + The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. - - Indicates that the client will not the dwl_manager object anymore. + + Indicates that the client will not the dwl_ipc_manager object anymore. Objects created through this instance are not affected. - - Get a dwl_output for the specified wl_output. + + Get a dwl_ipc_outout for the specified wl_output. - + - - + + This event is sent after binding. A roundtrip after binding guarantees the client recieved all tags. - + @@ -60,12 +60,12 @@ I would probably just submit raphi's patchset but I don't think that would be po - + Observe and control a dwl output. Events are double-buffered: - Clients should cache events and redraw when a dwl_output.done event is sent. + Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. Request are not double-buffered: The compositor will update immediately upon request. @@ -78,8 +78,8 @@ I would probably just submit raphi's patchset but I don't think that would be po - - Indicates to that the client no longer needs this dwl_output. + + Indicates to that the client no longer needs this dwl_ipc_output. @@ -121,34 +121,28 @@ I would probably just submit raphi's patchset but I don't think that would be po - + Indicates the appid has changed. - + - Indicates the layout has changed. Since layout symbols are now dynamic. - As opposed to the zdwl_manager_v1.layout event, this should take precendence when displaying. - This also means ignoring the zdwl_output_v1.layout event. + Indicates the layout has changed. Since layout symbols are dynamic. + As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. + You can ignore the zdwl_ipc_output.layout event. - Indicates that a sequence of status updates have finished and the client should redraw. - - - - - @@ -163,5 +157,10 @@ I would probably just submit raphi's patchset but I don't think that would be po + + + + + diff --git a/protocol/meson.build b/protocol/meson.build index 9c1b019..e1e745a 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -30,7 +30,7 @@ client_protocols = [ ['ext-workspace-unstable-v1.xml'], ['river-status-unstable-v1.xml'], ['river-control-unstable-v1.xml'], - ['dwl-bar-ipc-unstable-v1.xml'], + ['dwl-ipc-unstable-v2.xml'], ] client_protos_src = [] diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index fa64099..6d40331 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -8,7 +8,7 @@ #include #include "client.hpp" -#include "dwl-bar-ipc-unstable-v1-client-protocol.h" +#include "dwl-ipc-unstable-v2-client-protocol.h" #define TAG_INACTIVE 0 #define TAG_ACTIVE 1 @@ -21,43 +21,42 @@ wl_array tags, layouts; static uint num_tags = 0; -void toggle_visibility(void *data, zdwl_output_v1 *zdwl_output_v1) { + +void toggle_visibility(void* data, zdwl_ipc_output_v2* zdwl_output_v2) { // Intentionally empty } -void active(void *data, zdwl_output_v1 *zdwl_output_v1, uint32_t active) { +void active(void* data, zdwl_ipc_output_v2* zdwl_output_v2, uint32_t active) { // Intentionally empty } -static void set_tag(void *data, zdwl_output_v1 *zdwl_output_v1, uint32_t tag, uint32_t state, - uint32_t clients, uint32_t focused) { +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) { static_cast(data)->handle_view_tags(tag, state, clients, focused); - num_tags = - (state & ZDWL_OUTPUT_V1_TAG_STATE_ACTIVE) ? num_tags | (1 << tag) : num_tags & ~(1 << tag); + num_tags = (state & ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE) ? num_tags | (1 << tag) : num_tags & ~(1 << tag); } -void set_layout_symbol(void *data, zdwl_output_v1 *zdwl_output_v1, const char *layout) { +void set_layout_symbol(void* data, zdwl_ipc_output_v2* zdwl_output_v2, const char *layout) { // Intentionally empty } -void title(void *data, zdwl_output_v1 *zdwl_output_v1, const char *title) { +void title(void* data, zdwl_ipc_output_v2* zdwl_output_v2, const char* title) { // Intentionally empty } -void dwl_frame(void *data, zdwl_output_v1 *zdwl_output_v1) { +void dwl_frame(void* data, zdwl_ipc_output_v2* zdwl_output_v2) { // Intentionally empty } -static void set_layout(void *data, zdwl_output_v1 *zdwl_output_v1, uint32_t layout) { +static void set_layout(void* data, zdwl_ipc_output_v2* zdwl_output_v2, uint32_t layout) { // Intentionally empty } -static void appid(void *data, zdwl_output_v1 *zdwl_output_v1, 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_output_v1_listener output_status_listener_impl{ +static const zdwl_ipc_output_v2_listener output_status_listener_impl { .toggle_visibility = toggle_visibility, .active = active, .tag = set_tag, @@ -70,9 +69,9 @@ static const zdwl_output_v1_listener output_status_listener_impl{ static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - if (std::strcmp(interface, zdwl_manager_v1_interface.name) == 0) { - static_cast(data)->status_manager_ = static_cast( - (zdwl_manager_v1 *)wl_registry_bind(registry, name, &zdwl_manager_v1_interface, 3)); + 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, 3)); } if (std::strcmp(interface, wl_seat_interface.name) == 0) { version = std::min(version, 1); @@ -101,7 +100,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con wl_display_roundtrip(display); if (!status_manager_) { - spdlog::error("dwl_status_manager_v1 not advertised"); + spdlog::error("dwl_status_manager_v2 not advertised"); return; } @@ -146,29 +145,29 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con } struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); - output_status_ = zdwl_manager_v1_get_output(status_manager_, output); - zdwl_output_v1_add_listener(output_status_, &output_status_listener_impl, this); + 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_manager_v1_destroy(status_manager_); + zdwl_ipc_manager_v2_destroy(status_manager_); status_manager_ = nullptr; } Tags::~Tags() { if (status_manager_) { - zdwl_manager_v1_destroy(status_manager_); + zdwl_ipc_manager_v2_destroy(status_manager_); } } void Tags::handle_primary_clicked(uint32_t tag) { if (!output_status_) return; - zdwl_output_v1_set_tags(output_status_, tag, 1); + zdwl_ipc_output_v2_set_tags(output_status_, tag, 1); } bool Tags::handle_button_press(GdkEventButton *event_button, uint32_t tag) { if (event_button->type == GDK_BUTTON_PRESS && event_button->button == 3) { if (!output_status_) return true; - zdwl_output_v1_set_tags(output_status_, num_tags ^ tag, 0); + zdwl_ipc_output_v2_set_tags(output_status_, num_tags ^ tag, 0); } return true; } From b97b0ae605bdeb7430ac420c4360162a3b95cc5d Mon Sep 17 00:00:00 2001 From: David Delarosa Date: Thu, 25 May 2023 01:25:55 +0300 Subject: [PATCH 150/217] Fix linter complains --- 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 6d40331..02744f9 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -71,7 +71,7 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam 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, 3)); + (zdwl_ipc_manager_v2 *)wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 3)); } if (std::strcmp(interface, wl_seat_interface.name) == 0) { version = std::min(version, 1); From ebba529682e9c1b139d7d8fb6ec7eef0e0c989c2 Mon Sep 17 00:00:00 2001 From: David Delarosa Date: Thu, 25 May 2023 01:29:28 +0300 Subject: [PATCH 151/217] Apply clang-format --- src/modules/dwl/tags.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index 02744f9..6a3d4ee 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -21,42 +21,43 @@ wl_array tags, layouts; static uint num_tags = 0; - -void toggle_visibility(void* data, zdwl_ipc_output_v2* zdwl_output_v2) { +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) { +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) { +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) { static_cast(data)->handle_view_tags(tag, state, clients, focused); - num_tags = (state & ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE) ? num_tags | (1 << tag) : num_tags & ~(1 << tag); + num_tags = (state & ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE) ? num_tags | (1 << tag) + : num_tags & ~(1 << tag); } -void set_layout_symbol(void* data, zdwl_ipc_output_v2* zdwl_output_v2, const char *layout) { +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) { +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) { +void dwl_frame(void *data, zdwl_ipc_output_v2 *zdwl_output_v2) { // Intentionally empty } -static void set_layout(void* data, zdwl_ipc_output_v2* zdwl_output_v2, uint32_t layout) { +static void set_layout(void *data, zdwl_ipc_output_v2 *zdwl_output_v2, uint32_t layout) { // 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 { +static const zdwl_ipc_output_v2_listener output_status_listener_impl{ .toggle_visibility = toggle_visibility, .active = active, .tag = set_tag, From 097cbc0c53c33afa41e226f8bb18906c22c03002 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 29 May 2023 09:24:33 +0200 Subject: [PATCH 152/217] fix: lint --- include/util/command.hpp | 2 +- src/modules/network.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/util/command.hpp b/include/util/command.hpp index fe0543d..0d729b7 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -1,10 +1,10 @@ #pragma once +#include #include #include #include #include -#include #ifdef __linux__ #include diff --git a/src/modules/network.cpp b/src/modules/network.cpp index ef8b2bd..5eef166 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -188,7 +188,7 @@ void waybar::modules::Network::createEventSocket() { throw std::runtime_error("Can't create epoll"); } { - ev_fd_ = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC); + ev_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); struct epoll_event event; memset(&event, 0, sizeof(event)); event.events = EPOLLIN | EPOLLET; From 47193a3d2f81a8ce7177449f92e927db74d873b0 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 29 May 2023 09:25:01 +0200 Subject: [PATCH 153/217] chore: v0.9.18 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index c874e19..beee053 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.17', + version: '0.9.18', license: 'MIT', meson_version: '>= 0.50.0', default_options : [ From a3912436be3aac10f97e01dcccd348e0533d8088 Mon Sep 17 00:00:00 2001 From: Calvin Chu Date: Thu, 1 Jun 2023 21:38:27 +1000 Subject: [PATCH 154/217] hyprland/window: add css class for empty label --- src/modules/hyprland/window.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 40b6620..67631d5 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -92,6 +92,11 @@ void Window::onEvent(const std::string& ev) { if (windowName == lastView) return; lastView = windowName; + if (windowName[0] == '\0') { + label_.get_style_context()->add_class("empty"); + } else { + label_.get_style_context()->remove_class("empty"); + } spdlog::debug("hyprland window onevent with {}", windowName); From 655bc8f215f0dae727e05e683f8d5f9dd439e797 Mon Sep 17 00:00:00 2001 From: Visne <39844191+Visne@users.noreply.github.com> Date: Thu, 1 Jun 2023 22:08:54 +0200 Subject: [PATCH 155/217] Fix broken link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b6ecd96..718ceb4 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/community/x86_64/waybar/) or +> Available in Arch [community](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 17a56aa4f7a5058868a7dba954ff6cb32560648b Mon Sep 17 00:00:00 2001 From: Calvin Chu Date: Sat, 3 Jun 2023 12:29:36 +1000 Subject: [PATCH 156/217] hyprland/window: use empty() for empty window name check --- src/modules/hyprland/window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 67631d5..714d0a7 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -92,7 +92,8 @@ void Window::onEvent(const std::string& ev) { if (windowName == lastView) return; lastView = windowName; - if (windowName[0] == '\0') { + + if (windowName.empty()) { label_.get_style_context()->add_class("empty"); } else { label_.get_style_context()->remove_class("empty"); From d2787cc2d8484cf4eb532a60a9bb0980aada7f85 Mon Sep 17 00:00:00 2001 From: dejor <71755747+dejor@users.noreply.github.com> Date: Sun, 4 Jun 2023 12:45:33 +0200 Subject: [PATCH 157/217] fix: typo to avoid null pointer --- 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 6a3d4ee..7faa5c5 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -72,7 +72,7 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam 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, 3)); + (zdwl_ipc_manager_v2 *)wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 1)); } if (std::strcmp(interface, wl_seat_interface.name) == 0) { version = std::min(version, 1); From ff6f7276318e71a9537d600f281470014ae9629d Mon Sep 17 00:00:00 2001 From: Matias-Hall <55637804+Matias-Hall@users.noreply.github.com> Date: Mon, 5 Jun 2023 18:29:19 +0800 Subject: [PATCH 158/217] river/window: Add tooltip. --- src/modules/river/window.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/river/window.cpp b/src/modules/river/window.cpp index d93938c..dc7a645 100644 --- a/src/modules/river/window.cpp +++ b/src/modules/river/window.cpp @@ -106,7 +106,11 @@ void Window::handle_focused_view(const char *title) { label_.hide(); // hide empty labels or labels with empty format } else { label_.show(); - label_.set_markup(fmt::format(fmt::runtime(format_), Glib::Markup::escape_text(title).raw())); + auto text = fmt::format(fmt::runtime(format_), Glib::Markup::escape_text(title).raw()); + label_.set_markup(text); + if (tooltipEnabled()) { + label_.set_tooltip_markup(text); + } } ALabel::update(); From b728a37b6dc7bc2c2846737373429dbf3f538094 Mon Sep 17 00:00:00 2001 From: D3vil0p3r Date: Mon, 5 Jun 2023 16:28:32 +0200 Subject: [PATCH 159/217] Fixing 'incomplete type error' during building Fixing errors during the building due to missing library after latest GCC updates. --- include/modules/sway/ipc/client.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/modules/sway/ipc/client.hpp b/include/modules/sway/ipc/client.hpp index a6705ea..1ab0292 100644 --- a/include/modules/sway/ipc/client.hpp +++ b/include/modules/sway/ipc/client.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include From 88a1a702b4598669bc6807c4f4454283f23ef27d Mon Sep 17 00:00:00 2001 From: Evyatar Stalinsky Date: Mon, 5 Jun 2023 22:03:46 +0300 Subject: [PATCH 160/217] wireplumber: Support for scrolling --- include/modules/wireplumber.hpp | 3 ++ src/modules/wireplumber.cpp | 63 +++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/include/modules/wireplumber.hpp b/include/modules/wireplumber.hpp index 5cf462e..81b2e68 100644 --- a/include/modules/wireplumber.hpp +++ b/include/modules/wireplumber.hpp @@ -27,6 +27,8 @@ class Wireplumber : public ALabel { static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id); static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self); + bool handleScroll(GdkEventScroll* e) override; + WpCore* wp_core_; GPtrArray* apis_; WpObjectManager* om_; @@ -36,6 +38,7 @@ class Wireplumber : public ALabel { uint32_t pending_plugins_; bool muted_; double volume_; + double step_; uint32_t node_id_{0}; std::string node_name_; }; diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index 0c4848a..cfdbccc 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -15,6 +15,7 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val pending_plugins_(0), muted_(false), volume_(0.0), + step_(0.0), node_id_(0) { wp_init(WP_INIT_PIPEWIRE); wp_core_ = wp_core_new(NULL, NULL); @@ -39,6 +40,9 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val 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)); } waybar::modules::Wireplumber::~Wireplumber() { @@ -83,7 +87,6 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self, uint32_t id) { spdlog::debug("[{}]: updating volume", self->name_); - double vol; GVariant* variant = NULL; if (!isValidNodeId(id)) { @@ -99,11 +102,11 @@ void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* se throw std::runtime_error(err); } - g_variant_lookup(variant, "volume", "d", &vol); + g_variant_lookup(variant, "volume", "d", &self->volume_); + g_variant_lookup(variant, "step", "d", &self->step_); g_variant_lookup(variant, "mute", "b", &self->muted_); g_clear_pointer(&variant, g_variant_unref); - self->volume_ = std::round(vol * 100.0F); self->dp.emit(); } @@ -280,11 +283,12 @@ auto waybar::modules::Wireplumber::update() -> void { label_.get_style_context()->remove_class("muted"); } + int vol = volume_ * 100.0; std::string markup = fmt::format(fmt::runtime(format), fmt::arg("node_name", node_name_), - fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_))); + fmt::arg("volume", vol), fmt::arg("icon", getIcon(vol))); label_.set_markup(markup); - getState(volume_); + getState(vol); if (tooltipEnabled()) { if (tooltip_format.empty() && config_["tooltip-format"].isString()) { @@ -294,7 +298,7 @@ auto waybar::modules::Wireplumber::update() -> void { if (!tooltip_format.empty()) { label_.set_tooltip_text( fmt::format(fmt::runtime(tooltip_format), fmt::arg("node_name", node_name_), - fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_)))); + fmt::arg("volume", vol), fmt::arg("icon", getIcon(vol)))); } else { label_.set_tooltip_text(node_name_); } @@ -303,3 +307,50 @@ auto waybar::modules::Wireplumber::update() -> void { // Call parent update ALabel::update(); } + +bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll *e) { + if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { + return AModule::handleScroll(e); + } + auto dir = AModule::getScrollDir(e); + if (dir == SCROLL_DIR::NONE) { + return true; + } + 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 = step_; + // isDouble returns true for integers as well, just in case + if (config_["scroll-step"].isDouble()) { + step = config_["scroll-step"].asDouble() / 100.0; + } + if (config_["max-volume"].isInt()) { + max_volume = config_["max-volume"].asInt() / 100.0; + } + + double new_vol = volume_; + if (dir == SCROLL_DIR::UP) { + if (volume_ < max_volume) { + new_vol = volume_ + step; + if (new_vol > max_volume) + new_vol = max_volume; + } + } else if (dir == SCROLL_DIR::DOWN) { + if (volume_ > 0) { + new_vol = volume_ - step; + if (new_vol < 0) + new_vol = 0; + } + } + if (new_vol != volume_) { + GVariant* variant = g_variant_new_double(new_vol); + gboolean ret; + g_signal_emit_by_name(mixer_api_, "set-volume", node_id_, variant, &ret); + } + return true; +} From 75990c2867717b5d734cb348152c5cef53fc99bd Mon Sep 17 00:00:00 2001 From: Evyatar Stalinsky Date: Mon, 5 Jun 2023 22:22:35 +0300 Subject: [PATCH 161/217] Fix linting --- src/modules/wireplumber.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index cfdbccc..1d8d1f8 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -296,9 +296,9 @@ auto waybar::modules::Wireplumber::update() -> void { } if (!tooltip_format.empty()) { - label_.set_tooltip_text( - fmt::format(fmt::runtime(tooltip_format), fmt::arg("node_name", node_name_), - fmt::arg("volume", vol), fmt::arg("icon", getIcon(vol)))); + label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), + fmt::arg("node_name", node_name_), + fmt::arg("volume", vol), fmt::arg("icon", getIcon(vol)))); } else { label_.set_tooltip_text(node_name_); } @@ -308,7 +308,7 @@ auto waybar::modules::Wireplumber::update() -> void { ALabel::update(); } -bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll *e) { +bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) { if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { return AModule::handleScroll(e); } @@ -337,14 +337,12 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll *e) { if (dir == SCROLL_DIR::UP) { if (volume_ < max_volume) { new_vol = volume_ + step; - if (new_vol > max_volume) - new_vol = max_volume; + if (new_vol > max_volume) new_vol = max_volume; } } else if (dir == SCROLL_DIR::DOWN) { if (volume_ > 0) { new_vol = volume_ - step; - if (new_vol < 0) - new_vol = 0; + if (new_vol < 0) new_vol = 0; } } if (new_vol != volume_) { From d22fd3bbd1b09809cc025ca6b5b50015fba8acbd Mon Sep 17 00:00:00 2001 From: Evyatar Stalinsky Date: Tue, 6 Jun 2023 11:42:02 +0300 Subject: [PATCH 162/217] Use a minimum step as provided by wireplubmer; Default step to 1 --- include/modules/wireplumber.hpp | 2 +- src/modules/wireplumber.cpp | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/modules/wireplumber.hpp b/include/modules/wireplumber.hpp index 81b2e68..9bbf4d4 100644 --- a/include/modules/wireplumber.hpp +++ b/include/modules/wireplumber.hpp @@ -38,7 +38,7 @@ class Wireplumber : public ALabel { uint32_t pending_plugins_; bool muted_; double volume_; - double step_; + double min_step_; uint32_t node_id_{0}; std::string node_name_; }; diff --git a/src/modules/wireplumber.cpp b/src/modules/wireplumber.cpp index 1d8d1f8..cd02d42 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -15,7 +15,7 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val pending_plugins_(0), muted_(false), volume_(0.0), - step_(0.0), + min_step_(0.0), node_id_(0) { wp_init(WP_INIT_PIPEWIRE); wp_core_ = wp_core_new(NULL, NULL); @@ -103,7 +103,7 @@ void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* se } g_variant_lookup(variant, "volume", "d", &self->volume_); - g_variant_lookup(variant, "step", "d", &self->step_); + g_variant_lookup(variant, "step", "d", &self->min_step_); g_variant_lookup(variant, "mute", "b", &self->muted_); g_clear_pointer(&variant, g_variant_unref); @@ -324,15 +324,16 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) { } } double max_volume = 1; - double step = step_; - // isDouble returns true for integers as well, just in case + double step = 1.0 / 100.0; if (config_["scroll-step"].isDouble()) { step = config_["scroll-step"].asDouble() / 100.0; } - if (config_["max-volume"].isInt()) { - max_volume = config_["max-volume"].asInt() / 100.0; + if (config_["max-volume"].isDouble()) { + max_volume = config_["max-volume"].asDouble() / 100.0; } + if (step < min_step_) step = min_step_; + double new_vol = volume_; if (dir == SCROLL_DIR::UP) { if (volume_ < max_volume) { From e397f568b736d169eefda3913224dfe42f148f61 Mon Sep 17 00:00:00 2001 From: Evyatar Stalinsky Date: Tue, 6 Jun 2023 11:42:31 +0300 Subject: [PATCH 163/217] Round volume instead of truncating it --- 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 cd02d42..b2d9b39 100644 --- a/src/modules/wireplumber.cpp +++ b/src/modules/wireplumber.cpp @@ -283,7 +283,7 @@ auto waybar::modules::Wireplumber::update() -> void { label_.get_style_context()->remove_class("muted"); } - int vol = volume_ * 100.0; + int vol = round(volume_ * 100.0); std::string markup = fmt::format(fmt::runtime(format), fmt::arg("node_name", node_name_), fmt::arg("volume", vol), fmt::arg("icon", getIcon(vol))); label_.set_markup(markup); From d61b1d54de65293033f975aa4ecd08efe870f391 Mon Sep 17 00:00:00 2001 From: Evyatar Stalinsky Date: Tue, 6 Jun 2023 11:44:17 +0300 Subject: [PATCH 164/217] Document new wireplumber module functionality --- man/waybar-wireplumber.5.scd | 76 ++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/man/waybar-wireplumber.5.scd b/man/waybar-wireplumber.5.scd index 6088377..473df92 100644 --- a/man/waybar-wireplumber.5.scd +++ b/man/waybar-wireplumber.5.scd @@ -11,59 +11,77 @@ The *wireplumber* module displays the current volume reported by WirePlumber. # CONFIGURATION *format*: ++ - typeof: string ++ - default: *{volume}%* ++ - The format, how information should be displayed. This format is used when other formats aren't specified. + typeof: string ++ + default: *{volume}%* ++ + The format, how information should be displayed. This format is used when other formats aren't specified. *format-muted*: ++ - typeof: string ++ - This format is used when the sound is muted. + typeof: string ++ + This format is used when the sound is muted. *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: *{node_name}* ++ - The format of information displayed in the tooltip. + typeof: string ++ + default: *{node_name}* ++ + The format of information displayed in the tooltip. *rotate*: ++ - typeof: integer ++ - Positive value to rotate the text label. + typeof: integer ++ + Positive value to rotate the text label. *states*: ++ - typeof: object ++ - A number of volume states which get activated on certain volume levels. See *waybar-states(5)*. + typeof: object ++ + A number of volume states which get activated on certain volume levels. See *waybar-states(5)*. *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. + +*scroll-step*: ++ + typeof: float ++ + default: 1.0 ++ + The speed in which to change the volume when scrolling. *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. This replaces the default behaviour of volume control. + +*on-scroll-down*: ++ + typeof: string ++ + Command to execute when scrolling down on the module. This replaces the default behaviour of volume control. + +*max-volume*: ++ + typeof: float ++ + default: 100 ++ + The maximum volume that can be set, in percentage. # FORMAT REPLACEMENTS From e96610e31a7f1b788740a32f61db1b184027db0e Mon Sep 17 00:00:00 2001 From: Evyatar Stalinsky Date: Tue, 6 Jun 2023 12:01:25 +0300 Subject: [PATCH 165/217] Use consistent tabbing --- man/waybar-pulseaudio.5.scd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/man/waybar-pulseaudio.5.scd b/man/waybar-pulseaudio.5.scd index b941c22..655fbc4 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -51,12 +51,12 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu 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: float ++ From 87023c39f80142e43ce7f98b7cb5fbb3becd2608 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 6 Jun 2023 13:58:05 +0300 Subject: [PATCH 166/217] Small performance fixies Signed-off-by: Viktar Lukashonak --- src/modules/cava.cpp | 1 - src/modules/wlr/workspace_manager.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/cava.cpp b/src/modules/cava.cpp index 8e788f5..be9bef4 100644 --- a/src/modules/cava.cpp +++ b/src/modules/cava.cpp @@ -102,7 +102,6 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config) thread_fetch_input_ = [this] { thread_fetch_input_.sleep_for(fetch_input_delay_); input_source_(&audio_data_); - dp.emit(); }; thread_ = [this] { diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 0b1dc72..2d4c93b 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -73,7 +73,7 @@ auto WorkspaceManager::workspace_comparator() const try { auto is_number_less = std::stoi(lhs->get_name()) < std::stoi(rhs->get_name()); return is_number_less; - } catch (std::invalid_argument) { + } catch (const std::invalid_argument &) { } } From 3af1853260dafc43c992fc2357a3f3bace3bccaa Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 6 Jun 2023 23:02:36 +0300 Subject: [PATCH 167/217] Tray module cause error g_bus_unwatch_name() Signed-off-by: Viktar Lukashonak --- src/modules/sni/host.cpp | 4 ---- src/modules/sni/watcher.cpp | 7 +------ 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/modules/sni/host.cpp b/src/modules/sni/host.cpp index 007862d..fff8e01 100644 --- a/src/modules/sni/host.cpp +++ b/src/modules/sni/host.cpp @@ -22,10 +22,6 @@ Host::~Host() { Gio::DBus::unwatch_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 34bcef6..6881677 100644 --- a/src/modules/sni/watcher.cpp +++ b/src/modules/sni/watcher.cpp @@ -14,12 +14,7 @@ Watcher::Watcher() watcher_(sn_watcher_skeleton_new()) {} Watcher::~Watcher() { - if (hosts_ != nullptr) { - g_slist_free_full(hosts_, gfWatchFree); - hosts_ = nullptr; - } - - if (items_ != nullptr) { + if (items_ != nullptr) { g_slist_free_full(items_, gfWatchFree); items_ = nullptr; } From a9779c2aa2099249f3d329e3a2c226703c4b5577 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 6 Jun 2023 23:06:11 +0300 Subject: [PATCH 168/217] Happy Linter Signed-off-by: Viktar Lukashonak --- src/modules/sni/watcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sni/watcher.cpp b/src/modules/sni/watcher.cpp index 6881677..663fdcd 100644 --- a/src/modules/sni/watcher.cpp +++ b/src/modules/sni/watcher.cpp @@ -14,7 +14,7 @@ Watcher::Watcher() watcher_(sn_watcher_skeleton_new()) {} Watcher::~Watcher() { - if (items_ != nullptr) { + if (items_ != nullptr) { g_slist_free_full(items_, gfWatchFree); items_ = nullptr; } From 6bf5b15c13878f66e4c311ac6f816bf8102ca0b0 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 6 Jun 2023 23:31:12 +0300 Subject: [PATCH 169/217] deprecated: implicit capture of 'this' via '[=]' Signed-off-by: Viktar Lukashonak --- src/modules/wlr/workspace_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 2d4c93b..8933d69 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -64,7 +64,7 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar auto WorkspaceManager::workspace_comparator() const -> std::function &, std::unique_ptr &)> { - return [=](std::unique_ptr &lhs, std::unique_ptr &rhs) { + return [=, this](std::unique_ptr &lhs, std::unique_ptr &rhs) { auto is_name_less = lhs->get_name() < rhs->get_name(); auto is_name_eq = lhs->get_name() == rhs->get_name(); auto is_coords_less = lhs->get_coords() < rhs->get_coords(); From 3c9cbc99d73638c7c3929de86c17d188438975b0 Mon Sep 17 00:00:00 2001 From: Patrick Nicolas Date: Wed, 7 Jun 2023 10:17:42 +0200 Subject: [PATCH 170/217] Wake all sleeping threads when leaving suspend std::condition_variable::wait_for does not count time spent in sleep mode, resulting in longer than expected waits. --- include/util/prepare_for_sleep.h | 9 ++++++ include/util/sleeper_thread.hpp | 16 +++++++++-- meson.build | 1 + src/util/prepare_for_sleep.cpp | 49 ++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 include/util/prepare_for_sleep.h create mode 100644 src/util/prepare_for_sleep.cpp diff --git a/include/util/prepare_for_sleep.h b/include/util/prepare_for_sleep.h new file mode 100644 index 0000000..68db8d8 --- /dev/null +++ b/include/util/prepare_for_sleep.h @@ -0,0 +1,9 @@ +#pragma once + +#include "SafeSignal.hpp" + +namespace waybar::util { + +// Get a signal emited with value true when entering sleep, and false when exiting +SafeSignal& prepare_for_sleep(); +} // namespace waybar::util diff --git a/include/util/sleeper_thread.hpp b/include/util/sleeper_thread.hpp index e12287a..a724b1e 100644 --- a/include/util/sleeper_thread.hpp +++ b/include/util/sleeper_thread.hpp @@ -6,6 +6,8 @@ #include #include +#include "prepare_for_sleep.h" + namespace waybar::util { /** @@ -33,7 +35,11 @@ class SleeperThread { signal_ = false; func(); } - }} {} + }} { + connection_ = prepare_for_sleep().connect([this](bool sleep) { + if (not sleep) wake_up(); + }); + } SleeperThread& operator=(std::function func) { thread_ = std::thread([this, func] { @@ -42,6 +48,11 @@ class SleeperThread { func(); } }); + if (connection_.empty()) { + connection_ = prepare_for_sleep().connect([this](bool sleep) { + if (not sleep) wake_up(); + }); + } return *this; } @@ -61,7 +72,7 @@ class SleeperThread { return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; }); } - auto wake_up() { + void wake_up() { { std::lock_guard lck(mutex_); signal_ = true; @@ -96,6 +107,7 @@ class SleeperThread { std::mutex mutex_; bool do_run_ = true; bool signal_ = false; + sigc::connection connection_; }; } // namespace waybar::util diff --git a/meson.build b/meson.build index beee053..0e42def 100644 --- a/meson.build +++ b/meson.build @@ -170,6 +170,7 @@ src_files = files( 'src/client.cpp', 'src/config.cpp', 'src/group.cpp', + 'src/util/prepare_for_sleep.cpp', 'src/util/ustring_clen.cpp', 'src/util/sanitize_str.cpp', 'src/util/rewrite_string.cpp' diff --git a/src/util/prepare_for_sleep.cpp b/src/util/prepare_for_sleep.cpp new file mode 100644 index 0000000..221497e --- /dev/null +++ b/src/util/prepare_for_sleep.cpp @@ -0,0 +1,49 @@ +#include "util/prepare_for_sleep.h" + +#include + +namespace { +class PrepareForSleep { + private: + 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!..."); + } 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); + } + } + + 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) { + if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)"))) { + gboolean sleeping; + g_variant_get(parameters, "(b)", &sleeping); + + PrepareForSleep *self = static_cast(user_data); + self->signal.emit(sleeping); + } + } + + public: + static PrepareForSleep &GetInstance() { + static PrepareForSleep instance; + return instance; + } + waybar::SafeSignal signal; + + private: + guint login1_id; + GDBusConnection *login1_connection; +}; +} // namespace + +waybar::SafeSignal &waybar::util::prepare_for_sleep() { + return PrepareForSleep::GetInstance().signal; +} From a67e692d4ac491849d93e6b05aea19b19c662dc9 Mon Sep 17 00:00:00 2001 From: sigboe Date: Wed, 7 Jun 2023 15:25:59 +0200 Subject: [PATCH 171/217] sway: warp-on-scroll toggle --- man/waybar-sway-workspaces.5.scd | 5 +++++ resources/config | 1 + src/modules/sway/workspaces.cpp | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 644cba4..50e61b7 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -77,6 +77,11 @@ Addressed by *sway/workspaces* 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. + # FORMAT REPLACEMENTS *{value}*: Name of the workspace, as defined by sway. diff --git a/resources/config b/resources/config index ad76e93..daad8ab 100644 --- a/resources/config +++ b/resources/config @@ -12,6 +12,7 @@ // "sway/workspaces": { // "disable-scroll": true, // "all-outputs": true, + // "warp-on-scroll": false, // "format": "{name}: {icon}", // "format-icons": { // "1": "", diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 2bf0247..564a023 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -326,11 +326,17 @@ bool Workspaces::handleScroll(GdkEventScroll *e) { return true; } } + if (!config_["warp-on-scroll"].asBool()) { + 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)); } catch (const std::exception &e) { spdlog::error("Workspaces: {}", e.what()); } + if (!config_["warp-on-scroll"].asBool()) { + ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping container")); + } return true; } From 62f41259271dca4da811b1c7d7de6b702126fa51 Mon Sep 17 00:00:00 2001 From: dmitry Date: Thu, 8 Jun 2023 23:31:14 +0300 Subject: [PATCH 172/217] change order --- 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 2bf0247..bdf4ece 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -278,7 +278,7 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) { } std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) { - std::vector keys = {name, "urgent", "focused", "visible", "default"}; + std::vector keys = {"urgent", "focused", name, "visible", "default"}; for (auto const &key : keys) { if (key == "focused" || key == "visible" || key == "urgent") { if (config_["format-icons"][key].isString() && node[key].asBool()) { From 17af49d4214a64ac2bb2d3d0b6cb276fc98208ab Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Fri, 9 Jun 2023 18:22:01 +0300 Subject: [PATCH 173/217] Upower native-path filter Signed-off-by: Viktar Lukashonak --- include/modules/upower/upower.hpp | 1 + src/modules/upower/upower.cpp | 92 +++++++++++++++++++------------ 2 files changed, 59 insertions(+), 34 deletions(-) diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index 51fee0a..446d1f5 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -74,6 +74,7 @@ class UPower : public AModule { bool showAltText; bool upowerRunning; guint upowerWatcher_id; + std::string nativePath_; }; } // namespace waybar::modules::upower diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index 38c1f7f..7aafdc6 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -6,9 +6,7 @@ #include #include "gtkmm/icontheme.h" -#include "gtkmm/label.h" #include "gtkmm/tooltip.h" -#include "modules/upower/upower_tooltip.hpp" namespace waybar::modules::upower { UPower::UPower(const std::string& id, const Json::Value& config) @@ -25,6 +23,8 @@ UPower::UPower(const std::string& id, const Json::Value& config) 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(); @@ -195,8 +195,26 @@ void UPower::addDevice(UpDevice* device) { void UPower::setDisplayDevice() { std::lock_guard guard(m_Mutex); - displayDevice = up_client_get_display_device(client); - g_signal_connect(displayDevice, "notify", G_CALLBACK(deviceNotify_cb), this); + + 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() { @@ -278,14 +296,22 @@ auto UPower::update() -> void { double percentage; gint64 time_empty; gint64 time_full; - gchar* icon_name; + gchar* icon_name{(char*)'\0'}; + std::string percentString{""}; + std::string time_format{""}; - 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); + bool displayDeviceValid{false}; - bool displayDeviceValid = - kind == UpDeviceKind::UP_DEVICE_KIND_BATTERY || kind == UpDeviceKind::UP_DEVICE_KIND_UPS; + 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); + /* 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); @@ -308,32 +334,30 @@ auto UPower::update() -> void { event_box_.set_visible(true); - // 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 - std::string percentString = ""; if (displayDeviceValid) { - percentString = std::to_string(int(percentage + 0.5)) + "%"; - } + // 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); + } - // Label format - std::string time_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; + // 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), From fff4509723131b56274749b38e7fa5194a2948b2 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 11 Jun 2023 08:41:20 +0200 Subject: [PATCH 174/217] sway/window: fix appid style not cleared (#2227) Probably a rebase error during development of #1419. The code block now removed was not supposed to be there anymore. --- src/modules/sway/window.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 3c528e0..030f93b 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -178,10 +178,6 @@ auto Window::update() -> void { } else { mode += 32; } - if (!app_id_.empty() && !bar_.window.get_style_context()->has_class(app_id_)) { - bar_.window.get_style_context()->add_class(app_id_); - old_app_id_ = app_id_; - } } if (!old_app_id_.empty() && ((mode & 2) == 0 || old_app_id_ != app_id_) && From 0f8c156f243c0765dfc24f725357a1bbbce2628e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20G=C3=BCnzler?= Date: Mon, 12 Jun 2023 13:55:28 +0900 Subject: [PATCH 175/217] Lift reverse-scrolling option into AModule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The option is generally useful when scrolling is used, when configuring input devices to use "natural scroll direction". Both backlight and pulseaudio were using different implementations, this unifies and documents them. Signed-off-by: Robert Günzler --- src/AModule.cpp | 7 +++++-- src/modules/backlight.cpp | 8 -------- src/modules/pulseaudio.cpp | 7 ------- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 459e562..1bfb2de 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -97,11 +97,14 @@ bool AModule::handleToggle(GdkEventButton* const& e) { } AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { + // only affects up/down + bool reverse = config_["reverse-scrolling"].asBool(); + switch (e->direction) { case GDK_SCROLL_UP: - return SCROLL_DIR::UP; + return reverse ? SCROLL_DIR::DOWN : SCROLL_DIR::UP; case GDK_SCROLL_DOWN: - return SCROLL_DIR::DOWN; + return reverse ? SCROLL_DIR::UP : SCROLL_DIR::DOWN; case GDK_SCROLL_LEFT: return SCROLL_DIR::LEFT; case GDK_SCROLL_RIGHT: diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index eb7a7e9..58d14dd 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -305,14 +305,6 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) { return true; } - if (config_["reverse-scrolling"].asBool()) { - if (dir == SCROLL_DIR::UP) { - dir = SCROLL_DIR::DOWN; - } else if (dir == SCROLL_DIR::DOWN) { - dir = SCROLL_DIR::UP; - } - } - // Get scroll step double step = 1; diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 586b6cc..d35e298 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -81,13 +81,6 @@ bool waybar::modules::Pulseaudio::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 volume_tick = static_cast(PA_VOLUME_NORM) / 100; pa_volume_t change = volume_tick; pa_cvolume pa_volume = pa_volume_; From 73c7e5453540e5883bf83297a765e8fcc5d92801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20G=C3=BCnzler?= Date: Mon, 12 Jun 2023 14:08:22 +0900 Subject: [PATCH 176/217] pulseaudio: document reverse-scrolling option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Günzler --- 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 b941c22..b072265 100644 --- a/man/waybar-pulseaudio.5.scd +++ b/man/waybar-pulseaudio.5.scd @@ -91,6 +91,10 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu typeof: double ++ Threshold to be used when scrolling. +*reverse-scrolling*: ++ + typeof: bool ++ + Option to reverse the scroll direction. + *tooltip*: ++ typeof: bool ++ default: true ++ From 192cea97f29f39ec9cc710ec1db3e5a74d816409 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 13 Jun 2023 18:44:29 +0300 Subject: [PATCH 177/217] Upower man Signed-off-by: Viktar Lukashonak --- man/waybar-upower.5.scd | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/man/waybar-upower.5.scd b/man/waybar-upower.5.scd index a6ba4df..fc37b66 100644 --- a/man/waybar-upower.5.scd +++ b/man/waybar-upower.5.scd @@ -11,6 +11,12 @@ 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` + *icon-size*: ++ typeof: integer ++ default: 20 ++ @@ -68,6 +74,25 @@ depending on the charging state. "tooltip-spacing": 20 } +``` +``` +"upower": { + "native-path": "/org/bluez/hci0/dev_D4_AE_41_38_D0_EF", + "icon-size": 20, + "hide-if-empty": true, + "tooltip": true, + "tooltip-spacing": 20 +} + +``` +``` +"upower": { + "native-path": "battery_sony_controller_battery_d0o27o88o32ofcoee", + "icon-size": 20, + "hide-if-empty": true, + "tooltip": true, + "tooltip-spacing": 20 +} ``` # STYLE From 4d8515930f615d5b5165e8e1216703be6eae603c Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 13 Jun 2023 23:32:28 +0300 Subject: [PATCH 178/217] Use local TZ, when user sets blank TZ in config Signed-off-by: Viktar Lukashonak --- src/modules/clock.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 7e351d4..17cfd8d 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -22,19 +22,25 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) is_timezoned_list_in_tooltip_(false) { if (config_["timezones"].isArray() && !config_["timezones"].empty()) { for (const auto& zone_name : config_["timezones"]) { - if (!zone_name.isString() || zone_name.asString().empty()) continue; + if (!zone_name.isString()) continue; + if (zone_name.asString().empty()) + time_zones_.push_back(date::current_zone()); + else + try { + time_zones_.push_back(date::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()); + else try { - time_zones_.push_back(date::locate_zone(zone_name.asString())); + time_zones_.push_back(date::locate_zone(config_["timezone"].asString())); } catch (const std::exception& e) { - spdlog::warn("Timezone: {0}. {1}", zone_name.asString(), e.what()); + spdlog::warn("Timezone: {0}. {1}", config_["timezone"].asString(), e.what()); } - } - } else if (config_["timezone"].isString() && !config_["timezone"].asString().empty()) { - try { - time_zones_.push_back(date::locate_zone(config_["timezone"].asString())); - } catch (const std::exception& e) { - spdlog::warn("Timezone: {0}. {1}", config_["timezone"].asString(), e.what()); - } } // If all timezones are parsed and no one is good, add current time zone. nullptr in timezones From 51960096568aa9278933f7d40791b3840fd05ca0 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Thu, 15 Jun 2023 13:58:33 +0300 Subject: [PATCH 179/217] Refresh Gentoo docker Signed-off-by: Viktar Lukashonak --- Dockerfiles/gentoo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfiles/gentoo b/Dockerfiles/gentoo index 406f523..f2ec0dc 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" 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.24.6 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 + media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl dev-libs/iniparser sci-libs/fftw From d650c597f932cba6c90c7776ef2e35d69e224f7c Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Thu, 15 Jun 2023 16:36:33 +0300 Subject: [PATCH 180/217] Renew clock man page Signed-off-by: Viktar Lukashonak --- man/waybar-clock.5.scd | 321 ++++++++++++++++++++++++++++++----------- 1 file changed, 239 insertions(+), 82 deletions(-) diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index fb470e0..3c67056 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -1,105 +1,167 @@ -waybar-clock(5) +waybar-clock(5) "waybar-clock" "User Manual" # NAME -waybar - clock module +clock # DESCRIPTION -The *clock* module displays the current date and time. +*clock* module displays current date and time + +# FILES + +$XDG_CONFIG_HOME/waybar/config ++ + Per user configuration file # CONFIGURATION -*interval*: ++ - typeof: integer ++ - default: 60 ++ - The interval in which the information gets polled. +1. Addressed by *clock* +[- *Option* +:- *Typeof* +:- *Default* +:- *Description* +|[ *interval* +:[ integer +:[ 60 +:[ The interval in which the information gets polled +|[ *format* +:[ string +:[ *{:%H:%M}* +:[ The format, how the date and time should be displayed. See format options below +|[ *timezone* +:[ string +:[ +:[ The timezone to display the time in, e.g. America/New_York. "" represents + the system's local timezone. See Wikipedia's unofficial list of timezones +|[ *timezones* +:[ 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. + "" 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 +|[ *max-length* +:[ integer +:[ +:[ The maximum length in character the module should display +|[ *rotate* +:[ integer +:[ +:[ Positive value to rotate the text label +|[ *on-click* +:[ string +:[ +:[ Command to execute when clicked on the module +|[ *on-click-middle* +:[ string +:[ +:[ Command to execute when you middle clicked on the module using mousewheel +|[ *on-click-right* +:[ string +:[ +:[ Command to execute when you right clicked on the module +|[ *on-scroll-up* +:[ string +:[ +:[ Command to execute when scrolling up on the module +|[ *on-scroll-down* +:[ string +:[ +:[ Command to execute when scrolling down on the module +|[ *smooth-scrolling-threshold* +:[ double +:[ +:[ Threshold to be used when scrolling +|[ *tooltip* +:[ bool +:[ true +:[ Option to enable tooltip on hover +|[ *tooltip-format* +:[ string +:[ same as format +:[ Tooltip on hover -*format*: ++ - typeof: string ++ - default: {:%H:%M} ++ - The format, how the date and time should be displayed. ++ - It uses the format of the date library. See https://howardhinnant.github.io/date/date.html#to_stream_formatting for details. +View all valid format options in *strftime(3)* or have a look -*timezone*: ++ - typeof: string ++ - default: inferred local timezone ++ - The timezone to display the time in, e.g. America/New_York. ++ - This field will be ignored if *timezones* field is set and have at least one value. +2. Addressed by *clock: calendar* +[- *Option* +:- *Typeof* +:- *Default* +:- *Description* +|[ *mode* +:[ string +:[ month +:[ Calendar view mode. Possible values: year|month +|[ *mode-mon-col* +:[ integer +:[ 3 +:[ Relevant for *mode=year*. Count of months per row +|[ *weeks-pos* +:[ integer +:[ +:[ The position where week numbers should be displayed. Disabled when is empty. + Possible values: left|right +|[ *on-scroll* +:[ integer +:[ 1 +:[ Value to scroll months/years forward/backward. Can be negative. Is + configured under *on-scroll* option -*timezones*: ++ - typeof: list of strings ++ - A list of timezones to use for time display, changed using the scroll wheel. ++ - Use "" to represent the system's local timezone. Using %Z in the format or tooltip format is useful to track which time zone is currently displayed. +3. Adressed by *clock: calendar: format* +[- *Option* +:- *Typeof* +:- *Default* +:- *Description* +|[ *months* +:[ string +:[ +:[ Format is applied to months header(January, February,...etc.) +|[ *days* +:[ string +:[ +:[ Format is applied to days +|[ *weeks* +:[ string +:[ *{:%U}* +:[ Format is applied to week numbers. When weekday format is not provided then + is used default format: '{:%W}' when week starts with Monday, '{:%U}' otherwise +|[ *weekdays* +:[ string +:[ +:[ Format is applied to weeks header(Su,Mo,...etc.) +|[ *today* +:[ string +:[ *{}* +:[ Format is applied to Today -*locale*: ++ - typeof: string ++ - default: inferred from current locale ++ - A locale to be used to display the time. Intended to render times in custom timezones with the proper language and format. +## Actions -*today-format*: ++ - typeof: string ++ - default: {} ++ - The format of today's date in the calendar. - -*max-length*: ++ - typeof: integer ++ - The maximum length in character the module should display. - -*min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. - -*align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. - -*rotate*: ++ - typeof: integer ++ - Positive value to rotate the text label. - -*on-click*: ++ - typeof: string ++ - Command to execute when clicked on the module. - -*on-click-middle*: ++ - typeof: string ++ - Command to execute when middle-clicked on the module using mousewheel. - -*on-click-right*: ++ - typeof: string ++ - Command to execute when you right clicked on the module. - -*on-update*: ++ - typeof: string ++ - Command to execute when the module is updated. - -*on-scroll-up*: ++ - typeof: string ++ - Command to execute when scrolling up on the module. - -*on-scroll-down*: ++ - typeof: string ++ - Command to execute when scrolling down on the module. - -*smooth-scrolling-threshold*: ++ - typeof: double ++ - Threshold to be used when scrolling. - -*tooltip*: ++ - typeof: bool ++ - default: true ++ - Option to disable tooltip on hover. - -View all valid format options in *strftime(3)*. +[- *String* +:- *Action* +|[ *mode* +:[ Switch calendar mode between year/month +|[ *tz_up* +:[ Switch to the next provided time zone +|[ *tz_down* +:[ Switch to the previous provided time zone +|[ *shift_up* +:[ Switch to the next calendar month/year +|[ *shift_down* +:[ Switch to the previous calendar month/year # FORMAT REPLACEMENTS -*{calendar}*: Current month calendar -*{timezoned_time_list}*: List of time in the rest timezones, if more than one timezone is set in the config +- *{calendar}*: Current month calendar +- *{timezoned_time_list}*: List of time in the rest timezones, if more than one timezone is set in the config # EXAMPLES +1. General + ``` "clock": { "interval": 60, @@ -108,6 +170,101 @@ View all valid format options in *strftime(3)*. } ``` +2. Calendar + +``` +"clock": { + "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" + } +}, +``` + +3. Full date on hover + +``` +"clock": { + "interval": 60, + "tooltip": true, + "format": "{:%H.%M}", + "tooltip-format": "{:%Y-%m-%d}", +} +``` + # STYLE - *#clock* + +# Troubleshooting + +If clock module is disabled at startup with locale::facet::\_S\_create\_c\_locale ++ +name not valid error message try one of the followings: + +- check if LC_TIME is set properly (glibc) +- set locale to C in the config file (musl) + +The locale option must be set for {calendar} to use the correct start-of-week, regardless of system locale. + +## Calendar in Chinese. Alignment + +In order to have aligned Chinese calendar there are some useful recommendations: + +. Use "WenQuanYi Zen Hei Mono" which is provided in most Linux distributions +. Try different font sizes and find best for you. size = 9pt should be fine +. In case when "WenQuanYi Zen Hei Mono" font is used disable monospace font pango tag + +Example of working config + +``` +"clock": { + "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 + +Alexis Rouillard From e403c3b71b498abd8d9bc47a85418382f5e9a63d Mon Sep 17 00:00:00 2001 From: yangyingchao Date: Sat, 17 Jun 2023 11:33:14 +0800 Subject: [PATCH 181/217] support multiple items in hwmon-path of temperature module So user can share configuration file among different machines with different hardware configurations. --- man/waybar-temperature.5.scd | 4 +++- src/modules/temperature.cpp | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 8d11e51..cc689dc 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -19,6 +19,8 @@ Addressed by *temperature* *hwmon-path*: ++ typeof: string ++ The temperature path to use, e.g. */sys/class/hwmon/hwmon2/temp1_input* instead of one in */sys/class/thermal/*. + This can also be an array of strings. In this case, waybar will check each item in the array and use the first valid one. + This is suitable if you want to share the same configuration file among different machines with different hardware configurations. *hwmon-path-abs*: ++ typeof: string ++ @@ -117,7 +119,7 @@ Addressed by *temperature* ``` "temperature": { // "thermal-zone": 2, - // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", + // "hwmon-path": ["/sys/class/hwmon/hwmon2/temp1_input", "/sys/class/thermal/thermal_zone0/temp"], // "critical-threshold": 80, // "format-critical": "{temperatureC}°C ", "format": "{temperatureC}°C " diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index ff722d7..5ef2f4c 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -11,8 +11,18 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val #if defined(__FreeBSD__) // try to read sysctl? #else - if (config_["hwmon-path"].isString()) { - file_path_ = config_["hwmon-path"].asString(); + 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()) { file_path_ = (*std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())) .path() From e233022d1a4472a3af146a91dfa4f19f5123fb41 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Mon, 19 Jun 2023 23:08:03 +0300 Subject: [PATCH 182/217] hyprland/window: Rework, add .empty, .solo and . CSS classes --- include/modules/hyprland/window.hpp | 22 ++++- src/modules/hyprland/window.cpp | 144 ++++++++++++++++++++-------- 2 files changed, 119 insertions(+), 47 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index b5ab6fb..a0681d4 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -1,7 +1,5 @@ #include -#include - #include "ALabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" @@ -17,15 +15,29 @@ class Window : public waybar::ALabel, public EventHandler { auto update() -> void override; private: - int getActiveWorkspaceID(std::string); - std::string getLastWindowTitle(int); + struct Workspace { + int windows; + std::string last_window; + std::string last_window_title; + + static auto parse(const Json::Value&) -> Workspace; + }; + + auto getActiveWorkspace(const std::string&) -> Workspace; + auto getActiveWorkspace() -> Workspace; + auto getWindowClass(const std::string&) -> std::string; void onEvent(const std::string&) override; + void queryActiveWorkspace(); + void setClass(const std::string&, bool enable); bool separate_outputs; std::mutex mutex_; const Bar& bar_; util::JsonParser parser_; - std::string lastView; + std::string last_title_; + Workspace workspace_; + std::string solo_class_; + std::string last_solo_class_; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 714d0a7..2025b61 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -6,7 +6,6 @@ #include #include "modules/hyprland/backend.hpp" -#include "util/command.hpp" #include "util/json.hpp" #include "util/rewrite_string.hpp" @@ -21,11 +20,13 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) gIPC = std::make_unique(); } - label_.hide(); - ALabel::update(); + queryActiveWorkspace(); + update(); // register for hyprland ipc gIPC->registerForIPC("activewindow", this); + gIPC->registerForIPC("closewindow", this); + gIPC->registerForIPC("movewindow", this); } Window::~Window() { @@ -38,69 +39,128 @@ auto Window::update() -> void { // fix ampersands std::lock_guard lg(mutex_); + std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title); + + if (window_name != last_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 (!format_.empty()) { label_.show(); label_.set_markup(fmt::format(fmt::runtime(format_), - waybar::util::rewriteString(lastView, config_["rewrite"]))); + waybar::util::rewriteString(window_name, config_["rewrite"]))); } else { label_.hide(); } + + setClass("empty", workspace_.windows == 0); + setClass("solo", workspace_.windows == 1); + + 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 (!solo_class_.empty() && solo_class_ != last_solo_class_) { + last_solo_class_ = solo_class_; + bar_.window.get_style_context()->add_class(solo_class_); + spdlog::trace("Adding solo class: {}", solo_class_); + } + ALabel::update(); } -int Window::getActiveWorkspaceID(std::string monitorName) { - auto cmd = waybar::util::command::exec("hyprctl monitors -j"); - assert(cmd.exit_code == 0); - Json::Value json = parser_.parse(cmd.out); +auto Window::getActiveWorkspace() -> Workspace { + const auto workspace = gIPC->getSocket1Reply("j/activeworkspace"); + Json::Value json = parser_.parse(workspace); + assert(json.isObject()); + return Workspace::parse(json); +} + +auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { + const auto monitors = gIPC->getSocket1Reply("j/monitors"); + Json::Value json = parser_.parse(monitors); assert(json.isArray()); auto monitor = std::find_if(json.begin(), json.end(), [&](Json::Value monitor) { return monitor["name"] == monitorName; }); if (monitor == std::end(json)) { - return 0; + spdlog::warn("Monitor not found: {}", monitorName); + return Workspace{0, "", ""}; } - return (*monitor)["activeWorkspace"]["id"].as(); + const int id = (*monitor)["activeWorkspace"]["id"].as(); + + const auto workspaces = gIPC->getSocket1Reply("j/workspaces"); + json = parser_.parse(workspaces); + assert(json.isArray()); + auto workspace = std::find_if(json.begin(), json.end(), + [&](Json::Value workspace) { return workspace["id"] == id; }); + if (workspace == std::end(json)) { + spdlog::warn("No workspace with id {}", id); + return Workspace{0, "", ""}; + } + return Workspace::parse(*workspace); } -std::string Window::getLastWindowTitle(int workspaceID) { - auto cmd = waybar::util::command::exec("hyprctl workspaces -j"); - assert(cmd.exit_code == 0); - Json::Value json = parser_.parse(cmd.out); - assert(json.isArray()); - auto workspace = std::find_if(json.begin(), json.end(), [&](Json::Value workspace) { - return workspace["id"].as() == workspaceID; - }); +auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { + return Workspace{ + value["windows"].as(), + value["lastwindow"].as(), + value["lastwindowtitle"].as() + }; +} - if (workspace == std::end(json)) { +auto Window::getWindowClass(const std::string& address) -> std::string { + const auto clients = gIPC->getSocket1Reply("j/clients"); + Json::Value json = parser_.parse(clients); + assert(json.isArray()); + auto client = std::find_if(json.begin(), json.end(), + [&](Json::Value window) { return window["address"] == address; }); + if (client == std::end(json)) { return ""; } - return (*workspace)["lastwindowtitle"].as(); + return (*client)["class"].as(); +} + +void Window::queryActiveWorkspace() { + std::lock_guard lg(mutex_); + + if (separate_outputs) { + workspace_ = getActiveWorkspace(this->bar_.output->name); + } else { + workspace_ = getActiveWorkspace(); + } + + if (workspace_.windows == 1) { + solo_class_ = getWindowClass(workspace_.last_window); + } else { + solo_class_ = ""; + } } void Window::onEvent(const std::string& ev) { - std::lock_guard lg(mutex_); - - std::string windowName; - if (separate_outputs) { - windowName = getLastWindowTitle(getActiveWorkspaceID(this->bar_.output->name)); - } else { - windowName = ev.substr(ev.find_first_of(',') + 1).substr(0, 256); - } - - windowName = waybar::util::sanitize_string(windowName); - - if (windowName == lastView) return; - - lastView = windowName; - - if (windowName.empty()) { - label_.get_style_context()->add_class("empty"); - } else { - label_.get_style_context()->remove_class("empty"); - } - - spdlog::debug("hyprland window onevent with {}", windowName); + queryActiveWorkspace(); dp.emit(); } + + +void Window::setClass(const std::string& classname, bool enable) { + if (enable) { + if (!bar_.window.get_style_context()->has_class(classname)) { + bar_.window.get_style_context()->add_class(classname); + } + } else { + bar_.window.get_style_context()->remove_class(classname); + } +} + } // namespace waybar::modules::hyprland From 4f14ce3285c8dab4243d68bbfc2324f27b414f1b Mon Sep 17 00:00:00 2001 From: gardenapple Date: Tue, 20 Jun 2023 00:42:19 +0300 Subject: [PATCH 183/217] hyprland/window: add .floating and .fullscreen CSS classes --- include/modules/hyprland/window.hpp | 4 +- src/modules/hyprland/window.cpp | 62 ++++++++++++++++++----------- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index a0681d4..19aff50 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -16,6 +16,7 @@ class Window : public waybar::ALabel, public EventHandler { private: struct Workspace { + int id; int windows; std::string last_window; std::string last_window_title; @@ -25,7 +26,6 @@ class Window : public waybar::ALabel, public EventHandler { auto getActiveWorkspace(const std::string&) -> Workspace; auto getActiveWorkspace() -> Workspace; - auto getWindowClass(const std::string&) -> std::string; void onEvent(const std::string&) override; void queryActiveWorkspace(); void setClass(const std::string&, bool enable); @@ -38,6 +38,8 @@ class Window : public waybar::ALabel, public EventHandler { Workspace workspace_; std::string solo_class_; std::string last_solo_class_; + bool fullscreen_; + bool all_floating_; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 2025b61..47c68bb 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -2,19 +2,20 @@ #include +#include #include -#include #include "modules/hyprland/backend.hpp" #include "util/json.hpp" #include "util/rewrite_string.hpp" +#include 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) { modulesReady = true; - separate_outputs = config["separate-outputs"].as(); + separate_outputs = config["separate-outputs"].asBool(); if (!gIPC.get()) { gIPC = std::make_unique(); @@ -27,6 +28,8 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) gIPC->registerForIPC("activewindow", this); gIPC->registerForIPC("closewindow", this); gIPC->registerForIPC("movewindow", this); + gIPC->registerForIPC("changefloatingmode", this); + gIPC->registerForIPC("fullscreen", this); } Window::~Window() { @@ -62,6 +65,8 @@ auto Window::update() -> void { setClass("empty", workspace_.windows == 0); setClass("solo", workspace_.windows == 1); + setClass("fullscreen", fullscreen_); + setClass("floating", all_floating_); if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) { if (bar_.window.get_style_context()->has_class(last_solo_class_)) { @@ -71,10 +76,10 @@ auto Window::update() -> void { } if (!solo_class_.empty() && solo_class_ != last_solo_class_) { - last_solo_class_ = solo_class_; bar_.window.get_style_context()->add_class(solo_class_); spdlog::trace("Adding solo class: {}", solo_class_); } + last_solo_class_ = solo_class_; ALabel::update(); } @@ -94,42 +99,31 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { [&](Json::Value monitor) { return monitor["name"] == monitorName; }); if (monitor == std::end(json)) { spdlog::warn("Monitor not found: {}", monitorName); - return Workspace{0, "", ""}; + return Workspace{-1, 0, "", ""}; } - const int id = (*monitor)["activeWorkspace"]["id"].as(); + const int id = (*monitor)["activeWorkspace"]["id"].asInt(); const auto workspaces = gIPC->getSocket1Reply("j/workspaces"); json = parser_.parse(workspaces); assert(json.isArray()); auto workspace = std::find_if(json.begin(), json.end(), - [&](Json::Value workspace) { return workspace["id"] == id; }); + [&](Json::Value workspace) { return workspace["id"] == id; }); if (workspace == std::end(json)) { spdlog::warn("No workspace with id {}", id); - return Workspace{0, "", ""}; + return Workspace{-1, 0, "", ""}; } return Workspace::parse(*workspace); } auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { return Workspace{ - value["windows"].as(), - value["lastwindow"].as(), - value["lastwindowtitle"].as() + value["id"].asInt(), + value["windows"].asInt(), + value["lastwindow"].asString(), + value["lastwindowtitle"].asString() }; } -auto Window::getWindowClass(const std::string& address) -> std::string { - const auto clients = gIPC->getSocket1Reply("j/clients"); - Json::Value json = parser_.parse(clients); - assert(json.isArray()); - auto client = std::find_if(json.begin(), json.end(), - [&](Json::Value window) { return window["address"] == address; }); - if (client == std::end(json)) { - return ""; - } - return (*client)["class"].as(); -} - void Window::queryActiveWorkspace() { std::lock_guard lg(mutex_); @@ -139,10 +133,30 @@ void Window::queryActiveWorkspace() { workspace_ = getActiveWorkspace(); } - if (workspace_.windows == 1) { - solo_class_ = getWindowClass(workspace_.last_window); + + 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) { return window["address"] == workspace_.last_window; }); + if (active_window == std::end(json)) { + return; + } + + if (workspace_.windows == 1 && !(*active_window)["floating"].asBool()) { + solo_class_ = (*active_window)["class"].asString(); + } else { + solo_class_ = ""; + } + all_floating_ = std::all_of(json.begin(), json.end(), + [&](Json::Value window) { return window["floating"].asBool() || + window["workspace"]["id"] != workspace_.id; }); + fullscreen_ = (*active_window)["fullscreen"].asBool(); } else { solo_class_ = ""; + all_floating_ = false; + fullscreen_ = false; } } From fd7c2a2012e492d3db51ace23d9910e6a5a91d48 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Tue, 20 Jun 2023 00:43:33 +0300 Subject: [PATCH 184/217] hyprland/language: Show language on startup --- 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 3dfdb83..aa22a48 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -22,7 +22,7 @@ Language::Language(const std::string& id, const Bar& bar, const Json::Value& con initLanguage(); label_.hide(); - ALabel::update(); + update(); // register for hyprland ipc gIPC->registerForIPC("activelayout", this); From 30c4f08773e5608ac886251024fadace3dcdcea9 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Tue, 20 Jun 2023 03:23:03 +0300 Subject: [PATCH 185/217] hyprland/window: Correct application of .solo class --- include/modules/hyprland/window.hpp | 3 ++- src/modules/hyprland/window.cpp | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index 19aff50..950be05 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -38,8 +38,9 @@ class Window : public waybar::ALabel, public EventHandler { Workspace workspace_; std::string solo_class_; std::string last_solo_class_; - bool fullscreen_; + bool solo_; bool all_floating_; + bool fullscreen_; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 47c68bb..ba476e9 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "modules/hyprland/backend.hpp" #include "util/json.hpp" @@ -64,7 +65,7 @@ auto Window::update() -> void { setClass("empty", workspace_.windows == 0); - setClass("solo", workspace_.windows == 1); + setClass("solo", solo_); setClass("fullscreen", fullscreen_); setClass("floating", all_floating_); @@ -149,12 +150,18 @@ void Window::queryActiveWorkspace() { } else { solo_class_ = ""; } - all_floating_ = std::all_of(json.begin(), json.end(), - [&](Json::Value window) { return window["floating"].asBool() || - window["workspace"]["id"] != workspace_.id; }); + std::vector workspace_windows; + std::copy_if(json.begin(), json.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(); }); + all_floating_ = std::all_of(workspace_windows.begin(), workspace_windows.end(), + [&](Json::Value window) { return window["floating"].asBool(); }); fullscreen_ = (*active_window)["fullscreen"].asBool(); } else { solo_class_ = ""; + solo_ = false; all_floating_ = false; fullscreen_ = false; } From b163b21acee0bb22a5994b177e4e0c070c28ef32 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Tue, 20 Jun 2023 13:36:48 +0300 Subject: [PATCH 186/217] More robust Hyprland backend --- src/modules/hyprland/backend.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 1e684d9..997fe11 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -181,17 +181,21 @@ std::string IPC::getSocket1Reply(const std::string& rq) { } char buffer[8192] = {0}; + std::string response; - sizeWritten = read(SERVERSOCKET, buffer, 8192); + do { + sizeWritten = read(SERVERSOCKET, buffer, 8192); - if (sizeWritten < 0) { - spdlog::error("Hyprland IPC: Couldn't read (5)"); - return ""; - } + if (sizeWritten < 0) { + spdlog::error("Hyprland IPC: Couldn't read (5)"); + close(SERVERSOCKET); + return ""; + } + response.append(buffer, sizeWritten); + } while (sizeWritten == 8192); close(SERVERSOCKET); - - return std::string(buffer); + return response; } } // namespace waybar::modules::hyprland From 77a8420aafa53482bf56422cfadaa3d1b26af30a Mon Sep 17 00:00:00 2001 From: Erik Rodriguez Date: Fri, 2 Jun 2023 16:14:28 -0300 Subject: [PATCH 187/217] mpris: Add dynamic-order and dynamic-separator This commit allows better handling of ordering and exclusion of the tags in Dynamics tags. It also becomes possible to choose the separator between the tags. --- include/modules/mpris/mpris.hpp | 2 + man/waybar-mpris.5.scd | 15 +++++ src/modules/mpris/mpris.cpp | 98 +++++++++++++++++++++++++-------- 3 files changed, 91 insertions(+), 24 deletions(-) diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index ea5f7de..aff2673 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -66,6 +66,8 @@ 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_; bool truncate_hours_; bool tooltip_len_limits_; diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index 145e6da..eaa1ec9 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -71,12 +71,27 @@ The *mpris* module displays currently playing media via libplayerctl. something less than or equal to this value, so the title will always be ++ displayed. +*dynamic-order*: ++ + typeof: []string ++ + default: ["title", "artist", "album", "position", "length"] ++ + Order of the tags shown by Dynamic tag. The position and length tags ++ + will always be combined in the format [{position}/{length}]. The order ++ + of these tags in relation to other tags will be determined based on the ++ + declaration of the first among the two tags. Absence in this list means ++ + force exclusion. + *dynamic-priority*: ++ typeof: []string ++ default: ["title", "length", "position", "artist", "album"] ++ Priority of the tags when truncating the Dynamic tag (absence in this list means force inclusion). +*dynamic-separator*: ++ + typeof: string ++ + default: " - " ++ + These characters will be used to separate two different tags, except ++ + when one of these tags is position and length. + *truncate-hours*: ++ typeof: bool ++ default: true ++ diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index e434694..0f83f87 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -24,7 +24,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) album_len_(-1), title_len_(-1), dynamic_len_(-1), - dynamic_prio_({"title", "length", "position", "artist", "album"}), + dynamic_prio_({"title", "artist", "album", "position", "length"}), + dynamic_order_({"title", "artist", "album", "position", "length"}), + dynamic_separator_(" - "), truncate_hours_(true), tooltip_len_limits_(false), // this character is used in Gnome so it's fine to use it here @@ -44,6 +46,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) if (config_["ellipsis"].isString()) { ellipsis_ = config_["ellipsis"].asString(); } + if (config_["dynamic-separator"].isString()) { + dynamic_separator_ = config_["dynamic-separator"].asString(); + } if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { tooltip_ = config_["tooltip-format"].asString(); @@ -83,6 +88,14 @@ 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()); + } + } + } if (config_["truncate-hours"].isBool()) { truncate_hours_ = config["truncate-hours"].asBool(); @@ -272,18 +285,28 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> size_t lengthLen = length.length(); size_t posLen = position.length(); - bool showArtist = artistLen != 0; - bool showAlbum = albumLen != 0; - bool showTitle = titleLen != 0; - bool showLength = lengthLen != 0; - bool showPos = posLen != 0; + bool showArtist = (artistLen != 0) && (std::find(dynamic_order_.begin(), dynamic_order_.end(), + "artist") != dynamic_order_.end()); + bool showAlbum = (albumLen != 0) && (std::find(dynamic_order_.begin(), dynamic_order_.end(), + "album") != dynamic_order_.end()); + bool showTitle = (titleLen != 0) && (std::find(dynamic_order_.begin(), dynamic_order_.end(), + "title") != dynamic_order_.end()); + bool showLength = (lengthLen != 0) && (std::find(dynamic_order_.begin(), dynamic_order_.end(), + "length") != dynamic_order_.end()); + bool showPos = (posLen != 0) && (std::find(dynamic_order_.begin(), dynamic_order_.end(), + "position") != dynamic_order_.end()); if (truncated && dynamic_len_ >= 0) { - size_t dynamicLen = dynamic_len_; - if (showArtist) artistLen += 3; - if (showAlbum) albumLen += 3; - if (showLength) lengthLen += 3; - if (showPos) posLen += 3; + //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; + if (showAlbum) albumLen += separatorLen; + if (showTitle) albumLen += separatorLen; + if (showLength) lengthLen += separatorLen; + if (showPos) posLen += separatorLen; size_t totalLen = 0; @@ -330,20 +353,47 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> album = Glib::Markup::escape_text(album); title = Glib::Markup::escape_text(title); } - if (showArtist) dynamic << artist << " - "; - if (showAlbum) dynamic << album << " - "; - if (showTitle) dynamic << title; - if (showLength || showPos) { - dynamic << ' '; - if (html) dynamic << ""; - dynamic << '['; - if (showPos) { - dynamic << position; - if (showLength) dynamic << '/'; + + bool lengthOrPositionShown = false; + bool previousShown = false; + std::string previousOrder = ""; + + for (const std::string& order : dynamic_order_) { + if ((order == "artist" && showArtist) || + (order == "album" && showAlbum) || + (order == "title" && showTitle)) { + if (previousShown && + previousOrder != "length" && + previousOrder != "position") { + dynamic << dynamic_separator_; + } + + if (order == "artist") { + dynamic << artist; + } else if (order == "album") { + dynamic << album; + } else if (order == "title") { + dynamic << title; + } + + previousShown = true; + } else if (order == "length" || order == "position") { + if (!lengthOrPositionShown && (showLength || showPos)) { + if (html) dynamic << ""; + if (previousShown) dynamic << ' '; + dynamic << '['; + if (showPos) { + dynamic << position; + if (showLength) dynamic << '/'; + } + if (showLength) dynamic << length; + dynamic << ']'; + if (!dynamic.str().empty()) dynamic << ' '; + if (html) dynamic << ""; + lengthOrPositionShown = true; + } } - if (showLength) dynamic << length; - dynamic << ']'; - if (html) dynamic << ""; + previousOrder = order; } return dynamic.str(); } From 66ce74d29ba9fdbfde1c5f7fa2e86118096f439c Mon Sep 17 00:00:00 2001 From: Erik Rodriguez Date: Sat, 3 Jun 2023 10:43:12 -0300 Subject: [PATCH 188/217] mpris: Rename dynamic-priority to dynamic-importance-order keeping backward compatibility --- man/waybar-mpris.5.scd | 9 +++++---- src/modules/mpris/mpris.cpp | 11 ++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index eaa1ec9..ad5c1df 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -80,11 +80,12 @@ The *mpris* module displays currently playing media via libplayerctl. declaration of the first among the two tags. Absence in this list means ++ force exclusion. -*dynamic-priority*: ++ +*dynamic-importance-order*: ++ typeof: []string ++ - default: ["title", "length", "position", "artist", "album"] ++ - Priority of the tags when truncating the Dynamic tag (absence in this - list means force inclusion). + default: ["title", "artist", "album", "position", "length"] ++ + Priority of the tags when truncating the Dynamic tag. The final ones ++ + will be the first to be truncated. Absence in this list means force ++ + inclusion. *dynamic-separator*: ++ typeof: string ++ diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 0f83f87..61b395f 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -79,12 +79,13 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) if (config["dynamic-len"].isUInt()) { dynamic_len_ = config["dynamic-len"].asUInt(); } - if (config_["dynamic-priority"].isArray()) { + // "dynamic-priority" has been kept for backward compatibility + if (config_["dynamic-importance-order"].isArray() || config_["dynamic-priority"].isArray()) { dynamic_prio_.clear(); - for (auto it = config_["dynamic-priority"].begin(); it != config_["dynamic-priority"].end(); - ++it) { - if (it->isString()) { - dynamic_prio_.push_back(it->asString()); + 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()); } } } From 43434254e0ea575f45cfe7d9781cfa22025f1fda Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Mon, 26 Jun 2023 11:01:50 +0200 Subject: [PATCH 189/217] Add output port of workspace to template --- src/modules/sway/workspaces.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index bdf4ece..324c397 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -235,7 +235,8 @@ auto Workspaces::update() -> void { 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("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 ce4da59f34ca7dffadf956182b2fb35573990f86 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 27 Jun 2023 00:07:40 +0300 Subject: [PATCH 190/217] finish --- src/modules/wlr/taskbar.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 74b8be5..0fd4443 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -506,11 +506,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) { @@ -710,6 +710,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("empty"); event_box_.add(box_); struct wl_display *display = Client::inst()->wl_display; @@ -862,11 +863,19 @@ void Taskbar::handle_finished() { manager_ = nullptr; } -void Taskbar::add_button(Gtk::Button &bt) { box_.pack_start(bt, false, false); } +void Taskbar::add_button(Gtk::Button &bt) { + box_.pack_start(bt, false, false); + box_.get_style_context()->remove_class("empty"); +} void Taskbar::move_button(Gtk::Button &bt, int pos) { box_.reorder_child(bt, pos); } -void Taskbar::remove_button(Gtk::Button &bt) { box_.remove(bt); } +void Taskbar::remove_button(Gtk::Button &bt) { + box_.remove(bt); + if (tasks_.empty()) { + box_.get_style_context()->add_class("empty"); + } +} void Taskbar::remove_task(uint32_t id) { auto it = std::find_if(std::begin(tasks_), std::end(tasks_), From afc489869af83ce6c8cc646f842ef9dfb9b6655d Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 27 Jun 2023 00:18:49 +0300 Subject: [PATCH 191/217] fix format --- src/modules/hyprland/window.cpp | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index ba476e9..b122fc9 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -4,12 +4,12 @@ #include #include +#include #include #include "modules/hyprland/backend.hpp" #include "util/json.hpp" #include "util/rewrite_string.hpp" -#include namespace waybar::modules::hyprland { @@ -54,7 +54,6 @@ auto Window::update() -> void { last_title_ = window_name; } - if (!format_.empty()) { label_.show(); label_.set_markup(fmt::format(fmt::runtime(format_), @@ -63,7 +62,6 @@ auto Window::update() -> void { label_.hide(); } - setClass("empty", workspace_.windows == 0); setClass("solo", solo_); setClass("fullscreen", fullscreen_); @@ -117,12 +115,8 @@ 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()}; } void Window::queryActiveWorkspace() { @@ -134,13 +128,13 @@ void Window::queryActiveWorkspace() { workspace_ = getActiveWorkspace(); } - 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) { return window["address"] == workspace_.last_window; }); + auto active_window = std::find_if(json.begin(), json.end(), [&](Json::Value window) { + return window["address"] == workspace_.last_window; + }); if (active_window == std::end(json)) { return; } @@ -152,8 +146,9 @@ void Window::queryActiveWorkspace() { } std::vector workspace_windows; std::copy_if(json.begin(), json.end(), std::back_inserter(workspace_windows), - [&](Json::Value window) { return window["workspace"]["id"] == workspace_.id && - window["mapped"].asBool(); }); + [&](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(); }); all_floating_ = std::all_of(workspace_windows.begin(), workspace_windows.end(), @@ -173,7 +168,6 @@ void Window::onEvent(const std::string& ev) { dp.emit(); } - void Window::setClass(const std::string& classname, bool enable) { if (enable) { if (!bar_.window.get_style_context()->has_class(classname)) { From 08e18387c92b3ca4b8149650034318daf5ca1666 Mon Sep 17 00:00:00 2001 From: Lasse Luttermann Date: Mon, 26 Jun 2023 11:01:50 +0200 Subject: [PATCH 192/217] Add output port of workspace to template --- man/waybar-sway-workspaces.5.scd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 644cba4..628115a 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -87,6 +87,8 @@ Addressed by *sway/workspaces* *{index}*: Index of the workspace. +*{output}*: Output where the workspace is located. + # ICONS Additional to workspace name matching, the following *format-icons* can be set. From 33236c222f947d8d360b208c161181922445277a Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 28 Jun 2023 02:52:01 +0300 Subject: [PATCH 193/217] save --- include/factory.hpp | 1 + include/modules/hyprland/backend.hpp | 3 ++ include/modules/hyprland/workspaces.hpp | 44 +++++++++++++++ meson.build | 10 +--- src/factory.cpp | 3 ++ src/modules/hyprland/backend.cpp | 4 ++ src/modules/hyprland/workspaces.cpp | 72 +++++++++++++++++++++++++ 7 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 include/modules/hyprland/workspaces.hpp create mode 100644 src/modules/hyprland/workspaces.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 7afc89f..90d0ac1 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -31,6 +31,7 @@ #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__) && !defined(NO_FILESYSTEM)) #include "modules/battery.hpp" diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index d876781..179c82f 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -5,6 +5,7 @@ #include #include #include +#include "util/json.hpp" namespace waybar::modules::hyprland { @@ -22,12 +23,14 @@ class IPC { void unregisterForIPC(EventHandler*); 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; }; diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp new file mode 100644 index 0000000..7903fef --- /dev/null +++ b/include/modules/hyprland/workspaces.hpp @@ -0,0 +1,44 @@ +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "modules/hyprland/backend.hpp" + +namespace waybar::modules::hyprland { + +class Workspace { + public: + Workspace(int id); + int id() { return id_; }; + Gtk::Button& button() { return button_; }; + + static Workspace parse(const Json::Value&); + void update(); + + private: + int id_; + + Gtk::Button button_; + Gtk::Box content_; + Gtk::Label label_; +}; + +class Workspaces : public AModule, public EventHandler { + public: + Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); + virtual ~Workspaces(); + void update() override; + void init(); + + private: + void onEvent(const std::string&) override; + + std::vector workspaces; + + std::mutex mutex_; + const Bar& bar_; + Gtk::Box box_; +}; + +} // namespace waybar::modules::hyprland diff --git a/meson.build b/meson.build index beee053..9ef4db0 100644 --- a/meson.build +++ b/meson.build @@ -240,6 +240,7 @@ if true 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' endif if libnl.found() and libnlgen.found() @@ -479,15 +480,6 @@ if scdoc.found() endforeach endif -catch2 = dependency( - 'catch2', - version: '>=2.0.0', - fallback: ['catch2', 'catch2_dep'], - required: get_option('tests'), -) -if catch2.found() - subdir('test') -endif clangtidy = find_program('clang-tidy', required: false) diff --git a/src/factory.cpp b/src/factory.cpp index bd2e7a2..1d7a00b 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -83,6 +83,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "hyprland/submap") { return new waybar::modules::hyprland::Submap(id, bar_, config_[name]); } + if (ref == "hyprland/workspaces") { + return new waybar::modules::hyprland::Workspaces(id, bar_, config_[name]); + } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 997fe11..79bc637 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -198,4 +198,8 @@ std::string IPC::getSocket1Reply(const std::string& rq) { return response; } +Json::Value IPC::getSocket1JsonReply(const std::string& rq) { + return parser_.parse(getSocket1Reply("j/" + rq)); +} + } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp new file mode 100644 index 0000000..4a19a59 --- /dev/null +++ b/src/modules/hyprland/workspaces.cpp @@ -0,0 +1,72 @@ +#include "modules/hyprland/workspaces.hpp" + +#include + +#include +#include + +namespace waybar::modules::hyprland { +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) { + box_.set_name("workspaces"); + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + event_box_.add(box_); + modulesReady = true; + if (!gIPC.get()) { + gIPC = std::make_unique(); + } + + init(); + + gIPC->registerForIPC("createworkspace", this); + gIPC->registerForIPC("destroyworkspace", this); + gIPC->registerForIPC("urgent", this); +} + +auto Workspaces::update() -> void { + std::lock_guard lock(mutex_); + for (Workspace &workspace : workspaces) { + workspace.update(); + } + AModule::update(); +} + +void Workspaces::onEvent(const std::string &ev) { dp.emit(); } + +void Workspaces::init() { + const auto activeWorkspace = Workspace::parse(gIPC->getSocket1JsonReply("activeworkspace")); + const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); + for (const Json::Value &workspace_json : workspaces_json) { + workspaces.push_back(Workspace::parse(workspace_json)); + } + std::sort(workspaces.begin(), workspaces.end(), + [](Workspace &lhs, Workspace &rhs) { return lhs.id() < rhs.id(); }); + for (auto &workspace : workspaces) { + box_.pack_start(workspace.button(), false, false); + } + + dp.emit(); +} + +Workspaces::~Workspaces() { + gIPC->unregisterForIPC(this); + // wait for possible event handler to finish + std::lock_guard lg(mutex_); +} + +Workspace Workspace::Workspace::parse(const Json::Value &value) { + return Workspace{value["id"].asInt()}; +} + +Workspace::Workspace(int id) : id_(id) { + button_.set_relief(Gtk::RELIEF_NONE); + content_.set_center_widget(label_); + button_.add(content_); +}; + +void Workspace::update() { label_.set_text(std::to_string(id_)); } +} // namespace waybar::modules::hyprland From 887c44bf68fa7baab99b7f6960dc771fc3e261a5 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 1 Jul 2023 00:18:57 +0300 Subject: [PATCH 194/217] finish MVP --- include/modules/hyprland/workspaces.hpp | 24 ++++- src/modules/hyprland/workspaces.cpp | 119 +++++++++++++++++++++--- 2 files changed, 126 insertions(+), 17 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 7903fef..0b8a452 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -7,17 +7,27 @@ namespace waybar::modules::hyprland { +struct WorkspaceDto { + int id; + + static WorkspaceDto parse(const Json::Value& value); +}; + class Workspace { public: Workspace(int id); - int id() { return id_; }; + Workspace(WorkspaceDto dto); + int id() const { return id_; }; + int active() const { return active_; }; + std::string& select_icon(std::map& icons_map); + void set_active(bool value = true) { active_ = value; }; Gtk::Button& button() { return button_; }; - static Workspace parse(const Json::Value&); - void update(); + void update(const std::string& format, const std::string& icon); private: int id_; + bool active_; Gtk::Button button_; Gtk::Box content_; @@ -33,9 +43,13 @@ class Workspaces : public AModule, public EventHandler { private: void onEvent(const std::string&) override; + void sort_workspaces(); - std::vector workspaces; - + std::string format_; + std::map icons_map_; + bool with_icon_; + int active_workspace_id; + std::vector 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 4a19a59..54a425d 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -1,15 +1,32 @@ #include "modules/hyprland/workspaces.hpp" +#include #include #include +#include #include namespace waybar::modules::hyprland { + 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) { + Json::Value config_format = config["format"]; + + format_ = config_format.isString() ? config_format.asString() : "{id}"; + with_icon_ = 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()); + } + + icons_map_.emplace("", ""); + } + box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -22,33 +39,64 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value init(); + gIPC->registerForIPC("workspace", this); gIPC->registerForIPC("createworkspace", this); gIPC->registerForIPC("destroyworkspace", this); - gIPC->registerForIPC("urgent", this); } auto Workspaces::update() -> void { std::lock_guard lock(mutex_); - for (Workspace &workspace : workspaces) { - workspace.update(); + for (Workspace &workspace : workspaces_) { + workspace.set_active(workspace.id() == active_workspace_id); + + std::string &workspace_icon = icons_map_[""]; + if (with_icon_) { + workspace_icon = workspace.select_icon(icons_map_); + } + + workspace.update(format_, workspace_icon); } + AModule::update(); } -void Workspaces::onEvent(const std::string &ev) { dp.emit(); } +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") { + std::from_chars(payload.data(), payload.data() + payload.size(), active_workspace_id); + } else if (eventName == "destroyworkspace") { + int deleted_workspace_id; + std::from_chars(payload.data(), payload.data() + payload.size(), deleted_workspace_id); + workspaces_.erase(std::remove_if(workspaces_.begin(), workspaces_.end(), + [&](Workspace &x) { return x.id() == deleted_workspace_id; })); + } else if (eventName == "createworkspace") { + int new_workspace_id; + std::from_chars(payload.data(), payload.data() + payload.size(), new_workspace_id); + workspaces_.push_back(new_workspace_id); + Gtk::Button& new_workspace_button = workspaces_.back().button(); + box_.pack_end(new_workspace_button, false, false); + sort_workspaces(); + new_workspace_button.show_all(); + } + + dp.emit(); +} void Workspaces::init() { - const auto activeWorkspace = Workspace::parse(gIPC->getSocket1JsonReply("activeworkspace")); + const auto activeWorkspace = WorkspaceDto::parse(gIPC->getSocket1JsonReply("activeworkspace")); + active_workspace_id = activeWorkspace.id; const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (const Json::Value &workspace_json : workspaces_json) { - workspaces.push_back(Workspace::parse(workspace_json)); + workspaces_.push_back(Workspace(WorkspaceDto::parse(workspace_json))); } - std::sort(workspaces.begin(), workspaces.end(), - [](Workspace &lhs, Workspace &rhs) { return lhs.id() < rhs.id(); }); - for (auto &workspace : workspaces) { + + for (auto &workspace : workspaces_) { box_.pack_start(workspace.button(), false, false); } + sort_workspaces(); + dp.emit(); } @@ -58,15 +106,62 @@ Workspaces::~Workspaces() { std::lock_guard lg(mutex_); } -Workspace Workspace::Workspace::parse(const Json::Value &value) { - return Workspace{value["id"].asInt()}; +WorkspaceDto WorkspaceDto::parse(const Json::Value &value) { + return WorkspaceDto{value["id"].asInt()}; } +Workspace::Workspace(WorkspaceDto dto) : Workspace(dto.id){}; + Workspace::Workspace(int id) : id_(id) { button_.set_relief(Gtk::RELIEF_NONE); content_.set_center_widget(label_); button_.add(content_); }; -void Workspace::update() { label_.set_text(std::to_string(id_)); } +void add_or_remove_class(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) { + 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))); +} + +void Workspaces::sort_workspaces() { + std::sort(workspaces_.begin(), workspaces_.end(), + [](Workspace &lhs, Workspace &rhs) { return lhs.id() < rhs.id(); }); + + for (size_t i = 0; i < workspaces_.size(); ++i) { + box_.reorder_child(workspaces_[i].button(), i); + } +} + +std::string &Workspace::select_icon(std::map &icons_map) { + if (active()) { + auto active_icon_it = icons_map.find("active"); + if (active_icon_it != icons_map.end()) { + return active_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; + } + + auto default_icon_it = icons_map.find("default"); + if (default_icon_it != icons_map.end()) { + return default_icon_it->second; + } + + return icons_map[""]; +} } // namespace waybar::modules::hyprland From dbc7471f83331a265367f908b8edeeaa391359cf Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 1 Jul 2023 02:13:36 +0300 Subject: [PATCH 195/217] add docs --- man/waybar-hyprland-workspaces.5.scd | 59 ++++++++++++++++++++++++++++ man/waybar-wlr-workspaces.5.scd | 4 +- 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 man/waybar-hyprland-workspaces.5.scd diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd new file mode 100644 index 0000000..0678fb2 --- /dev/null +++ b/man/waybar-hyprland-workspaces.5.scd @@ -0,0 +1,59 @@ +waybar-wlr-workspaces(5) + +# NAME + +waybar - hyprland workspaces module + +# DESCRIPTION + +The *workspaces* module displays the currently used workspaces in hyprland compositor. + +# CONFIGURATION + +Addressed by *hyprland/workspaces* + +*format*: ++ + typeof: string ++ + default: {id} ++ + The format, how information should be displayed. + +*format-icons*: ++ + typeof: array ++ + Based on the workspace id and state, the corresponding icon gets selected. See *icons*. + +# FORMAT REPLACEMENTS + +*{id}*: id of workspace assigned by compositor + +*{icon}*: Icon, as defined in *format-icons*. + +# ICONS + +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 + +# EXAMPLES + +``` +"wlr/workspaces": { + "format": "{name}: {icon}", + "format-icons": { + "1": "", + "2": "", + "3": "", + "4": "", + "5": "", + "active": "", + "default": "" + }, + "sort-by-number": true +} +``` + +# Style + +- *#workspaces* +- *#workspaces button* +- *#workspaces button.active* diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-wlr-workspaces.5.scd index 169112f..4a256f0 100644 --- a/man/waybar-wlr-workspaces.5.scd +++ b/man/waybar-wlr-workspaces.5.scd @@ -65,7 +65,7 @@ Addressed by *wlr/workspaces* Additional to workspace name matching, the following *format-icons* can be set. - *default*: Will be shown, when no string match is found. -- *focused*: Will be shown, when workspace is focused +- *active*: Will be shown, when workspace is active # EXAMPLES @@ -78,7 +78,7 @@ Additional to workspace name matching, the following *format-icons* can be set. "3": "", "4": "", "5": "", - "focused": "", + "active": "", "default": "" }, "sort-by-number": true From 0b602632f2e607fac2944bc71d791506943a42ca Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 1 Jul 2023 02:23:37 +0300 Subject: [PATCH 196/217] return catch2 --- meson.build | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/meson.build b/meson.build index 9ef4db0..085dcd0 100644 --- a/meson.build +++ b/meson.build @@ -480,6 +480,15 @@ if scdoc.found() endforeach endif +catch2 = dependency( + 'catch2', + version: '>=2.0.0', + fallback: ['catch2', 'catch2_dep'], + required: get_option('tests'), +) +if catch2.found() + subdir('test') +endif clangtidy = find_program('clang-tidy', required: false) From 4f9fbbfa543c04b76e159391578c2ad25a7a4364 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 1 Jul 2023 02:25:15 +0300 Subject: [PATCH 197/217] fix format --- 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 54a425d..cdcb54e 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -74,7 +74,7 @@ void Workspaces::onEvent(const std::string &ev) { int new_workspace_id; std::from_chars(payload.data(), payload.data() + payload.size(), new_workspace_id); workspaces_.push_back(new_workspace_id); - Gtk::Button& new_workspace_button = workspaces_.back().button(); + Gtk::Button &new_workspace_button = workspaces_.back().button(); box_.pack_end(new_workspace_button, false, false); sort_workspaces(); new_workspace_button.show_all(); From 25c2aaabcb1fa52b4ab689099d8471f9b81be56c Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 1 Jul 2023 10:05:41 +0200 Subject: [PATCH 198/217] Fixed build warnings --- src/modules/network.cpp | 5 +++-- src/util/rfkill.cpp | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 5eef166..3079794 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -91,10 +91,11 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf cidr_(0), signal_strength_dbm_(0), signal_strength_(0), + frequency_(0.0), #ifdef WANT_RFKILL - rfkill_{RFKILL_TYPE_WLAN}, + rfkill_{RFKILL_TYPE_WLAN} #endif - frequency_(0.0) { +{ // Start with some "text" in the module's label_. update() will then // update it. Since the text should be different, update() will be able diff --git a/src/util/rfkill.cpp b/src/util/rfkill.cpp index 47da3b5..61be7c5 100644 --- a/src/util/rfkill.cpp +++ b/src/util/rfkill.cpp @@ -63,7 +63,7 @@ bool waybar::util::Rfkill::on_event(Glib::IOCondition cond) { return false; } - if (len < RFKILL_EVENT_SIZE_V1) { + if (static_cast(len) < RFKILL_EVENT_SIZE_V1) { spdlog::error("Wrong size of RFKILL event: {} < {}", len, RFKILL_EVENT_SIZE_V1); return true; } @@ -73,10 +73,9 @@ bool waybar::util::Rfkill::on_event(Glib::IOCondition cond) { on_update.emit(event); } return true; - } else { - spdlog::error("Failed to poll RFKILL control device"); - return false; } + spdlog::error("Failed to poll RFKILL control device"); + return false; } bool waybar::util::Rfkill::getState() const { return state_; } From cc4370f1b29920beb50951cd1d6cb99989722dfb Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 1 Jul 2023 11:08:31 +0200 Subject: [PATCH 199/217] fix: build --- include/modules/network.hpp | 2 +- src/modules/network.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 8568bf9..47701b4 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -78,7 +78,6 @@ class Network : public ALabel { int32_t signal_strength_dbm_; uint8_t signal_strength_; std::string signal_strength_app_; - float frequency_; uint32_t route_priority; util::SleeperThread thread_; @@ -86,6 +85,7 @@ class Network : public ALabel { #ifdef WANT_RFKILL util::Rfkill rfkill_; #endif + float frequency_; }; } // namespace waybar::modules diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 3079794..d9005dd 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -91,11 +91,10 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf cidr_(0), signal_strength_dbm_(0), signal_strength_(0), - frequency_(0.0), #ifdef WANT_RFKILL - rfkill_{RFKILL_TYPE_WLAN} + rfkill_{RFKILL_TYPE_WLAN}, #endif -{ + frequency_(0.0), { // Start with some "text" in the module's label_. update() will then // update it. Since the text should be different, update() will be able From 9d741f89e2ff92d063629401716ebe4d0e25a37b Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 1 Jul 2023 11:12:14 +0200 Subject: [PATCH 200/217] fix: lint --- include/modules/hyprland/backend.hpp | 1 + include/modules/sway/ipc/client.hpp | 2 +- src/modules/network.cpp | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index 179c82f..e23b158 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -5,6 +5,7 @@ #include #include #include + #include "util/json.hpp" namespace waybar::modules::hyprland { diff --git a/include/modules/sway/ipc/client.hpp b/include/modules/sway/ipc/client.hpp index 1ab0292..a9a3e4e 100644 --- a/include/modules/sway/ipc/client.hpp +++ b/include/modules/sway/ipc/client.hpp @@ -7,8 +7,8 @@ #include #include -#include #include +#include #include #include "ipc.hpp" diff --git a/src/modules/network.cpp b/src/modules/network.cpp index d9005dd..e3d0f35 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -94,7 +94,8 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf #ifdef WANT_RFKILL rfkill_{RFKILL_TYPE_WLAN}, #endif - frequency_(0.0), { + frequency_(0.0), +{ // Start with some "text" in the module's label_. update() will then // update it. Since the text should be different, update() will be able From b9cd0287f48309da0ca43da42efb5156afbc41c4 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 1 Jul 2023 11:12:46 +0200 Subject: [PATCH 201/217] fix: typo --- 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 e3d0f35..5eef166 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -94,8 +94,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf #ifdef WANT_RFKILL rfkill_{RFKILL_TYPE_WLAN}, #endif - frequency_(0.0), -{ + frequency_(0.0) { // Start with some "text" in the module's label_. update() will then // update it. Since the text should be different, update() will be able From 35496f461f6afa4b0ae0d07f429817543e98105c Mon Sep 17 00:00:00 2001 From: Kory Prince Date: Sat, 1 Jul 2023 03:09:47 -0500 Subject: [PATCH 202/217] fix regression from #2232: reverse-scrolling was not applied to GTK_SCROLL_SMOOTH events --- src/AModule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 1bfb2de..dc866d4 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -121,9 +121,9 @@ AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { } if (distance_scrolled_y_ < -threshold) { - dir = SCROLL_DIR::UP; + dir = reverse ? SCROLL_DIR::DOWN : SCROLL_DIR::UP; } else if (distance_scrolled_y_ > threshold) { - dir = SCROLL_DIR::DOWN; + dir = reverse ? SCROLL_DIR::UP : SCROLL_DIR::DOWN; } else if (distance_scrolled_x_ > threshold) { dir = SCROLL_DIR::RIGHT; } else if (distance_scrolled_x_ < -threshold) { From 7a01143359228a3bda8874b12b9fb420d7e76f3b Mon Sep 17 00:00:00 2001 From: Kory Prince Date: Sat, 1 Jul 2023 01:53:20 -0500 Subject: [PATCH 203/217] ignore reverse-scrolling from mouse wheel --- src/AModule.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/AModule.cpp b/src/AModule.cpp index dc866d4..76999e1 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -100,6 +100,12 @@ AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { // only affects up/down bool reverse = config_["reverse-scrolling"].asBool(); + // 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) { + reverse = false; + } + switch (e->direction) { case GDK_SCROLL_UP: return reverse ? SCROLL_DIR::DOWN : SCROLL_DIR::UP; From 1ba05d1ffa00014497fafbf84617ed135b4f6cdc Mon Sep 17 00:00:00 2001 From: Kory Prince Date: Sat, 1 Jul 2023 10:35:37 -0500 Subject: [PATCH 204/217] add reverse-mouse-scrolling to configure mouse wheel reverse scrolling --- src/AModule.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 76999e1..b84f551 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -99,11 +99,12 @@ bool AModule::handleToggle(GdkEventButton* const& e) { AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { // only affects up/down bool reverse = config_["reverse-scrolling"].asBool(); + bool reverse_mouse = config_["reverse-mouse-scrolling"].asBool(); // ignore reverse-scrolling if event comes from a mouse wheel GdkDevice* device = gdk_event_get_source_device((GdkEvent *)e); if (device != NULL && gdk_device_get_source(device) == GDK_SOURCE_MOUSE) { - reverse = false; + reverse = reverse_mouse; } switch (e->direction) { From f6a62e258e41b0a342cf2f125cdcc7b61663101f Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Sat, 1 Jul 2023 12:37:46 +0200 Subject: [PATCH 205/217] fixes for hyprland/workspaces --- src/modules/hyprland/window.cpp | 25 +++++++++++-------------- src/modules/hyprland/workspaces.cpp | 6 ++++-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index b122fc9..cb820bc 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -84,30 +84,27 @@ auto Window::update() -> void { } auto Window::getActiveWorkspace() -> Workspace { - const auto workspace = gIPC->getSocket1Reply("j/activeworkspace"); - Json::Value json = parser_.parse(workspace); - assert(json.isObject()); - return Workspace::parse(json); + const auto workspace = gIPC->getSocket1JsonReply("activeworkspace"); + assert(workspace.isObject()); + return Workspace::parse(workspace); } auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { - const auto monitors = gIPC->getSocket1Reply("j/monitors"); - Json::Value json = parser_.parse(monitors); - assert(json.isArray()); - auto monitor = std::find_if(json.begin(), json.end(), + 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(json)) { + 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->getSocket1Reply("j/workspaces"); - json = parser_.parse(workspaces); - assert(json.isArray()); - auto workspace = std::find_if(json.begin(), json.end(), + const auto workspaces = gIPC->getSocket1JsonReply("workspaces"); + assert(workspaces.isArray()); + auto workspace = std::find_if(monitors.begin(), monitors.end(), [&](Json::Value workspace) { return workspace["id"] == id; }); - if (workspace == std::end(json)) { + if (workspace == std::end(monitors)) { spdlog::warn("No workspace with id {}", id); return Workspace{-1, 0, "", ""}; } diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index cdcb54e..4321e22 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -68,8 +68,10 @@ void Workspaces::onEvent(const std::string &ev) { } else if (eventName == "destroyworkspace") { int deleted_workspace_id; std::from_chars(payload.data(), payload.data() + payload.size(), deleted_workspace_id); - workspaces_.erase(std::remove_if(workspaces_.begin(), workspaces_.end(), - [&](Workspace &x) { return x.id() == deleted_workspace_id; })); + auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(), + [&](Workspace &x) { return x.id() == deleted_workspace_id; }); + box_.remove(workspace->button()); + workspaces_.erase(workspace); } else if (eventName == "createworkspace") { int new_workspace_id; std::from_chars(payload.data(), payload.data() + payload.size(), new_workspace_id); From c55cd82b3912a5ba0eeafabdeaf757ed4a49f62a Mon Sep 17 00:00:00 2001 From: David Schulte Date: Sun, 2 Jul 2023 12:23:36 +0200 Subject: [PATCH 206/217] wlr/taskbar: add support for rewrite rules --- man/waybar-wlr-taskbar.5.scd | 22 +++++++++++++++++++++- src/modules/wlr/taskbar.cpp | 8 ++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 8737196..9019dfa 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -81,6 +81,10 @@ Addressed by *wlr/taskbar* typeof: object ++ Dictionary of app_id to be replaced with +*rewrite*: ++ + typeof: object ++ + Rules to rewrite the module format output. See *rewrite rules*. + # FORMAT REPLACEMENTS *{icon}*: The icon of the application. @@ -109,6 +113,18 @@ Addressed by *wlr/taskbar* *close*: Close the application. +# 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 ``` @@ -124,7 +140,11 @@ Addressed by *wlr/taskbar* ], "app_ids-mapping": { "firefoxdeveloperedition": "firefox-developer-edition" - } + }, + "rewrite": { + "Firefox Web Browser": "Firefox", + "Foot Server": "Terminal" + } } ``` diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 0fd4443..3a071b9 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -22,6 +22,8 @@ #include "util/format.hpp" #include "util/string.hpp" +#include "util/rewrite_string.hpp" + namespace waybar::modules::wlr { /* Icon loading functions */ @@ -622,6 +624,9 @@ void Task::update() { fmt::format(fmt::runtime(format_before_), fmt::arg("title", title), fmt::arg("name", name), fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true))); + + txt = waybar::util::rewriteString(txt, config_["rewrite"]); + if (markup) text_before_.set_markup(txt); else @@ -633,6 +638,9 @@ void Task::update() { fmt::format(fmt::runtime(format_after_), fmt::arg("title", title), fmt::arg("name", name), fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true))); + + txt = waybar::util::rewriteString(txt, config_["rewrite"]); + if (markup) text_after_.set_markup(txt); else From 58bdc6a41c1f4967e429c45f6788d7385ba057b9 Mon Sep 17 00:00:00 2001 From: David Schulte Date: Sun, 2 Jul 2023 12:50:18 +0200 Subject: [PATCH 207/217] fix spaces in waybar-wlr-taskbar.5.scd --- man/waybar-wlr-taskbar.5.scd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 9019dfa..5626eae 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -142,9 +142,9 @@ Invalid expressions (e.g., mismatched parentheses) are skipped. "firefoxdeveloperedition": "firefox-developer-edition" }, "rewrite": { - "Firefox Web Browser": "Firefox", - "Foot Server": "Terminal" - } + "Firefox Web Browser": "Firefox", + "Foot Server": "Terminal" + } } ``` From c2f9d889f4cc1aa70fa77a6eec3eb63ff29b8f0f Mon Sep 17 00:00:00 2001 From: Skirmantas Kligys Date: Mon, 3 Jul 2023 13:46:50 -0700 Subject: [PATCH 208/217] Stop MPRIS module from updating every ~20ms as onPlayerMetadata(), onPlayerPlay() callbacks get triggered without regard for update interval. --- include/modules/mpris/mpris.hpp | 1 + src/modules/mpris/mpris.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index ea5f7de..3f2a6a6 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -80,6 +80,7 @@ class Mpris : public ALabel { std::string lastPlayer; util::SleeperThread thread_; + std::chrono::time_point last_update_; }; } // namespace waybar::modules::mpris diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index e434694..7e8efee 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -31,7 +31,8 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) ellipsis_("\u2026"), player_("playerctld"), manager(), - player() { + player(), + last_update_(std::chrono::system_clock::now() - interval_) { if (config_["format-playing"].isString()) { format_playing_ = config_["format-playing"].asString(); } @@ -559,6 +560,10 @@ bool Mpris::handleToggle(GdkEventButton* const& e) { } auto Mpris::update() -> void { + const auto now = std::chrono::system_clock::now(); + if (now - last_update_ < interval_) return; + last_update_ = now; + auto opt = getPlayerInfo(); if (!opt) { event_box_.set_visible(false); From a1cd0acac5d35659fb062923da2eb3771fd79c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apar=C3=ADcio?= Date: Wed, 28 Jun 2023 23:28:38 +0100 Subject: [PATCH 209/217] Fix random segfault on GTK icon functions The segfaults were happening on GTK icon theme functions, which are called via the C++ interface functions such as Gtk::IconTheme::has_icon. There are multiple modules and threads using this functions on the default icon theme by calling Gtk::IconTheme::get_default(), which returns the same object for all callers, and was causing concurrent access to the same internal data structures on the GTK lib. Even a seemingly read-only function such as has_icon can cause writes due to the internal icon cache being updated. To avoid this issues, a program wide global mutex must be used to ensure a single thread is accessing the default icon theme instance. This commit implements wrappers for the existing IconTheme function calls, ensuring the global lock is held while calling the underling GTK functions. --- include/util/gtk_icon.hpp | 12 ++++++++++++ meson.build | 3 ++- src/modules/gamemode.cpp | 5 +++-- src/modules/sni/item.cpp | 7 +++---- src/modules/sway/window.cpp | 11 +++++------ src/modules/upower/upower.cpp | 5 +++-- src/modules/upower/upower_tooltip.cpp | 7 ++++--- src/util/gtk_icon.cpp | 26 ++++++++++++++++++++++++++ 8 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 include/util/gtk_icon.hpp create mode 100644 src/util/gtk_icon.cpp diff --git a/include/util/gtk_icon.hpp b/include/util/gtk_icon.hpp new file mode 100644 index 0000000..67261c1 --- /dev/null +++ b/include/util/gtk_icon.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include +#include + +class DefaultGtkIconThemeWrapper { + private: + static std::mutex default_theme_mutex; + public: + static bool has_icon(const std::string&); + static Glib::RefPtr load_icon(const char*, int, Gtk::IconLookupFlags); +}; diff --git a/meson.build b/meson.build index 87c03bf..aa250b7 100644 --- a/meson.build +++ b/meson.build @@ -173,7 +173,8 @@ src_files = files( 'src/util/prepare_for_sleep.cpp', 'src/util/ustring_clen.cpp', 'src/util/sanitize_str.cpp', - 'src/util/rewrite_string.cpp' + 'src/util/rewrite_string.cpp', + 'src/util/gtk_icon.cpp' ) inc_dirs = ['include'] diff --git a/src/modules/gamemode.cpp b/src/modules/gamemode.cpp index 3b2213e..10a7ec7 100644 --- a/src/modules/gamemode.cpp +++ b/src/modules/gamemode.cpp @@ -16,10 +16,11 @@ #include "glibmm/ustring.h" #include "glibmm/variant.h" #include "glibmm/varianttype.h" -#include "gtkmm/icontheme.h" #include "gtkmm/label.h" #include "gtkmm/tooltip.h" +#include "util/gtk_icon.hpp" + namespace waybar::modules { Gamemode::Gamemode(const std::string& id, const Json::Value& config) : AModule(config, "gamemode", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0), icon_(), label_() { @@ -224,7 +225,7 @@ auto Gamemode::update() -> void { label_.set_markup(str); if (useIcon) { - if (!Gtk::IconTheme::get_default()->has_icon(iconName)) { + if (!DefaultGtkIconThemeWrapper::has_icon(iconName)) { iconName = DEFAULT_ICON_NAME; } icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID); diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index e5bd6ab..9d3fc4b 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -9,6 +9,7 @@ #include #include "util/format.hpp" +#include "util/gtk_icon.hpp" template <> struct fmt::formatter : formatter { @@ -379,10 +380,8 @@ Glib::RefPtr Item::getIconByName(const std::string& name, int reque return icon_theme->load_icon(name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } - Glib::RefPtr default_theme = Gtk::IconTheme::get_default(); - default_theme->rescan_if_needed(); - return default_theme->load_icon(name.c_str(), tmp_size, - Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); + return DefaultGtkIconThemeWrapper::load_icon(name.c_str(), tmp_size, + Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } double Item::getScaledIconSize() { diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 030f93b..6f97b2e 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -13,6 +12,7 @@ #include #include "util/rewrite_string.hpp" +#include "util/gtk_icon.hpp" namespace waybar::modules::sway { @@ -81,13 +81,12 @@ std::optional getIconName(const std::string& app_id, const std::s if (!desktop_file_path.has_value()) { // Try some heuristics to find a matching icon - const auto default_icon_theme = Gtk::IconTheme::get_default(); - if (default_icon_theme->has_icon(app_id)) { + if (DefaultGtkIconThemeWrapper::has_icon(app_id)) { return app_id; } const auto app_id_desktop = app_id + "-desktop"; - if (default_icon_theme->has_icon(app_id_desktop)) { + if (DefaultGtkIconThemeWrapper::has_icon(app_id_desktop)) { return app_id_desktop; } @@ -101,7 +100,7 @@ std::optional getIconName(const std::string& app_id, const std::s 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 (default_icon_theme->has_icon(first_word)) { + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { return first_word; } } @@ -109,7 +108,7 @@ std::optional getIconName(const std::string& app_id, const std::s 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 (default_icon_theme->has_icon(first_word)) { + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { return first_word; } } diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index 7aafdc6..ea03934 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -5,9 +5,10 @@ #include #include -#include "gtkmm/icontheme.h" #include "gtkmm/tooltip.h" +#include "util/gtk_icon.hpp" + namespace waybar::modules::upower { UPower::UPower(const std::string& id, const Json::Value& config) : AModule(config, "upower", id), @@ -372,7 +373,7 @@ auto UPower::update() -> void { label_.set_markup(onlySpaces ? "" : label_format); // Set icon - if (icon_name == NULL || !Gtk::IconTheme::get_default()->has_icon(icon_name)) { + 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); diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp index 7dd5d10..fddb688 100644 --- a/src/modules/upower/upower_tooltip.cpp +++ b/src/modules/upower/upower_tooltip.cpp @@ -2,10 +2,11 @@ #include "gtkmm/box.h" #include "gtkmm/enums.h" -#include "gtkmm/icontheme.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(), @@ -62,7 +63,7 @@ uint UPowerTooltip::updateTooltip(Devices& devices) { std::string deviceIconName = getDeviceIcon(kind); Gtk::Image* deviceIcon = new Gtk::Image(); deviceIcon->set_pixel_size(iconSize); - if (!Gtk::IconTheme::get_default()->has_icon(deviceIconName)) { + if (!DefaultGtkIconThemeWrapper::has_icon(deviceIconName)) { deviceIconName = "battery-missing-symbolic"; } deviceIcon->set_from_icon_name(deviceIconName, Gtk::ICON_SIZE_INVALID); @@ -79,7 +80,7 @@ uint UPowerTooltip::updateTooltip(Devices& devices) { // Set icon Gtk::Image* icon = new Gtk::Image(); icon->set_pixel_size(iconSize); - if (icon_name == NULL || !Gtk::IconTheme::get_default()->has_icon(icon_name)) { + 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); diff --git a/src/util/gtk_icon.cpp b/src/util/gtk_icon.cpp new file mode 100644 index 0000000..70aeec2 --- /dev/null +++ b/src/util/gtk_icon.cpp @@ -0,0 +1,26 @@ +#include "util/gtk_icon.hpp" + +/* We need a global mutex for accessing the object returned by Gtk::IconTheme::get_default() + * because it always returns the same object across different threads, and concurrent + * access can cause data corruption and lead to invalid memory access and crashes. + * Even concurrent calls that seem read only such as has_icon can cause issues because + * the GTK lib may update the internal icon cache on this calls. +*/ + +std::mutex DefaultGtkIconThemeWrapper::default_theme_mutex; + +bool DefaultGtkIconThemeWrapper::has_icon(const std::string& value) { + + const std::lock_guard lock(default_theme_mutex); + + return Gtk::IconTheme::get_default()->has_icon(value); +} + +Glib::RefPtr DefaultGtkIconThemeWrapper::load_icon(const char *name, int tmp_size, Gtk::IconLookupFlags flags) { + + const std::lock_guard lock(default_theme_mutex); + + auto default_theme = Gtk::IconTheme::get_default(); + default_theme->rescan_if_needed(); + return default_theme->load_icon(name, tmp_size, flags); +} From c9b963e82b751e5af9f6105cf1cbae46c537829e Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 4 Jul 2023 01:17:26 +0300 Subject: [PATCH 210/217] fix segfault --- include/modules/hyprland/workspaces.hpp | 7 ++- src/modules/hyprland/workspaces.cpp | 67 ++++++++++++++++++------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 0b8a452..5033b8d 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -1,5 +1,6 @@ #include #include +#include #include "AModule.hpp" #include "bar.hpp" @@ -44,12 +45,16 @@ class Workspaces : public AModule, public EventHandler { private: void onEvent(const std::string&) override; void sort_workspaces(); + void create_workspace(int id); + void remove_workspace(int id); std::string format_; std::map icons_map_; bool with_icon_; int active_workspace_id; - std::vector workspaces_; + std::vector> workspaces_; + std::vector workspaces_to_create_; + std::vector workspaces_to_remove_; std::mutex mutex_; const Bar& bar_; Gtk::Box box_; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 4321e22..23a323a 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -5,6 +5,7 @@ #include #include +#include #include namespace waybar::modules::hyprland { @@ -45,22 +46,34 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value } auto Workspaces::update() -> void { - std::lock_guard lock(mutex_); - for (Workspace &workspace : workspaces_) { - workspace.set_active(workspace.id() == active_workspace_id); + for (int &workspace_to_remove : workspaces_to_remove_) { + remove_workspace(workspace_to_remove); + } + + workspaces_to_remove_.clear(); + + for (int &workspace_to_create : workspaces_to_create_) { + create_workspace(workspace_to_create); + } + + workspaces_to_create_.clear(); + + for (std::unique_ptr &workspace : workspaces_) { + workspace->set_active(workspace->id() == active_workspace_id); std::string &workspace_icon = icons_map_[""]; if (with_icon_) { - workspace_icon = workspace.select_icon(icons_map_); + workspace_icon = workspace->select_icon(icons_map_); } - workspace.update(format_, workspace_icon); + workspace->update(format_, workspace_icon); } AModule::update(); } 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") { @@ -68,33 +81,49 @@ void Workspaces::onEvent(const std::string &ev) { } else if (eventName == "destroyworkspace") { int deleted_workspace_id; std::from_chars(payload.data(), payload.data() + payload.size(), deleted_workspace_id); - auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(), - [&](Workspace &x) { return x.id() == deleted_workspace_id; }); - box_.remove(workspace->button()); - workspaces_.erase(workspace); + workspaces_to_remove_.push_back(deleted_workspace_id); } else if (eventName == "createworkspace") { int new_workspace_id; std::from_chars(payload.data(), payload.data() + payload.size(), new_workspace_id); - workspaces_.push_back(new_workspace_id); - Gtk::Button &new_workspace_button = workspaces_.back().button(); - box_.pack_end(new_workspace_button, false, false); - sort_workspaces(); - new_workspace_button.show_all(); + workspaces_to_create_.push_back(new_workspace_id); } dp.emit(); } +void Workspaces::create_workspace(int id) { + workspaces_.push_back(std::make_unique(id)); + 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) { + auto workspace = std::find_if( + workspaces_.begin(), workspaces_.end(), + [&](std::unique_ptr &x) { return x->id() == id; }); + + if (workspace == workspaces_.end()) { + spdlog::warn("Can't find workspace with id {}", workspace->get()->id()); + return; + } + + box_.remove(workspace->get()->button()); + workspaces_.erase(workspace); +} + void Workspaces::init() { const auto activeWorkspace = WorkspaceDto::parse(gIPC->getSocket1JsonReply("activeworkspace")); active_workspace_id = activeWorkspace.id; const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (const Json::Value &workspace_json : workspaces_json) { - workspaces_.push_back(Workspace(WorkspaceDto::parse(workspace_json))); + workspaces_.push_back( + std::make_unique(Workspace(WorkspaceDto::parse(workspace_json)))); } for (auto &workspace : workspaces_) { - box_.pack_start(workspace.button(), false, false); + box_.pack_start(workspace->button(), false, false); } sort_workspaces(); @@ -139,10 +168,12 @@ void Workspace::update(const std::string &format, const std::string &icon) { void Workspaces::sort_workspaces() { std::sort(workspaces_.begin(), workspaces_.end(), - [](Workspace &lhs, Workspace &rhs) { return lhs.id() < rhs.id(); }); + [](std::unique_ptr &lhs, std::unique_ptr &rhs) { + return lhs->id() < rhs->id(); + }); for (size_t i = 0; i < workspaces_.size(); ++i) { - box_.reorder_child(workspaces_[i].button(), i); + box_.reorder_child(workspaces_[i]->button(), i); } } From de626dcbbccb5e20b597dec7018a930059ca8ab9 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 4 Jul 2023 01:24:34 +0300 Subject: [PATCH 211/217] format --- include/modules/hyprland/workspaces.hpp | 1 + src/modules/hyprland/workspaces.cpp | 17 ++++++++--------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 5033b8d..500bbe3 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -1,5 +1,6 @@ #include #include + #include #include "AModule.hpp" diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 23a323a..f5cc832 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -100,17 +100,16 @@ void Workspaces::create_workspace(int id) { } void Workspaces::remove_workspace(int id) { - auto workspace = std::find_if( - workspaces_.begin(), workspaces_.end(), - [&](std::unique_ptr &x) { return x->id() == id; }); + auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(), + [&](std::unique_ptr &x) { return x->id() == id; }); - if (workspace == workspaces_.end()) { - spdlog::warn("Can't find workspace with id {}", workspace->get()->id()); - return; - } + if (workspace == workspaces_.end()) { + spdlog::warn("Can't find workspace with id {}", workspace->get()->id()); + return; + } - box_.remove(workspace->get()->button()); - workspaces_.erase(workspace); + box_.remove(workspace->get()->button()); + workspaces_.erase(workspace); } void Workspaces::init() { From f26a125d150735286f855b15b1634231dfedf8d4 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 4 Jul 2023 01:26:16 +0300 Subject: [PATCH 212/217] format --- src/AModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index b84f551..2626cd8 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -102,7 +102,7 @@ AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) { bool reverse_mouse = config_["reverse-mouse-scrolling"].asBool(); // ignore reverse-scrolling if event comes from a mouse wheel - GdkDevice* device = gdk_event_get_source_device((GdkEvent *)e); + GdkDevice* device = gdk_event_get_source_device((GdkEvent*)e); if (device != NULL && gdk_device_get_source(device) == GDK_SOURCE_MOUSE) { reverse = reverse_mouse; } From cf4d58f30a0060f3ab96e8a39db537ffcf6860fe Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 4 Jul 2023 11:30:44 -0400 Subject: [PATCH 213/217] Catch2 bump --- subprojects/catch2.wrap | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap index ea61b94..4a6f836 100644 --- a/subprojects/catch2.wrap +++ b/subprojects/catch2.wrap @@ -1,12 +1,10 @@ [wrap-file] -directory = Catch2-3.1.0 -source_url = https://github.com/catchorg/Catch2/archive/v3.1.0.tar.gz -source_filename = Catch2-3.1.0.tar.gz -source_hash = c252b2d9537e18046d8b82535069d2567f77043f8e644acf9a9fffc22ea6e6f7 -patch_filename = catch2_3.1.0-1_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/catch2_3.1.0-1/get_patch -patch_hash = 4ebf4277aed574a9912a79f4817a310d837798e099bbafa6097be23a7f5e3ae4 -wrapdb_version = 3.1.0-1 +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 [provide] catch2 = catch2_dep From d3be9a736367e75c2db7791e62ed99a1652acb30 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Tue, 4 Jul 2023 19:40:43 +0200 Subject: [PATCH 214/217] Fix rare segfault when destroying workspace --- 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 f5cc832..e169f91 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -104,7 +104,7 @@ void Workspaces::remove_workspace(int id) { [&](std::unique_ptr &x) { return x->id() == id; }); if (workspace == workspaces_.end()) { - spdlog::warn("Can't find workspace with id {}", workspace->get()->id()); + spdlog::warn("Can't find workspace with id {}", id); return; } From 7ef80d563b156733b42746a4d385a9c27914727f Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Tue, 4 Jul 2023 19:48:04 +0200 Subject: [PATCH 215/217] Formatting fixes --- src/modules/gamemode.cpp | 1 - src/modules/sway/window.cpp | 2 +- src/modules/upower/upower.cpp | 1 - src/modules/upower/upower_tooltip.cpp | 1 - src/util/gtk_icon.cpp | 7 +++---- 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/modules/gamemode.cpp b/src/modules/gamemode.cpp index 10a7ec7..811f13c 100644 --- a/src/modules/gamemode.cpp +++ b/src/modules/gamemode.cpp @@ -18,7 +18,6 @@ #include "glibmm/varianttype.h" #include "gtkmm/label.h" #include "gtkmm/tooltip.h" - #include "util/gtk_icon.hpp" namespace waybar::modules { diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 6f97b2e..50aea60 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -11,8 +11,8 @@ #include #include -#include "util/rewrite_string.hpp" #include "util/gtk_icon.hpp" +#include "util/rewrite_string.hpp" namespace waybar::modules::sway { diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index ea03934..1262d0a 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -6,7 +6,6 @@ #include #include "gtkmm/tooltip.h" - #include "util/gtk_icon.hpp" namespace waybar::modules::upower { diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp index fddb688..45544bb 100644 --- a/src/modules/upower/upower_tooltip.cpp +++ b/src/modules/upower/upower_tooltip.cpp @@ -4,7 +4,6 @@ #include "gtkmm/enums.h" #include "gtkmm/image.h" #include "gtkmm/label.h" - #include "util/gtk_icon.hpp" namespace waybar::modules::upower { diff --git a/src/util/gtk_icon.cpp b/src/util/gtk_icon.cpp index 70aeec2..5dd741f 100644 --- a/src/util/gtk_icon.cpp +++ b/src/util/gtk_icon.cpp @@ -5,19 +5,18 @@ * access can cause data corruption and lead to invalid memory access and crashes. * Even concurrent calls that seem read only such as has_icon can cause issues because * the GTK lib may update the internal icon cache on this calls. -*/ + */ std::mutex DefaultGtkIconThemeWrapper::default_theme_mutex; bool DefaultGtkIconThemeWrapper::has_icon(const std::string& value) { - const std::lock_guard lock(default_theme_mutex); return Gtk::IconTheme::get_default()->has_icon(value); } -Glib::RefPtr DefaultGtkIconThemeWrapper::load_icon(const char *name, int tmp_size, Gtk::IconLookupFlags flags) { - +Glib::RefPtr DefaultGtkIconThemeWrapper::load_icon(const char* name, int tmp_size, + Gtk::IconLookupFlags flags) { const std::lock_guard lock(default_theme_mutex); auto default_theme = Gtk::IconTheme::get_default(); From 18d6dfea8818c2f35766c58bd1a4221f35cf8a77 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 4 Jul 2023 22:35:15 +0200 Subject: [PATCH 216/217] feat(man): start_hidden --- man/waybar.5.scd.in | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 7061bc6..c8e24d0 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -78,7 +78,12 @@ Also a minimal example configuration can be found on the at the bottom of this m Selects one of the preconfigured display modes. This is an equivalent of the sway-bar(5) *mode* command and supports the same values: *dock*, *hide*, *invisible*, *overlay*. ++ Note: *hide* and *invisible* modes may be not as useful without Sway IPC. -modifier-reset ++ +*start_hidden* ++ + typeof: bool ++ + default: *false* ++ + Option to start the bar hidden. + +*modifier-reset* ++ typeof: string ++ default: *press* Defines the timing of modifier key to reset the bar visibility. From 6c196b8f8deed2199e94955dc8e70d58ac9a796a Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 4 Jul 2023 22:35:27 +0200 Subject: [PATCH 217/217] fix: lint --- include/util/gtk_icon.hpp | 16 +++++++++------- src/modules/wlr/taskbar.cpp | 3 +-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/util/gtk_icon.hpp b/include/util/gtk_icon.hpp index 67261c1..44555f6 100644 --- a/include/util/gtk_icon.hpp +++ b/include/util/gtk_icon.hpp @@ -1,12 +1,14 @@ #pragma once -#include -#include #include +#include +#include + class DefaultGtkIconThemeWrapper { - private: - static std::mutex default_theme_mutex; - public: - static bool has_icon(const std::string&); - static Glib::RefPtr load_icon(const char*, int, Gtk::IconLookupFlags); + private: + static std::mutex default_theme_mutex; + + public: + static bool has_icon(const std::string&); + static Glib::RefPtr load_icon(const char*, int, Gtk::IconLookupFlags); }; diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 3a071b9..9e09d7a 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -20,9 +20,8 @@ #include "glibmm/fileutils.h" #include "glibmm/refptr.h" #include "util/format.hpp" -#include "util/string.hpp" - #include "util/rewrite_string.hpp" +#include "util/string.hpp" namespace waybar::modules::wlr {