From d5a86526bca19ec9032976a5397e34915edd5324 Mon Sep 17 00:00:00 2001 From: Narice Date: Tue, 6 Dec 2022 18:44:26 +0100 Subject: [PATCH 01/66] 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 00000000..3550a30f --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index 56a2f73d..11cc390c 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 00000000..6d3b6b8e --- /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 00000000..d9091867 --- /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 00000000..26654466 --- /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 02/66] 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 08ab05e0..e97f05f3 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 a40d4128..d6a9570b 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 03/66] 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 d6a9570b..3114e16a 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 04/66] 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 b7a9cd05..f577e063 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 2c855d39..401b7594 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 7a358c18..e6f1bd07 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 05/66] 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 3114e16a..910637b6 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 06/66] 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 910637b6..776ae11b 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 07/66] 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 e97f05f3..ef129fbd 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 776ae11b..b99e26b3 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 08/66] 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 b99e26b3..151c048a 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 09/66] 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 13e30ec6..738c835c 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 10/66] 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 738c835c..19a06328 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 11/66] 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 cb169951..4a4e1759 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 12/66] 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 d69930f9..2ec3cf0f 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 00000000..e33cdae2 --- /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 00000000..a00a2762 --- /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 557a02dc..d6b9e177 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 d16cb523..9e3890e7 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 00000000..6a122920 --- /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 13/66] 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 6a122920..6eb0942d 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 14/66] Ignore .cache generated by clangd --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 56a2f73d..d1535662 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 15/66] 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 d1535662..bf2bdbf9 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 19a06328..6cadd623 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 16/66] 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 151c048a..55f2c5bc 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 17/66] 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 b575e098..644cba46 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 b7e51e4e..f8c18915 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 18/66] 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 2ec3cf0f..e2548a9a 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 e33cdae2..c36578cd 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 00b8393c..06b61eef 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 f8c18915..b621b83b 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 19/66] 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 cdbbc3d4..8fedf842 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 20/66] 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 d69930f9..688b9acc 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 00000000..4f8ddb16 --- /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 00000000..d2a72d98 --- /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 54340f21..b1ed4c52 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 557a02dc..83f5998f 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 402912f4..98cd4949 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 d16cb523..3ccf2581 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 00000000..651dfd51 --- /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 21/66] 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 03836aaa..d9a3dd58 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 40a1b2e3..e7cbba69 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 026d8fdb..578588c7 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 a61dcd3e..e1abd44a 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 536ef637..2b683983 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 49dea272..bdb42fbf 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 22/66] 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 4f8ddb16..040401f8 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 23/66] 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 6f11e1f2..aa80b54b 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 24/66] 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 aa80b54b..ade02695 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 25/66] chore: 0.9.17 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 5b3c4756..ebf68d45 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 26/66] 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 97d84bd0..54602446 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 27/66] 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 c13d5cee..e99e94f1 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 6e5ebdb6..2ad1a2b0 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 5da7d3d4..0e74b762 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 28/66] 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 97ff0a54..b3e51a6c 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 29/66] 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 2ad1a2b0..19e0cd2f 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 30/66] 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 23dba38d..39d93d4d 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 4e8ccf36b54cacf5281726d23ea14312a133f977 Mon Sep 17 00:00:00 2001 From: Sasha Moak Date: Thu, 12 Jan 2023 16:17:11 -0800 Subject: [PATCH 31/66] 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 c0ee7f0b..fa988fcf 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 b1ed4c52..7566dd00 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 9a12a9b5..9652e1e2 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 32/66] 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 6eb0942d..d61c8d41 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 33/66] 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 77dab083..a6705eaa 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 4d6495cb..5c3df7b2 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 34/66] 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 ebf68d45..7b631816 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 7ff6f2ae..f496d7ab 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 cdc96b0c..3d0f007d 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 7970c262..daeee69e 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 79469d41..9f9f5dc5 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 35/66] 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 55f2c5bc..9871024c 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 36/66] 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 ef129fbd..c97565de 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 b9f9ea98..ec948bd5 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 9871024c..360b7467 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 00000000..704feb22 --- /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 b1e11237..02cbb2a4 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 9f9f5dc5..00000000 --- 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 37/66] 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 e6df067f..f8a55fa3 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 fac0377d..00b6a31c 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 aa734a4f..77c1dc0e 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 b3e51a6c..abd1240c 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 e6a1fe39..c3a25473 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 360b7467..0dbd255d 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 23dba38d..3100adc5 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 5578dc2f..eb4d902f 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 7129297d..1b8d7fcb 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 6cadd623..d398b232 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 d61c8d41..22acbf31 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 d3d06cc5..47daae9b 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 c4109b0e..a5fc9ac7 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 e4340b14..fe2a4be4 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 3a92110c..9bd6fcd6 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 b2750b68..4c081d6a 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 4a0e6347..544d7814 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 401b7594..e7288974 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 651dfd51..f11821fc 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 a4797eec..84093119 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 c7979976..06307103 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 4a51c832..1f788e09 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 d0f492f6..d93938c1 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 e6f1bd07..72e7207c 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 d3730a11..a5860bd0 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 7eaa523a..b81735e5 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 59e30530..17dc2707 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 0e74b762..7d60d292 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 b621b83b..08742ae6 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 eca05a7d..ff722d7a 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 eb29913e..38c1f7fe 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 2f7c6e9c..418fc585 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 9652e1e2..fd1a0d38 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 54602446..427083ba 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 ade02695..c1b68c84 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 38/66] 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 704feb22..aa6d79b0 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 39/66] 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 21dc647b..558a8d4e 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 c97565de..a7290f6a 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 ec948bd5..380bb6e7 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 7b631816..32bfd2bb 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 0dbd255d..76ec73ce 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 40/66] 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 a6da7ef7..550f9453 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 e9f1656f..d11d2ccc 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 4c77c3af..c82af85b 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 41/66] 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 e7cbba69..cab4146b 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 e1abd44a..5892159c 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 42/66] 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 357f70ec..c5f0ebc1 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 f46b7d0f..62ff80c9 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 43/66] 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 feff9f6e..df7086f0 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 7566dd00..704d666b 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 ebf68d45..96ef1f3d 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 44/66] 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 c5f0ebc1..35625cd1 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 9818f5a5..b7e1d2df 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 45/66] 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 d398b232..f9ad0915 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 46/66] 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 a6da7ef7..550f9453 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 e9f1656f..d11d2ccc 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 4c77c3af..c82af85b 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 e7cbba69..cab4146b 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 e1abd44a..5892159c 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 357f70ec..35625cd1 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 21dc647b..558a8d4e 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 ef129fbd..9d615ae2 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 77dab083..a6705eaa 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 e6df067f..f8a55fa3 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 00000000..380bb6e7 --- /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 fac0377d..00b6a31c 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 b9f9ea98..00000000 --- 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 feff9f6e..df7086f0 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 7566dd00..704d666b 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 ebf68d45..77b292a7 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 f46b7d0f..62ff80c9 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 aa734a4f..77c1dc0e 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 b3e51a6c..abd1240c 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 e6a1fe39..c3a25473 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 55f2c5bc..37d5e30e 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 23dba38d..b7e1d2df 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 5578dc2f..eb4d902f 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 7129297d..1b8d7fcb 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 6cadd623..f9ad0915 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 6eb0942d..22acbf31 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 d3d06cc5..47daae9b 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 c4109b0e..a5fc9ac7 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 e4340b14..fe2a4be4 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 3a92110c..9bd6fcd6 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 b2750b68..4c081d6a 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 4a0e6347..544d7814 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 401b7594..e7288974 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 651dfd51..f11821fc 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 a4797eec..84093119 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 c7979976..06307103 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 4a51c832..1f788e09 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 d0f492f6..d93938c1 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 e6f1bd07..72e7207c 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 4d6495cb..5c3df7b2 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 d3730a11..a5860bd0 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 7eaa523a..b81735e5 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 59e30530..17dc2707 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 0e74b762..7d60d292 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 b621b83b..08742ae6 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 eca05a7d..ff722d7a 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 eb29913e..38c1f7fe 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 2f7c6e9c..418fc585 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 9652e1e2..fd1a0d38 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 54602446..427083ba 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 ade02695..c1b68c84 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 7ff6f2ae..f496d7ab 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 cdc96b0c..3d0f007d 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 00000000..aa6d79b0 --- /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 7970c262..daeee69e 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 b1e11237..02cbb2a4 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 79469d41..00000000 --- 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 47/66] 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 48/66] 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 06b61eef..b2c89460 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 df7086f0..2d09f666 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 c5b70a99..028d69e0 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 eed19aea..769c16ad 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 49/66] 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 028d69e0..4f196f5d 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 769c16ad..130fe790 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 50/66] 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 37d5e30e..012d3d64 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 51/66] 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 06b61eef..391922c2 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 eed19aea..01f1375b 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 52/66] 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 01f1375b..2388d608 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 53/66] 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 9d615ae2..10012ab6 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 012d3d64..0e1ea606 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 54/66] 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 2388d608..069f723f 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 55/66] 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 f9ad0915..622c28d2 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 56/66] 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 069f723f..d2eb7798 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 57/66] 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 391922c2..06b61eef 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 d2eb7798..b153523f 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 a9c9f1d705991c7f6ff9de7eac3430a219011978 Mon Sep 17 00:00:00 2001 From: Sasha Moak Date: Tue, 31 Jan 2023 17:56:58 -0800 Subject: [PATCH 58/66] 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 fd1a0d38..4c7a2d0b 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 59/66] 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 81e2516a..a2ce878d 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 9c8ba791..ca3d922b 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 77c1dc0e..65eb11ad 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 60/66] 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 df7086f0..ff0e34c3 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 61/66] 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 0e1ea606..6a06193f 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 62/66] 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 a2ce878d..1882a83d 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 65eb11ad..7aa5889d 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 63/66] 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 7aa5889d..a46b2968 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 64/66] 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 0e1ea606..2c22721f 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 65/66] 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 e06774b4..ff446ffc 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 bec55c7e..7dd5d10e 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 66/66] 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 1882a83d..6597d39c 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 10012ab6..c318e8ba 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 22f49821..a938617a 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_);