mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
Compare commits
245 Commits
Author | SHA1 | Date | |
---|---|---|---|
e30fba0b8f | |||
b5ea14c896 | |||
5b33a5917c | |||
66b71cc857 | |||
c8237437d2 | |||
57544fe694 | |||
e90c66a102 | |||
db1d859881 | |||
cc961d40b3 | |||
29cebaa0a7 | |||
2c7bbe9852 | |||
eb74623b7d | |||
b9260575e5 | |||
8cf676176d | |||
3db5673e70 | |||
46f5034030 | |||
6f7e7c5199 | |||
2ba11e8401 | |||
b084bf721e | |||
e40860c3e9 | |||
2bcd0eb09f | |||
94c34a29c4 | |||
7fb37c0069 | |||
ce2ede70f2 | |||
b91adc9f29 | |||
86b3e456e1 | |||
1eb4684b60 | |||
a3904ff039 | |||
88828265c0 | |||
600653538b | |||
28635c1f6d | |||
b09f6cc731 | |||
c8910901e5 | |||
6b7fd36863 | |||
20b091dcd8 | |||
106535e3eb | |||
d3520536c4 | |||
05e5a7e5fd | |||
2b07dea3a6 | |||
629686f760 | |||
c71c0fca6e | |||
9207fff627 | |||
dae7794bdf | |||
c087d8c318 | |||
6dfa74bd5d | |||
e9b29613b2 | |||
d01ce7d812 | |||
8ce1d15885 | |||
4d9e0ea802 | |||
2f04a49129 | |||
6dc33fe88f | |||
effad1a5c3 | |||
05efdb74f0 | |||
d3bcff31e5 | |||
430f0e5d65 | |||
4dff1d4b2b | |||
56df72f61c | |||
8fdd456fa9 | |||
3e1176e896 | |||
52a8b1d911 | |||
3ecd4030e3 | |||
841a004acd | |||
839975c348 | |||
185aa104b0 | |||
24d56023fd | |||
d3e7a8c797 | |||
b2279c9565 | |||
52906407af | |||
2721e19ee6 | |||
388912d4a7 | |||
f62b3d0e9d | |||
2d0fdaeec6 | |||
b9348180f5 | |||
1e2b9cb5ed | |||
62702a4878 | |||
c85738574c | |||
96a8e5df83 | |||
85b4ff4f81 | |||
0f6eff1f20 | |||
7a5e702334 | |||
8687ed2068 | |||
2be0e966e1 | |||
facb53e81f | |||
2bfc0e1da6 | |||
6d24b22b21 | |||
5f0fa71f32 | |||
4f81e55e41 | |||
ca0122c3cb | |||
7200b16520 | |||
e488daae16 | |||
f3df15650a | |||
f8a9a970b2 | |||
75e21c4853 | |||
495b63d7dc | |||
52983c7188 | |||
2211a79840 | |||
daca57129f | |||
19c7c0763f | |||
14c6550593 | |||
7aae93e7ed | |||
dffba78401 | |||
a8a1a4985f | |||
31683d9e2a | |||
00e143d47e | |||
6e9ba3fc01 | |||
a373f6b654 | |||
91bd28d410 | |||
acde076913 | |||
f5655526d0 | |||
56f956ff90 | |||
f97c1c7136 | |||
9ee883ee1b | |||
1887512ba1 | |||
2ae13c4092 | |||
c5f1771375 | |||
c4bace504c | |||
3bfeed31bc | |||
d774de6c46 | |||
b20041d85d | |||
e4900db9a2 | |||
e2bfa5e019 | |||
423d8495e4 | |||
1fb2b8efd5 | |||
3299d4a25c | |||
e125bbeb4d | |||
55c59253d6 | |||
e7deab92c7 | |||
d21f29cb14 | |||
d8a808f76c | |||
5ef6636237 | |||
fc632f50ec | |||
31e4c9023e | |||
3e1c341933 | |||
0d1016d4d2 | |||
12725f4418 | |||
5c64d034a2 | |||
14fa9cf7b7 | |||
6c196b8f8d | |||
18d6dfea88 | |||
d2eb8eb9fc | |||
65f73d3e95 | |||
265b4edb2e | |||
18f5af835f | |||
7ef80d563b | |||
d3be9a7363 | |||
5f29e5a5e8 | |||
cf4d58f30a | |||
bb61461aad | |||
c91c8bbc45 | |||
f26a125d15 | |||
de626dcbbc | |||
c9b963e82b | |||
a1cd0acac5 | |||
c2f9d889f4 | |||
cdece498c1 | |||
58bdc6a41c | |||
c55cd82b39 | |||
91588fb8bb | |||
6b9600fecd | |||
0bfb29789c | |||
f6a62e258e | |||
1ba05d1ffa | |||
7a01143359 | |||
35496f461f | |||
b9cd0287f4 | |||
9d741f89e2 | |||
cc4370f1b2 | |||
85854c71d6 | |||
6ed550117c | |||
15fe85d18d | |||
b62e5eb822 | |||
25c2aaabcb | |||
cd49eef229 | |||
4f9fbbfa54 | |||
0b602632f2 | |||
dbc7471f83 | |||
887c44bf68 | |||
33236c222f | |||
4116490535 | |||
08e18387c9 | |||
0fff38c751 | |||
afc489869a | |||
ce4da59f34 | |||
43434254e0 | |||
66ce74d29b | |||
77a8420aaf | |||
b0f89f2bc1 | |||
7cda2dfd1a | |||
238cfa9547 | |||
b163b21ace | |||
30c4f08773 | |||
fd7c2a2012 | |||
4f14ce3285 | |||
e233022d1a | |||
e403c3b71b | |||
d367b7e1d6 | |||
038644f8d9 | |||
d650c597f9 | |||
5196009656 | |||
4d8515930f | |||
192cea97f2 | |||
5bfdbf116d | |||
83f3c2321e | |||
dff0583c12 | |||
73c7e54535 | |||
0f8c156f24 | |||
fff4509723 | |||
17af49d421 | |||
9f38631c7f | |||
62f4125927 | |||
a67e692d4a | |||
3c9cbc99d7 | |||
c5379fa52d | |||
070110af0c | |||
6bf5b15c13 | |||
a9779c2aa2 | |||
3af1853260 | |||
d638610db1 | |||
87023c39f8 | |||
e96610e31a | |||
d61b1d54de | |||
e397f568b7 | |||
d22fd3bbd1 | |||
75990c2867 | |||
88a1a702b4 | |||
24407dbf4a | |||
108285e9ac | |||
b728a37b6d | |||
938a93a0d7 | |||
ff6f727631 | |||
84077e0253 | |||
d2787cc2d8 | |||
17a56aa4f7 | |||
ca52892ab9 | |||
655bc8f215 | |||
a3912436be | |||
339bea1213 | |||
9389c8d854 | |||
6a17139423 | |||
3f23792df0 | |||
529031f44f | |||
f51894614d | |||
a7dbab79e5 | |||
4dcce810d2 | |||
1130e8c8ec |
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
- fedora
|
- fedora
|
||||||
- opensuse
|
- opensuse
|
||||||
- gentoo
|
- gentoo
|
||||||
cpp_std: [c++17]
|
cpp_std: [c++20]
|
||||||
include:
|
include:
|
||||||
- distro: fedora
|
- distro: fedora
|
||||||
cpp_std: c++20
|
cpp_std: c++20
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
FROM archlinux:base-devel
|
FROM archlinux:base-devel
|
||||||
|
|
||||||
RUN pacman -Syu --noconfirm && \
|
RUN pacman -Syu --noconfirm && \
|
||||||
pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl && \
|
pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw && \
|
||||||
sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen
|
sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen
|
||||||
|
@ -6,6 +6,6 @@ RUN export FEATURES="-ipc-sandbox -network-sandbox -pid-sandbox -sandbox -usersa
|
|||||||
emerge --sync && \
|
emerge --sync && \
|
||||||
eselect news read --quiet new 1>/dev/null 2>&1 && \
|
eselect news read --quiet new 1>/dev/null 2>&1 && \
|
||||||
emerge --verbose --update --deep --with-bdeps=y --backtrack=30 --newuse @world && \
|
emerge --verbose --update --deep --with-bdeps=y --backtrack=30 --newuse @world && \
|
||||||
USE="wayland gtk3 gtk -doc X" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols =dev-cpp/gtkmm-3.24.6 x11-libs/libxkbcommon \
|
USE="wayland gtk3 gtk -doc X pulseaudio minimal" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols =dev-cpp/gtkmm-3.24.6 x11-libs/libxkbcommon \
|
||||||
x11-libs/gtk+:3 dev-libs/libdbusmenu dev-libs/libnl sys-power/upower media-libs/libpulse dev-libs/libevdev media-libs/libmpdclient \
|
x11-libs/gtk+:3 dev-libs/libdbusmenu dev-libs/libnl sys-power/upower media-libs/libpulse dev-libs/libevdev media-libs/libmpdclient \
|
||||||
media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl dev-libs/iniparser
|
media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl dev-libs/iniparser sci-libs/fftw
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Waybar [](LICENSE) [](https://paypal.me/ARouillard)<br>
|
# Waybar [](LICENSE) [](https://paypal.me/ARouillard)<br>
|
||||||
|
|
||||||
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
|
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
|
||||||
> Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or
|
> Available in Arch [extra](https://www.archlinux.org/packages/extra/x86_64/waybar/) or
|
||||||
[AUR](https://aur.archlinux.org/packages/waybar-git/), [Gentoo](https://packages.gentoo.org/packages/gui-apps/waybar), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar).<br>
|
[AUR](https://aur.archlinux.org/packages/waybar-git/), [Gentoo](https://packages.gentoo.org/packages/gui-apps/waybar), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar).<br>
|
||||||
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
|
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
|
||||||
|
|
||||||
|
10
default.nix
Normal file
10
default.nix
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
(import
|
||||||
|
(
|
||||||
|
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
|
||||||
|
fetchTarball {
|
||||||
|
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||||
|
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{ src = ./.; }
|
||||||
|
).defaultNix
|
35
flake.lock
generated
35
flake.lock
generated
@ -6,11 +6,11 @@
|
|||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1667210711,
|
"lastModified": 1676293499,
|
||||||
"narHash": "sha256-IoErjXZAkzYWHEpQqwu/DeRNJGFdR7X2OGbkhMqMrpw=",
|
"narHash": "sha256-uIOTlTxvrXxpKeTvwBI1JGDGtCxMXE3BI0LFwoQMhiQ=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "devshell",
|
"repo": "devshell",
|
||||||
"rev": "96a9dd12b8a447840cc246e17a47b81a4268bba7",
|
"rev": "71e3022e3ab20bbf1342640547ef5bc14fb43bf4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -19,6 +19,22 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1673956053,
|
||||||
|
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1642700792,
|
"lastModified": 1642700792,
|
||||||
@ -36,11 +52,11 @@
|
|||||||
},
|
},
|
||||||
"flake-utils_2": {
|
"flake-utils_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1667395993,
|
"lastModified": 1676283394,
|
||||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
"narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
"rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -67,11 +83,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1670152712,
|
"lastModified": 1676300157,
|
||||||
"narHash": "sha256-LJttwIvJqsZIj8u1LxVRv82vwUtkzVqQVi7Wb8gxPS4=",
|
"narHash": "sha256-1HjRzfp6LOLfcj/HJHdVKWAkX9QRAouoh6AjzJiIerU=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "14ddeaebcbe9a25748221d1d7ecdf98e20e2325e",
|
"rev": "545c7a31e5dedea4a6d372712a18e00ce097d462",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -84,6 +100,7 @@
|
|||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"devshell": "devshell",
|
"devshell": "devshell",
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
"flake-utils": "flake-utils_2",
|
"flake-utils": "flake-utils_2",
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
}
|
}
|
||||||
|
42
flake.nix
42
flake.nix
@ -5,9 +5,13 @@
|
|||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
devshell.url = "github:numtide/devshell";
|
devshell.url = "github:numtide/devshell";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
flake-compat = {
|
||||||
|
url = "github:edolstra/flake-compat";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, flake-utils, devshell, nixpkgs }:
|
outputs = { self, flake-utils, devshell, nixpkgs, flake-compat }:
|
||||||
let
|
let
|
||||||
inherit (nixpkgs) lib;
|
inherit (nixpkgs) lib;
|
||||||
genSystems = lib.genAttrs [
|
genSystems = lib.genAttrs [
|
||||||
@ -26,14 +30,14 @@
|
|||||||
]);
|
]);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
overlays.default = _: prev: rec {
|
overlays.default = final: prev: {
|
||||||
waybar = prev.callPackage ./nix/default.nix {
|
waybar = final.callPackage ./nix/default.nix {
|
||||||
version = "0.9.16" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
version = prev.waybar.version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
packages = genSystems
|
packages = genSystems
|
||||||
(system:
|
(system:
|
||||||
(self.overlays.default null pkgsFor.${system})
|
(self.overlays.default pkgsFor.${system} pkgsFor.${system})
|
||||||
// {
|
// {
|
||||||
default = self.packages.${system}.waybar;
|
default = self.packages.${system}.waybar;
|
||||||
});
|
});
|
||||||
@ -57,8 +61,32 @@
|
|||||||
devshell.packages = with pkgs; [
|
devshell.packages = with pkgs; [
|
||||||
clang-tools
|
clang-tools
|
||||||
gdb
|
gdb
|
||||||
];
|
# from nativeBuildInputs
|
||||||
language.c.libraries = with pkgs; [
|
gnumake
|
||||||
|
meson
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
scdoc
|
||||||
|
] ++ (map lib.getDev [
|
||||||
|
# from buildInputs
|
||||||
|
wayland wlroots gtkmm3 libsigcxx jsoncpp spdlog gtk-layer-shell howard-hinnant-date libxkbcommon
|
||||||
|
# optional dependencies
|
||||||
|
gobject-introspection glib playerctl python3.pkgs.pygobject3
|
||||||
|
libevdev libinput libjack2 libmpdclient playerctl libnl
|
||||||
|
libpulseaudio sndio sway libdbusmenu-gtk3 udev upower wireplumber
|
||||||
|
|
||||||
|
# from propagated build inputs?
|
||||||
|
at-spi2-atk atkmm cairo cairomm catch2 fmt_8 fontconfig
|
||||||
|
gdk-pixbuf glibmm gtk3 harfbuzz pango pangomm wayland-protocols
|
||||||
|
]);
|
||||||
|
env = with pkgs; [
|
||||||
|
{ name = "CPLUS_INCLUDE_PATH"; prefix = "$DEVSHELL_DIR/include"; }
|
||||||
|
{ name = "PKG_CONFIG_PATH"; prefix = "$DEVSHELL_DIR/lib/pkgconfig"; }
|
||||||
|
{ name = "PKG_CONFIG_PATH"; prefix = "$DEVSHELL_DIR/share/pkgconfig"; }
|
||||||
|
{ name = "PATH"; prefix = "${wayland.bin}/bin"; }
|
||||||
|
{ name = "LIBRARY_PATH"; prefix = "${lib.getLib sndio}/lib"; }
|
||||||
|
{ name = "LIBRARY_PATH"; prefix = "${lib.getLib zlib}/lib"; }
|
||||||
|
{ name = "LIBRARY_PATH"; prefix = "${lib.getLib howard-hinnant-date}/lib"; }
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
27
include/AAppIconLabel.hpp
Normal file
27
include/AAppIconLabel.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtkmm/box.h>
|
||||||
|
#include <gtkmm/image.h>
|
||||||
|
|
||||||
|
#include "AIconLabel.hpp"
|
||||||
|
|
||||||
|
namespace waybar {
|
||||||
|
|
||||||
|
class AAppIconLabel : public AIconLabel {
|
||||||
|
public:
|
||||||
|
AAppIconLabel(const Json::Value &config, const std::string &name, const std::string &id,
|
||||||
|
const std::string &format, uint16_t interval = 0, bool ellipsize = false,
|
||||||
|
bool enable_click = false, bool enable_scroll = false);
|
||||||
|
virtual ~AAppIconLabel() = default;
|
||||||
|
auto update() -> void override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void updateAppIconName(const std::string &app_identifier,
|
||||||
|
const std::string &alternative_app_identifier);
|
||||||
|
void updateAppIcon();
|
||||||
|
unsigned app_icon_size_{24};
|
||||||
|
bool update_app_icon_{true};
|
||||||
|
std::string app_icon_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar
|
@ -31,6 +31,7 @@
|
|||||||
#include "modules/hyprland/language.hpp"
|
#include "modules/hyprland/language.hpp"
|
||||||
#include "modules/hyprland/submap.hpp"
|
#include "modules/hyprland/submap.hpp"
|
||||||
#include "modules/hyprland/window.hpp"
|
#include "modules/hyprland/window.hpp"
|
||||||
|
#include "modules/hyprland/workspaces.hpp"
|
||||||
#endif
|
#endif
|
||||||
#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM))
|
#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM))
|
||||||
#include "modules/battery.hpp"
|
#include "modules/battery.hpp"
|
||||||
|
@ -34,11 +34,18 @@ class Clock final : public ALabel {
|
|||||||
|
|
||||||
auto first_day_of_week() -> date::weekday;
|
auto first_day_of_week() -> date::weekday;
|
||||||
const date::time_zone* current_timezone();
|
const date::time_zone* current_timezone();
|
||||||
bool is_timezone_fixed();
|
auto timezones_text(std::chrono::system_clock::time_point now) -> std::string;
|
||||||
auto timezones_text(std::chrono::system_clock::time_point* now) -> std::string;
|
|
||||||
|
|
||||||
/*Calendar properties*/
|
/*Calendar properties*/
|
||||||
WeeksSide cldWPos_{WeeksSide::HIDDEN};
|
WeeksSide cldWPos_{WeeksSide::HIDDEN};
|
||||||
|
/*
|
||||||
|
0 - calendar.format.months
|
||||||
|
1 - calendar.format.weekdays
|
||||||
|
2 - calendar.format.days
|
||||||
|
3 - calendar.format.today
|
||||||
|
4 - calendar.format.weeks
|
||||||
|
5 - tooltip-format
|
||||||
|
*/
|
||||||
std::map<int, std::string const> fmtMap_;
|
std::map<int, std::string const> fmtMap_;
|
||||||
CldMode cldMode_{CldMode::MONTH};
|
CldMode cldMode_{CldMode::MONTH};
|
||||||
uint cldMonCols_{3}; // Count of the month in the row
|
uint cldMonCols_{3}; // Count of the month in the row
|
||||||
@ -52,8 +59,8 @@ class Clock final : public ALabel {
|
|||||||
std::string cldMonCached_{};
|
std::string cldMonCached_{};
|
||||||
date::day cldBaseDay_{0};
|
date::day cldBaseDay_{0};
|
||||||
/*Calendar functions*/
|
/*Calendar functions*/
|
||||||
auto get_calendar(const date::zoned_seconds& now, const date::zoned_seconds& wtime)
|
auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd,
|
||||||
-> std::string;
|
const date::time_zone* tz) -> const std::string;
|
||||||
/*Clock actions*/
|
/*Clock actions*/
|
||||||
void cldModeSwitch();
|
void cldModeSwitch();
|
||||||
void cldShift_up();
|
void cldShift_up();
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include "util/json.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
class EventHandler {
|
class EventHandler {
|
||||||
@ -22,12 +24,14 @@ class IPC {
|
|||||||
void unregisterForIPC(EventHandler*);
|
void unregisterForIPC(EventHandler*);
|
||||||
|
|
||||||
std::string getSocket1Reply(const std::string& rq);
|
std::string getSocket1Reply(const std::string& rq);
|
||||||
|
Json::Value getSocket1JsonReply(const std::string& rq);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startIPC();
|
void startIPC();
|
||||||
void parseIPC(const std::string&);
|
void parseIPC(const std::string&);
|
||||||
|
|
||||||
std::mutex callbackMutex;
|
std::mutex callbackMutex;
|
||||||
|
util::JsonParser parser_;
|
||||||
std::list<std::pair<std::string, EventHandler*>> callbacks;
|
std::list<std::pair<std::string, EventHandler*>> callbacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include <tuple>
|
#include "AAppIconLabel.hpp"
|
||||||
|
|
||||||
#include "ALabel.hpp"
|
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "modules/hyprland/backend.hpp"
|
#include "modules/hyprland/backend.hpp"
|
||||||
#include "util/json.hpp"
|
#include "util/json.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
class Window : public waybar::ALabel, public EventHandler {
|
class Window : public waybar::AAppIconLabel, public EventHandler {
|
||||||
public:
|
public:
|
||||||
Window(const std::string&, const waybar::Bar&, const Json::Value&);
|
Window(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
virtual ~Window();
|
virtual ~Window();
|
||||||
@ -17,15 +15,46 @@ class Window : public waybar::ALabel, public EventHandler {
|
|||||||
auto update() -> void override;
|
auto update() -> void override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int getActiveWorkspaceID(std::string);
|
struct Workspace {
|
||||||
std::string getLastWindowTitle(int);
|
int id;
|
||||||
|
int windows;
|
||||||
|
std::string last_window;
|
||||||
|
std::string last_window_title;
|
||||||
|
|
||||||
|
static auto parse(const Json::Value&) -> Workspace;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WindowData {
|
||||||
|
bool floating;
|
||||||
|
int monitor = -1;
|
||||||
|
std::string class_name;
|
||||||
|
std::string initial_class_name;
|
||||||
|
std::string title;
|
||||||
|
std::string initial_title;
|
||||||
|
bool fullscreen;
|
||||||
|
bool grouped;
|
||||||
|
|
||||||
|
static auto parse(const Json::Value&) -> WindowData;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getActiveWorkspace(const std::string&) -> Workspace;
|
||||||
|
auto getActiveWorkspace() -> Workspace;
|
||||||
void onEvent(const std::string&) override;
|
void onEvent(const std::string&) override;
|
||||||
|
void queryActiveWorkspace();
|
||||||
|
void setClass(const std::string&, bool enable);
|
||||||
|
|
||||||
bool separate_outputs;
|
bool separate_outputs;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
std::string lastView;
|
WindowData window_data_;
|
||||||
|
Workspace workspace_;
|
||||||
|
std::string solo_class_;
|
||||||
|
std::string last_solo_class_;
|
||||||
|
bool solo_;
|
||||||
|
bool all_floating_;
|
||||||
|
bool swallowing_;
|
||||||
|
bool fullscreen_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::hyprland
|
} // namespace waybar::modules::hyprland
|
||||||
|
87
include/modules/hyprland/workspaces.hpp
Normal file
87
include/modules/hyprland/workspaces.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include <gtkmm/button.h>
|
||||||
|
#include <gtkmm/label.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "AModule.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "modules/hyprland/backend.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
class Workspace {
|
||||||
|
public:
|
||||||
|
Workspace(const Json::Value& workspace_data);
|
||||||
|
std::string& select_icon(std::map<std::string, std::string>& icons_map);
|
||||||
|
Gtk::Button& button() { return button_; };
|
||||||
|
|
||||||
|
int id() const { return id_; };
|
||||||
|
std::string name() const { return name_; };
|
||||||
|
std::string output() const { return output_; };
|
||||||
|
bool active() const { return active_; };
|
||||||
|
bool is_special() const { return is_special_; };
|
||||||
|
bool is_persistent() const { return is_persistent_; };
|
||||||
|
bool is_empty() const { return windows_ == 0; };
|
||||||
|
|
||||||
|
auto handle_clicked(GdkEventButton* bt) -> bool;
|
||||||
|
void set_active(bool value = true) { active_ = value; };
|
||||||
|
void set_persistent(bool value = true) { is_persistent_ = value; };
|
||||||
|
void set_windows(uint value) { windows_ = value; };
|
||||||
|
|
||||||
|
void update(const std::string& format, const std::string& icon);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int id_;
|
||||||
|
std::string name_;
|
||||||
|
std::string output_;
|
||||||
|
uint windows_;
|
||||||
|
bool active_ = false;
|
||||||
|
bool is_special_ = false;
|
||||||
|
bool is_persistent_ = false;
|
||||||
|
|
||||||
|
Gtk::Button button_;
|
||||||
|
Gtk::Box content_;
|
||||||
|
Gtk::Label label_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Workspaces : public AModule, public EventHandler {
|
||||||
|
public:
|
||||||
|
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
|
~Workspaces() override;
|
||||||
|
void update() override;
|
||||||
|
void init();
|
||||||
|
|
||||||
|
auto all_outputs() const -> bool { return all_outputs_; }
|
||||||
|
auto show_special() const -> bool { return show_special_; }
|
||||||
|
|
||||||
|
auto get_bar_output() const -> std::string { return bar_.output->name; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onEvent(const std::string&) override;
|
||||||
|
void update_window_count();
|
||||||
|
void sort_workspaces();
|
||||||
|
void create_workspace(Json::Value& value);
|
||||||
|
void remove_workspace(std::string name);
|
||||||
|
|
||||||
|
bool all_outputs_ = false;
|
||||||
|
bool show_special_ = false;
|
||||||
|
|
||||||
|
void fill_persistent_workspaces();
|
||||||
|
void create_persistent_workspaces();
|
||||||
|
std::vector<std::string> persistent_workspaces_to_create_;
|
||||||
|
bool persistent_created_ = false;
|
||||||
|
|
||||||
|
std::string format_;
|
||||||
|
std::map<std::string, std::string> icons_map_;
|
||||||
|
bool with_icon_;
|
||||||
|
uint64_t monitor_id_;
|
||||||
|
std::string active_workspace_name_;
|
||||||
|
std::vector<std::unique_ptr<Workspace>> workspaces_;
|
||||||
|
std::vector<Json::Value> workspaces_to_create_;
|
||||||
|
std::vector<std::string> workspaces_to_remove_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
const Bar& bar_;
|
||||||
|
Gtk::Box box_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules::hyprland
|
@ -24,12 +24,15 @@ class Image : public AModule {
|
|||||||
private:
|
private:
|
||||||
void delayWorker();
|
void delayWorker();
|
||||||
void handleEvent();
|
void handleEvent();
|
||||||
|
void parseOutputRaw();
|
||||||
|
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
Gtk::Image image_;
|
Gtk::Image image_;
|
||||||
std::string path_;
|
std::string path_;
|
||||||
|
std::string tooltip_;
|
||||||
int size_;
|
int size_;
|
||||||
int interval_;
|
int interval_;
|
||||||
|
util::command::res output_;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
};
|
};
|
||||||
|
@ -67,6 +67,8 @@ class Mpris : public ALabel {
|
|||||||
int title_len_;
|
int title_len_;
|
||||||
int dynamic_len_;
|
int dynamic_len_;
|
||||||
std::vector<std::string> dynamic_prio_;
|
std::vector<std::string> dynamic_prio_;
|
||||||
|
std::vector<std::string> dynamic_order_;
|
||||||
|
std::string dynamic_separator_;
|
||||||
bool truncate_hours_;
|
bool truncate_hours_;
|
||||||
bool tooltip_len_limits_;
|
bool tooltip_len_limits_;
|
||||||
std::string ellipsis_;
|
std::string ellipsis_;
|
||||||
@ -80,6 +82,7 @@ class Mpris : public ALabel {
|
|||||||
std::string lastPlayer;
|
std::string lastPlayer;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> last_update_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::mpris
|
} // namespace waybar::modules::mpris
|
||||||
|
@ -78,7 +78,6 @@ class Network : public ALabel {
|
|||||||
int32_t signal_strength_dbm_;
|
int32_t signal_strength_dbm_;
|
||||||
uint8_t signal_strength_;
|
uint8_t signal_strength_;
|
||||||
std::string signal_strength_app_;
|
std::string signal_strength_app_;
|
||||||
float frequency_;
|
|
||||||
uint32_t route_priority;
|
uint32_t route_priority;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
@ -86,6 +85,7 @@ class Network : public ALabel {
|
|||||||
#ifdef WANT_RFKILL
|
#ifdef WANT_RFKILL
|
||||||
util::Rfkill rfkill_;
|
util::Rfkill rfkill_;
|
||||||
#endif
|
#endif
|
||||||
|
float frequency_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include "AIconLabel.hpp"
|
#include "AAppIconLabel.hpp"
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
#include "modules/sway/ipc/client.hpp"
|
#include "modules/sway/ipc/client.hpp"
|
||||||
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
namespace waybar::modules::sway {
|
namespace waybar::modules::sway {
|
||||||
|
|
||||||
class Window : public AIconLabel, public sigc::trackable {
|
class Window : public AAppIconLabel, public sigc::trackable {
|
||||||
public:
|
public:
|
||||||
Window(const std::string&, const waybar::Bar&, const Json::Value&);
|
Window(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
virtual ~Window() = default;
|
virtual ~Window() = default;
|
||||||
@ -25,8 +25,6 @@ class Window : public AIconLabel, public sigc::trackable {
|
|||||||
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
|
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
|
||||||
getFocusedNode(const Json::Value& nodes, std::string& output);
|
getFocusedNode(const Json::Value& nodes, std::string& output);
|
||||||
void getTree();
|
void getTree();
|
||||||
void updateAppIconName();
|
|
||||||
void updateAppIcon();
|
|
||||||
|
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
std::string window_;
|
std::string window_;
|
||||||
@ -37,9 +35,6 @@ class Window : public AIconLabel, public sigc::trackable {
|
|||||||
std::string old_app_id_;
|
std::string old_app_id_;
|
||||||
std::size_t app_nb_;
|
std::size_t app_nb_;
|
||||||
std::string shell_;
|
std::string shell_;
|
||||||
unsigned app_icon_size_{24};
|
|
||||||
bool update_app_icon_{true};
|
|
||||||
std::string app_icon_name_;
|
|
||||||
int floating_count_;
|
int floating_count_;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
|
@ -41,6 +41,7 @@ class Workspaces : public AModule, public sigc::trackable {
|
|||||||
|
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
std::vector<Json::Value> workspaces_;
|
std::vector<Json::Value> workspaces_;
|
||||||
|
std::vector<std::string> high_priority_named_;
|
||||||
std::vector<std::string> workspaces_order_;
|
std::vector<std::string> workspaces_order_;
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
|
@ -74,6 +74,7 @@ class UPower : public AModule {
|
|||||||
bool showAltText;
|
bool showAltText;
|
||||||
bool upowerRunning;
|
bool upowerRunning;
|
||||||
guint upowerWatcher_id;
|
guint upowerWatcher_id;
|
||||||
|
std::string nativePath_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::upower
|
} // namespace waybar::modules::upower
|
||||||
|
@ -27,6 +27,8 @@ class Wireplumber : public ALabel {
|
|||||||
static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id);
|
static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id);
|
||||||
static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self);
|
static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self);
|
||||||
|
|
||||||
|
bool handleScroll(GdkEventScroll* e) override;
|
||||||
|
|
||||||
WpCore* wp_core_;
|
WpCore* wp_core_;
|
||||||
GPtrArray* apis_;
|
GPtrArray* apis_;
|
||||||
WpObjectManager* om_;
|
WpObjectManager* om_;
|
||||||
@ -36,6 +38,7 @@ class Wireplumber : public ALabel {
|
|||||||
uint32_t pending_plugins_;
|
uint32_t pending_plugins_;
|
||||||
bool muted_;
|
bool muted_;
|
||||||
double volume_;
|
double volume_;
|
||||||
|
double min_step_;
|
||||||
uint32_t node_id_{0};
|
uint32_t node_id_{0};
|
||||||
std::string node_name_;
|
std::string node_name_;
|
||||||
};
|
};
|
||||||
|
@ -93,7 +93,7 @@ template <>
|
|||||||
struct formatter<Glib::ustring> : formatter<std::string> {
|
struct formatter<Glib::ustring> : formatter<std::string> {
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const Glib::ustring& value, FormatContext& ctx) {
|
auto format(const Glib::ustring& value, FormatContext& ctx) {
|
||||||
return formatter<std::string>::format(value, ctx);
|
return formatter<std::string>::format(static_cast<std::string>(value), ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace fmt
|
} // namespace fmt
|
||||||
|
14
include/util/gtk_icon.hpp
Normal file
14
include/util/gtk_icon.hpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <gtkmm/icontheme.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class DefaultGtkIconThemeWrapper {
|
||||||
|
private:
|
||||||
|
static std::mutex default_theme_mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static bool has_icon(const std::string&);
|
||||||
|
static Glib::RefPtr<Gdk::Pixbuf> load_icon(const char*, int, Gtk::IconLookupFlags);
|
||||||
|
};
|
9
include/util/prepare_for_sleep.h
Normal file
9
include/util/prepare_for_sleep.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SafeSignal.hpp"
|
||||||
|
|
||||||
|
namespace waybar::util {
|
||||||
|
|
||||||
|
// Get a signal emited with value true when entering sleep, and false when exiting
|
||||||
|
SafeSignal<bool>& prepare_for_sleep();
|
||||||
|
} // namespace waybar::util
|
@ -6,6 +6,8 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include "prepare_for_sleep.h"
|
||||||
|
|
||||||
namespace waybar::util {
|
namespace waybar::util {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,7 +35,11 @@ class SleeperThread {
|
|||||||
signal_ = false;
|
signal_ = false;
|
||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
}} {}
|
}} {
|
||||||
|
connection_ = prepare_for_sleep().connect([this](bool sleep) {
|
||||||
|
if (not sleep) wake_up();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
SleeperThread& operator=(std::function<void()> func) {
|
SleeperThread& operator=(std::function<void()> func) {
|
||||||
thread_ = std::thread([this, func] {
|
thread_ = std::thread([this, func] {
|
||||||
@ -42,6 +48,11 @@ class SleeperThread {
|
|||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (connection_.empty()) {
|
||||||
|
connection_ = prepare_for_sleep().connect([this](bool sleep) {
|
||||||
|
if (not sleep) wake_up();
|
||||||
|
});
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +72,7 @@ class SleeperThread {
|
|||||||
return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; });
|
return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; });
|
||||||
}
|
}
|
||||||
|
|
||||||
auto wake_up() {
|
void wake_up() {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lck(mutex_);
|
std::lock_guard<std::mutex> lck(mutex_);
|
||||||
signal_ = true;
|
signal_ = true;
|
||||||
@ -84,6 +95,7 @@ class SleeperThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
~SleeperThread() {
|
~SleeperThread() {
|
||||||
|
connection_.disconnect();
|
||||||
stop();
|
stop();
|
||||||
if (thread_.joinable()) {
|
if (thread_.joinable()) {
|
||||||
thread_.join();
|
thread_.join();
|
||||||
@ -96,6 +108,7 @@ class SleeperThread {
|
|||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
bool do_run_ = true;
|
bool do_run_ = true;
|
||||||
bool signal_ = false;
|
bool signal_ = false;
|
||||||
|
sigc::connection connection_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::util
|
} // namespace waybar::util
|
||||||
|
@ -132,8 +132,8 @@ The *battery* module allows one to define custom formats based on up to two fact
|
|||||||
# STATES
|
# STATES
|
||||||
|
|
||||||
- Every entry (*state*) consists of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
|
- Every entry (*state*) consists of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
|
||||||
- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the *<name>* of the state. Each class gets activated when the current capacity is equal or below the configured *<value>*.
|
- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the *<name>* of the state. Each class gets activated when the current capacity is equal or below the configured *<value>*.
|
||||||
- Also each state can have its own *format*. Those con be configured via *format-<name>*. Or if you want to differentiate a bit more even as *format-<status>-<state>*. For more information see *custom-formats*.
|
- Also each state can have its own *format*. Those con be configured via *format-<name>*. Or if you want to differentiate a bit more even as *format-<status>-<state>*. For more information see *custom-formats*.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
182
man/waybar-cava.5.scd
Normal file
182
man/waybar-cava.5.scd
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
waybar-cava(5) "waybar-cava" "User Manual"
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - cava module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
*cava* module for karlstav/cava project. See it on github: https://github.com/karlstav/cava.
|
||||||
|
|
||||||
|
|
||||||
|
# FILES
|
||||||
|
|
||||||
|
$XDG_CONFIG_HOME/waybar/config ++
|
||||||
|
Per user configuration file
|
||||||
|
|
||||||
|
# ADDITIONAL FILES
|
||||||
|
|
||||||
|
libcava lives in:
|
||||||
|
|
||||||
|
. /usr/lib/libcava.so or /usr/lib64/libcava.so
|
||||||
|
. /usr/lib/pkgconfig/cava.pc or /usr/lib64/pkgconfig/cava.pc
|
||||||
|
. /usr/include/cava
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[- *Option*
|
||||||
|
:- *Typeof*
|
||||||
|
:- *Default*
|
||||||
|
:- *Description*
|
||||||
|
|[ *cava_config*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:< Path where cava configuration file is placed to
|
||||||
|
|[ *framerate*
|
||||||
|
:[ integer
|
||||||
|
:[ 30
|
||||||
|
:[ rames per second. Is used as a replacement for *interval*
|
||||||
|
|[ *autosens*
|
||||||
|
:[ integer
|
||||||
|
:[ 1
|
||||||
|
:[ Will attempt to decrease sensitivity if the bars peak
|
||||||
|
|[ *sensitivity*
|
||||||
|
:[ integer
|
||||||
|
:[ 100
|
||||||
|
:[ Manual sensitivity in %. It's recommended to be omitted when *autosens* = 1
|
||||||
|
|[ *bars*
|
||||||
|
:[ integer
|
||||||
|
:[ 12
|
||||||
|
:[ The number of bars
|
||||||
|
|[ *lower_cutoff_freq*
|
||||||
|
:[ long integer
|
||||||
|
:[ 50
|
||||||
|
:[ Lower cutoff frequencies for lowest bars the bandwidth of the visualizer
|
||||||
|
|[ *higher_cutoff_freq*
|
||||||
|
:[ long integer
|
||||||
|
:[ 10000
|
||||||
|
:[ Higher cutoff frequencies for highest bars the bandwidth of the visualizer
|
||||||
|
|[ *sleep_timer*
|
||||||
|
:[ integer
|
||||||
|
:[ 5
|
||||||
|
:[ Seconds with no input before cava main thread goes to sleep mode
|
||||||
|
|[ *method*
|
||||||
|
:[ string
|
||||||
|
:[ pulse
|
||||||
|
:[ Audio capturing method. Possible methods are: pipewire, pulse, alsa, fifo, sndio or shmem
|
||||||
|
|[ *source*
|
||||||
|
:[ string
|
||||||
|
:[ auto
|
||||||
|
:[ See cava configuration
|
||||||
|
|[ *sample_rate*
|
||||||
|
:[ long integer
|
||||||
|
:[ 44100
|
||||||
|
:[ See cava configuration
|
||||||
|
|[ *sample_bits*
|
||||||
|
:[ integer
|
||||||
|
:[ 16
|
||||||
|
:[ See cava configuration
|
||||||
|
|[ *stereo*
|
||||||
|
:[ bool
|
||||||
|
:[ true
|
||||||
|
:[ Visual channels
|
||||||
|
|[ *reverse*
|
||||||
|
:[ bool
|
||||||
|
:[ false
|
||||||
|
:[ Displays frequencies the other way around
|
||||||
|
|[ *bar_delimiter*
|
||||||
|
:[ integer
|
||||||
|
:[ 0
|
||||||
|
:[ Each bar is separated by a delimiter. Use decimal value in ascii table(i.e. 59 = ";"). 0 means no delimiter
|
||||||
|
|[ *monstercat*
|
||||||
|
:[ bool
|
||||||
|
:[ false
|
||||||
|
:[ Disables or enables the so-called "Monstercat smoothing" with of without "waves"
|
||||||
|
|[ *waves*
|
||||||
|
:[ bool
|
||||||
|
:[ false
|
||||||
|
:[ Disables or enables the so-called "Monstercat smoothing" with of without "waves"
|
||||||
|
|[ *noise_reduction*
|
||||||
|
:[ double
|
||||||
|
:[ 0.77
|
||||||
|
:[ Range between 0 - 1. The raw visualization is very noisy, this factor adjust the integral and gravity filters to keep the signal smooth. 1 - will be very slow and smooth, 0 - will be fast but noisy
|
||||||
|
|[ *input_delay*
|
||||||
|
:[ integer
|
||||||
|
:[ 2
|
||||||
|
:[ Sets the delay before fetching audio source thread start working. On author machine Waybar starts much faster then pipewire audio server, and without a little delay cava module fails due to pipewire is not ready
|
||||||
|
|[ *ascii_max_range*
|
||||||
|
:[ integer
|
||||||
|
:[ 7
|
||||||
|
:[ It's impossible to set it directly. The value is dictated by the number of icons in the array *format-icons*
|
||||||
|
|[ *data_format*
|
||||||
|
:[ string
|
||||||
|
:[ asci
|
||||||
|
:[ It's impossible to set it. Waybar sets it to = asci for internal needs
|
||||||
|
|[ *raw_target*
|
||||||
|
:[ string
|
||||||
|
:[ /dev/stdout
|
||||||
|
:[ It's impossible to set it. Waybar sets it to = /dev/stdout for internal needs
|
||||||
|
|
||||||
|
Configuration can be provided as:
|
||||||
|
- The only cava configuration file which is provided through *cava_config*. The rest configuration can be skipped
|
||||||
|
- Without cava configuration file. In such case cava should be configured through provided list of the configuration option
|
||||||
|
- Mix. When provided both And cava configuration file And configuration options. In such case waybar applies configuration file first then overrides particular options by the provided list of configuration options
|
||||||
|
|
||||||
|
# ACTIONS
|
||||||
|
|
||||||
|
[- *String*
|
||||||
|
:- *Action*
|
||||||
|
|[ *mode*
|
||||||
|
:< Switch main cava thread and fetching audio source thread from/to pause/resume
|
||||||
|
|
||||||
|
# DEPENDENCIES
|
||||||
|
|
||||||
|
- iniparser
|
||||||
|
- fftw3
|
||||||
|
|
||||||
|
# SOLVING ISSUES
|
||||||
|
|
||||||
|
. On start Waybar throws an exception "error while loading shared libraries: libcava.so: cannot open shared object file: No such file or directory".
|
||||||
|
It might happen when libcava for some reason hasn't been registered in the system. sudo ldconfig should help
|
||||||
|
. Waybar is starting but cava module doesn't react on the music
|
||||||
|
1. In such case for at first need to make sure usual cava application is working as well
|
||||||
|
2. If so, need to comment all configuration options. Uncomment cava_config and provide the path to the working cava config
|
||||||
|
3. You might set too huge or too small input_delay. Try to setup to 4 seconds, restart waybar and check again 4 seconds past. Usual even on weak machines it should be enough
|
||||||
|
4. You might accidentally switched action mode to pause mode
|
||||||
|
|
||||||
|
# RISING ISSUES
|
||||||
|
|
||||||
|
For clear understanding: this module is a cava API's consumer. So for any bugs related to cava engine you should contact to Cava upstream(https://github.com/karlstav/cava) ++
|
||||||
|
with the one Exception. Cava upstream doesn't provide cava as a shared library. For that this module author made a fork libcava(https://github.com/LukashonakV/cava). ++
|
||||||
|
So the order is:
|
||||||
|
. cava upstream
|
||||||
|
. libcava upstream.
|
||||||
|
In case when cava releases new version and you're wanna get it, it should be raised an issue to libcava(https://github.com/LukashonakV/cava) with title ++
|
||||||
|
\[Bump\]x.x.x where x.x.x is cava release version.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"cava": {
|
||||||
|
//"cava_config": "$XDG_CONFIG_HOME/cava/cava.conf",
|
||||||
|
"framerate": 30,
|
||||||
|
"autosens": 1,
|
||||||
|
//"sensitivity": 100,
|
||||||
|
"bars": 14,
|
||||||
|
"lower_cutoff_freq": 50,
|
||||||
|
"higher_cutoff_freq": 10000,
|
||||||
|
"method": "pulse",
|
||||||
|
"source": "auto",
|
||||||
|
"stereo": true,
|
||||||
|
"reverse": false,
|
||||||
|
"bar_delimiter": 0,
|
||||||
|
"monstercat": false,
|
||||||
|
"waves": false,
|
||||||
|
"noise_reduction": 0.77,
|
||||||
|
"input_delay": 2,
|
||||||
|
"format-icons" : ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ],
|
||||||
|
"actions": {
|
||||||
|
"on-click-right": "mode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
@ -1,4 +1,4 @@
|
|||||||
waybar-clock(5)
|
waybar-clock(5) "waybar-clock" "User Manual"
|
||||||
|
|
||||||
# NAME
|
# NAME
|
||||||
|
|
||||||
@ -6,100 +6,162 @@ waybar - clock module
|
|||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
The *clock* module displays the current date and time.
|
*clock* module displays current date and time
|
||||||
|
|
||||||
|
# FILES
|
||||||
|
|
||||||
|
$XDG_CONFIG_HOME/waybar/config ++
|
||||||
|
Per user configuration file
|
||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
*interval*: ++
|
1. Addressed by *clock*
|
||||||
typeof: integer ++
|
[- *Option*
|
||||||
default: 60 ++
|
:- *Typeof*
|
||||||
The interval in which the information gets polled.
|
:- *Default*
|
||||||
|
:- *Description*
|
||||||
|
|[ *interval*
|
||||||
|
:[ integer
|
||||||
|
:[ 60
|
||||||
|
:[ The interval in which the information gets polled
|
||||||
|
|[ *format*
|
||||||
|
:[ string
|
||||||
|
:[ *{:%H:%M}*
|
||||||
|
:[ The format, how the date and time should be displayed. See format options below
|
||||||
|
|[ *timezone*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:[ The timezone to display the time in, e.g. America/New_York. "" represents
|
||||||
|
the system's local timezone. See Wikipedia's unofficial list of timezones <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>
|
||||||
|
|[ *timezones*
|
||||||
|
:[ list of strings
|
||||||
|
:[
|
||||||
|
:[ A list of timezones (as in *timezone*) to use for time display, changed using
|
||||||
|
the scroll wheel. Do not specify *timezone* option when *timezones* is specified.
|
||||||
|
"" represents the system's local timezone
|
||||||
|
|[ *locale*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:[ A locale to be used to display the time. Intended to render times in custom
|
||||||
|
timezones with the proper language and format
|
||||||
|
|[ *max-length*
|
||||||
|
:[ integer
|
||||||
|
:[
|
||||||
|
:[ The maximum length in character the module should display
|
||||||
|
|[ *rotate*
|
||||||
|
:[ integer
|
||||||
|
:[
|
||||||
|
:[ Positive value to rotate the text label
|
||||||
|
|[ *on-click*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:[ Command to execute when clicked on the module
|
||||||
|
|[ *on-click-middle*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:[ Command to execute when you middle clicked on the module using mousewheel
|
||||||
|
|[ *on-click-right*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:[ Command to execute when you right clicked on the module
|
||||||
|
|[ *on-scroll-up*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:[ Command to execute when scrolling up on the module
|
||||||
|
|[ *on-scroll-down*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:[ Command to execute when scrolling down on the module
|
||||||
|
|[ *smooth-scrolling-threshold*
|
||||||
|
:[ double
|
||||||
|
:[
|
||||||
|
:[ Threshold to be used when scrolling
|
||||||
|
|[ *tooltip*
|
||||||
|
:[ bool
|
||||||
|
:[ true
|
||||||
|
:[ Option to enable tooltip on hover
|
||||||
|
|[ *tooltip-format*
|
||||||
|
:[ string
|
||||||
|
:[ same as format
|
||||||
|
:[ Tooltip on hover
|
||||||
|
|
||||||
*format*: ++
|
View all valid format options in *strftime(3)* or have a look <https://fmt.dev/latest/syntax.html#chrono-specs>
|
||||||
typeof: string ++
|
|
||||||
default: {:%H:%M} ++
|
|
||||||
The format, how the date and time should be displayed. ++
|
|
||||||
It uses the format of the date library. See https://howardhinnant.github.io/date/date.html#to_stream_formatting for details.
|
|
||||||
|
|
||||||
*timezone*: ++
|
2. Addressed by *clock: calendar*
|
||||||
typeof: string ++
|
[- *Option*
|
||||||
default: inferred local timezone ++
|
:- *Typeof*
|
||||||
The timezone to display the time in, e.g. America/New_York. ++
|
:- *Default*
|
||||||
This field will be ignored if *timezones* field is set and have at least one value.
|
:- *Description*
|
||||||
|
|[ *mode*
|
||||||
|
:[ string
|
||||||
|
:[ month
|
||||||
|
:[ Calendar view mode. Possible values: year|month
|
||||||
|
|[ *mode-mon-col*
|
||||||
|
:[ integer
|
||||||
|
:[ 3
|
||||||
|
:[ Relevant for *mode=year*. Count of months per row
|
||||||
|
|[ *weeks-pos*
|
||||||
|
:[ integer
|
||||||
|
:[
|
||||||
|
:[ The position where week numbers should be displayed. Disabled when is empty.
|
||||||
|
Possible values: left|right
|
||||||
|
|[ *on-scroll*
|
||||||
|
:[ integer
|
||||||
|
:[ 1
|
||||||
|
:[ Value to scroll months/years forward/backward. Can be negative. Is
|
||||||
|
configured under *on-scroll* option
|
||||||
|
|
||||||
*timezones*: ++
|
3. Addressed by *clock: calendar: format*
|
||||||
typeof: list of strings ++
|
[- *Option*
|
||||||
A list of timezones to use for time display, changed using the scroll wheel. ++
|
:- *Typeof*
|
||||||
Use "" to represent the system's local timezone. Using %Z in the format or tooltip format is useful to track which time zone is currently displayed.
|
:- *Default*
|
||||||
|
:- *Description*
|
||||||
|
|[ *months*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:[ Format is applied to months header(January, February,...etc.)
|
||||||
|
|[ *days*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:[ Format is applied to days
|
||||||
|
|[ *weeks*
|
||||||
|
:[ string
|
||||||
|
:[ *{:%U}*
|
||||||
|
:[ Format is applied to week numbers. When weekday format is not provided then
|
||||||
|
is used default format: '{:%W}' when week starts with Monday, '{:%U}' otherwise
|
||||||
|
|[ *weekdays*
|
||||||
|
:[ string
|
||||||
|
:[
|
||||||
|
:[ Format is applied to weeks header(Su,Mo,...etc.)
|
||||||
|
|[ *today*
|
||||||
|
:[ string
|
||||||
|
:[ *<b><u>{}</u></b>*
|
||||||
|
:[ Format is applied to Today
|
||||||
|
|
||||||
*locale*: ++
|
## Actions
|
||||||
typeof: string ++
|
|
||||||
default: inferred from current locale ++
|
|
||||||
A locale to be used to display the time. Intended to render times in custom timezones with the proper language and format.
|
|
||||||
|
|
||||||
*today-format*: ++
|
[- *String*
|
||||||
typeof: string ++
|
:- *Action*
|
||||||
default: <b><u>{}</u></b> ++
|
|[ *mode*
|
||||||
The format of today's date in the calendar.
|
:[ Switch calendar mode between year/month
|
||||||
|
|[ *tz_up*
|
||||||
*max-length*: ++
|
:[ Switch to the next provided time zone
|
||||||
typeof: integer ++
|
|[ *tz_down*
|
||||||
The maximum length in character the module should display.
|
:[ Switch to the previous provided time zone
|
||||||
|
|[ *shift_up*
|
||||||
*min-length*: ++
|
:[ Switch to the next calendar month/year
|
||||||
typeof: integer ++
|
|[ *shift_down*
|
||||||
The minimum length in characters the module should take up.
|
:[ Switch to the previous calendar month/year
|
||||||
|
|
||||||
*align*: ++
|
|
||||||
typeof: float ++
|
|
||||||
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
|
||||||
|
|
||||||
*rotate*: ++
|
|
||||||
typeof: integer ++
|
|
||||||
Positive value to rotate the text label.
|
|
||||||
|
|
||||||
*on-click*: ++
|
|
||||||
typeof: string ++
|
|
||||||
Command to execute when clicked on the module.
|
|
||||||
|
|
||||||
*on-click-middle*: ++
|
|
||||||
typeof: string ++
|
|
||||||
Command to execute when middle-clicked on the module using mousewheel.
|
|
||||||
|
|
||||||
*on-click-right*: ++
|
|
||||||
typeof: string ++
|
|
||||||
Command to execute when you right clicked on the module.
|
|
||||||
|
|
||||||
*on-update*: ++
|
|
||||||
typeof: string ++
|
|
||||||
Command to execute when the module is updated.
|
|
||||||
|
|
||||||
*on-scroll-up*: ++
|
|
||||||
typeof: string ++
|
|
||||||
Command to execute when scrolling up on the module.
|
|
||||||
|
|
||||||
*on-scroll-down*: ++
|
|
||||||
typeof: string ++
|
|
||||||
Command to execute when scrolling down on the module.
|
|
||||||
|
|
||||||
*smooth-scrolling-threshold*: ++
|
|
||||||
typeof: double ++
|
|
||||||
Threshold to be used when scrolling.
|
|
||||||
|
|
||||||
*tooltip*: ++
|
|
||||||
typeof: bool ++
|
|
||||||
default: true ++
|
|
||||||
Option to disable tooltip on hover.
|
|
||||||
|
|
||||||
View all valid format options in *strftime(3)*.
|
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{calendar}*: Current month calendar
|
- *{calendar}*: Current month calendar
|
||||||
*{timezoned_time_list}*: List of time in the rest timezones, if more than one timezone is set in the config
|
- *{timezoned_time_list}*: List of time in the rest timezones, if more than one timezone is set in the config
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
|
1. General
|
||||||
|
|
||||||
```
|
```
|
||||||
"clock": {
|
"clock": {
|
||||||
"interval": 60,
|
"interval": 60,
|
||||||
@ -108,6 +170,101 @@ View all valid format options in *strftime(3)*.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
2. Calendar
|
||||||
|
|
||||||
|
```
|
||||||
|
"clock": {
|
||||||
|
"format": "{:%H:%M} ",
|
||||||
|
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||||
|
"tooltip-format": "<tt><small>{calendar}</small></tt>",
|
||||||
|
"calendar": {
|
||||||
|
"mode" : "year",
|
||||||
|
"mode-mon-col" : 3,
|
||||||
|
"weeks-pos" : "right",
|
||||||
|
"on-scroll" : 1,
|
||||||
|
"on-click-right": "mode",
|
||||||
|
"format": {
|
||||||
|
"months": "<span color='#ffead3'><b>{}</b></span>",
|
||||||
|
"days": "<span color='#ecc6d9'><b>{}</b></span>",
|
||||||
|
"weeks": "<span color='#99ffdd'><b>W{}</b></span>",
|
||||||
|
"weekdays": "<span color='#ffcc66'><b>{}</b></span>",
|
||||||
|
"today": "<span color='#ff6699'><b><u>{}</u></b></span>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"on-click-right": "mode",
|
||||||
|
"on-click-forward": "tz_up",
|
||||||
|
"on-click-backward": "tz_down",
|
||||||
|
"on-scroll-up": "shift_up",
|
||||||
|
"on-scroll-down": "shift_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Full date on hover
|
||||||
|
|
||||||
|
```
|
||||||
|
"clock": {
|
||||||
|
"interval": 60,
|
||||||
|
"tooltip": true,
|
||||||
|
"format": "{:%H.%M}",
|
||||||
|
"tooltip-format": "{:%Y-%m-%d}",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# STYLE
|
# STYLE
|
||||||
|
|
||||||
- *#clock*
|
- *#clock*
|
||||||
|
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
If clock module is disabled at startup with locale::facet::\_S\_create\_c\_locale ++
|
||||||
|
name not valid error message try one of the followings:
|
||||||
|
|
||||||
|
- check if LC_TIME is set properly (glibc)
|
||||||
|
- set locale to C in the config file (musl)
|
||||||
|
|
||||||
|
The locale option must be set for {calendar} to use the correct start-of-week, regardless of system locale.
|
||||||
|
|
||||||
|
## Calendar in Chinese. Alignment
|
||||||
|
|
||||||
|
In order to have aligned Chinese calendar there are some useful recommendations:
|
||||||
|
|
||||||
|
. Use "WenQuanYi Zen Hei Mono" which is provided in most Linux distributions
|
||||||
|
. Try different font sizes and find best for you. size = 9pt should be fine
|
||||||
|
. In case when "WenQuanYi Zen Hei Mono" font is used disable monospace font pango tag
|
||||||
|
|
||||||
|
Example of working config
|
||||||
|
|
||||||
|
```
|
||||||
|
"clock": {
|
||||||
|
"format": "{:%H:%M} ",
|
||||||
|
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||||
|
"tooltip-format": "\n<span size='9pt' font='WenQuanYi Zen Hei Mono'>{calendar}</span>",
|
||||||
|
"calendar": {
|
||||||
|
"mode" : "year",
|
||||||
|
"mode-mon-col" : 3,
|
||||||
|
"weeks-pos" : "right",
|
||||||
|
"on-scroll" : 1,
|
||||||
|
"on-click-right": "mode",
|
||||||
|
"format": {
|
||||||
|
"months": "<span color='#ffead3'><b>{}</b></span>",
|
||||||
|
"days": "<span color='#ecc6d9'><b>{}</b></span>",
|
||||||
|
"weeks": "<span color='#99ffdd'><b>W{}</b></span>",
|
||||||
|
"weekdays": "<span color='#ffcc66'><b>{}</b></span>",
|
||||||
|
"today": "<span color='#ff6699'><b><u>{}</u></b></span>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"on-click-right": "mode",
|
||||||
|
"on-click-forward": "tz_up",
|
||||||
|
"on-click-backward": "tz_down",
|
||||||
|
"on-scroll-up": "shift_up",
|
||||||
|
"on-scroll-down": "shift_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
# AUTHOR
|
||||||
|
|
||||||
|
Alexis Rouillard <contact@arouillard.fr>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
waybar-custom(5)
|
waybar-custom(5)
|
||||||
|
|
||||||
# NAME
|
# NAME
|
||||||
|
|
||||||
waybar - custom module
|
waybar - custom module
|
||||||
@ -19,14 +18,13 @@ Addressed by *custom/<name>*
|
|||||||
|
|
||||||
*exec-if*: ++
|
*exec-if*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
The path to a script, which determines if the script in *exec* should be executed.
|
The path to a script, which determines if the script in *exec* should be executed. ++
|
||||||
*exec* will be executed if the exit code of *exec-if* equals 0.
|
*exec* will be executed if the exit code of *exec-if* equals 0.
|
||||||
|
|
||||||
*exec-on-event*: ++
|
*exec-on-event*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: true ++
|
||||||
If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after
|
If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after executing the event command.
|
||||||
executing the event command.
|
|
||||||
|
|
||||||
*return-type*: ++
|
*return-type*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
@ -34,20 +32,19 @@ Addressed by *custom/<name>*
|
|||||||
|
|
||||||
*interval*: ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The interval (in seconds) in which the information gets polled.
|
The interval (in seconds) in which the information gets polled. ++
|
||||||
Use *once* if you want to execute the module only on startup.
|
Use *once* if you want to execute the module only on startup. ++
|
||||||
You can update it manually with a signal. If no *interval* is defined,
|
You can update it manually with a signal. If no *interval* is defined, it is assumed that the out script loops it self.
|
||||||
it is assumed that the out script loops it self.
|
|
||||||
|
|
||||||
*restart-interval*: ++
|
*restart-interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The restart interval (in seconds).
|
The restart interval (in seconds). ++
|
||||||
Can't be used with the *interval* option, so only with continuous scripts.
|
Can't be used with the *interval* option, so only with continuous scripts. ++
|
||||||
Once the script exit, it'll be re-executed after the *restart-interval*.
|
Once the script exit, it'll be re-executed after the *restart-interval*.
|
||||||
|
|
||||||
*signal*: ++
|
*signal*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The signal number used to update the module.
|
The signal number used to update the module. ++
|
||||||
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*.
|
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*.
|
||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
|
@ -14,13 +14,38 @@ Addressed by *hyprland/window*
|
|||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {} ++
|
default: {title} ++
|
||||||
The format, how information should be displayed. On {} the current window title is displayed.
|
The format, how information should be displayed. On {} the current window title is displayed.
|
||||||
|
|
||||||
*rewrite*: ++
|
*rewrite*: ++
|
||||||
typeof: object ++
|
typeof: object ++
|
||||||
Rules to rewrite window title. See *rewrite rules*.
|
Rules to rewrite window title. See *rewrite rules*.
|
||||||
|
|
||||||
|
*separate-outputs*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
Show the active window of the monitor the bar belongs to, instead of the focused window.
|
||||||
|
|
||||||
|
*icon*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Option to hide the application icon.
|
||||||
|
|
||||||
|
*icon-size*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
default: 24 ++
|
||||||
|
Option to change the size of the application icon.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
See the output of "hyprctl clients" for examples
|
||||||
|
|
||||||
|
*{title}*: The current title of the focused window.
|
||||||
|
|
||||||
|
*{initialTitle}*: The initial title of the focused window.
|
||||||
|
|
||||||
|
*{class}*: The current class of the focused window.
|
||||||
|
|
||||||
|
*{initialClass}*: The initial class of the focused window.
|
||||||
|
|
||||||
# REWRITE RULES
|
# REWRITE RULES
|
||||||
|
|
||||||
*rewrite* is an object where keys are regular expressions and values are
|
*rewrite* is an object where keys are regular expressions and values are
|
||||||
@ -48,3 +73,17 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
|
|||||||
# STYLE
|
# STYLE
|
||||||
|
|
||||||
- *#window*
|
- *#window*
|
||||||
|
- *window#waybar.empty #window* When no windows are in the workspace
|
||||||
|
|
||||||
|
The following classes are applied to the entire Waybar rather than just the
|
||||||
|
window widget:
|
||||||
|
|
||||||
|
- *window#waybar.empty* When no windows are in the workspace
|
||||||
|
- *window#waybar.solo* When one tiled window is visible in the workspace
|
||||||
|
(floating windows may be present)
|
||||||
|
- *window#waybar.<app_id>* Where *<app_id>* is the *class* (e.g. *chromium*) of
|
||||||
|
the solo tiled window in the workspace (use *hyprctl clients* to see classes)
|
||||||
|
- *window#waybar.floating* When there are only floating windows in the workspace
|
||||||
|
- *window#waybar.fullscreen* When there is a fullscreen window in the workspace;
|
||||||
|
useful with Hyprland's *fullscreen, 1* mode
|
||||||
|
- *window#waybar.swallowing* When there is a swallowed window in the workspace
|
||||||
|
78
man/waybar-hyprland-workspaces.5.scd
Normal file
78
man/waybar-hyprland-workspaces.5.scd
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
waybar-hyprland-workspaces(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - hyprland workspaces module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *workspaces* module displays the currently used workspaces in hyprland compositor.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *hyprland/workspaces*
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {id} ++
|
||||||
|
The format, how information should be displayed.
|
||||||
|
|
||||||
|
*format-icons*: ++
|
||||||
|
typeof: array ++
|
||||||
|
Based on the workspace id and state, the corresponding icon gets selected. See *icons*.
|
||||||
|
|
||||||
|
*show-special*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to true special workspaces will be shown.
|
||||||
|
|
||||||
|
*all-outputs*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to false workspaces group will be shown only in assigned output. Otherwise all workspace groups are shown.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{id}*: id of workspace assigned by compositor
|
||||||
|
|
||||||
|
*{name}*: workspace name assigned by compositor
|
||||||
|
|
||||||
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
|
# ICONS
|
||||||
|
|
||||||
|
Additional to workspace name matching, the following *format-icons* can be set.
|
||||||
|
|
||||||
|
- *default*: Will be shown, when no string match is found.
|
||||||
|
- *active*: Will be shown, when workspace is active
|
||||||
|
- *special*: Will be shown on non-active special workspaces
|
||||||
|
- *persistent*: Will be shown on non-active persistent workspaces
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"hyprland/workspaces": {
|
||||||
|
"format": "{name}: {icon}",
|
||||||
|
"format-icons": {
|
||||||
|
"1": "",
|
||||||
|
"2": "",
|
||||||
|
"3": "",
|
||||||
|
"4": "",
|
||||||
|
"5": "",
|
||||||
|
"active": "",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
"persistent_workspaces": {
|
||||||
|
"*": 5, // 5 workspaces by default on every monitor
|
||||||
|
"HDMI-A-1": 3 // but only three on HDMI-A-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Style
|
||||||
|
|
||||||
|
- *#workspaces*
|
||||||
|
- *#workspaces button*
|
||||||
|
- *#workspaces button.active*
|
||||||
|
- *#workspaces button.persistent*
|
||||||
|
- *#workspaces button.special*
|
@ -13,24 +13,26 @@ The *image* module displays an image from a path.
|
|||||||
*path*: ++
|
*path*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
The path to the image.
|
The path to the image.
|
||||||
|
|
||||||
*exec*: ++
|
*exec*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
The path to the script, which should return image path file
|
The path to the script, which should return image path file. ++
|
||||||
it will only execute if the path is not set
|
It will only execute if the path is not set
|
||||||
|
|
||||||
*size*: ++
|
*size*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The width/height to render the image.
|
The width/height to render the image.
|
||||||
|
|
||||||
*interval*: ++
|
*interval*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The interval (in seconds) to re-render the image.
|
The interval (in seconds) to re-render the image. ++
|
||||||
This is useful if the contents of *path* changes.
|
This is useful if the contents of *path* changes. ++
|
||||||
If no *interval* is defined, the image will only be rendered once.
|
If no *interval* is defined, the image will only be rendered once.
|
||||||
|
|
||||||
*signal*: ++
|
*signal*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The signal number used to update the module.
|
The signal number used to update the module. ++
|
||||||
This can be used instead of *interval* if the file changes irregularly.
|
This can be used instead of *interval* if the file changes irregularly. ++
|
||||||
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*.
|
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*.
|
||||||
|
|
||||||
*on-click*: ++
|
*on-click*: ++
|
||||||
@ -57,6 +59,20 @@ The *image* module displays an image from a path.
|
|||||||
typeof: double ++
|
typeof: double ++
|
||||||
Threshold to be used when scrolling.
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
Option to enable tooltip on hover.
|
||||||
|
|
||||||
|
# SCRIPT OUTPUT
|
||||||
|
|
||||||
|
Similar to the *custom* module, output values of the script is *newline* separated.
|
||||||
|
The following is the output format:
|
||||||
|
|
||||||
|
```
|
||||||
|
$path\\n$tooltip
|
||||||
|
```
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -13,8 +13,7 @@ The *mpris* module displays currently playing media via libplayerctl.
|
|||||||
*player*: ++
|
*player*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: playerctld ++
|
default: playerctld ++
|
||||||
Name of the MPRIS player to attach to. Using the default value always
|
Name of the MPRIS player to attach to. Using the default value always follows the currenly active player.
|
||||||
follows the currenly active player.
|
|
||||||
|
|
||||||
*ignored-players*: ++
|
*ignored-players*: ++
|
||||||
typeof: []string ++
|
typeof: []string ++
|
||||||
@ -49,18 +48,15 @@ The *mpris* module displays currently playing media via libplayerctl.
|
|||||||
|
|
||||||
*artist-len*: ++
|
*artist-len*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Maximum length of the Artist tag (Wide/Fullwidth Unicode characters
|
Maximum length of the Artist tag (Wide/Fullwidth Unicode characters count as two). Set to zero to hide the artist in `{dynamic}` tag.
|
||||||
count as two). Set to zero to hide the artist in `{dynamic}` tag.
|
|
||||||
|
|
||||||
*album-len*: ++
|
*album-len*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Maximum length of the Album tag (Wide/Fullwidth Unicode characters count
|
Maximum length of the Album tag (Wide/Fullwidth Unicode characters count as two). Set to zero to hide the album in `{dynamic}` tag.
|
||||||
as two). Set to zero to hide the album in `{dynamic}` tag.
|
|
||||||
|
|
||||||
*title-len*: ++
|
*title-len*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Maximum length of the Title tag (Wide/Fullwidth Unicode characters count
|
Maximum length of the Title tag (Wide/Fullwidth Unicode characters count as two). Set to zero to hide the title in `{dynamic}` tag.
|
||||||
as two). Set to zero to hide the title in `{dynamic}` tag.
|
|
||||||
|
|
||||||
*dynamic-len*: ++
|
*dynamic-len*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
@ -71,11 +67,27 @@ The *mpris* module displays currently playing media via libplayerctl.
|
|||||||
something less than or equal to this value, so the title will always be ++
|
something less than or equal to this value, so the title will always be ++
|
||||||
displayed.
|
displayed.
|
||||||
|
|
||||||
*dynamic-priority*: ++
|
*dynamic-order*: ++
|
||||||
typeof: []string ++
|
typeof: []string ++
|
||||||
default: ["title", "length", "position", "artist", "album"] ++
|
default: ["title", "artist", "album", "position", "length"] ++
|
||||||
Priority of the tags when truncating the Dynamic tag (absence in this
|
Order of the tags shown by Dynamic tag. The position and length tags ++
|
||||||
list means force inclusion).
|
will always be combined in the format [{position}/{length}]. The order ++
|
||||||
|
of these tags in relation to other tags will be determined based on the ++
|
||||||
|
declaration of the first among the two tags. Absence in this list means ++
|
||||||
|
force exclusion.
|
||||||
|
|
||||||
|
*dynamic-importance-order*: ++
|
||||||
|
typeof: []string ++
|
||||||
|
default: ["title", "artist", "album", "position", "length"] ++
|
||||||
|
Priority of the tags when truncating the Dynamic tag. The final ones ++
|
||||||
|
will be the first to be truncated. Absence in this list means force ++
|
||||||
|
inclusion.
|
||||||
|
|
||||||
|
*dynamic-separator*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: " - " ++
|
||||||
|
These characters will be used to separate two different tags, except ++
|
||||||
|
when one of these tags is position and length.
|
||||||
|
|
||||||
*truncate-hours*: ++
|
*truncate-hours*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
@ -85,14 +97,12 @@ The *mpris* module displays currently playing media via libplayerctl.
|
|||||||
*enable-tooltip-len-limits*: ++
|
*enable-tooltip-len-limits*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
default: false ++
|
||||||
Option to enable the length limits for the tooltip as well. By default
|
Option to enable the length limits for the tooltip as well. By default the tooltip ignores all length limits.
|
||||||
the tooltip ignores all length limits.
|
|
||||||
|
|
||||||
*ellipsis*: ++
|
*ellipsis*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: "…" ++
|
default: "…" ++
|
||||||
This character will be used when any of the tags exceed their maximum
|
This character will be used when any of the tags exceed their maximum length. If you don't want to use an ellipsis, set this to empty string.
|
||||||
length. If you don't want to use an ellipsis, set this to empty string.
|
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
@ -108,7 +118,7 @@ The *mpris* module displays currently playing media via libplayerctl.
|
|||||||
|
|
||||||
*align*: ++
|
*align*: ++
|
||||||
typeof: float ++
|
typeof: float ++
|
||||||
The alignment of the text, where 0 is left-aligned and 1 is right-aligned.
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. ++
|
||||||
If the module is rotated, it will follow the flow of the text.
|
If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
*on-click*: ++
|
*on-click*: ++
|
||||||
@ -132,8 +142,7 @@ The *mpris* module displays currently playing media via libplayerctl.
|
|||||||
|
|
||||||
*status-icons*: ++
|
*status-icons*: ++
|
||||||
typeof: map[string]string ++
|
typeof: map[string]string ++
|
||||||
Allows setting _{status-icon}_ based on player status (playing, paused,
|
Allows setting _{status-icon}_ based on player status (playing, paused, stopped).
|
||||||
stopped).
|
|
||||||
|
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
@ -91,6 +91,10 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu
|
|||||||
typeof: double ++
|
typeof: double ++
|
||||||
Threshold to be used when scrolling.
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
*reverse-scrolling*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
Option to reverse the scroll direction.
|
||||||
|
|
||||||
*tooltip*: ++
|
*tooltip*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: true ++
|
||||||
|
@ -58,12 +58,12 @@ cursor is over the module, and clicking on the module toggles mute.
|
|||||||
|
|
||||||
*on-scroll-up*: ++
|
*on-scroll-up*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling up on the module.
|
Command to execute when scrolling up on the module. ++
|
||||||
This replaces the default behaviour of volume control.
|
This replaces the default behaviour of volume control.
|
||||||
|
|
||||||
*on-scroll-down*: ++
|
*on-scroll-down*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when scrolling down on the module.
|
Command to execute when scrolling down on the module. ++
|
||||||
This replaces the default behaviour of volume control.
|
This replaces the default behaviour of volume control.
|
||||||
|
|
||||||
*smooth-scrolling-threshold*: ++
|
*smooth-scrolling-threshold*: ++
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
waybar-states(5)
|
waybar-states(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - states property
|
||||||
|
|
||||||
# OVERVIEW
|
# OVERVIEW
|
||||||
|
|
||||||
Some modules support 'states' which allows percentage values to be used as styling triggers to
|
Some modules support 'states' which allows percentage values to be used as styling triggers to
|
||||||
|
@ -136,6 +136,10 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
|
|||||||
# STYLE
|
# STYLE
|
||||||
|
|
||||||
- *#window*
|
- *#window*
|
||||||
|
|
||||||
|
The following classes are applied to the entire Waybar rather than just the
|
||||||
|
window widget:
|
||||||
|
|
||||||
- *window#waybar.empty* When no windows are in the workspace, or screen is not focused and offscreen-css option is not set
|
- *window#waybar.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.solo* When one tiled window is in the workspace
|
||||||
- *window#waybar.floating* When there are only floating windows in the workspace
|
- *window#waybar.floating* When there are only floating windows in the workspace
|
||||||
|
@ -77,6 +77,11 @@ Addressed by *sway/workspaces*
|
|||||||
typeof: bool ++
|
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.
|
Whether to sort workspaces alphabetically. Please note this can make "swaymsg workspace prev/next" move to workspaces inconsistent with the ordering shown in Waybar.
|
||||||
|
|
||||||
|
warp-on-scroll: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
If set to false, you can scroll to cycle through workspaces without mouse warping being enabled. If set to true this behaviour is disabled.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{value}*: Name of the workspace, as defined by sway.
|
*{value}*: Name of the workspace, as defined by sway.
|
||||||
@ -87,6 +92,8 @@ Addressed by *sway/workspaces*
|
|||||||
|
|
||||||
*{index}*: Index of the workspace.
|
*{index}*: Index of the workspace.
|
||||||
|
|
||||||
|
*{output}*: Output where the workspace is located.
|
||||||
|
|
||||||
# ICONS
|
# ICONS
|
||||||
|
|
||||||
Additional to workspace name matching, the following *format-icons* can be set.
|
Additional to workspace name matching, the following *format-icons* can be set.
|
||||||
@ -95,6 +102,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
|||||||
- *urgent*: Will be shown, when workspace is flagged as urgent
|
- *urgent*: Will be shown, when workspace is flagged as urgent
|
||||||
- *focused*: Will be shown, when workspace is focused
|
- *focused*: Will be shown, when workspace is focused
|
||||||
- *persistent*: Will be shown, when workspace is persistent one.
|
- *persistent*: Will be shown, when workspace is persistent one.
|
||||||
|
- *high-priority-named*: Icons by names will be shown always for that workspaces, independent by state.
|
||||||
|
|
||||||
# PERSISTENT WORKSPACES
|
# PERSISTENT WORKSPACES
|
||||||
|
|
||||||
@ -127,6 +135,7 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge
|
|||||||
"3": "",
|
"3": "",
|
||||||
"4": "",
|
"4": "",
|
||||||
"5": "",
|
"5": "",
|
||||||
|
"high-priority-named": [ "1", "2" ],
|
||||||
"urgent": "",
|
"urgent": "",
|
||||||
"focused": "",
|
"focused": "",
|
||||||
"default": ""
|
"default": ""
|
||||||
|
@ -19,6 +19,8 @@ Addressed by *temperature*
|
|||||||
*hwmon-path*: ++
|
*hwmon-path*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
The temperature path to use, e.g. */sys/class/hwmon/hwmon2/temp1_input* instead of one in */sys/class/thermal/*.
|
The temperature path to use, e.g. */sys/class/hwmon/hwmon2/temp1_input* instead of one in */sys/class/thermal/*.
|
||||||
|
This can also be an array of strings. In this case, waybar will check each item in the array and use the first valid one.
|
||||||
|
This is suitable if you want to share the same configuration file among different machines with different hardware configurations.
|
||||||
|
|
||||||
*hwmon-path-abs*: ++
|
*hwmon-path-abs*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
@ -117,7 +119,7 @@ Addressed by *temperature*
|
|||||||
```
|
```
|
||||||
"temperature": {
|
"temperature": {
|
||||||
// "thermal-zone": 2,
|
// "thermal-zone": 2,
|
||||||
// "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input",
|
// "hwmon-path": ["/sys/class/hwmon/hwmon2/temp1_input", "/sys/class/thermal/thermal_zone0/temp"],
|
||||||
// "critical-threshold": 80,
|
// "critical-threshold": 80,
|
||||||
// "format-critical": "{temperatureC}°C ",
|
// "format-critical": "{temperatureC}°C ",
|
||||||
"format": "{temperatureC}°C "
|
"format": "{temperatureC}°C "
|
||||||
|
@ -6,7 +6,7 @@ waybar - tray module
|
|||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
_WARNING_ *tray* is still in beta. There may me bugs. Breaking changes may occur.
|
_WARNING_ *tray* is still in beta. There may be bugs. Breaking changes may occur.
|
||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
|
@ -11,6 +11,12 @@ compatible devices in the tooltip.
|
|||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
|
*native-path*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: ++
|
||||||
|
The battery to monitor. Refer to the https://upower.freedesktop.org/docs/UpDevice.html#UpDevice--native-path ++
|
||||||
|
Can be obtained using `upower --dump`
|
||||||
|
|
||||||
*icon-size*: ++
|
*icon-size*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
default: 20 ++
|
default: 20 ++
|
||||||
@ -68,6 +74,25 @@ depending on the charging state.
|
|||||||
"tooltip-spacing": 20
|
"tooltip-spacing": 20
|
||||||
}
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
```
|
||||||
|
"upower": {
|
||||||
|
"native-path": "/org/bluez/hci0/dev_D4_AE_41_38_D0_EF",
|
||||||
|
"icon-size": 20,
|
||||||
|
"hide-if-empty": true,
|
||||||
|
"tooltip": true,
|
||||||
|
"tooltip-spacing": 20
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
```
|
||||||
|
"upower": {
|
||||||
|
"native-path": "battery_sony_controller_battery_d0o27o88o32ofcoee",
|
||||||
|
"icon-size": 20,
|
||||||
|
"hide-if-empty": true,
|
||||||
|
"tooltip": true,
|
||||||
|
"tooltip-spacing": 20
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# STYLE
|
# STYLE
|
||||||
|
@ -49,6 +49,11 @@ The *wireplumber* module displays the current volume reported by WirePlumber.
|
|||||||
typeof: float ++
|
typeof: float ++
|
||||||
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
|
*scroll-step*: ++
|
||||||
|
typeof: float ++
|
||||||
|
default: 1.0 ++
|
||||||
|
The speed in which to change the volume when scrolling.
|
||||||
|
|
||||||
*on-click*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when clicked on the module.
|
Command to execute when clicked on the module.
|
||||||
@ -65,6 +70,19 @@ The *wireplumber* module displays the current volume reported by WirePlumber.
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
Command to execute when the module is updated.
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
*on-scroll-up*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling up on the module. This replaces the default behaviour of volume control.
|
||||||
|
|
||||||
|
*on-scroll-down*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling down on the module. This replaces the default behaviour of volume control.
|
||||||
|
|
||||||
|
*max-volume*: ++
|
||||||
|
typeof: float ++
|
||||||
|
default: 100 ++
|
||||||
|
The maximum volume that can be set, in percentage.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{volume}*: Volume in percentage.
|
*{volume}*: Volume in percentage.
|
||||||
|
@ -2,7 +2,7 @@ waybar-wlr-taskbar(5)
|
|||||||
|
|
||||||
# NAME
|
# NAME
|
||||||
|
|
||||||
wlroots - Taskbar module
|
waybar - wlr taskbar module
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
@ -81,11 +81,15 @@ Addressed by *wlr/taskbar*
|
|||||||
typeof: object ++
|
typeof: object ++
|
||||||
Dictionary of app_id to be replaced with
|
Dictionary of app_id to be replaced with
|
||||||
|
|
||||||
|
*rewrite*: ++
|
||||||
|
typeof: object ++
|
||||||
|
Rules to rewrite the module format output. See *rewrite rules*.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{icon}*: The icon of the application.
|
*{icon}*: The icon of the application.
|
||||||
|
|
||||||
*{title}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id}
|
*{name}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id}
|
||||||
|
|
||||||
*{title}*: The title of the application.
|
*{title}*: The title of the application.
|
||||||
|
|
||||||
@ -109,6 +113,18 @@ Addressed by *wlr/taskbar*
|
|||||||
|
|
||||||
*close*: Close the application.
|
*close*: Close the application.
|
||||||
|
|
||||||
|
# REWRITE RULES
|
||||||
|
|
||||||
|
*rewrite* is an object where keys are regular expressions and values are
|
||||||
|
rewrite rules if the expression matches. Rules may contain references to
|
||||||
|
captures of the expression.
|
||||||
|
|
||||||
|
Regular expression and replacement follow ECMA-script rules.
|
||||||
|
|
||||||
|
If no expression matches, the format output is left unchanged.
|
||||||
|
|
||||||
|
Invalid expressions (e.g., mismatched parentheses) are skipped.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -124,6 +140,10 @@ Addressed by *wlr/taskbar*
|
|||||||
],
|
],
|
||||||
"app_ids-mapping": {
|
"app_ids-mapping": {
|
||||||
"firefoxdeveloperedition": "firefox-developer-edition"
|
"firefoxdeveloperedition": "firefox-developer-edition"
|
||||||
|
},
|
||||||
|
"rewrite": {
|
||||||
|
"Firefox Web Browser": "Firefox",
|
||||||
|
"Foot Server": "Terminal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -29,9 +29,8 @@ Addressed by *wlr/workspaces*
|
|||||||
*sort-by-coordinates*: ++
|
*sort-by-coordinates*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: true ++
|
default: true ++
|
||||||
Should workspaces be sorted by coordinates.
|
Should workspaces be sorted by coordinates. ++
|
||||||
Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first.
|
Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first. If both are false - sort by id will be performed.
|
||||||
If both are false - sort by id will be performed.
|
|
||||||
|
|
||||||
*sort-by-number*: ++
|
*sort-by-number*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
@ -65,7 +64,7 @@ Addressed by *wlr/workspaces*
|
|||||||
Additional to workspace name matching, the following *format-icons* can be set.
|
Additional to workspace name matching, the following *format-icons* can be set.
|
||||||
|
|
||||||
- *default*: Will be shown, when no string match is found.
|
- *default*: Will be shown, when no string match is found.
|
||||||
- *focused*: Will be shown, when workspace is focused
|
- *active*: Will be shown, when workspace is active
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
@ -78,7 +77,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
|||||||
"3": "",
|
"3": "",
|
||||||
"4": "",
|
"4": "",
|
||||||
"5": "",
|
"5": "",
|
||||||
"focused": "",
|
"active": "",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"sort-by-number": true
|
"sort-by-number": true
|
||||||
|
@ -30,6 +30,7 @@ Also a minimal example configuration can be found on the at the bottom of this m
|
|||||||
*output* ++
|
*output* ++
|
||||||
typeof: string|array ++
|
typeof: string|array ++
|
||||||
Specifies on which screen this bar will be displayed. Exclamation mark(*!*) can be used to exclude specific output.
|
Specifies on which screen this bar will be displayed. Exclamation mark(*!*) can be used to exclude specific output.
|
||||||
|
Output specification follows sway's and can either be the output port such as "HDMI-A-1" or a string consisting of the make, model and serial such as "Some Company ABC123 0x00000000". See *sway-output(5)* for details.
|
||||||
In an array, star '*\**' can be used at the end to accept all outputs, in case all previous entries are exclusions.
|
In an array, star '*\**' can be used at the end to accept all outputs, in case all previous entries are exclusions.
|
||||||
|
|
||||||
*position* ++
|
*position* ++
|
||||||
@ -78,7 +79,12 @@ Also a minimal example configuration can be found on the at the bottom of this m
|
|||||||
Selects one of the preconfigured display modes. This is an equivalent of the sway-bar(5) *mode* command and supports the same values: *dock*, *hide*, *invisible*, *overlay*. ++
|
Selects one of the preconfigured display modes. This is an equivalent of the sway-bar(5) *mode* command and supports the same values: *dock*, *hide*, *invisible*, *overlay*. ++
|
||||||
Note: *hide* and *invisible* modes may be not as useful without Sway IPC.
|
Note: *hide* and *invisible* modes may be not as useful without Sway IPC.
|
||||||
|
|
||||||
modifier-reset ++
|
*start_hidden* ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: *false* ++
|
||||||
|
Option to start the bar hidden.
|
||||||
|
|
||||||
|
*modifier-reset* ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: *press*
|
default: *press*
|
||||||
Defines the timing of modifier key to reset the bar visibility.
|
Defines the timing of modifier key to reset the bar visibility.
|
||||||
@ -262,6 +268,7 @@ Valid options for the (optional) "orientation" property are: "horizontal", "vert
|
|||||||
- *waybar-backlight(5)*
|
- *waybar-backlight(5)*
|
||||||
- *waybar-battery(5)*
|
- *waybar-battery(5)*
|
||||||
- *waybar-bluetooth(5)*
|
- *waybar-bluetooth(5)*
|
||||||
|
- *waybar-cava(5)*
|
||||||
- *waybar-clock(5)*
|
- *waybar-clock(5)*
|
||||||
- *waybar-cpu(5)*
|
- *waybar-cpu(5)*
|
||||||
- *waybar-custom(5)*
|
- *waybar-custom(5)*
|
||||||
@ -288,3 +295,7 @@ Valid options for the (optional) "orientation" property are: "horizontal", "vert
|
|||||||
- *waybar-wlr-workspaces(5)*
|
- *waybar-wlr-workspaces(5)*
|
||||||
- *waybar-temperature(5)*
|
- *waybar-temperature(5)*
|
||||||
- *waybar-tray(5)*
|
- *waybar-tray(5)*
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*sway-output(5)*
|
||||||
|
13
meson.build
13
meson.build
@ -1,10 +1,10 @@
|
|||||||
project(
|
project(
|
||||||
'waybar', 'cpp', 'c',
|
'waybar', 'cpp', 'c',
|
||||||
version: '0.9.18',
|
version: '0.9.22',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>= 0.50.0',
|
meson_version: '>= 0.50.0',
|
||||||
default_options : [
|
default_options : [
|
||||||
'cpp_std=c++17',
|
'cpp_std=c++20',
|
||||||
'buildtype=release',
|
'buildtype=release',
|
||||||
'default_library=static'
|
'default_library=static'
|
||||||
],
|
],
|
||||||
@ -159,6 +159,7 @@ src_files = files(
|
|||||||
'src/AModule.cpp',
|
'src/AModule.cpp',
|
||||||
'src/ALabel.cpp',
|
'src/ALabel.cpp',
|
||||||
'src/AIconLabel.cpp',
|
'src/AIconLabel.cpp',
|
||||||
|
'src/AAppIconLabel.cpp',
|
||||||
'src/modules/custom.cpp',
|
'src/modules/custom.cpp',
|
||||||
'src/modules/disk.cpp',
|
'src/modules/disk.cpp',
|
||||||
'src/modules/idle_inhibitor.cpp',
|
'src/modules/idle_inhibitor.cpp',
|
||||||
@ -170,9 +171,11 @@ src_files = files(
|
|||||||
'src/client.cpp',
|
'src/client.cpp',
|
||||||
'src/config.cpp',
|
'src/config.cpp',
|
||||||
'src/group.cpp',
|
'src/group.cpp',
|
||||||
|
'src/util/prepare_for_sleep.cpp',
|
||||||
'src/util/ustring_clen.cpp',
|
'src/util/ustring_clen.cpp',
|
||||||
'src/util/sanitize_str.cpp',
|
'src/util/sanitize_str.cpp',
|
||||||
'src/util/rewrite_string.cpp'
|
'src/util/rewrite_string.cpp',
|
||||||
|
'src/util/gtk_icon.cpp'
|
||||||
)
|
)
|
||||||
|
|
||||||
inc_dirs = ['include']
|
inc_dirs = ['include']
|
||||||
@ -240,6 +243,7 @@ if true
|
|||||||
src_files += 'src/modules/hyprland/window.cpp'
|
src_files += 'src/modules/hyprland/window.cpp'
|
||||||
src_files += 'src/modules/hyprland/language.cpp'
|
src_files += 'src/modules/hyprland/language.cpp'
|
||||||
src_files += 'src/modules/hyprland/submap.cpp'
|
src_files += 'src/modules/hyprland/submap.cpp'
|
||||||
|
src_files += 'src/modules/hyprland/workspaces.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if libnl.found() and libnlgen.found()
|
if libnl.found() and libnlgen.found()
|
||||||
@ -342,7 +346,7 @@ if get_option('experimental')
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
cava = dependency('cava',
|
cava = dependency('cava',
|
||||||
version : '>=0.8.4',
|
version : '>=0.8.5',
|
||||||
required: get_option('cava'),
|
required: get_option('cava'),
|
||||||
fallback : ['cava', 'cava_dep'],
|
fallback : ['cava', 'cava_dep'],
|
||||||
not_found_message: 'cava is not found. Building waybar without cava')
|
not_found_message: 'cava is not found. Building waybar without cava')
|
||||||
@ -419,6 +423,7 @@ if scdoc.found()
|
|||||||
main_manpage_path,
|
main_manpage_path,
|
||||||
'waybar-backlight.5.scd',
|
'waybar-backlight.5.scd',
|
||||||
'waybar-battery.5.scd',
|
'waybar-battery.5.scd',
|
||||||
|
'waybar-cava.5.scd',
|
||||||
'waybar-clock.5.scd',
|
'waybar-clock.5.scd',
|
||||||
'waybar-cpu.5.scd',
|
'waybar-cpu.5.scd',
|
||||||
'waybar-custom.5.scd',
|
'waybar-custom.5.scd',
|
||||||
|
127
nix/default.nix
127
nix/default.nix
@ -1,60 +1,11 @@
|
|||||||
{ lib
|
{ lib
|
||||||
, stdenv
|
, waybar
|
||||||
, 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
|
, version
|
||||||
}:
|
}:
|
||||||
|
|
||||||
stdenv.mkDerivation rec {
|
waybar.overrideAttrs (prev: {
|
||||||
pname = "waybar";
|
|
||||||
inherit version;
|
inherit version;
|
||||||
# version = "0.9.16";
|
# version = "0.9.17";
|
||||||
|
|
||||||
src = lib.cleanSourceWith {
|
src = lib.cleanSourceWith {
|
||||||
filter = name: type:
|
filter = name: type:
|
||||||
@ -66,74 +17,4 @@ stdenv.mkDerivation rec {
|
|||||||
);
|
);
|
||||||
src = lib.cleanSource ../.;
|
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";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
// "sway/workspaces": {
|
// "sway/workspaces": {
|
||||||
// "disable-scroll": true,
|
// "disable-scroll": true,
|
||||||
// "all-outputs": true,
|
// "all-outputs": true,
|
||||||
|
// "warp-on-scroll": false,
|
||||||
// "format": "{name}: {icon}",
|
// "format": "{name}: {icon}",
|
||||||
// "format-icons": {
|
// "format-icons": {
|
||||||
// "1": "",
|
// "1": "",
|
||||||
|
@ -1,89 +1,158 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import gi
|
||||||
|
gi.require_version("Playerctl", "2.0")
|
||||||
|
from gi.repository import Playerctl, GLib
|
||||||
|
from gi.repository.Playerctl import Player
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import signal
|
import signal
|
||||||
import gi
|
import gi
|
||||||
import json
|
import json
|
||||||
gi.require_version('Playerctl', '2.0')
|
import os
|
||||||
from gi.repository import Playerctl, GLib
|
from typing import List
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def write_output(text, player):
|
|
||||||
logger.info('Writing output')
|
|
||||||
|
|
||||||
output = {'text': text,
|
|
||||||
'class': 'custom-' + player.props.player_name,
|
|
||||||
'alt': player.props.player_name}
|
|
||||||
|
|
||||||
sys.stdout.write(json.dumps(output) + '\n')
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
|
|
||||||
def on_play(player, status, manager):
|
|
||||||
logger.info('Received new playback status')
|
|
||||||
on_metadata(player, player.props.metadata, manager)
|
|
||||||
|
|
||||||
|
|
||||||
def on_metadata(player, metadata, manager):
|
|
||||||
logger.info('Received new metadata')
|
|
||||||
track_info = ''
|
|
||||||
|
|
||||||
if player.props.player_name == 'spotify' and \
|
|
||||||
'mpris:trackid' in metadata.keys() and \
|
|
||||||
':ad:' in player.props.metadata['mpris:trackid']:
|
|
||||||
track_info = 'AD PLAYING'
|
|
||||||
elif player.get_artist() != '' and player.get_title() != '':
|
|
||||||
track_info = '{artist} - {title}'.format(artist=player.get_artist(),
|
|
||||||
title=player.get_title())
|
|
||||||
else:
|
|
||||||
track_info = player.get_title()
|
|
||||||
|
|
||||||
if player.props.status != 'Playing' and track_info:
|
|
||||||
track_info = ' ' + track_info
|
|
||||||
write_output(track_info, player)
|
|
||||||
|
|
||||||
|
|
||||||
def on_player_appeared(manager, player, selected_player=None):
|
|
||||||
if player is not None and (selected_player is None or player.name == selected_player):
|
|
||||||
init_player(manager, player)
|
|
||||||
else:
|
|
||||||
logger.debug("New player appeared, but it's not the selected player, skipping")
|
|
||||||
|
|
||||||
|
|
||||||
def on_player_vanished(manager, player):
|
|
||||||
logger.info('Player has vanished')
|
|
||||||
sys.stdout.write('\n')
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
|
|
||||||
def init_player(manager, name):
|
|
||||||
logger.debug('Initialize player: {player}'.format(player=name.name))
|
|
||||||
player = Playerctl.Player.new_from_name(name)
|
|
||||||
player.connect('playback-status', on_play, manager)
|
|
||||||
player.connect('metadata', on_metadata, manager)
|
|
||||||
manager.manage_player(player)
|
|
||||||
on_metadata(player, player.props.metadata, manager)
|
|
||||||
|
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
def signal_handler(sig, frame):
|
||||||
logger.debug('Received signal to stop, exiting')
|
logger.info("Received signal to stop, exiting")
|
||||||
sys.stdout.write('\n')
|
sys.stdout.write("\n")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
# loop.quit()
|
# loop.quit()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerManager:
|
||||||
|
def __init__(self, selected_player=None):
|
||||||
|
self.manager = Playerctl.PlayerManager()
|
||||||
|
self.loop = GLib.MainLoop()
|
||||||
|
self.manager.connect(
|
||||||
|
"name-appeared", lambda *args: self.on_player_appeared(*args))
|
||||||
|
self.manager.connect(
|
||||||
|
"player-vanished", lambda *args: self.on_player_vanished(*args))
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||||
|
self.selected_player = selected_player
|
||||||
|
|
||||||
|
self.init_players()
|
||||||
|
|
||||||
|
def init_players(self):
|
||||||
|
for player in self.manager.props.player_names:
|
||||||
|
if self.selected_player is not None and self.selected_player != player.name:
|
||||||
|
logger.debug(f"{player.name} is not the filtered player, skipping it")
|
||||||
|
continue
|
||||||
|
self.init_player(player)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
logger.info("Starting main loop")
|
||||||
|
self.loop.run()
|
||||||
|
|
||||||
|
def init_player(self, player):
|
||||||
|
logger.info(f"Initialize new player: {player.name}")
|
||||||
|
player = Playerctl.Player.new_from_name(player)
|
||||||
|
player.connect("playback-status",
|
||||||
|
self.on_playback_status_changed, None)
|
||||||
|
player.connect("metadata", self.on_metadata_changed, None)
|
||||||
|
self.manager.manage_player(player)
|
||||||
|
self.on_metadata_changed(player, player.props.metadata)
|
||||||
|
|
||||||
|
def get_players(self) -> List[Player]:
|
||||||
|
return self.manager.props.players
|
||||||
|
|
||||||
|
def write_output(self, text, player):
|
||||||
|
logger.debug(f"Writing output: {text}")
|
||||||
|
|
||||||
|
output = {"text": text,
|
||||||
|
"class": "custom-" + player.props.player_name,
|
||||||
|
"alt": player.props.player_name}
|
||||||
|
|
||||||
|
sys.stdout.write(json.dumps(output) + "\n")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
def clear_output(self):
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
def on_playback_status_changed(self, player, status, _=None):
|
||||||
|
logger.debug(f"Playback status changed for player {player.props.player_name}: {status}")
|
||||||
|
self.on_metadata_changed(player, player.props.metadata)
|
||||||
|
|
||||||
|
def get_first_playing_player(self):
|
||||||
|
players = self.get_players()
|
||||||
|
logger.debug(f"Getting first playing player from {len(players)} players")
|
||||||
|
if len(players) > 0:
|
||||||
|
# if any are playing, show the first one that is playing
|
||||||
|
# reverse order, so that the most recently added ones are preferred
|
||||||
|
for player in players[::-1]:
|
||||||
|
if player.props.status == "Playing":
|
||||||
|
return player
|
||||||
|
# if none are playing, show the first one
|
||||||
|
return players[0]
|
||||||
|
else:
|
||||||
|
logger.debug("No players found")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def show_most_important_player(self):
|
||||||
|
logger.debug("Showing most important player")
|
||||||
|
# show the currently playing player
|
||||||
|
# or else show the first paused player
|
||||||
|
# or else show nothing
|
||||||
|
current_player = self.get_first_playing_player()
|
||||||
|
if current_player is not None:
|
||||||
|
self.on_metadata_changed(current_player, current_player.props.metadata)
|
||||||
|
else:
|
||||||
|
self.clear_output()
|
||||||
|
|
||||||
|
def on_metadata_changed(self, player, metadata, _=None):
|
||||||
|
logger.debug(f"Metadata changed for player {player.props.player_name}")
|
||||||
|
player_name = player.props.player_name
|
||||||
|
artist = player.get_artist()
|
||||||
|
title = player.get_title()
|
||||||
|
|
||||||
|
track_info = ""
|
||||||
|
if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]:
|
||||||
|
track_info = "Advertisement"
|
||||||
|
elif artist is not None and title is not None:
|
||||||
|
track_info = f"{artist} - {title}"
|
||||||
|
else:
|
||||||
|
track_info = title
|
||||||
|
|
||||||
|
if track_info:
|
||||||
|
if player.props.status == "Playing":
|
||||||
|
track_info = " " + track_info
|
||||||
|
else:
|
||||||
|
track_info = " " + track_info
|
||||||
|
# only print output if no other player is playing
|
||||||
|
current_playing = self.get_first_playing_player()
|
||||||
|
if current_playing is None or current_playing.props.player_name == player.props.player_name:
|
||||||
|
self.write_output(track_info, player)
|
||||||
|
else:
|
||||||
|
logger.debug(f"Other player {current_playing.props.player_name} is playing, skipping")
|
||||||
|
|
||||||
|
def on_player_appeared(self, _, player):
|
||||||
|
logger.info(f"Player has appeared: {player.name}")
|
||||||
|
if player is not None and (self.selected_player is None or player.name == self.selected_player):
|
||||||
|
self.init_player(player)
|
||||||
|
else:
|
||||||
|
logger.debug(
|
||||||
|
"New player appeared, but it's not the selected player, skipping")
|
||||||
|
|
||||||
|
def on_player_vanished(self, _, player):
|
||||||
|
logger.info(f"Player {player.props.player_name} has vanished")
|
||||||
|
self.show_most_important_player()
|
||||||
|
|
||||||
def parse_arguments():
|
def parse_arguments():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
# Increase verbosity with every occurrence of -v
|
# Increase verbosity with every occurrence of -v
|
||||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
parser.add_argument("-v", "--verbose", action="count", default=0)
|
||||||
|
|
||||||
# Define for which player we're listening
|
# Define for which player we"re listening
|
||||||
parser.add_argument('--player')
|
parser.add_argument("--player")
|
||||||
|
|
||||||
|
parser.add_argument("--enable-logging", action="store_true")
|
||||||
|
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
@ -92,37 +161,22 @@ def main():
|
|||||||
arguments = parse_arguments()
|
arguments = parse_arguments()
|
||||||
|
|
||||||
# Initialize logging
|
# Initialize logging
|
||||||
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG,
|
if arguments.enable_logging:
|
||||||
format='%(name)s %(levelname)s %(message)s')
|
logfile = os.path.join(os.path.dirname(
|
||||||
|
os.path.realpath(__file__)), "media-player.log")
|
||||||
|
logging.basicConfig(filename=logfile, level=logging.DEBUG,
|
||||||
|
format="%(asctime)s %(name)s %(levelname)s:%(lineno)d %(message)s")
|
||||||
|
|
||||||
# Logging is set by default to WARN and higher.
|
# Logging is set by default to WARN and higher.
|
||||||
# With every occurrence of -v it's lowered by one
|
# With every occurrence of -v it's lowered by one
|
||||||
logger.setLevel(max((3 - arguments.verbose) * 10, 0))
|
logger.setLevel(max((3 - arguments.verbose) * 10, 0))
|
||||||
|
|
||||||
# Log the sent command line arguments
|
logger.info("Creating player manager")
|
||||||
logger.debug('Arguments received {}'.format(vars(arguments)))
|
if arguments.player:
|
||||||
|
logger.info(f"Filtering for player: {arguments.player}")
|
||||||
manager = Playerctl.PlayerManager()
|
player = PlayerManager(arguments.player)
|
||||||
loop = GLib.MainLoop()
|
player.run()
|
||||||
|
|
||||||
manager.connect('name-appeared', lambda *args: on_player_appeared(*args, arguments.player))
|
|
||||||
manager.connect('player-vanished', on_player_vanished)
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
|
||||||
signal.signal(signal.SIGTERM, signal_handler)
|
|
||||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
|
||||||
|
|
||||||
for player in manager.props.player_names:
|
|
||||||
if arguments.player is not None and arguments.player != player.name:
|
|
||||||
logger.debug('{player} is not the filtered player, skipping it'
|
|
||||||
.format(player=player.name)
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
init_player(manager, player)
|
|
||||||
|
|
||||||
loop.run()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
133
src/AAppIconLabel.cpp
Normal file
133
src/AAppIconLabel.cpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#include "AAppIconLabel.hpp"
|
||||||
|
|
||||||
|
#include <gdkmm/pixbuf.h>
|
||||||
|
#include <glibmm/fileutils.h>
|
||||||
|
#include <glibmm/keyfile.h>
|
||||||
|
#include <glibmm/miscutils.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "util/gtk_icon.hpp"
|
||||||
|
|
||||||
|
namespace waybar {
|
||||||
|
|
||||||
|
AAppIconLabel::AAppIconLabel(const Json::Value& config, const std::string& name,
|
||||||
|
const std::string& id, const std::string& format, uint16_t interval,
|
||||||
|
bool ellipsize, bool enable_click, bool enable_scroll)
|
||||||
|
: AIconLabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) {
|
||||||
|
// Icon size
|
||||||
|
if (config["icon-size"].isUInt()) {
|
||||||
|
app_icon_size_ = config["icon-size"].asUInt();
|
||||||
|
}
|
||||||
|
image_.set_pixel_size(app_icon_size_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> getDesktopFilePath(const std::string& app_identifier,
|
||||||
|
const std::string& alternative_app_identifier) {
|
||||||
|
const auto data_dirs = Glib::get_system_data_dirs();
|
||||||
|
for (const auto& data_dir : data_dirs) {
|
||||||
|
const auto data_app_dir = data_dir + "applications/";
|
||||||
|
auto desktop_file_path = data_app_dir + app_identifier + ".desktop";
|
||||||
|
if (std::filesystem::exists(desktop_file_path)) {
|
||||||
|
return desktop_file_path;
|
||||||
|
}
|
||||||
|
if (!alternative_app_identifier.empty()) {
|
||||||
|
desktop_file_path = data_app_dir + alternative_app_identifier + ".desktop";
|
||||||
|
if (std::filesystem::exists(desktop_file_path)) {
|
||||||
|
return desktop_file_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Glib::ustring> getIconName(const std::string& app_identifier,
|
||||||
|
const std::string& alternative_app_identifier) {
|
||||||
|
const auto desktop_file_path = getDesktopFilePath(app_identifier, alternative_app_identifier);
|
||||||
|
if (!desktop_file_path.has_value()) {
|
||||||
|
// Try some heuristics to find a matching icon
|
||||||
|
|
||||||
|
if (DefaultGtkIconThemeWrapper::has_icon(app_identifier)) {
|
||||||
|
return app_identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto app_identifier_desktop = app_identifier + "-desktop";
|
||||||
|
if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) {
|
||||||
|
return app_identifier_desktop;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto to_lower = [](const std::string& str) {
|
||||||
|
auto str_cpy = str;
|
||||||
|
std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(),
|
||||||
|
[](unsigned char c) { return std::tolower(c); });
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto first_space = app_identifier.find_first_of(' ');
|
||||||
|
if (first_space != std::string::npos) {
|
||||||
|
const auto first_word = to_lower(app_identifier.substr(0, first_space));
|
||||||
|
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
|
||||||
|
return first_word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto first_dash = app_identifier.find_first_of('-');
|
||||||
|
if (first_dash != std::string::npos) {
|
||||||
|
const auto first_word = to_lower(app_identifier.substr(0, first_dash));
|
||||||
|
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
|
||||||
|
return first_word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Glib::KeyFile desktop_file;
|
||||||
|
desktop_file.load_from_file(desktop_file_path.value());
|
||||||
|
return desktop_file.get_string("Desktop Entry", "Icon");
|
||||||
|
} catch (Glib::FileError& error) {
|
||||||
|
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
|
||||||
|
error.what().c_str());
|
||||||
|
} catch (Glib::KeyFileError& error) {
|
||||||
|
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
|
||||||
|
error.what().c_str());
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAppIconLabel::updateAppIconName(const std::string& app_identifier,
|
||||||
|
const std::string& alternative_app_identifier) {
|
||||||
|
if (!iconEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto icon_name = getIconName(app_identifier, alternative_app_identifier);
|
||||||
|
if (icon_name.has_value()) {
|
||||||
|
app_icon_name_ = icon_name.value();
|
||||||
|
} else {
|
||||||
|
app_icon_name_ = "";
|
||||||
|
}
|
||||||
|
update_app_icon_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAppIconLabel::updateAppIcon() {
|
||||||
|
if (update_app_icon_) {
|
||||||
|
update_app_icon_ = false;
|
||||||
|
if (app_icon_name_.empty()) {
|
||||||
|
image_.set_visible(false);
|
||||||
|
} else {
|
||||||
|
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
|
||||||
|
image_.set_visible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto AAppIconLabel::update() -> void {
|
||||||
|
updateAppIcon();
|
||||||
|
AIconLabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar
|
@ -97,11 +97,21 @@ bool AModule::handleToggle(GdkEventButton* const& e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) {
|
AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) {
|
||||||
|
// only affects up/down
|
||||||
|
bool reverse = config_["reverse-scrolling"].asBool();
|
||||||
|
bool reverse_mouse = config_["reverse-mouse-scrolling"].asBool();
|
||||||
|
|
||||||
|
// ignore reverse-scrolling if event comes from a mouse wheel
|
||||||
|
GdkDevice* device = gdk_event_get_source_device((GdkEvent*)e);
|
||||||
|
if (device != NULL && gdk_device_get_source(device) == GDK_SOURCE_MOUSE) {
|
||||||
|
reverse = reverse_mouse;
|
||||||
|
}
|
||||||
|
|
||||||
switch (e->direction) {
|
switch (e->direction) {
|
||||||
case GDK_SCROLL_UP:
|
case GDK_SCROLL_UP:
|
||||||
return SCROLL_DIR::UP;
|
return reverse ? SCROLL_DIR::DOWN : SCROLL_DIR::UP;
|
||||||
case GDK_SCROLL_DOWN:
|
case GDK_SCROLL_DOWN:
|
||||||
return SCROLL_DIR::DOWN;
|
return reverse ? SCROLL_DIR::UP : SCROLL_DIR::DOWN;
|
||||||
case GDK_SCROLL_LEFT:
|
case GDK_SCROLL_LEFT:
|
||||||
return SCROLL_DIR::LEFT;
|
return SCROLL_DIR::LEFT;
|
||||||
case GDK_SCROLL_RIGHT:
|
case GDK_SCROLL_RIGHT:
|
||||||
@ -118,9 +128,9 @@ AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (distance_scrolled_y_ < -threshold) {
|
if (distance_scrolled_y_ < -threshold) {
|
||||||
dir = SCROLL_DIR::UP;
|
dir = reverse ? SCROLL_DIR::DOWN : SCROLL_DIR::UP;
|
||||||
} else if (distance_scrolled_y_ > threshold) {
|
} else if (distance_scrolled_y_ > threshold) {
|
||||||
dir = SCROLL_DIR::DOWN;
|
dir = reverse ? SCROLL_DIR::UP : SCROLL_DIR::DOWN;
|
||||||
} else if (distance_scrolled_x_ > threshold) {
|
} else if (distance_scrolled_x_ > threshold) {
|
||||||
dir = SCROLL_DIR::RIGHT;
|
dir = SCROLL_DIR::RIGHT;
|
||||||
} else if (distance_scrolled_x_ < -threshold) {
|
} else if (distance_scrolled_x_ < -threshold) {
|
||||||
|
@ -593,6 +593,10 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
setMode(MODE_DEFAULT);
|
setMode(MODE_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config["start_hidden"].asBool()) {
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
||||||
|
|
||||||
#if HAVE_SWAY
|
#if HAVE_SWAY
|
||||||
|
@ -83,6 +83,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
if (ref == "hyprland/submap") {
|
if (ref == "hyprland/submap") {
|
||||||
return new waybar::modules::hyprland::Submap(id, bar_, config_[name]);
|
return new waybar::modules::hyprland::Submap(id, bar_, config_[name]);
|
||||||
}
|
}
|
||||||
|
if (ref == "hyprland/workspaces") {
|
||||||
|
return new waybar::modules::hyprland::Workspaces(id, bar_, config_[name]);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ref == "idle_inhibitor") {
|
if (ref == "idle_inhibitor") {
|
||||||
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
|
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
|
||||||
|
@ -190,8 +190,7 @@ auto waybar::modules::Backlight::update() -> void {
|
|||||||
event_box_.show();
|
event_box_.show();
|
||||||
const uint8_t percent =
|
const uint8_t percent =
|
||||||
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
|
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
|
||||||
std::string desc =
|
std::string desc = fmt::format(fmt::runtime(format_), fmt::arg("percent", percent),
|
||||||
fmt::format(fmt::runtime(format_), fmt::arg("percent", std::to_string(percent)),
|
|
||||||
fmt::arg("icon", getIcon(percent)));
|
fmt::arg("icon", getIcon(percent)));
|
||||||
label_.set_markup(desc);
|
label_.set_markup(desc);
|
||||||
getState(percent);
|
getState(percent);
|
||||||
@ -202,7 +201,7 @@ auto waybar::modules::Backlight::update() -> void {
|
|||||||
}
|
}
|
||||||
if (!tooltip_format.empty()) {
|
if (!tooltip_format.empty()) {
|
||||||
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format),
|
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format),
|
||||||
fmt::arg("percent", std::to_string(percent)),
|
fmt::arg("percent", percent),
|
||||||
fmt::arg("icon", getIcon(percent))));
|
fmt::arg("icon", getIcon(percent))));
|
||||||
} else {
|
} else {
|
||||||
label_.set_tooltip_text(desc);
|
label_.set_tooltip_text(desc);
|
||||||
@ -305,14 +304,6 @@ bool waybar::modules::Backlight::handleScroll(GdkEventScroll *e) {
|
|||||||
return true;
|
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
|
// Get scroll step
|
||||||
double step = 1;
|
double step = 1;
|
||||||
|
|
||||||
|
@ -102,7 +102,6 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config)
|
|||||||
thread_fetch_input_ = [this] {
|
thread_fetch_input_ = [this] {
|
||||||
thread_fetch_input_.sleep_for(fetch_input_delay_);
|
thread_fetch_input_.sleep_for(fetch_input_delay_);
|
||||||
input_source_(&audio_data_);
|
input_source_(&audio_data_);
|
||||||
dp.emit();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
|
@ -17,19 +17,26 @@
|
|||||||
|
|
||||||
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||||
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
|
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
|
||||||
current_time_zone_idx_(0),
|
current_time_zone_idx_{0},
|
||||||
is_calendar_in_tooltip_(false),
|
is_calendar_in_tooltip_{false},
|
||||||
is_timezoned_list_in_tooltip_(false) {
|
is_timezoned_list_in_tooltip_{false} {
|
||||||
if (config_["timezones"].isArray() && !config_["timezones"].empty()) {
|
if (config_["timezones"].isArray() && !config_["timezones"].empty()) {
|
||||||
for (const auto& zone_name : config_["timezones"]) {
|
for (const auto& zone_name : config_["timezones"]) {
|
||||||
if (!zone_name.isString() || zone_name.asString().empty()) continue;
|
if (!zone_name.isString()) continue;
|
||||||
|
if (zone_name.asString().empty())
|
||||||
|
// local time should be shown
|
||||||
|
time_zones_.push_back(date::current_zone());
|
||||||
|
else
|
||||||
try {
|
try {
|
||||||
time_zones_.push_back(date::locate_zone(zone_name.asString()));
|
time_zones_.push_back(date::locate_zone(zone_name.asString()));
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
spdlog::warn("Timezone: {0}. {1}", zone_name.asString(), e.what());
|
spdlog::warn("Timezone: {0}. {1}", zone_name.asString(), e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (config_["timezone"].isString() && !config_["timezone"].asString().empty()) {
|
} else if (config_["timezone"].isString()) {
|
||||||
|
if (config_["timezone"].asString().empty())
|
||||||
|
time_zones_.push_back(date::current_zone());
|
||||||
|
else
|
||||||
try {
|
try {
|
||||||
time_zones_.push_back(date::locate_zone(config_["timezone"].asString()));
|
time_zones_.push_back(date::locate_zone(config_["timezone"].asString()));
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
@ -37,8 +44,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If all timezones are parsed and no one is good, add current time zone. nullptr in timezones
|
// If all timezones are parsed and no one is good
|
||||||
// vector means that local time should be shown
|
|
||||||
if (!time_zones_.size()) {
|
if (!time_zones_.size()) {
|
||||||
time_zones_.push_back(date::current_zone());
|
time_zones_.push_back(date::current_zone());
|
||||||
}
|
}
|
||||||
@ -46,10 +52,12 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
|||||||
// Check if a particular placeholder is present in the tooltip format, to know what to calculate
|
// Check if a particular placeholder is present in the tooltip format, to know what to calculate
|
||||||
// on update.
|
// on update.
|
||||||
if (config_["tooltip-format"].isString()) {
|
if (config_["tooltip-format"].isString()) {
|
||||||
std::string trimmed_format = config_["tooltip-format"].asString();
|
std::string trimmed_format{config_["tooltip-format"].asString()};
|
||||||
|
fmtMap_.insert({5, trimmed_format});
|
||||||
trimmed_format.erase(std::remove_if(trimmed_format.begin(), trimmed_format.end(),
|
trimmed_format.erase(std::remove_if(trimmed_format.begin(), trimmed_format.end(),
|
||||||
[](unsigned char x) { return std::isspace(x); }),
|
[](unsigned char x) { return std::isspace(x); }),
|
||||||
trimmed_format.end());
|
trimmed_format.end());
|
||||||
|
|
||||||
if (trimmed_format.find("{" + kCalendarPlaceholder + "}") != std::string::npos) {
|
if (trimmed_format.find("{" + kCalendarPlaceholder + "}") != std::string::npos) {
|
||||||
is_calendar_in_tooltip_ = true;
|
is_calendar_in_tooltip_ = true;
|
||||||
}
|
}
|
||||||
@ -150,54 +158,36 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const date::time_zone* waybar::modules::Clock::current_timezone() {
|
const date::time_zone* waybar::modules::Clock::current_timezone() {
|
||||||
return time_zones_[current_time_zone_idx_] ? time_zones_[current_time_zone_idx_]
|
return time_zones_[current_time_zone_idx_];
|
||||||
: date::current_zone();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waybar::modules::Clock::is_timezone_fixed() {
|
|
||||||
return time_zones_[current_time_zone_idx_] != nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Clock::update() -> void {
|
auto waybar::modules::Clock::update() -> void {
|
||||||
const auto* time_zone = current_timezone();
|
const auto* tz{current_timezone()};
|
||||||
auto now = std::chrono::system_clock::now();
|
const date::zoned_time now{
|
||||||
auto ztime = date::zoned_time{time_zone, date::floor<std::chrono::seconds>(now)};
|
tz,
|
||||||
|
date::floor<std::chrono::seconds>(
|
||||||
|
std::chrono::system_clock::now())}; // Define local time is based on provided time zone
|
||||||
|
const date::year_month_day today{
|
||||||
|
date::floor<date::days>(now.get_local_time())}; // Convert now to year_month_day
|
||||||
|
const date::year_month_day shiftedDay{today + cldCurrShift_}; // Shift today
|
||||||
|
// Define shift local time
|
||||||
|
const auto shiftedNow{date::make_zoned(
|
||||||
|
tz, date::local_days(shiftedDay) +
|
||||||
|
(now.get_local_time() - date::floor<date::days>(now.get_local_time())))};
|
||||||
|
|
||||||
auto shifted_date = date::year_month_day{date::floor<date::days>(now)} + cldCurrShift_;
|
label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now));
|
||||||
if (cldCurrShift_.count()) {
|
|
||||||
shifted_date = date::year_month_day(shifted_date.year(), shifted_date.month(), date::day(1));
|
|
||||||
}
|
|
||||||
auto now_shifted = date::sys_days{shifted_date} + (now - date::floor<date::days>(now));
|
|
||||||
auto shifted_ztime = date::zoned_time{time_zone, date::floor<std::chrono::seconds>(now_shifted)};
|
|
||||||
|
|
||||||
std::string text{""};
|
|
||||||
if (!is_timezone_fixed()) {
|
|
||||||
// As date dep is not fully compatible, prefer fmt
|
|
||||||
tzset();
|
|
||||||
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
|
|
||||||
text = fmt::format(locale_, fmt::runtime(format_), localtime);
|
|
||||||
} else {
|
|
||||||
text = fmt::format(locale_, fmt::runtime(format_), ztime);
|
|
||||||
}
|
|
||||||
label_.set_markup(text);
|
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
if (config_["tooltip-format"].isString()) {
|
const std::string tz_text{(is_timezoned_list_in_tooltip_) ? timezones_text(now.get_sys_time())
|
||||||
std::string calendar_lines{""};
|
: ""};
|
||||||
std::string timezoned_time_lines{""};
|
const std::string cld_text{(is_calendar_in_tooltip_) ? get_calendar(today, shiftedDay, tz)
|
||||||
if (is_calendar_in_tooltip_) {
|
: ""};
|
||||||
calendar_lines = get_calendar(ztime, shifted_ztime);
|
|
||||||
}
|
const std::string text{fmt::format(locale_, fmt::runtime(fmtMap_[5]), shiftedNow,
|
||||||
if (is_timezoned_list_in_tooltip_) {
|
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), tz_text),
|
||||||
timezoned_time_lines = timezones_text(&now);
|
fmt::arg(kCalendarPlaceholder.c_str(), cld_text))};
|
||||||
}
|
|
||||||
auto tooltip_format = config_["tooltip-format"].asString();
|
|
||||||
text = fmt::format(locale_, fmt::runtime(tooltip_format), shifted_ztime,
|
|
||||||
fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
|
|
||||||
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
|
||||||
label_.set_tooltip_markup(text);
|
label_.set_tooltip_markup(text);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
@ -212,15 +202,15 @@ auto waybar::modules::Clock::doAction(const std::string& name) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The number of weeks in calendar month layout plus 1 more for calendar titles
|
// The number of weeks in calendar month layout plus 1 more for calendar titles
|
||||||
unsigned cldRowsInMonth(date::year_month const ym, date::weekday const firstdow) {
|
const unsigned cldRowsInMonth(const date::year_month& ym, const date::weekday& firstdow) {
|
||||||
using namespace date;
|
using namespace date;
|
||||||
return static_cast<unsigned>(
|
return static_cast<unsigned>(
|
||||||
ceil<weeks>((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count()) +
|
ceil<weeks>((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count()) +
|
||||||
2;
|
2;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow, unsigned const line)
|
auto cldGetWeekForLine(const date::year_month& ym, const date::weekday& firstdow,
|
||||||
-> const date::year_month_weekday {
|
unsigned const line) -> const date::year_month_weekday {
|
||||||
unsigned index = line - 2;
|
unsigned index = line - 2;
|
||||||
auto sd = date::sys_days{ym / 1};
|
auto sd = date::sys_days{ym / 1};
|
||||||
if (date::weekday{sd} == firstdow) ++index;
|
if (date::weekday{sd} == firstdow) ++index;
|
||||||
@ -228,8 +218,8 @@ auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow,
|
|||||||
return ymdw;
|
return ymdw;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto getCalendarLine(date::year_month_day const currDate, date::year_month const ym,
|
auto getCalendarLine(const date::year_month_day& currDate, const date::year_month ym,
|
||||||
unsigned const line, date::weekday const firstdow,
|
const unsigned line, const date::weekday& firstdow,
|
||||||
const std::locale* const locale_) -> std::string {
|
const std::locale* const locale_) -> std::string {
|
||||||
using namespace date::literals;
|
using namespace date::literals;
|
||||||
std::ostringstream res;
|
std::ostringstream res;
|
||||||
@ -311,10 +301,9 @@ auto getCalendarLine(date::year_month_day const currDate, date::year_month const
|
|||||||
return res.str();
|
return res.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
auto waybar::modules::Clock::get_calendar(const date::year_month_day& today,
|
||||||
const date::zoned_seconds& wtime) -> std::string {
|
const date::year_month_day& ymd,
|
||||||
auto daypoint = date::floor<date::days>(wtime.get_local_time());
|
const date::time_zone* tz) -> const std::string {
|
||||||
const auto ymd{date::year_month_day{daypoint}};
|
|
||||||
const auto ym{ymd.year() / ymd.month()};
|
const auto ym{ymd.year() / ymd.month()};
|
||||||
const auto y{ymd.year()};
|
const auto y{ymd.year()};
|
||||||
const auto d{ymd.day()};
|
const auto d{ymd.day()};
|
||||||
@ -322,9 +311,6 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
|||||||
const auto maxRows{12 / cldMonCols_};
|
const auto maxRows{12 / cldMonCols_};
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
std::ostringstream tmp;
|
std::ostringstream tmp;
|
||||||
// get currdate
|
|
||||||
daypoint = date::floor<date::days>(now.get_local_time());
|
|
||||||
const auto currDate{date::year_month_day{daypoint}};
|
|
||||||
|
|
||||||
if (cldMode_ == CldMode::YEAR) {
|
if (cldMode_ == CldMode::YEAR) {
|
||||||
if (y / date::month{1} / 1 == cldYearShift_)
|
if (y / date::month{1} / 1 == cldYearShift_)
|
||||||
@ -354,6 +340,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
|||||||
else
|
else
|
||||||
m = 0u;
|
m = 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto row{0u}; row < maxRows; ++row) {
|
for (auto row{0u}; row < maxRows; ++row) {
|
||||||
const auto lines = *std::max_element(std::begin(ml) + (row * cldMonCols_),
|
const auto lines = *std::max_element(std::begin(ml) + (row * cldMonCols_),
|
||||||
std::begin(ml) + ((row + 1) * cldMonCols_));
|
std::begin(ml) + ((row + 1) * cldMonCols_));
|
||||||
@ -370,8 +357,9 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
|||||||
if (line < ml[static_cast<unsigned>(ymTmp.month()) - 1u])
|
if (line < ml[static_cast<unsigned>(ymTmp.month()) - 1u])
|
||||||
os << fmt::format(fmt::runtime(fmtMap_[4]),
|
os << fmt::format(fmt::runtime(fmtMap_[4]),
|
||||||
(line == 2)
|
(line == 2)
|
||||||
? date::sys_days{ymTmp / 1}
|
? date::zoned_seconds{tz, date::local_days{ymTmp / 1}}
|
||||||
: date::sys_days{cldGetWeekForLine(ymTmp, firstdow, line)})
|
: date::zoned_seconds{tz, date::local_days{cldGetWeekForLine(
|
||||||
|
ymTmp, firstdow, line)}})
|
||||||
<< ' ';
|
<< ' ';
|
||||||
else
|
else
|
||||||
os << std::string(cldWnLen_, ' ');
|
os << std::string(cldWnLen_, ' ');
|
||||||
@ -380,7 +368,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
|||||||
|
|
||||||
os << fmt::format(
|
os << fmt::format(
|
||||||
fmt::runtime((cldWPos_ != WeeksSide::LEFT || line == 0) ? "{:<{}}" : "{:>{}}"),
|
fmt::runtime((cldWPos_ != WeeksSide::LEFT || line == 0) ? "{:<{}}" : "{:>{}}"),
|
||||||
getCalendarLine(currDate, ymTmp, line, firstdow, &locale_),
|
getCalendarLine(today, ymTmp, line, firstdow, &locale_),
|
||||||
(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)));
|
(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)));
|
||||||
|
|
||||||
// Week numbers on the right
|
// Week numbers on the right
|
||||||
@ -390,8 +378,9 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
|||||||
os << ' '
|
os << ' '
|
||||||
<< fmt::format(fmt::runtime(fmtMap_[4]),
|
<< fmt::format(fmt::runtime(fmtMap_[4]),
|
||||||
(line == 2)
|
(line == 2)
|
||||||
? date::sys_days{ymTmp / 1}
|
? date::zoned_seconds{tz, date::local_days{ymTmp / 1}}
|
||||||
: date::sys_days{cldGetWeekForLine(ymTmp, firstdow, line)});
|
: date::zoned_seconds{tz, date::local_days{cldGetWeekForLine(
|
||||||
|
ymTmp, firstdow, line)}});
|
||||||
else
|
else
|
||||||
os << std::string(cldWnLen_, ' ');
|
os << std::string(cldWnLen_, ' ');
|
||||||
}
|
}
|
||||||
@ -414,7 +403,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
|||||||
os << fmt::format( // Apply days format
|
os << fmt::format( // Apply days format
|
||||||
fmt::runtime(fmt::format(fmt::runtime(fmtMap_[2]), tmp.str())),
|
fmt::runtime(fmt::format(fmt::runtime(fmtMap_[2]), tmp.str())),
|
||||||
// Apply today format
|
// Apply today format
|
||||||
fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", ymd.day()))));
|
fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", d))));
|
||||||
|
|
||||||
if (cldMode_ == CldMode::YEAR)
|
if (cldMode_ == CldMode::YEAR)
|
||||||
cldYearCached_ = os.str();
|
cldYearCached_ = os.str();
|
||||||
@ -450,7 +439,7 @@ void waybar::modules::Clock::tz_down() {
|
|||||||
current_time_zone_idx_ = current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1;
|
current_time_zone_idx_ = current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point* now)
|
auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point now)
|
||||||
-> std::string {
|
-> std::string {
|
||||||
if (time_zones_.size() == 1) {
|
if (time_zones_.size() == 1) {
|
||||||
return "";
|
return "";
|
||||||
@ -464,7 +453,7 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin
|
|||||||
if (!timezone) {
|
if (!timezone) {
|
||||||
timezone = date::current_zone();
|
timezone = date::current_zone();
|
||||||
}
|
}
|
||||||
auto ztime = date::zoned_time{timezone, date::floor<std::chrono::seconds>(*now)};
|
auto ztime = date::zoned_time{timezone, date::floor<std::chrono::seconds>(now)};
|
||||||
os << fmt::format(locale_, fmt::runtime(format_), ztime) << '\n';
|
os << fmt::format(locale_, fmt::runtime(format_), ztime) << '\n';
|
||||||
}
|
}
|
||||||
return os.str();
|
return os.str();
|
||||||
|
@ -72,7 +72,7 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
|
|||||||
const char *interface, uint32_t version) {
|
const char *interface, uint32_t version) {
|
||||||
if (std::strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) {
|
if (std::strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) {
|
||||||
static_cast<Tags *>(data)->status_manager_ = static_cast<struct zdwl_ipc_manager_v2 *>(
|
static_cast<Tags *>(data)->status_manager_ = static_cast<struct zdwl_ipc_manager_v2 *>(
|
||||||
(zdwl_ipc_manager_v2 *)wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 3));
|
(zdwl_ipc_manager_v2 *)wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 1));
|
||||||
}
|
}
|
||||||
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
|
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
|
||||||
version = std::min<uint32_t>(version, 1);
|
version = std::min<uint32_t>(version, 1);
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
#include "glibmm/ustring.h"
|
#include "glibmm/ustring.h"
|
||||||
#include "glibmm/variant.h"
|
#include "glibmm/variant.h"
|
||||||
#include "glibmm/varianttype.h"
|
#include "glibmm/varianttype.h"
|
||||||
#include "gtkmm/icontheme.h"
|
|
||||||
#include "gtkmm/label.h"
|
#include "gtkmm/label.h"
|
||||||
#include "gtkmm/tooltip.h"
|
#include "gtkmm/tooltip.h"
|
||||||
|
#include "util/gtk_icon.hpp"
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
Gamemode::Gamemode(const std::string& id, const Json::Value& config)
|
Gamemode::Gamemode(const std::string& id, const Json::Value& config)
|
||||||
@ -224,7 +224,7 @@ auto Gamemode::update() -> void {
|
|||||||
label_.set_markup(str);
|
label_.set_markup(str);
|
||||||
|
|
||||||
if (useIcon) {
|
if (useIcon) {
|
||||||
if (!Gtk::IconTheme::get_default()->has_icon(iconName)) {
|
if (!DefaultGtkIconThemeWrapper::has_icon(iconName)) {
|
||||||
iconName = DEFAULT_ICON_NAME;
|
iconName = DEFAULT_ICON_NAME;
|
||||||
}
|
}
|
||||||
icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID);
|
icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID);
|
||||||
|
@ -181,17 +181,25 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char buffer[8192] = {0};
|
char buffer[8192] = {0};
|
||||||
|
std::string response;
|
||||||
|
|
||||||
|
do {
|
||||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||||
|
|
||||||
if (sizeWritten < 0) {
|
if (sizeWritten < 0) {
|
||||||
spdlog::error("Hyprland IPC: Couldn't read (5)");
|
spdlog::error("Hyprland IPC: Couldn't read (5)");
|
||||||
|
close(SERVERSOCKET);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
response.append(buffer, sizeWritten);
|
||||||
|
} while (sizeWritten > 0);
|
||||||
|
|
||||||
close(SERVERSOCKET);
|
close(SERVERSOCKET);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
return std::string(buffer);
|
Json::Value IPC::getSocket1JsonReply(const std::string& rq) {
|
||||||
|
return parser_.parse(getSocket1Reply("j/" + rq));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules::hyprland
|
} // namespace waybar::modules::hyprland
|
||||||
|
@ -22,7 +22,7 @@ Language::Language(const std::string& id, const Bar& bar, const Json::Value& con
|
|||||||
initLanguage();
|
initLanguage();
|
||||||
|
|
||||||
label_.hide();
|
label_.hide();
|
||||||
ALabel::update();
|
update();
|
||||||
|
|
||||||
// register for hyprland ipc
|
// register for hyprland ipc
|
||||||
gIPC->registerForIPC("activelayout", this);
|
gIPC->registerForIPC("activelayout", this);
|
||||||
@ -38,7 +38,10 @@ auto Language::update() -> void {
|
|||||||
std::lock_guard<std::mutex> lg(mutex_);
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
|
||||||
std::string layoutName = std::string{};
|
std::string layoutName = std::string{};
|
||||||
if (config_.isMember("format-" + layout_.short_description)) {
|
if (config_.isMember("format-" + layout_.short_description + "-" + layout_.variant)) {
|
||||||
|
const auto propName = "format-" + layout_.short_description + "-" + layout_.variant;
|
||||||
|
layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString());
|
||||||
|
} else if (config_.isMember("format-" + layout_.short_description)) {
|
||||||
const auto propName = "format-" + layout_.short_description;
|
const auto propName = "format-" + layout_.short_description;
|
||||||
layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString());
|
layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString());
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,31 +1,39 @@
|
|||||||
#include "modules/hyprland/window.hpp"
|
#include "modules/hyprland/window.hpp"
|
||||||
|
|
||||||
|
#include <glibmm/fileutils.h>
|
||||||
|
#include <glibmm/keyfile.h>
|
||||||
|
#include <glibmm/miscutils.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <util/sanitize_str.hpp>
|
#include <util/sanitize_str.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "modules/hyprland/backend.hpp"
|
#include "modules/hyprland/backend.hpp"
|
||||||
#include "util/command.hpp"
|
|
||||||
#include "util/json.hpp"
|
|
||||||
#include "util/rewrite_string.hpp"
|
#include "util/rewrite_string.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||||
: ALabel(config, "window", id, "{}", 0, true), bar_(bar) {
|
: AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) {
|
||||||
modulesReady = true;
|
modulesReady = true;
|
||||||
separate_outputs = config["separate-outputs"].as<bool>();
|
separate_outputs = config["separate-outputs"].asBool();
|
||||||
|
|
||||||
if (!gIPC.get()) {
|
if (!gIPC.get()) {
|
||||||
gIPC = std::make_unique<IPC>();
|
gIPC = std::make_unique<IPC>();
|
||||||
}
|
}
|
||||||
|
|
||||||
label_.hide();
|
queryActiveWorkspace();
|
||||||
ALabel::update();
|
update();
|
||||||
|
dp.emit();
|
||||||
|
|
||||||
// register for hyprland ipc
|
// register for hyprland ipc
|
||||||
gIPC->registerForIPC("activewindow", this);
|
gIPC->registerForIPC("activewindow", this);
|
||||||
|
gIPC->registerForIPC("closewindow", this);
|
||||||
|
gIPC->registerForIPC("movewindow", this);
|
||||||
|
gIPC->registerForIPC("changefloatingmode", this);
|
||||||
|
gIPC->registerForIPC("fullscreen", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::~Window() {
|
Window::~Window() {
|
||||||
@ -38,63 +46,163 @@ auto Window::update() -> void {
|
|||||||
// fix ampersands
|
// fix ampersands
|
||||||
std::lock_guard<std::mutex> lg(mutex_);
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
|
||||||
|
std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title);
|
||||||
|
std::string window_address = workspace_.last_window;
|
||||||
|
|
||||||
|
window_data_.title = window_name;
|
||||||
|
|
||||||
if (!format_.empty()) {
|
if (!format_.empty()) {
|
||||||
label_.show();
|
label_.show();
|
||||||
label_.set_markup(fmt::format(fmt::runtime(format_),
|
label_.set_markup(waybar::util::rewriteString(
|
||||||
waybar::util::rewriteString(lastView, config_["rewrite"])));
|
fmt::format(fmt::runtime(format_), fmt::arg("title", window_name),
|
||||||
|
fmt::arg("initialTitle", window_data_.initial_title),
|
||||||
|
fmt::arg("class", window_data_.class_name),
|
||||||
|
fmt::arg("initialClass", window_data_.initial_class_name)),
|
||||||
|
config_["rewrite"]));
|
||||||
} else {
|
} else {
|
||||||
label_.hide();
|
label_.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
ALabel::update();
|
setClass("empty", workspace_.windows == 0);
|
||||||
|
setClass("solo", solo_);
|
||||||
|
setClass("floating", all_floating_);
|
||||||
|
setClass("swallowing", swallowing_);
|
||||||
|
setClass("fullscreen", fullscreen_);
|
||||||
|
|
||||||
|
if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) {
|
||||||
|
if (bar_.window.get_style_context()->has_class(last_solo_class_)) {
|
||||||
|
bar_.window.get_style_context()->remove_class(last_solo_class_);
|
||||||
|
spdlog::trace("Removing solo class: {}", last_solo_class_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!solo_class_.empty() && solo_class_ != last_solo_class_) {
|
||||||
|
bar_.window.get_style_context()->add_class(solo_class_);
|
||||||
|
spdlog::trace("Adding solo class: {}", solo_class_);
|
||||||
|
}
|
||||||
|
last_solo_class_ = solo_class_;
|
||||||
|
|
||||||
|
AAppIconLabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Window::getActiveWorkspaceID(std::string monitorName) {
|
auto Window::getActiveWorkspace() -> Workspace {
|
||||||
auto cmd = waybar::util::command::exec("hyprctl monitors -j");
|
const auto workspace = gIPC->getSocket1JsonReply("activeworkspace");
|
||||||
assert(cmd.exit_code == 0);
|
assert(workspace.isObject());
|
||||||
Json::Value json = parser_.parse(cmd.out);
|
return Workspace::parse(workspace);
|
||||||
assert(json.isArray());
|
}
|
||||||
auto monitor = std::find_if(json.begin(), json.end(),
|
|
||||||
|
auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
|
||||||
|
const auto monitors = gIPC->getSocket1JsonReply("monitors");
|
||||||
|
assert(monitors.isArray());
|
||||||
|
auto monitor = std::find_if(monitors.begin(), monitors.end(),
|
||||||
[&](Json::Value monitor) { return monitor["name"] == monitorName; });
|
[&](Json::Value monitor) { return monitor["name"] == monitorName; });
|
||||||
if (monitor == std::end(json)) {
|
if (monitor == std::end(monitors)) {
|
||||||
return 0;
|
spdlog::warn("Monitor not found: {}", monitorName);
|
||||||
|
return Workspace{-1, 0, "", ""};
|
||||||
}
|
}
|
||||||
return (*monitor)["activeWorkspace"]["id"].as<int>();
|
const int id = (*monitor)["activeWorkspace"]["id"].asInt();
|
||||||
|
|
||||||
|
const auto workspaces = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
assert(workspaces.isArray());
|
||||||
|
auto workspace = std::find_if(workspaces.begin(), workspaces.end(),
|
||||||
|
[&](Json::Value workspace) { return workspace["id"] == id; });
|
||||||
|
if (workspace == std::end(workspaces)) {
|
||||||
|
spdlog::warn("No workspace with id {}", id);
|
||||||
|
return Workspace{-1, 0, "", ""};
|
||||||
|
}
|
||||||
|
return Workspace::parse(*workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Window::getLastWindowTitle(int workspaceID) {
|
auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace {
|
||||||
auto cmd = waybar::util::command::exec("hyprctl workspaces -j");
|
return Workspace{value["id"].asInt(), value["windows"].asInt(), value["lastwindow"].asString(),
|
||||||
assert(cmd.exit_code == 0);
|
value["lastwindowtitle"].asString()};
|
||||||
Json::Value json = parser_.parse(cmd.out);
|
}
|
||||||
assert(json.isArray());
|
|
||||||
auto workspace = std::find_if(json.begin(), json.end(), [&](Json::Value workspace) {
|
|
||||||
return workspace["id"].as<int>() == workspaceID;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (workspace == std::end(json)) {
|
auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData {
|
||||||
return "";
|
return WindowData{value["floating"].asBool(), value["monitor"].asInt(),
|
||||||
|
value["class"].asString(), value["initialClass"].asString(),
|
||||||
|
value["title"].asString(), value["initialTitle"].asString(),
|
||||||
|
value["fullscreen"].asBool(), !value["grouped"].empty()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::queryActiveWorkspace() {
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
|
||||||
|
if (separate_outputs) {
|
||||||
|
workspace_ = getActiveWorkspace(this->bar_.output->name);
|
||||||
|
} else {
|
||||||
|
workspace_ = getActiveWorkspace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspace_.windows > 0) {
|
||||||
|
const auto clients = gIPC->getSocket1JsonReply("clients");
|
||||||
|
assert(clients.isArray());
|
||||||
|
auto active_window = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) {
|
||||||
|
return window["address"] == workspace_.last_window;
|
||||||
|
});
|
||||||
|
if (active_window == std::end(clients)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window_data_ = WindowData::parse(*active_window);
|
||||||
|
updateAppIconName(window_data_.class_name, window_data_.initial_class_name);
|
||||||
|
std::vector<Json::Value> workspace_windows;
|
||||||
|
std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows),
|
||||||
|
[&](Json::Value window) {
|
||||||
|
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
|
||||||
|
});
|
||||||
|
swallowing_ = std::any_of(workspace_windows.begin(), workspace_windows.end(),
|
||||||
|
[&](Json::Value window) { return !window["swallowing"].isNull(); });
|
||||||
|
std::vector<Json::Value> visible_windows;
|
||||||
|
std::copy_if(workspace_windows.begin(), workspace_windows.end(),
|
||||||
|
std::back_inserter(visible_windows),
|
||||||
|
[&](Json::Value window) { return !window["hidden"].asBool(); });
|
||||||
|
solo_ = 1 == std::count_if(visible_windows.begin(), visible_windows.end(),
|
||||||
|
[&](Json::Value window) { return !window["floating"].asBool(); });
|
||||||
|
all_floating_ = std::all_of(visible_windows.begin(), visible_windows.end(),
|
||||||
|
[&](Json::Value window) { return window["floating"].asBool(); });
|
||||||
|
fullscreen_ = window_data_.fullscreen;
|
||||||
|
|
||||||
|
// Fullscreen windows look like they are solo
|
||||||
|
if (fullscreen_) {
|
||||||
|
solo_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grouped windows have a tab bar and therefore don't look fullscreen or solo
|
||||||
|
if (window_data_.grouped) {
|
||||||
|
fullscreen_ = false;
|
||||||
|
solo_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (solo_) {
|
||||||
|
solo_class_ = window_data_.class_name;
|
||||||
|
} else {
|
||||||
|
solo_class_ = "";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window_data_ = WindowData{};
|
||||||
|
all_floating_ = false;
|
||||||
|
swallowing_ = false;
|
||||||
|
fullscreen_ = false;
|
||||||
|
solo_ = false;
|
||||||
|
solo_class_ = "";
|
||||||
}
|
}
|
||||||
return (*workspace)["lastwindowtitle"].as<std::string>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::onEvent(const std::string& ev) {
|
void Window::onEvent(const std::string& ev) {
|
||||||
std::lock_guard<std::mutex> lg(mutex_);
|
queryActiveWorkspace();
|
||||||
|
|
||||||
std::string windowName;
|
|
||||||
if (separate_outputs) {
|
|
||||||
windowName = getLastWindowTitle(getActiveWorkspaceID(this->bar_.output->name));
|
|
||||||
} else {
|
|
||||||
windowName = ev.substr(ev.find_first_of(',') + 1).substr(0, 256);
|
|
||||||
}
|
|
||||||
|
|
||||||
windowName = waybar::util::sanitize_string(windowName);
|
|
||||||
|
|
||||||
if (windowName == lastView) return;
|
|
||||||
|
|
||||||
lastView = windowName;
|
|
||||||
|
|
||||||
spdlog::debug("hyprland window onevent with {}", windowName);
|
|
||||||
|
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Window::setClass(const std::string& classname, bool enable) {
|
||||||
|
if (enable) {
|
||||||
|
if (!bar_.window.get_style_context()->has_class(classname)) {
|
||||||
|
bar_.window.get_style_context()->add_class(classname);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bar_.window.get_style_context()->remove_class(classname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules::hyprland
|
} // namespace waybar::modules::hyprland
|
||||||
|
422
src/modules/hyprland/workspaces.cpp
Normal file
422
src/modules/hyprland/workspaces.cpp
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
#include "modules/hyprland/workspaces.hpp"
|
||||||
|
|
||||||
|
#include <json/value.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <charconv>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
|
||||||
|
: AModule(config, "workspaces", id, false, false),
|
||||||
|
bar_(bar),
|
||||||
|
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||||
|
Json::Value config_format = config["format"];
|
||||||
|
|
||||||
|
format_ = config_format.isString() ? config_format.asString() : "{name}";
|
||||||
|
with_icon_ = format_.find("{icon}") != std::string::npos;
|
||||||
|
|
||||||
|
if (with_icon_ && icons_map_.empty()) {
|
||||||
|
Json::Value format_icons = config["format-icons"];
|
||||||
|
for (std::string &name : format_icons.getMemberNames()) {
|
||||||
|
icons_map_.emplace(name, format_icons[name].asString());
|
||||||
|
}
|
||||||
|
|
||||||
|
icons_map_.emplace("", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto config_all_outputs = config_["all-outputs"];
|
||||||
|
if (config_all_outputs.isBool()) {
|
||||||
|
all_outputs_ = config_all_outputs.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto config_show_special = config_["show-special"];
|
||||||
|
if (config_show_special.isBool()) {
|
||||||
|
show_special_ = config_show_special.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
box_.set_name("workspaces");
|
||||||
|
if (!id.empty()) {
|
||||||
|
box_.get_style_context()->add_class(id);
|
||||||
|
}
|
||||||
|
event_box_.add(box_);
|
||||||
|
modulesReady = true;
|
||||||
|
if (!gIPC) {
|
||||||
|
gIPC = std::make_unique<IPC>();
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
gIPC->registerForIPC("workspace", this);
|
||||||
|
gIPC->registerForIPC("createworkspace", this);
|
||||||
|
gIPC->registerForIPC("destroyworkspace", this);
|
||||||
|
gIPC->registerForIPC("focusedmon", this);
|
||||||
|
gIPC->registerForIPC("moveworkspace", this);
|
||||||
|
gIPC->registerForIPC("openwindow", this);
|
||||||
|
gIPC->registerForIPC("closewindow", this);
|
||||||
|
gIPC->registerForIPC("movewindow", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Workspaces::update() -> void {
|
||||||
|
for (std::string workspace_to_remove : workspaces_to_remove_) {
|
||||||
|
remove_workspace(workspace_to_remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaces_to_remove_.clear();
|
||||||
|
|
||||||
|
for (Json::Value &workspace_to_create : workspaces_to_create_) {
|
||||||
|
create_workspace(workspace_to_create);
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaces_to_create_.clear();
|
||||||
|
|
||||||
|
for (auto &workspace : workspaces_) {
|
||||||
|
workspace->set_active(workspace->name() == active_workspace_name_);
|
||||||
|
std::string &workspace_icon = icons_map_[""];
|
||||||
|
if (with_icon_) {
|
||||||
|
workspace_icon = workspace->select_icon(icons_map_);
|
||||||
|
}
|
||||||
|
workspace->update(format_, workspace_icon);
|
||||||
|
}
|
||||||
|
AModule::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::onEvent(const std::string &ev) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>'));
|
||||||
|
std::string payload = ev.substr(eventName.size() + 2);
|
||||||
|
|
||||||
|
if (eventName == "workspace") {
|
||||||
|
active_workspace_name_ = payload;
|
||||||
|
|
||||||
|
} else if (eventName == "destroyworkspace") {
|
||||||
|
workspaces_to_remove_.push_back(payload);
|
||||||
|
|
||||||
|
} else if (eventName == "createworkspace") {
|
||||||
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
for (Json::Value workspace_json : workspaces_json) {
|
||||||
|
if (workspace_json["name"].asString() == payload &&
|
||||||
|
(all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
||||||
|
(show_special() || !workspace_json["name"].asString().starts_with("special"))) {
|
||||||
|
workspaces_to_create_.push_back(workspace_json);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (eventName == "focusedmon") {
|
||||||
|
active_workspace_name_ = payload.substr(payload.find(',') + 1);
|
||||||
|
|
||||||
|
} else if (eventName == "moveworkspace" && !all_outputs()) {
|
||||||
|
std::string workspace = payload.substr(0, payload.find(','));
|
||||||
|
std::string new_output = payload.substr(payload.find(',') + 1);
|
||||||
|
if (bar_.output->name == new_output) { // TODO: implement this better
|
||||||
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
for (Json::Value workspace_json : workspaces_json) {
|
||||||
|
if (workspace_json["name"].asString() == workspace &&
|
||||||
|
bar_.output->name == workspace_json["monitor"].asString()) {
|
||||||
|
workspaces_to_create_.push_back(workspace_json);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
workspaces_to_remove_.push_back(workspace);
|
||||||
|
}
|
||||||
|
} else if (eventName == "openwindow" || eventName == "closewindow" || eventName == "movewindow") {
|
||||||
|
update_window_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::update_window_count() {
|
||||||
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
for (auto &workspace : workspaces_) {
|
||||||
|
auto workspace_json = std::find_if(
|
||||||
|
workspaces_json.begin(), workspaces_json.end(),
|
||||||
|
[&](Json::Value const &x) { return x["name"].asString() == workspace->name(); });
|
||||||
|
if (workspace_json != workspaces_json.end()) {
|
||||||
|
try {
|
||||||
|
workspace->set_windows((*workspace_json)["windows"].asUInt());
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
spdlog::error("Failed to update window count: {}", e.what());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
workspace->set_windows(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::create_workspace(Json::Value &value) {
|
||||||
|
// replace the existing persistent workspace if it exists
|
||||||
|
auto workspace = std::find_if(
|
||||||
|
workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr<Workspace> const &x) {
|
||||||
|
auto name = value["name"].asString();
|
||||||
|
return x->is_persistent() &&
|
||||||
|
((name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name());
|
||||||
|
});
|
||||||
|
if (workspace != workspaces_.end()) {
|
||||||
|
// replace workspace, but keep persistent flag
|
||||||
|
workspaces_.erase(workspace);
|
||||||
|
value["persistent"] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new workspace
|
||||||
|
workspaces_.emplace_back(std::make_unique<Workspace>(value));
|
||||||
|
Gtk::Button &new_workspace_button = workspaces_.back()->button();
|
||||||
|
box_.pack_start(new_workspace_button, false, false);
|
||||||
|
sort_workspaces();
|
||||||
|
new_workspace_button.show_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::remove_workspace(std::string name) {
|
||||||
|
auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(),
|
||||||
|
[&](std::unique_ptr<Workspace> &x) { return x->name() == name; });
|
||||||
|
|
||||||
|
if (workspace == workspaces_.end()) {
|
||||||
|
// happens when a workspace on another monitor is destroyed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*workspace)->is_persistent()) {
|
||||||
|
// don't remove persistent workspaces, create_workspace will take care of replacement
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
box_.remove(workspace->get()->button());
|
||||||
|
workspaces_.erase(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::fill_persistent_workspaces() {
|
||||||
|
if (config_["persistent_workspaces"].isObject() && !all_outputs()) {
|
||||||
|
const Json::Value persistent_workspaces = config_["persistent_workspaces"];
|
||||||
|
const std::vector<std::string> keys = persistent_workspaces.getMemberNames();
|
||||||
|
|
||||||
|
for (const std::string &key : keys) {
|
||||||
|
const Json::Value &value = persistent_workspaces[key];
|
||||||
|
if (value.isInt()) {
|
||||||
|
// value is a number => create that many workspaces for this monitor
|
||||||
|
// only add if either:
|
||||||
|
// 1. key is "*" and this monitor is not already defined in the config
|
||||||
|
// 2. key is the current monitor name
|
||||||
|
if ((key == "*" && std::find(keys.begin(), keys.end(), bar_.output->name) == keys.end()) ||
|
||||||
|
key == bar_.output->name) {
|
||||||
|
int amount = value.asInt();
|
||||||
|
spdlog::debug("Creating {} persistent workspaces for monitor {}", amount,
|
||||||
|
bar_.output->name);
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
persistent_workspaces_to_create_.emplace_back(
|
||||||
|
std::to_string(monitor_id_ * amount + i + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (value.isArray() && !value.empty()) {
|
||||||
|
// value is an array => key is a workspace name
|
||||||
|
// values are monitor names this workspace should be shown on
|
||||||
|
for (const Json::Value &monitor : value) {
|
||||||
|
if (monitor.isString() && monitor.asString() == bar_.output->name) {
|
||||||
|
persistent_workspaces_to_create_.emplace_back(key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::create_persistent_workspaces() {
|
||||||
|
for (const std::string &workspace_name : persistent_workspaces_to_create_) {
|
||||||
|
Json::Value new_workspace;
|
||||||
|
try {
|
||||||
|
// numbered persistent workspaces get the name as ID
|
||||||
|
new_workspace["id"] = workspace_name == "special" ? -99 : std::stoi(workspace_name);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
// named persistent workspaces start with ID=0
|
||||||
|
new_workspace["id"] = 0;
|
||||||
|
}
|
||||||
|
new_workspace["name"] = workspace_name;
|
||||||
|
new_workspace["monitor"] = bar_.output->name;
|
||||||
|
new_workspace["windows"] = 0;
|
||||||
|
new_workspace["persistent"] = true;
|
||||||
|
|
||||||
|
create_workspace(new_workspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::init() {
|
||||||
|
active_workspace_name_ = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString();
|
||||||
|
|
||||||
|
// get monitor ID from name (used by persistent workspaces)
|
||||||
|
monitor_id_ = 0;
|
||||||
|
auto monitors = gIPC->getSocket1JsonReply("monitors");
|
||||||
|
auto current_monitor = std::find_if(
|
||||||
|
monitors.begin(), monitors.end(),
|
||||||
|
[this](const Json::Value &m) { return m["name"].asString() == bar_.output->name; });
|
||||||
|
if (current_monitor == monitors.end()) {
|
||||||
|
spdlog::error("Monitor '{}' does not have an ID? Using 0", bar_.output->name);
|
||||||
|
} else {
|
||||||
|
monitor_id_ = (*current_monitor)["id"].asInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
fill_persistent_workspaces();
|
||||||
|
create_persistent_workspaces();
|
||||||
|
|
||||||
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
for (Json::Value workspace_json : workspaces_json) {
|
||||||
|
if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
||||||
|
(!workspace_json["name"].asString().starts_with("special") || show_special()))
|
||||||
|
create_workspace(workspace_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_window_count();
|
||||||
|
|
||||||
|
sort_workspaces();
|
||||||
|
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Workspaces::~Workspaces() {
|
||||||
|
gIPC->unregisterForIPC(this);
|
||||||
|
// wait for possible event handler to finish
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Workspace::Workspace(const Json::Value &workspace_data)
|
||||||
|
: id_(workspace_data["id"].asInt()),
|
||||||
|
name_(workspace_data["name"].asString()),
|
||||||
|
output_(workspace_data["monitor"].asString()), // TODO:allow using monitor desc
|
||||||
|
windows_(workspace_data["windows"].asInt()),
|
||||||
|
active_(true) {
|
||||||
|
if (name_.starts_with("name:")) {
|
||||||
|
name_ = name_.substr(5);
|
||||||
|
} else if (name_.starts_with("special")) {
|
||||||
|
name_ = id_ == -99 ? name_ : name_.substr(8);
|
||||||
|
is_special_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspace_data.isMember("persistent")) {
|
||||||
|
is_persistent_ = workspace_data["persistent"].asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
button_.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||||
|
button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked),
|
||||||
|
false);
|
||||||
|
|
||||||
|
button_.set_relief(Gtk::RELIEF_NONE);
|
||||||
|
content_.set_center_widget(label_);
|
||||||
|
button_.add(content_);
|
||||||
|
};
|
||||||
|
|
||||||
|
void add_or_remove_class(const Glib::RefPtr<Gtk::StyleContext> &context, bool condition,
|
||||||
|
const std::string &class_name) {
|
||||||
|
if (condition) {
|
||||||
|
context->add_class(class_name);
|
||||||
|
} else {
|
||||||
|
context->remove_class(class_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspace::update(const std::string &format, const std::string &icon) {
|
||||||
|
auto style_context = button_.get_style_context();
|
||||||
|
add_or_remove_class(style_context, active(), "active");
|
||||||
|
add_or_remove_class(style_context, is_special(), "special");
|
||||||
|
add_or_remove_class(style_context, is_empty(), "persistent");
|
||||||
|
|
||||||
|
label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
|
||||||
|
fmt::arg("name", name()), fmt::arg("icon", icon)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::sort_workspaces() {
|
||||||
|
std::sort(workspaces_.begin(), workspaces_.end(),
|
||||||
|
[](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
|
||||||
|
// normal -> named persistent -> named -> special -> named special
|
||||||
|
|
||||||
|
// both normal (includes numbered persistent) => sort by ID
|
||||||
|
if (a->id() > 0 && b->id() > 0) {
|
||||||
|
return a->id() < b->id();
|
||||||
|
}
|
||||||
|
|
||||||
|
// one normal, one special => normal first
|
||||||
|
if ((a->is_special()) ^ (b->is_special())) {
|
||||||
|
return b->is_special();
|
||||||
|
}
|
||||||
|
|
||||||
|
// only one normal, one named
|
||||||
|
if ((a->id() > 0) ^ (b->id() > 0)) {
|
||||||
|
return a->id() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// both special
|
||||||
|
if (a->is_special() && b->is_special()) {
|
||||||
|
// if one is -99 => put it last
|
||||||
|
if (a->id() == -99 || b->id() == -99) {
|
||||||
|
return b->id() == -99;
|
||||||
|
}
|
||||||
|
// both are 0 (not yet named persistents) / both are named specials (-98 <= ID <=-1)
|
||||||
|
return a->name() < b->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort non-special named workspaces by name (ID <= -1377)
|
||||||
|
return a->name() < b->name();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (size_t i = 0; i < workspaces_.size(); ++i) {
|
||||||
|
box_.reorder_child(workspaces_[i]->button(), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string &Workspace::select_icon(std::map<std::string, std::string> &icons_map) {
|
||||||
|
if (active()) {
|
||||||
|
auto active_icon_it = icons_map.find("active");
|
||||||
|
if (active_icon_it != icons_map.end()) {
|
||||||
|
return active_icon_it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_special()) {
|
||||||
|
auto special_icon_it = icons_map.find("special");
|
||||||
|
if (special_icon_it != icons_map.end()) {
|
||||||
|
return special_icon_it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto named_icon_it = icons_map.find(name());
|
||||||
|
if (named_icon_it != icons_map.end()) {
|
||||||
|
return named_icon_it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_persistent()) {
|
||||||
|
auto persistent_icon_it = icons_map.find("persistent");
|
||||||
|
if (persistent_icon_it != icons_map.end()) {
|
||||||
|
return persistent_icon_it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto default_icon_it = icons_map.find("default");
|
||||||
|
if (default_icon_it != icons_map.end()) {
|
||||||
|
return default_icon_it->second;
|
||||||
|
}
|
||||||
|
return icons_map[""];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Workspace::handle_clicked(GdkEventButton *bt) -> bool {
|
||||||
|
try {
|
||||||
|
if (id() > 0) { // normal or numbered persistent
|
||||||
|
gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id()));
|
||||||
|
} else if (!is_special()) { // named
|
||||||
|
gIPC->getSocket1Reply("dispatch workspace name:" + name());
|
||||||
|
} else if (id() != -99) { // named special
|
||||||
|
gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name());
|
||||||
|
} else { // special
|
||||||
|
gIPC->getSocket1Reply("dispatch togglespecialworkspace");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
spdlog::error("Failed to dispatch workspace: {}", e.what());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar::modules::hyprland
|
@ -41,14 +41,12 @@ void waybar::modules::Image::refresh(int sig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Image::update() -> void {
|
auto waybar::modules::Image::update() -> void {
|
||||||
util::command::res output_;
|
|
||||||
|
|
||||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||||
if (config_["path"].isString()) {
|
if (config_["path"].isString()) {
|
||||||
path_ = config_["path"].asString();
|
path_ = config_["path"].asString();
|
||||||
} else if (config_["exec"].isString()) {
|
} else if (config_["exec"].isString()) {
|
||||||
output_ = util::command::exec(config_["exec"].asString());
|
output_ = util::command::exec(config_["exec"].asString());
|
||||||
path_ = output_.out;
|
parseOutputRaw();
|
||||||
} else {
|
} else {
|
||||||
path_ = "";
|
path_ = "";
|
||||||
}
|
}
|
||||||
@ -58,6 +56,11 @@ auto waybar::modules::Image::update() -> void {
|
|||||||
pixbuf = {};
|
pixbuf = {};
|
||||||
|
|
||||||
if (pixbuf) {
|
if (pixbuf) {
|
||||||
|
if (tooltipEnabled() && !tooltip_.empty()) {
|
||||||
|
if (box_.get_tooltip_markup() != tooltip_) {
|
||||||
|
box_.set_tooltip_markup(tooltip_);
|
||||||
|
}
|
||||||
|
}
|
||||||
image_.set(pixbuf);
|
image_.set(pixbuf);
|
||||||
image_.show();
|
image_.show();
|
||||||
} else {
|
} else {
|
||||||
@ -67,3 +70,19 @@ auto waybar::modules::Image::update() -> void {
|
|||||||
|
|
||||||
AModule::update();
|
AModule::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::modules::Image::parseOutputRaw() {
|
||||||
|
std::istringstream output(output_.out);
|
||||||
|
std::string line;
|
||||||
|
int i = 0;
|
||||||
|
while (getline(output, line)) {
|
||||||
|
if (i == 0) {
|
||||||
|
path_ = line;
|
||||||
|
} else if (i == 1) {
|
||||||
|
tooltip_ = line;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -24,14 +24,17 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
|
|||||||
album_len_(-1),
|
album_len_(-1),
|
||||||
title_len_(-1),
|
title_len_(-1),
|
||||||
dynamic_len_(-1),
|
dynamic_len_(-1),
|
||||||
dynamic_prio_({"title", "length", "position", "artist", "album"}),
|
dynamic_prio_({"title", "artist", "album", "position", "length"}),
|
||||||
|
dynamic_order_({"title", "artist", "album", "position", "length"}),
|
||||||
|
dynamic_separator_(" - "),
|
||||||
truncate_hours_(true),
|
truncate_hours_(true),
|
||||||
tooltip_len_limits_(false),
|
tooltip_len_limits_(false),
|
||||||
// this character is used in Gnome so it's fine to use it here
|
// this character is used in Gnome so it's fine to use it here
|
||||||
ellipsis_("\u2026"),
|
ellipsis_("\u2026"),
|
||||||
player_("playerctld"),
|
player_("playerctld"),
|
||||||
manager(),
|
manager(),
|
||||||
player() {
|
player(),
|
||||||
|
last_update_(std::chrono::system_clock::now() - interval_) {
|
||||||
if (config_["format-playing"].isString()) {
|
if (config_["format-playing"].isString()) {
|
||||||
format_playing_ = config_["format-playing"].asString();
|
format_playing_ = config_["format-playing"].asString();
|
||||||
}
|
}
|
||||||
@ -44,6 +47,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
|
|||||||
if (config_["ellipsis"].isString()) {
|
if (config_["ellipsis"].isString()) {
|
||||||
ellipsis_ = config_["ellipsis"].asString();
|
ellipsis_ = config_["ellipsis"].asString();
|
||||||
}
|
}
|
||||||
|
if (config_["dynamic-separator"].isString()) {
|
||||||
|
dynamic_separator_ = config_["dynamic-separator"].asString();
|
||||||
|
}
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
if (config_["tooltip-format"].isString()) {
|
if (config_["tooltip-format"].isString()) {
|
||||||
tooltip_ = config_["tooltip-format"].asString();
|
tooltip_ = config_["tooltip-format"].asString();
|
||||||
@ -74,12 +80,23 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
|
|||||||
if (config["dynamic-len"].isUInt()) {
|
if (config["dynamic-len"].isUInt()) {
|
||||||
dynamic_len_ = config["dynamic-len"].asUInt();
|
dynamic_len_ = config["dynamic-len"].asUInt();
|
||||||
}
|
}
|
||||||
if (config_["dynamic-priority"].isArray()) {
|
// "dynamic-priority" has been kept for backward compatibility
|
||||||
|
if (config_["dynamic-importance-order"].isArray() || config_["dynamic-priority"].isArray()) {
|
||||||
dynamic_prio_.clear();
|
dynamic_prio_.clear();
|
||||||
for (auto it = config_["dynamic-priority"].begin(); it != config_["dynamic-priority"].end();
|
const auto& dynamic_priority = config_["dynamic-importance-order"].isArray()
|
||||||
++it) {
|
? config_["dynamic-importance-order"]
|
||||||
|
: config_["dynamic-priority"];
|
||||||
|
for (const auto& value : dynamic_priority) {
|
||||||
|
if (value.isString()) {
|
||||||
|
dynamic_prio_.push_back(value.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config_["dynamic-order"].isArray()) {
|
||||||
|
dynamic_order_.clear();
|
||||||
|
for (auto it = config_["dynamic-order"].begin(); it != config_["dynamic-order"].end(); ++it) {
|
||||||
if (it->isString()) {
|
if (it->isString()) {
|
||||||
dynamic_prio_.push_back(it->asString());
|
dynamic_order_.push_back(it->asString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,18 +289,28 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) ->
|
|||||||
size_t lengthLen = length.length();
|
size_t lengthLen = length.length();
|
||||||
size_t posLen = position.length();
|
size_t posLen = position.length();
|
||||||
|
|
||||||
bool showArtist = artistLen != 0;
|
bool showArtist = (artistLen != 0) && (std::find(dynamic_order_.begin(), dynamic_order_.end(),
|
||||||
bool showAlbum = albumLen != 0;
|
"artist") != dynamic_order_.end());
|
||||||
bool showTitle = titleLen != 0;
|
bool showAlbum = (albumLen != 0) && (std::find(dynamic_order_.begin(), dynamic_order_.end(),
|
||||||
bool showLength = lengthLen != 0;
|
"album") != dynamic_order_.end());
|
||||||
bool showPos = posLen != 0;
|
bool showTitle = (titleLen != 0) && (std::find(dynamic_order_.begin(), dynamic_order_.end(),
|
||||||
|
"title") != dynamic_order_.end());
|
||||||
|
bool showLength = (lengthLen != 0) && (std::find(dynamic_order_.begin(), dynamic_order_.end(),
|
||||||
|
"length") != dynamic_order_.end());
|
||||||
|
bool showPos = (posLen != 0) && (std::find(dynamic_order_.begin(), dynamic_order_.end(),
|
||||||
|
"position") != dynamic_order_.end());
|
||||||
|
|
||||||
if (truncated && dynamic_len_ >= 0) {
|
if (truncated && dynamic_len_ >= 0) {
|
||||||
size_t dynamicLen = dynamic_len_;
|
// Since the first element doesn't present a separator and we don't know a priori which one
|
||||||
if (showArtist) artistLen += 3;
|
// it will be, we add a "virtual separatorLen" to the dynamicLen, since we are adding the
|
||||||
if (showAlbum) albumLen += 3;
|
// separatorLen to all the other lengths.
|
||||||
if (showLength) lengthLen += 3;
|
size_t separatorLen = utf8_width(dynamic_separator_);
|
||||||
if (showPos) posLen += 3;
|
size_t dynamicLen = dynamic_len_ + separatorLen;
|
||||||
|
if (showArtist) artistLen += separatorLen;
|
||||||
|
if (showAlbum) albumLen += separatorLen;
|
||||||
|
if (showTitle) albumLen += separatorLen;
|
||||||
|
if (showLength) lengthLen += separatorLen;
|
||||||
|
if (showPos) posLen += separatorLen;
|
||||||
|
|
||||||
size_t totalLen = 0;
|
size_t totalLen = 0;
|
||||||
|
|
||||||
@ -330,12 +357,31 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) ->
|
|||||||
album = Glib::Markup::escape_text(album);
|
album = Glib::Markup::escape_text(album);
|
||||||
title = Glib::Markup::escape_text(title);
|
title = Glib::Markup::escape_text(title);
|
||||||
}
|
}
|
||||||
if (showArtist) dynamic << artist << " - ";
|
|
||||||
if (showAlbum) dynamic << album << " - ";
|
bool lengthOrPositionShown = false;
|
||||||
if (showTitle) dynamic << title;
|
bool previousShown = false;
|
||||||
if (showLength || showPos) {
|
std::string previousOrder = "";
|
||||||
dynamic << ' ';
|
|
||||||
|
for (const std::string& order : dynamic_order_) {
|
||||||
|
if ((order == "artist" && showArtist) || (order == "album" && showAlbum) ||
|
||||||
|
(order == "title" && showTitle)) {
|
||||||
|
if (previousShown && previousOrder != "length" && previousOrder != "position") {
|
||||||
|
dynamic << dynamic_separator_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (order == "artist") {
|
||||||
|
dynamic << artist;
|
||||||
|
} else if (order == "album") {
|
||||||
|
dynamic << album;
|
||||||
|
} else if (order == "title") {
|
||||||
|
dynamic << title;
|
||||||
|
}
|
||||||
|
|
||||||
|
previousShown = true;
|
||||||
|
} else if (order == "length" || order == "position") {
|
||||||
|
if (!lengthOrPositionShown && (showLength || showPos)) {
|
||||||
if (html) dynamic << "<small>";
|
if (html) dynamic << "<small>";
|
||||||
|
if (previousShown) dynamic << ' ';
|
||||||
dynamic << '[';
|
dynamic << '[';
|
||||||
if (showPos) {
|
if (showPos) {
|
||||||
dynamic << position;
|
dynamic << position;
|
||||||
@ -343,7 +389,12 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) ->
|
|||||||
}
|
}
|
||||||
if (showLength) dynamic << length;
|
if (showLength) dynamic << length;
|
||||||
dynamic << ']';
|
dynamic << ']';
|
||||||
|
if (!dynamic.str().empty()) dynamic << ' ';
|
||||||
if (html) dynamic << "</small>";
|
if (html) dynamic << "</small>";
|
||||||
|
lengthOrPositionShown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousOrder = order;
|
||||||
}
|
}
|
||||||
return dynamic.str();
|
return dynamic.str();
|
||||||
}
|
}
|
||||||
@ -559,6 +610,10 @@ bool Mpris::handleToggle(GdkEventButton* const& e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Mpris::update() -> void {
|
auto Mpris::update() -> void {
|
||||||
|
const auto now = std::chrono::system_clock::now();
|
||||||
|
if (now - last_update_ < interval_) return;
|
||||||
|
last_update_ = now;
|
||||||
|
|
||||||
auto opt = getPlayerInfo();
|
auto opt = getPlayerInfo();
|
||||||
if (!opt) {
|
if (!opt) {
|
||||||
event_box_.set_visible(false);
|
event_box_.set_visible(false);
|
||||||
|
@ -81,13 +81,6 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
|
|||||||
if (dir == SCROLL_DIR::NONE) {
|
if (dir == SCROLL_DIR::NONE) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (config_["reverse-scrolling"].asInt() == 1) {
|
|
||||||
if (dir == SCROLL_DIR::UP) {
|
|
||||||
dir = SCROLL_DIR::DOWN;
|
|
||||||
} else if (dir == SCROLL_DIR::DOWN) {
|
|
||||||
dir = SCROLL_DIR::UP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
|
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
|
||||||
pa_volume_t change = volume_tick;
|
pa_volume_t change = volume_tick;
|
||||||
pa_cvolume pa_volume = pa_volume_;
|
pa_cvolume pa_volume = pa_volume_;
|
||||||
|
@ -106,7 +106,11 @@ void Window::handle_focused_view(const char *title) {
|
|||||||
label_.hide(); // hide empty labels or labels with empty format
|
label_.hide(); // hide empty labels or labels with empty format
|
||||||
} else {
|
} else {
|
||||||
label_.show();
|
label_.show();
|
||||||
label_.set_markup(fmt::format(fmt::runtime(format_), Glib::Markup::escape_text(title).raw()));
|
auto text = fmt::format(fmt::runtime(format_), Glib::Markup::escape_text(title).raw());
|
||||||
|
label_.set_markup(text);
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
label_.set_tooltip_markup(text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
|
@ -19,7 +19,7 @@ Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar,
|
|||||||
|
|
||||||
Host::~Host() {
|
Host::~Host() {
|
||||||
if (bus_name_id_ > 0) {
|
if (bus_name_id_ > 0) {
|
||||||
Gio::DBus::unwatch_name(bus_name_id_);
|
Gio::DBus::unown_name(bus_name_id_);
|
||||||
bus_name_id_ = 0;
|
bus_name_id_ = 0;
|
||||||
}
|
}
|
||||||
if (watcher_id_ > 0) {
|
if (watcher_id_ > 0) {
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "util/format.hpp"
|
#include "util/format.hpp"
|
||||||
|
#include "util/gtk_icon.hpp"
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct fmt::formatter<Glib::VariantBase> : formatter<std::string> {
|
struct fmt::formatter<Glib::VariantBase> : formatter<std::string> {
|
||||||
@ -21,7 +22,7 @@ struct fmt::formatter<Glib::VariantBase> : formatter<std::string> {
|
|||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const Glib::VariantBase& value, FormatContext& ctx) {
|
auto format(const Glib::VariantBase& value, FormatContext& ctx) {
|
||||||
if (is_printable(value)) {
|
if (is_printable(value)) {
|
||||||
return formatter<std::string>::format(value.print(), ctx);
|
return formatter<std::string>::format(static_cast<std::string>(value.print()), ctx);
|
||||||
} else {
|
} else {
|
||||||
return formatter<std::string>::format(value.get_type_string(), ctx);
|
return formatter<std::string>::format(value.get_type_string(), ctx);
|
||||||
}
|
}
|
||||||
@ -379,9 +380,7 @@ Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int reque
|
|||||||
return icon_theme->load_icon(name.c_str(), tmp_size,
|
return icon_theme->load_icon(name.c_str(), tmp_size,
|
||||||
Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
|
Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
|
||||||
}
|
}
|
||||||
Glib::RefPtr<Gtk::IconTheme> default_theme = Gtk::IconTheme::get_default();
|
return DefaultGtkIconThemeWrapper::load_icon(name.c_str(), tmp_size,
|
||||||
default_theme->rescan_if_needed();
|
|
||||||
return default_theme->load_icon(name.c_str(), tmp_size,
|
|
||||||
Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
|
Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ Watcher::~Watcher() {
|
|||||||
g_slist_free_full(hosts_, gfWatchFree);
|
g_slist_free_full(hosts_, gfWatchFree);
|
||||||
hosts_ = nullptr;
|
hosts_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items_ != nullptr) {
|
if (items_ != nullptr) {
|
||||||
g_slist_free_full(items_, gfWatchFree);
|
g_slist_free_full(items_, gfWatchFree);
|
||||||
items_ = nullptr;
|
items_ = nullptr;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "modules/sway/ipc/client.hpp"
|
#include "modules/sway/ipc/client.hpp"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@ -17,12 +18,16 @@ Ipc::~Ipc() {
|
|||||||
|
|
||||||
if (fd_ > 0) {
|
if (fd_ > 0) {
|
||||||
// To fail the IPC header
|
// To fail the IPC header
|
||||||
write(fd_, "close-sway-ipc", 14);
|
if (write(fd_, "close-sway-ipc", 14) == -1) {
|
||||||
|
spdlog::error("Failed to close sway IPC");
|
||||||
|
}
|
||||||
close(fd_);
|
close(fd_);
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
}
|
}
|
||||||
if (fd_event_ > 0) {
|
if (fd_event_ > 0) {
|
||||||
write(fd_event_, "close-sway-ipc", 14);
|
if (write(fd_event_, "close-sway-ipc", 14) == -1) {
|
||||||
|
spdlog::error("Failed to close sway IPC event handler");
|
||||||
|
}
|
||||||
close(fd_event_);
|
close(fd_event_);
|
||||||
fd_event_ = -1;
|
fd_event_ = -1;
|
||||||
}
|
}
|
||||||
|
@ -5,25 +5,19 @@
|
|||||||
#include <glibmm/keyfile.h>
|
#include <glibmm/keyfile.h>
|
||||||
#include <glibmm/miscutils.h>
|
#include <glibmm/miscutils.h>
|
||||||
#include <gtkmm/enums.h>
|
#include <gtkmm/enums.h>
|
||||||
#include <gtkmm/icontheme.h>
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "util/gtk_icon.hpp"
|
||||||
#include "util/rewrite_string.hpp"
|
#include "util/rewrite_string.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::sway {
|
namespace waybar::modules::sway {
|
||||||
|
|
||||||
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||||
: AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
|
: AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
|
||||||
// Icon size
|
|
||||||
if (config_["icon-size"].isUInt()) {
|
|
||||||
app_icon_size_ = config["icon-size"].asUInt();
|
|
||||||
}
|
|
||||||
image_.set_pixel_size(app_icon_size_);
|
|
||||||
|
|
||||||
ipc_.subscribe(R"(["window","workspace"])");
|
ipc_.subscribe(R"(["window","workspace"])");
|
||||||
ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent));
|
ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent));
|
||||||
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd));
|
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd));
|
||||||
@ -49,7 +43,7 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
|
|||||||
auto output = payload["output"].isString() ? payload["output"].asString() : "";
|
auto output = payload["output"].isString() ? payload["output"].asString() : "";
|
||||||
std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) =
|
std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) =
|
||||||
getFocusedNode(payload["nodes"], output);
|
getFocusedNode(payload["nodes"], output);
|
||||||
updateAppIconName();
|
updateAppIconName(app_id_, app_class_);
|
||||||
dp.emit();
|
dp.emit();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
spdlog::error("Window: {}", e.what());
|
spdlog::error("Window: {}", e.what());
|
||||||
@ -57,106 +51,6 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> getDesktopFilePath(const std::string& app_id,
|
|
||||||
const std::string& app_class) {
|
|
||||||
const auto data_dirs = Glib::get_system_data_dirs();
|
|
||||||
for (const auto& data_dir : data_dirs) {
|
|
||||||
const auto data_app_dir = data_dir + "applications/";
|
|
||||||
auto desktop_file_path = data_app_dir + app_id + ".desktop";
|
|
||||||
if (std::filesystem::exists(desktop_file_path)) {
|
|
||||||
return desktop_file_path;
|
|
||||||
}
|
|
||||||
if (!app_class.empty()) {
|
|
||||||
desktop_file_path = data_app_dir + app_class + ".desktop";
|
|
||||||
if (std::filesystem::exists(desktop_file_path)) {
|
|
||||||
return desktop_file_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<Glib::ustring> getIconName(const std::string& app_id, const std::string& app_class) {
|
|
||||||
const auto desktop_file_path = getDesktopFilePath(app_id, app_class);
|
|
||||||
if (!desktop_file_path.has_value()) {
|
|
||||||
// Try some heuristics to find a matching icon
|
|
||||||
|
|
||||||
const auto default_icon_theme = Gtk::IconTheme::get_default();
|
|
||||||
if (default_icon_theme->has_icon(app_id)) {
|
|
||||||
return app_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto app_id_desktop = app_id + "-desktop";
|
|
||||||
if (default_icon_theme->has_icon(app_id_desktop)) {
|
|
||||||
return app_id_desktop;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto to_lower = [](const std::string& str) {
|
|
||||||
auto str_cpy = str;
|
|
||||||
std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(),
|
|
||||||
[](unsigned char c) { return std::tolower(c); });
|
|
||||||
return str;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto first_space = app_id.find_first_of(' ');
|
|
||||||
if (first_space != std::string::npos) {
|
|
||||||
const auto first_word = to_lower(app_id.substr(0, first_space));
|
|
||||||
if (default_icon_theme->has_icon(first_word)) {
|
|
||||||
return first_word;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto first_dash = app_id.find_first_of('-');
|
|
||||||
if (first_dash != std::string::npos) {
|
|
||||||
const auto first_word = to_lower(app_id.substr(0, first_dash));
|
|
||||||
if (default_icon_theme->has_icon(first_word)) {
|
|
||||||
return first_word;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Glib::KeyFile desktop_file;
|
|
||||||
desktop_file.load_from_file(desktop_file_path.value());
|
|
||||||
return desktop_file.get_string("Desktop Entry", "Icon");
|
|
||||||
} catch (Glib::FileError& error) {
|
|
||||||
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
|
|
||||||
error.what().c_str());
|
|
||||||
} catch (Glib::KeyFileError& error) {
|
|
||||||
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
|
|
||||||
error.what().c_str());
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::updateAppIconName() {
|
|
||||||
if (!iconEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto icon_name = getIconName(app_id_, app_class_);
|
|
||||||
if (icon_name.has_value()) {
|
|
||||||
app_icon_name_ = icon_name.value();
|
|
||||||
} else {
|
|
||||||
app_icon_name_ = "";
|
|
||||||
}
|
|
||||||
update_app_icon_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::updateAppIcon() {
|
|
||||||
if (update_app_icon_) {
|
|
||||||
update_app_icon_ = false;
|
|
||||||
if (app_icon_name_.empty()) {
|
|
||||||
image_.set_visible(false);
|
|
||||||
} else {
|
|
||||||
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
|
|
||||||
image_.set_visible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Window::update() -> void {
|
auto Window::update() -> void {
|
||||||
spdlog::trace("workspace layout {}, tiled count {}, floating count {}", layout_, app_nb_,
|
spdlog::trace("workspace layout {}, tiled count {}, floating count {}", layout_, app_nb_,
|
||||||
floating_count_);
|
floating_count_);
|
||||||
@ -178,10 +72,6 @@ auto Window::update() -> void {
|
|||||||
} else {
|
} else {
|
||||||
mode += 32;
|
mode += 32;
|
||||||
}
|
}
|
||||||
if (!app_id_.empty() && !bar_.window.get_style_context()->has_class(app_id_)) {
|
|
||||||
bar_.window.get_style_context()->add_class(app_id_);
|
|
||||||
old_app_id_ = app_id_;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!old_app_id_.empty() && ((mode & 2) == 0 || old_app_id_ != app_id_) &&
|
if (!old_app_id_.empty() && ((mode & 2) == 0 || old_app_id_ != app_id_) &&
|
||||||
@ -215,7 +105,7 @@ auto Window::update() -> void {
|
|||||||
updateAppIcon();
|
updateAppIcon();
|
||||||
|
|
||||||
// Call parent update
|
// Call parent update
|
||||||
AIconLabel::update();
|
AAppIconLabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setClass(std::string classname, bool enable) {
|
void Window::setClass(std::string classname, bool enable) {
|
||||||
@ -255,6 +145,40 @@ std::pair<int, int> leafNodesInWorkspace(const Json::Value& node) {
|
|||||||
return {sum, floating_sum};
|
return {sum, floating_sum};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::reference_wrapper<const Json::Value>> getSingleChildNode(
|
||||||
|
const Json::Value& node) {
|
||||||
|
auto const& nodes = node["nodes"];
|
||||||
|
if (nodes.empty()) {
|
||||||
|
if (node["type"].asString() == "workspace")
|
||||||
|
return {};
|
||||||
|
else if (node["type"].asString() == "floating_con") {
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
return {std::cref(node)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto it = std::cbegin(nodes);
|
||||||
|
if (it == std::cend(nodes)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto const& child = *it;
|
||||||
|
++it;
|
||||||
|
if (it != std::cend(nodes)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {getSingleChildNode(child)};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<std::string, std::string, std::string> getWindowInfo(const Json::Value& node) {
|
||||||
|
const auto app_id = node["app_id"].isString() ? node["app_id"].asString()
|
||||||
|
: node["window_properties"]["instance"].asString();
|
||||||
|
const auto app_class = node["window_properties"]["class"].isString()
|
||||||
|
? node["window_properties"]["class"].asString()
|
||||||
|
: "";
|
||||||
|
const auto shell = node["shell"].isString() ? node["shell"].asString() : "";
|
||||||
|
return {app_id, app_class, shell};
|
||||||
|
}
|
||||||
|
|
||||||
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
|
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
|
||||||
gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Value& config_,
|
gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Value& config_,
|
||||||
const Bar& bar_, Json::Value& parentWorkspace,
|
const Bar& bar_, Json::Value& parentWorkspace,
|
||||||
@ -291,12 +215,7 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu
|
|||||||
// found node
|
// found node
|
||||||
spdlog::trace("actual output {}, output found {}, node (focused) found {}", bar_.output->name,
|
spdlog::trace("actual output {}, output found {}, node (focused) found {}", bar_.output->name,
|
||||||
output, node["name"].asString());
|
output, node["name"].asString());
|
||||||
auto app_id = node["app_id"].isString() ? node["app_id"].asString()
|
const auto [app_id, app_class, shell] = getWindowInfo(node);
|
||||||
: 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 nb = node.size();
|
||||||
int floating_count = 0;
|
int floating_count = 0;
|
||||||
std::string workspace_layout = "";
|
std::string workspace_layout = "";
|
||||||
@ -336,15 +255,24 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu
|
|||||||
std::pair all_leaf_nodes = leafNodesInWorkspace(immediateParent);
|
std::pair all_leaf_nodes = leafNodesInWorkspace(immediateParent);
|
||||||
// using an empty string as default ensures that no window depending styles are set due to the
|
// using an empty string as default ensures that no window depending styles are set due to the
|
||||||
// checks above for !name.empty()
|
// checks above for !name.empty()
|
||||||
|
std::string app_id = "";
|
||||||
|
std::string app_class = "";
|
||||||
|
std::string workspace_layout = "";
|
||||||
|
if (all_leaf_nodes.first == 1) {
|
||||||
|
const auto single_child = getSingleChildNode(immediateParent);
|
||||||
|
if (single_child.has_value()) {
|
||||||
|
std::tie(app_id, app_class, workspace_layout) = getWindowInfo(single_child.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
return {all_leaf_nodes.first,
|
return {all_leaf_nodes.first,
|
||||||
all_leaf_nodes.second,
|
all_leaf_nodes.second,
|
||||||
0,
|
0,
|
||||||
(all_leaf_nodes.first > 0 || all_leaf_nodes.second > 0)
|
(all_leaf_nodes.first > 0 || all_leaf_nodes.second > 0)
|
||||||
? config_["offscreen-css-text"].asString()
|
? config_["offscreen-css-text"].asString()
|
||||||
: "",
|
: "",
|
||||||
"",
|
app_id,
|
||||||
"",
|
app_class,
|
||||||
"",
|
workspace_layout,
|
||||||
immediateParent["layout"].asString()};
|
immediateParent["layout"].asString()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,11 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
|||||||
: AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()),
|
: AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()),
|
||||||
bar_(bar),
|
bar_(bar),
|
||||||
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
|
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||||
|
if (config["format-icons"]["high-priority-named"].isArray()) {
|
||||||
|
for (auto &it : config["format-icons"]["high-priority-named"]) {
|
||||||
|
high_priority_named_.push_back(it.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
box_.set_name("workspaces");
|
box_.set_name("workspaces");
|
||||||
if (!id.empty()) {
|
if (!id.empty()) {
|
||||||
box_.get_style_context()->add_class(id);
|
box_.get_style_context()->add_class(id);
|
||||||
@ -235,7 +240,8 @@ auto Workspaces::update() -> void {
|
|||||||
auto format = config_["format"].asString();
|
auto format = config_["format"].asString();
|
||||||
output = fmt::format(fmt::runtime(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("value", output), fmt::arg("name", trimWorkspaceName(output)),
|
||||||
fmt::arg("index", (*it)["num"].asString()));
|
fmt::arg("index", (*it)["num"].asString()),
|
||||||
|
fmt::arg("output", (*it)["output"].asString()));
|
||||||
}
|
}
|
||||||
if (!config_["disable-markup"].asBool()) {
|
if (!config_["disable-markup"].asBool()) {
|
||||||
static_cast<Gtk::Label *>(button.get_children()[0])->set_markup(output);
|
static_cast<Gtk::Label *>(button.get_children()[0])->set_markup(output);
|
||||||
@ -278,9 +284,24 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) {
|
std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) {
|
||||||
std::vector<std::string> keys = {name, "urgent", "focused", "visible", "default"};
|
std::vector<std::string> keys = {"high-priority-named", "urgent", "focused", name, "default"};
|
||||||
for (auto const &key : keys) {
|
for (auto const &key : keys) {
|
||||||
if (key == "focused" || key == "visible" || key == "urgent") {
|
if (key == "high-priority-named") {
|
||||||
|
auto it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(),
|
||||||
|
[&](const std::string &member) { return member == name; });
|
||||||
|
if (it != high_priority_named_.end()) {
|
||||||
|
return config_["format-icons"][name].asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(),
|
||||||
|
[&](const std::string &member) {
|
||||||
|
return trimWorkspaceName(member) == trimWorkspaceName(name);
|
||||||
|
});
|
||||||
|
if (it != high_priority_named_.end()) {
|
||||||
|
return config_["format-icons"][trimWorkspaceName(name)].asString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (key == "focused" || key == "urgent") {
|
||||||
if (config_["format-icons"][key].isString() && node[key].asBool()) {
|
if (config_["format-icons"][key].isString() && node[key].asBool()) {
|
||||||
return config_["format-icons"][key].asString();
|
return config_["format-icons"][key].asString();
|
||||||
}
|
}
|
||||||
@ -326,11 +347,17 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) {
|
||||||
|
ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping none"));
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, "--no-auto-back-and-forth", name));
|
ipc_.sendCmd(IPC_COMMAND, fmt::format(workspace_switch_cmd_, "--no-auto-back-and-forth", name));
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
spdlog::error("Workspaces: {}", e.what());
|
spdlog::error("Workspaces: {}", e.what());
|
||||||
}
|
}
|
||||||
|
if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) {
|
||||||
|
ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping container"));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,8 +11,18 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val
|
|||||||
#if defined(__FreeBSD__)
|
#if defined(__FreeBSD__)
|
||||||
// try to read sysctl?
|
// try to read sysctl?
|
||||||
#else
|
#else
|
||||||
if (config_["hwmon-path"].isString()) {
|
auto& hwmon_path = config_["hwmon-path"];
|
||||||
file_path_ = config_["hwmon-path"].asString();
|
if (hwmon_path.isString()) {
|
||||||
|
file_path_ = hwmon_path.asString();
|
||||||
|
} else if (hwmon_path.isArray()) {
|
||||||
|
// if hwmon_path is an array, loop to find first valid item
|
||||||
|
for (auto& item : hwmon_path) {
|
||||||
|
auto path = item.asString();
|
||||||
|
if (std::filesystem::exists(path)) {
|
||||||
|
file_path_ = path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (config_["hwmon-path-abs"].isString() && config_["input-filename"].isString()) {
|
} else if (config_["hwmon-path-abs"].isString() && config_["input-filename"].isString()) {
|
||||||
file_path_ = (*std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString()))
|
file_path_ = (*std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString()))
|
||||||
.path()
|
.path()
|
||||||
|
@ -5,10 +5,8 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "gtkmm/icontheme.h"
|
|
||||||
#include "gtkmm/label.h"
|
|
||||||
#include "gtkmm/tooltip.h"
|
#include "gtkmm/tooltip.h"
|
||||||
#include "modules/upower/upower_tooltip.hpp"
|
#include "util/gtk_icon.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::upower {
|
namespace waybar::modules::upower {
|
||||||
UPower::UPower(const std::string& id, const Json::Value& config)
|
UPower::UPower(const std::string& id, const Json::Value& config)
|
||||||
@ -25,6 +23,8 @@ UPower::UPower(const std::string& id, const Json::Value& config)
|
|||||||
box_.set_name(name_);
|
box_.set_name(name_);
|
||||||
event_box_.add(box_);
|
event_box_.add(box_);
|
||||||
|
|
||||||
|
// Device user wants
|
||||||
|
if (config_["native-path"].isString()) nativePath_ = config_["native-path"].asString();
|
||||||
// Icon Size
|
// Icon Size
|
||||||
if (config_["icon-size"].isUInt()) {
|
if (config_["icon-size"].isUInt()) {
|
||||||
iconSize = config_["icon-size"].asUInt();
|
iconSize = config_["icon-size"].asUInt();
|
||||||
@ -195,8 +195,26 @@ void UPower::addDevice(UpDevice* device) {
|
|||||||
|
|
||||||
void UPower::setDisplayDevice() {
|
void UPower::setDisplayDevice() {
|
||||||
std::lock_guard<std::mutex> guard(m_Mutex);
|
std::lock_guard<std::mutex> guard(m_Mutex);
|
||||||
|
|
||||||
|
if (nativePath_.empty())
|
||||||
displayDevice = up_client_get_display_device(client);
|
displayDevice = up_client_get_display_device(client);
|
||||||
g_signal_connect(displayDevice, "notify", G_CALLBACK(deviceNotify_cb), this);
|
else {
|
||||||
|
g_ptr_array_foreach(
|
||||||
|
up_client_get_devices2(client),
|
||||||
|
[](gpointer data, gpointer user_data) {
|
||||||
|
UpDevice* device{static_cast<UpDevice*>(data)};
|
||||||
|
UPower* thisPtr{static_cast<UPower*>(user_data)};
|
||||||
|
gchar* nativePath;
|
||||||
|
if (!thisPtr->displayDevice) {
|
||||||
|
g_object_get(device, "native-path", &nativePath, NULL);
|
||||||
|
if (!std::strcmp(nativePath, thisPtr->nativePath_.c_str()))
|
||||||
|
thisPtr->displayDevice = device;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayDevice) g_signal_connect(displayDevice, "notify", G_CALLBACK(deviceNotify_cb), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UPower::removeDevices() {
|
void UPower::removeDevices() {
|
||||||
@ -278,14 +296,22 @@ auto UPower::update() -> void {
|
|||||||
double percentage;
|
double percentage;
|
||||||
gint64 time_empty;
|
gint64 time_empty;
|
||||||
gint64 time_full;
|
gint64 time_full;
|
||||||
gchar* icon_name;
|
gchar* icon_name{(char*)'\0'};
|
||||||
|
std::string percentString{""};
|
||||||
|
std::string time_format{""};
|
||||||
|
|
||||||
|
bool displayDeviceValid{false};
|
||||||
|
|
||||||
|
if (displayDevice) {
|
||||||
g_object_get(displayDevice, "kind", &kind, "state", &state, "percentage", &percentage,
|
g_object_get(displayDevice, "kind", &kind, "state", &state, "percentage", &percentage,
|
||||||
"icon-name", &icon_name, "time-to-empty", &time_empty, "time-to-full", &time_full,
|
"icon-name", &icon_name, "time-to-empty", &time_empty, "time-to-full", &time_full,
|
||||||
NULL);
|
NULL);
|
||||||
|
/* Every Device which is handled by Upower and which is not
|
||||||
bool displayDeviceValid =
|
* UP_DEVICE_KIND_UNKNOWN (0) or UP_DEVICE_KIND_LINE_POWER (1) is a Battery
|
||||||
kind == UpDeviceKind::UP_DEVICE_KIND_BATTERY || kind == UpDeviceKind::UP_DEVICE_KIND_UPS;
|
*/
|
||||||
|
displayDeviceValid = (kind != UpDeviceKind::UP_DEVICE_KIND_UNKNOWN &&
|
||||||
|
kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER);
|
||||||
|
}
|
||||||
|
|
||||||
// CSS status class
|
// CSS status class
|
||||||
const std::string status = getDeviceStatus(state);
|
const std::string status = getDeviceStatus(state);
|
||||||
@ -308,6 +334,7 @@ auto UPower::update() -> void {
|
|||||||
|
|
||||||
event_box_.set_visible(true);
|
event_box_.set_visible(true);
|
||||||
|
|
||||||
|
if (displayDeviceValid) {
|
||||||
// Tooltip
|
// Tooltip
|
||||||
if (tooltip_enabled) {
|
if (tooltip_enabled) {
|
||||||
uint tooltipCount = upower_tooltip->updateTooltip(devices);
|
uint tooltipCount = upower_tooltip->updateTooltip(devices);
|
||||||
@ -316,13 +343,9 @@ auto UPower::update() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set percentage
|
// Set percentage
|
||||||
std::string percentString = "";
|
|
||||||
if (displayDeviceValid) {
|
|
||||||
percentString = std::to_string(int(percentage + 0.5)) + "%";
|
percentString = std::to_string(int(percentage + 0.5)) + "%";
|
||||||
}
|
|
||||||
|
|
||||||
// Label format
|
// Label format
|
||||||
std::string time_format = "";
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case UP_DEVICE_STATE_CHARGING:
|
case UP_DEVICE_STATE_CHARGING:
|
||||||
case UP_DEVICE_STATE_PENDING_CHARGE:
|
case UP_DEVICE_STATE_PENDING_CHARGE:
|
||||||
@ -335,6 +358,7 @@ auto UPower::update() -> void {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
std::string label_format =
|
std::string label_format =
|
||||||
fmt::format(fmt::runtime(showAltText ? format_alt : format),
|
fmt::format(fmt::runtime(showAltText ? format_alt : format),
|
||||||
fmt::arg("percentage", percentString), fmt::arg("time", time_format));
|
fmt::arg("percentage", percentString), fmt::arg("time", time_format));
|
||||||
@ -348,7 +372,7 @@ auto UPower::update() -> void {
|
|||||||
label_.set_markup(onlySpaces ? "" : label_format);
|
label_.set_markup(onlySpaces ? "" : label_format);
|
||||||
|
|
||||||
// Set icon
|
// Set icon
|
||||||
if (icon_name == NULL || !Gtk::IconTheme::get_default()->has_icon(icon_name)) {
|
if (icon_name == NULL || !DefaultGtkIconThemeWrapper::has_icon(icon_name)) {
|
||||||
icon_name = (char*)"battery-missing-symbolic";
|
icon_name = (char*)"battery-missing-symbolic";
|
||||||
}
|
}
|
||||||
icon_.set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID);
|
icon_.set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID);
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
#include "gtkmm/box.h"
|
#include "gtkmm/box.h"
|
||||||
#include "gtkmm/enums.h"
|
#include "gtkmm/enums.h"
|
||||||
#include "gtkmm/icontheme.h"
|
|
||||||
#include "gtkmm/image.h"
|
#include "gtkmm/image.h"
|
||||||
#include "gtkmm/label.h"
|
#include "gtkmm/label.h"
|
||||||
|
#include "util/gtk_icon.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::upower {
|
namespace waybar::modules::upower {
|
||||||
UPowerTooltip::UPowerTooltip(uint iconSize_, uint tooltipSpacing_, uint tooltipPadding_)
|
UPowerTooltip::UPowerTooltip(uint iconSize_, uint tooltipSpacing_, uint tooltipPadding_)
|
||||||
@ -62,7 +62,7 @@ uint UPowerTooltip::updateTooltip(Devices& devices) {
|
|||||||
std::string deviceIconName = getDeviceIcon(kind);
|
std::string deviceIconName = getDeviceIcon(kind);
|
||||||
Gtk::Image* deviceIcon = new Gtk::Image();
|
Gtk::Image* deviceIcon = new Gtk::Image();
|
||||||
deviceIcon->set_pixel_size(iconSize);
|
deviceIcon->set_pixel_size(iconSize);
|
||||||
if (!Gtk::IconTheme::get_default()->has_icon(deviceIconName)) {
|
if (!DefaultGtkIconThemeWrapper::has_icon(deviceIconName)) {
|
||||||
deviceIconName = "battery-missing-symbolic";
|
deviceIconName = "battery-missing-symbolic";
|
||||||
}
|
}
|
||||||
deviceIcon->set_from_icon_name(deviceIconName, Gtk::ICON_SIZE_INVALID);
|
deviceIcon->set_from_icon_name(deviceIconName, Gtk::ICON_SIZE_INVALID);
|
||||||
@ -79,7 +79,7 @@ uint UPowerTooltip::updateTooltip(Devices& devices) {
|
|||||||
// Set icon
|
// Set icon
|
||||||
Gtk::Image* icon = new Gtk::Image();
|
Gtk::Image* icon = new Gtk::Image();
|
||||||
icon->set_pixel_size(iconSize);
|
icon->set_pixel_size(iconSize);
|
||||||
if (icon_name == NULL || !Gtk::IconTheme::get_default()->has_icon(icon_name)) {
|
if (icon_name == NULL || !DefaultGtkIconThemeWrapper::has_icon(icon_name)) {
|
||||||
icon_name = (char*)"battery-missing-symbolic";
|
icon_name = (char*)"battery-missing-symbolic";
|
||||||
}
|
}
|
||||||
icon->set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID);
|
icon->set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID);
|
||||||
|
@ -15,6 +15,7 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val
|
|||||||
pending_plugins_(0),
|
pending_plugins_(0),
|
||||||
muted_(false),
|
muted_(false),
|
||||||
volume_(0.0),
|
volume_(0.0),
|
||||||
|
min_step_(0.0),
|
||||||
node_id_(0) {
|
node_id_(0) {
|
||||||
wp_init(WP_INIT_PIPEWIRE);
|
wp_init(WP_INIT_PIPEWIRE);
|
||||||
wp_core_ = wp_core_new(NULL, NULL);
|
wp_core_ = wp_core_new(NULL, NULL);
|
||||||
@ -39,6 +40,9 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val
|
|||||||
activatePlugins();
|
activatePlugins();
|
||||||
|
|
||||||
dp.emit();
|
dp.emit();
|
||||||
|
|
||||||
|
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
|
||||||
|
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Wireplumber::handleScroll));
|
||||||
}
|
}
|
||||||
|
|
||||||
waybar::modules::Wireplumber::~Wireplumber() {
|
waybar::modules::Wireplumber::~Wireplumber() {
|
||||||
@ -83,7 +87,6 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber*
|
|||||||
|
|
||||||
void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self, uint32_t id) {
|
void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self, uint32_t id) {
|
||||||
spdlog::debug("[{}]: updating volume", self->name_);
|
spdlog::debug("[{}]: updating volume", self->name_);
|
||||||
double vol;
|
|
||||||
GVariant* variant = NULL;
|
GVariant* variant = NULL;
|
||||||
|
|
||||||
if (!isValidNodeId(id)) {
|
if (!isValidNodeId(id)) {
|
||||||
@ -99,11 +102,11 @@ void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* se
|
|||||||
throw std::runtime_error(err);
|
throw std::runtime_error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_variant_lookup(variant, "volume", "d", &vol);
|
g_variant_lookup(variant, "volume", "d", &self->volume_);
|
||||||
|
g_variant_lookup(variant, "step", "d", &self->min_step_);
|
||||||
g_variant_lookup(variant, "mute", "b", &self->muted_);
|
g_variant_lookup(variant, "mute", "b", &self->muted_);
|
||||||
g_clear_pointer(&variant, g_variant_unref);
|
g_clear_pointer(&variant, g_variant_unref);
|
||||||
|
|
||||||
self->volume_ = std::round(vol * 100.0F);
|
|
||||||
self->dp.emit();
|
self->dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,11 +283,12 @@ auto waybar::modules::Wireplumber::update() -> void {
|
|||||||
label_.get_style_context()->remove_class("muted");
|
label_.get_style_context()->remove_class("muted");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int vol = round(volume_ * 100.0);
|
||||||
std::string markup = fmt::format(fmt::runtime(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_)));
|
fmt::arg("volume", vol), fmt::arg("icon", getIcon(vol)));
|
||||||
label_.set_markup(markup);
|
label_.set_markup(markup);
|
||||||
|
|
||||||
getState(volume_);
|
getState(vol);
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
|
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
|
||||||
@ -292,9 +296,9 @@ auto waybar::modules::Wireplumber::update() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!tooltip_format.empty()) {
|
if (!tooltip_format.empty()) {
|
||||||
label_.set_tooltip_text(
|
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format),
|
||||||
fmt::format(fmt::runtime(tooltip_format), fmt::arg("node_name", node_name_),
|
fmt::arg("node_name", node_name_),
|
||||||
fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_))));
|
fmt::arg("volume", vol), fmt::arg("icon", getIcon(vol))));
|
||||||
} else {
|
} else {
|
||||||
label_.set_tooltip_text(node_name_);
|
label_.set_tooltip_text(node_name_);
|
||||||
}
|
}
|
||||||
@ -303,3 +307,49 @@ auto waybar::modules::Wireplumber::update() -> void {
|
|||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) {
|
||||||
|
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
|
||||||
|
return AModule::handleScroll(e);
|
||||||
|
}
|
||||||
|
auto dir = AModule::getScrollDir(e);
|
||||||
|
if (dir == SCROLL_DIR::NONE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (config_["reverse-scrolling"].asInt() == 1) {
|
||||||
|
if (dir == SCROLL_DIR::UP) {
|
||||||
|
dir = SCROLL_DIR::DOWN;
|
||||||
|
} else if (dir == SCROLL_DIR::DOWN) {
|
||||||
|
dir = SCROLL_DIR::UP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double max_volume = 1;
|
||||||
|
double step = 1.0 / 100.0;
|
||||||
|
if (config_["scroll-step"].isDouble()) {
|
||||||
|
step = config_["scroll-step"].asDouble() / 100.0;
|
||||||
|
}
|
||||||
|
if (config_["max-volume"].isDouble()) {
|
||||||
|
max_volume = config_["max-volume"].asDouble() / 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step < min_step_) step = min_step_;
|
||||||
|
|
||||||
|
double new_vol = volume_;
|
||||||
|
if (dir == SCROLL_DIR::UP) {
|
||||||
|
if (volume_ < max_volume) {
|
||||||
|
new_vol = volume_ + step;
|
||||||
|
if (new_vol > max_volume) new_vol = max_volume;
|
||||||
|
}
|
||||||
|
} else if (dir == SCROLL_DIR::DOWN) {
|
||||||
|
if (volume_ > 0) {
|
||||||
|
new_vol = volume_ - step;
|
||||||
|
if (new_vol < 0) new_vol = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (new_vol != volume_) {
|
||||||
|
GVariant* variant = g_variant_new_double(new_vol);
|
||||||
|
gboolean ret;
|
||||||
|
g_signal_emit_by_name(mixer_api_, "set-volume", node_id_, variant, &ret);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "glibmm/fileutils.h"
|
#include "glibmm/fileutils.h"
|
||||||
#include "glibmm/refptr.h"
|
#include "glibmm/refptr.h"
|
||||||
#include "util/format.hpp"
|
#include "util/format.hpp"
|
||||||
|
#include "util/rewrite_string.hpp"
|
||||||
#include "util/string.hpp"
|
#include "util/string.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::wlr {
|
namespace waybar::modules::wlr {
|
||||||
@ -506,17 +507,17 @@ void Task::handle_closed() {
|
|||||||
spdlog::debug("{} closed", repr());
|
spdlog::debug("{} closed", repr());
|
||||||
zwlr_foreign_toplevel_handle_v1_destroy(handle_);
|
zwlr_foreign_toplevel_handle_v1_destroy(handle_);
|
||||||
handle_ = nullptr;
|
handle_ = nullptr;
|
||||||
|
tbar_->remove_task(id_);
|
||||||
if (button_visible_) {
|
if (button_visible_) {
|
||||||
tbar_->remove_button(button);
|
tbar_->remove_button(button);
|
||||||
button_visible_ = false;
|
button_visible_ = false;
|
||||||
}
|
}
|
||||||
tbar_->remove_task(id_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Task::handle_clicked(GdkEventButton *bt) {
|
bool Task::handle_clicked(GdkEventButton *bt) {
|
||||||
/* filter out additional events for double/triple clicks */
|
/* filter out additional events for double/triple clicks */
|
||||||
if (bt->type == GDK_BUTTON_PRESS) {
|
if (bt->type == GDK_BUTTON_PRESS) {
|
||||||
/* save where the button press ocurred in case it becomes a drag */
|
/* save where the button press occurred in case it becomes a drag */
|
||||||
drag_start_button = bt->button;
|
drag_start_button = bt->button;
|
||||||
drag_start_x = bt->x;
|
drag_start_x = bt->x;
|
||||||
drag_start_y = bt->y;
|
drag_start_y = bt->y;
|
||||||
@ -622,6 +623,9 @@ void Task::update() {
|
|||||||
fmt::format(fmt::runtime(format_before_), fmt::arg("title", title), fmt::arg("name", name),
|
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("app_id", app_id), fmt::arg("state", state_string()),
|
||||||
fmt::arg("short_state", state_string(true)));
|
fmt::arg("short_state", state_string(true)));
|
||||||
|
|
||||||
|
txt = waybar::util::rewriteString(txt, config_["rewrite"]);
|
||||||
|
|
||||||
if (markup)
|
if (markup)
|
||||||
text_before_.set_markup(txt);
|
text_before_.set_markup(txt);
|
||||||
else
|
else
|
||||||
@ -633,6 +637,9 @@ void Task::update() {
|
|||||||
fmt::format(fmt::runtime(format_after_), fmt::arg("title", title), fmt::arg("name", name),
|
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("app_id", app_id), fmt::arg("state", state_string()),
|
||||||
fmt::arg("short_state", state_string(true)));
|
fmt::arg("short_state", state_string(true)));
|
||||||
|
|
||||||
|
txt = waybar::util::rewriteString(txt, config_["rewrite"]);
|
||||||
|
|
||||||
if (markup)
|
if (markup)
|
||||||
text_after_.set_markup(txt);
|
text_after_.set_markup(txt);
|
||||||
else
|
else
|
||||||
@ -710,6 +717,7 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu
|
|||||||
if (!id.empty()) {
|
if (!id.empty()) {
|
||||||
box_.get_style_context()->add_class(id);
|
box_.get_style_context()->add_class(id);
|
||||||
}
|
}
|
||||||
|
box_.get_style_context()->add_class("empty");
|
||||||
event_box_.add(box_);
|
event_box_.add(box_);
|
||||||
|
|
||||||
struct wl_display *display = Client::inst()->wl_display;
|
struct wl_display *display = Client::inst()->wl_display;
|
||||||
@ -862,11 +870,19 @@ void Taskbar::handle_finished() {
|
|||||||
manager_ = nullptr;
|
manager_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Taskbar::add_button(Gtk::Button &bt) { box_.pack_start(bt, false, false); }
|
void Taskbar::add_button(Gtk::Button &bt) {
|
||||||
|
box_.pack_start(bt, false, false);
|
||||||
|
box_.get_style_context()->remove_class("empty");
|
||||||
|
}
|
||||||
|
|
||||||
void Taskbar::move_button(Gtk::Button &bt, int pos) { box_.reorder_child(bt, pos); }
|
void Taskbar::move_button(Gtk::Button &bt, int pos) { box_.reorder_child(bt, pos); }
|
||||||
|
|
||||||
void Taskbar::remove_button(Gtk::Button &bt) { box_.remove(bt); }
|
void Taskbar::remove_button(Gtk::Button &bt) {
|
||||||
|
box_.remove(bt);
|
||||||
|
if (tasks_.empty()) {
|
||||||
|
box_.get_style_context()->add_class("empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Taskbar::remove_task(uint32_t id) {
|
void Taskbar::remove_task(uint32_t id) {
|
||||||
auto it = std::find_if(std::begin(tasks_), std::end(tasks_),
|
auto it = std::find_if(std::begin(tasks_), std::end(tasks_),
|
||||||
|
@ -64,7 +64,7 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar
|
|||||||
|
|
||||||
auto WorkspaceManager::workspace_comparator() const
|
auto WorkspaceManager::workspace_comparator() const
|
||||||
-> std::function<bool(std::unique_ptr<Workspace> &, std::unique_ptr<Workspace> &)> {
|
-> std::function<bool(std::unique_ptr<Workspace> &, std::unique_ptr<Workspace> &)> {
|
||||||
return [=](std::unique_ptr<Workspace> &lhs, std::unique_ptr<Workspace> &rhs) {
|
return [=, this](std::unique_ptr<Workspace> &lhs, std::unique_ptr<Workspace> &rhs) {
|
||||||
auto is_name_less = lhs->get_name() < rhs->get_name();
|
auto is_name_less = lhs->get_name() < rhs->get_name();
|
||||||
auto is_name_eq = lhs->get_name() == rhs->get_name();
|
auto is_name_eq = lhs->get_name() == rhs->get_name();
|
||||||
auto is_coords_less = lhs->get_coords() < rhs->get_coords();
|
auto is_coords_less = lhs->get_coords() < rhs->get_coords();
|
||||||
@ -73,7 +73,7 @@ auto WorkspaceManager::workspace_comparator() const
|
|||||||
try {
|
try {
|
||||||
auto is_number_less = std::stoi(lhs->get_name()) < std::stoi(rhs->get_name());
|
auto is_number_less = std::stoi(lhs->get_name()) < std::stoi(rhs->get_name());
|
||||||
return is_number_less;
|
return is_number_less;
|
||||||
} catch (std::invalid_argument) {
|
} catch (const std::invalid_argument &) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
25
src/util/gtk_icon.cpp
Normal file
25
src/util/gtk_icon.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "util/gtk_icon.hpp"
|
||||||
|
|
||||||
|
/* We need a global mutex for accessing the object returned by Gtk::IconTheme::get_default()
|
||||||
|
* because it always returns the same object across different threads, and concurrent
|
||||||
|
* access can cause data corruption and lead to invalid memory access and crashes.
|
||||||
|
* Even concurrent calls that seem read only such as has_icon can cause issues because
|
||||||
|
* the GTK lib may update the internal icon cache on this calls.
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::mutex DefaultGtkIconThemeWrapper::default_theme_mutex;
|
||||||
|
|
||||||
|
bool DefaultGtkIconThemeWrapper::has_icon(const std::string& value) {
|
||||||
|
const std::lock_guard<std::mutex> lock(default_theme_mutex);
|
||||||
|
|
||||||
|
return Gtk::IconTheme::get_default()->has_icon(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> DefaultGtkIconThemeWrapper::load_icon(const char* name, int tmp_size,
|
||||||
|
Gtk::IconLookupFlags flags) {
|
||||||
|
const std::lock_guard<std::mutex> lock(default_theme_mutex);
|
||||||
|
|
||||||
|
auto default_theme = Gtk::IconTheme::get_default();
|
||||||
|
default_theme->rescan_if_needed();
|
||||||
|
return default_theme->load_icon(name, tmp_size, flags);
|
||||||
|
}
|
50
src/util/prepare_for_sleep.cpp
Normal file
50
src/util/prepare_for_sleep.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include "util/prepare_for_sleep.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class PrepareForSleep {
|
||||||
|
private:
|
||||||
|
PrepareForSleep() {
|
||||||
|
GError *error = NULL;
|
||||||
|
login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
|
||||||
|
if (!login1_connection) {
|
||||||
|
spdlog::warn("Unable to connect to the SYSTEM Bus!...");
|
||||||
|
} else {
|
||||||
|
login1_id = g_dbus_connection_signal_subscribe(
|
||||||
|
login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager",
|
||||||
|
"PrepareForSleep", "/org/freedesktop/login1", NULL, G_DBUS_SIGNAL_FLAGS_NONE,
|
||||||
|
prepareForSleep_cb, this, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prepareForSleep_cb(GDBusConnection *system_bus, const gchar *sender_name,
|
||||||
|
const gchar *object_path, const gchar *interface_name,
|
||||||
|
const gchar *signal_name, GVariant *parameters,
|
||||||
|
gpointer user_data) {
|
||||||
|
if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)"))) {
|
||||||
|
gboolean sleeping;
|
||||||
|
g_variant_get(parameters, "(b)", &sleeping);
|
||||||
|
|
||||||
|
PrepareForSleep *self = static_cast<PrepareForSleep *>(user_data);
|
||||||
|
self->signal.emit(sleeping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static PrepareForSleep &GetInstance() {
|
||||||
|
static PrepareForSleep instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
waybar::SafeSignal<bool> signal;
|
||||||
|
|
||||||
|
private:
|
||||||
|
guint login1_id;
|
||||||
|
GDBusConnection *login1_connection;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
waybar::SafeSignal<bool> &waybar::util::prepare_for_sleep() {
|
||||||
|
return PrepareForSleep::GetInstance().signal;
|
||||||
|
}
|
@ -63,7 +63,7 @@ bool waybar::util::Rfkill::on_event(Glib::IOCondition cond) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len < RFKILL_EVENT_SIZE_V1) {
|
if (static_cast<size_t>(len) < RFKILL_EVENT_SIZE_V1) {
|
||||||
spdlog::error("Wrong size of RFKILL event: {} < {}", len, RFKILL_EVENT_SIZE_V1);
|
spdlog::error("Wrong size of RFKILL event: {} < {}", len, RFKILL_EVENT_SIZE_V1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -73,10 +73,9 @@ bool waybar::util::Rfkill::on_event(Glib::IOCondition cond) {
|
|||||||
on_update.emit(event);
|
on_update.emit(event);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
spdlog::error("Failed to poll RFKILL control device");
|
spdlog::error("Failed to poll RFKILL control device");
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::util::Rfkill::getState() const { return state_; }
|
bool waybar::util::Rfkill::getState() const { return state_; }
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
[wrap-file]
|
[wrap-file]
|
||||||
directory = Catch2-3.1.0
|
directory = Catch2-3.3.2
|
||||||
source_url = https://github.com/catchorg/Catch2/archive/v3.1.0.tar.gz
|
source_url = https://github.com/catchorg/Catch2/archive/v3.3.2.tar.gz
|
||||||
source_filename = Catch2-3.1.0.tar.gz
|
source_filename = Catch2-3.3.2.tar.gz
|
||||||
source_hash = c252b2d9537e18046d8b82535069d2567f77043f8e644acf9a9fffc22ea6e6f7
|
source_hash = 8361907f4d9bff3ae7c1edb027f813659f793053c99b67837a0c0375f065bae2
|
||||||
patch_filename = catch2_3.1.0-1_patch.zip
|
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.3.2-1/Catch2-3.3.2.tar.gz
|
||||||
patch_url = https://wrapdb.mesonbuild.com/v2/catch2_3.1.0-1/get_patch
|
wrapdb_version = 3.3.2-1
|
||||||
patch_hash = 4ebf4277aed574a9912a79f4817a310d837798e099bbafa6097be23a7f5e3ae4
|
|
||||||
wrapdb_version = 3.1.0-1
|
|
||||||
|
|
||||||
[provide]
|
[provide]
|
||||||
catch2 = catch2_dep
|
catch2 = catch2_dep
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[wrap-file]
|
[wrap-file]
|
||||||
directory = cava-0.8.4
|
directory = cava-0.8.5
|
||||||
source_url = https://github.com/LukashonakV/cava/archive/0.8.4.tar.gz
|
source_url = https://github.com/LukashonakV/cava/archive/0.8.5.tar.gz
|
||||||
source_filename = cava-0.8.4.tar.gz
|
source_filename = cava-0.8.5.tar.gz
|
||||||
source_hash = 523353f446570277d40b8e1efb84468d70fdec53e1356a555c14bf466557a3ed
|
source_hash = 9ce3df7d374dc83ed0704fe3caef5e00600ce061d85608aad4142d2c59aa4647
|
||||||
[provide]
|
[provide]
|
||||||
cava = cava_dep
|
cava = cava_dep
|
||||||
|
Reference in New Issue
Block a user