Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75d7b2f812 | ||
|
|
dd0f8a0ecf | ||
|
|
b54e2c16f3 | ||
|
|
b32abf9966 | ||
|
|
2469851865 | ||
|
|
a6d33db20f | ||
|
|
5a4a1a5595 | ||
|
|
d6d164a27d | ||
|
|
8e33333c0b | ||
|
|
f1abc186b2 | ||
|
|
d5bc2f3335 | ||
|
|
f744d906be | ||
|
|
fa3ce14b25 | ||
|
|
e6097229ce | ||
|
|
b239c77d74 | ||
|
|
9e08512927 | ||
|
|
eedd1f8e6a | ||
|
|
c69a6dde67 | ||
|
|
4339030c9d | ||
|
|
bc7acbde5c | ||
|
|
512c6fb127 | ||
|
|
f8eb01def2 | ||
|
|
158767b5b4 | ||
|
|
aa95c04842 | ||
|
|
b90af74d15 | ||
|
|
4a88cb693a | ||
|
|
d4d5a54e63 | ||
|
|
748fc809b5 | ||
|
|
bdd7271da9 | ||
|
|
a34e3ccc86 | ||
|
|
4b20f522f0 | ||
|
|
4636309117 | ||
|
|
7783c81861 | ||
|
|
3e55d0d7f2 | ||
|
|
ff09ef6d71 | ||
|
|
99d26e44ee | ||
|
|
19bf4d0544 | ||
|
|
6b860f8690 | ||
|
|
182272b8b5 | ||
|
|
e64d66ab24 | ||
|
|
41ebdc3cb6 | ||
|
|
8ccb959992 | ||
|
|
13e904c20d | ||
|
|
77f0584d0d | ||
|
|
d2767fb84a | ||
|
|
49caab47a6 | ||
|
|
08361be9f0 | ||
|
|
2674982c7b | ||
|
|
dbe02ebe41 | ||
|
|
0ea5143493 | ||
|
|
f5370fcff5 | ||
|
|
7b79281437 | ||
|
|
acc2da7310 | ||
|
|
68e525df58 | ||
|
|
2cd67fdd0a | ||
|
|
2600f53bdd | ||
|
|
da9cc77db8 | ||
|
|
75f9141cac | ||
|
|
f03b684bd8 | ||
|
|
4822f967b2 | ||
|
|
dc208b66b3 | ||
|
|
77ebc45336 | ||
|
|
d94519a93c | ||
|
|
4d5b715dc4 | ||
|
|
8a84cb609e | ||
|
|
5f8ae44233 | ||
|
|
4283195803 | ||
|
|
44f309678b | ||
|
|
4846ff7950 | ||
|
|
0ec2aada26 | ||
|
|
dfa0f5701e | ||
|
|
b1744278d1 | ||
|
|
2e2cb67cf1 | ||
|
|
1572bc8c75 | ||
|
|
6be5f7cb29 | ||
|
|
2334faa7fd | ||
|
|
58db0baaf4 | ||
|
|
0162dbd485 | ||
|
|
1fe4a4ee9d | ||
|
|
50a4fe9623 | ||
|
|
061c68ce92 | ||
|
|
20b89d5d13 | ||
|
|
c299ea9369 | ||
|
|
d2dd2d553f | ||
|
|
b6b495e1bc | ||
|
|
d4559c7f00 | ||
|
|
0a747ddafc | ||
|
|
375a38a489 | ||
|
|
9ac27de7d8 | ||
|
|
7308893adb | ||
|
|
a51dd8fc52 | ||
|
|
86a38980e4 | ||
|
|
c4330a1e36 | ||
|
|
4d339f05af | ||
|
|
e24adbc3c2 | ||
|
|
7d7a047fcc | ||
|
|
aa1f8a428b | ||
|
|
f21b1dfa4d | ||
|
|
6050fa3a43 | ||
|
|
ca7c9a68f1 | ||
|
|
23bc4d66bf | ||
|
|
49caa4bf31 | ||
|
|
c60a8e9836 | ||
|
|
c4226f3745 | ||
|
|
d32da917e4 | ||
|
|
46e36c0e68 | ||
|
|
86491e1512 | ||
|
|
4a4c888d7d | ||
|
|
ace319b515 | ||
|
|
e73ea8d608 | ||
|
|
f7224d8459 | ||
|
|
02c64f3f1e | ||
|
|
d86059016e | ||
|
|
088ca6b963 | ||
|
|
6ae354f564 |
29
.clang-tidy
Normal file
29
.clang-tidy
Normal file
@@ -0,0 +1,29 @@
|
||||
Checks: >
|
||||
-*,
|
||||
bugprone-*
|
||||
misc-*,
|
||||
modernize-*,
|
||||
performance-*,
|
||||
portability-*,
|
||||
readability-*,
|
||||
-fuchsia-trailing-return,
|
||||
-readability-magic-numbers,
|
||||
-modernize-use-nodiscard,
|
||||
-modernize-use-trailing-return-type,
|
||||
-readability-braces-around-statements,
|
||||
-readability-redundant-access-specifiers,
|
||||
-readability-redundant-member-init,
|
||||
-readability-redundant-string-init,
|
||||
-readability-identifier-length
|
||||
CheckOptions:
|
||||
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.StructCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.FunctionCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.VariableCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.PrivateMemberCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
|
||||
- { key: readability-identifier-naming.EnumCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE }
|
||||
- { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE }
|
||||
- { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE }
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Linter
|
||||
name: clang-format
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
@@ -8,7 +8,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: DoozyX/clang-format-lint-action@v0.16.2
|
||||
name: clang-format
|
||||
with:
|
||||
source: '.'
|
||||
extensions: 'hpp,h,cpp,c'
|
||||
clangFormatVersion: 16
|
||||
clangFormatVersion: 16
|
||||
31
.github/workflows/clang-tidy.yml
vendored
Normal file
31
.github/workflows/clang-tidy.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: clang-tidy
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: alexays/waybar:debian
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: configure
|
||||
run: |
|
||||
meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json
|
||||
ninja -C build # necessary to find certain .h files (xdg, wayland, etc.)
|
||||
- uses: cpp-linter/cpp-linter-action@v2.7.5
|
||||
name: clang-tidy
|
||||
id: clang-tidy-check
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PIP_NO_CACHE_DIR: false
|
||||
with:
|
||||
style: "" # empty string => don't do clang-format checks here, we do them in clang-format.yml
|
||||
files-changed-only: true # only check files that have changed
|
||||
lines-changed-only: true # only check lines that have changed
|
||||
tidy-checks: "" # empty string => use the .clang-tidy file
|
||||
version: "17" # clang-tools version
|
||||
database: "build" # path to the compile_commands.json file
|
||||
- name: Check if clang-tidy failed on any files
|
||||
if: steps.clang-tidy-check.outputs.checks-failed > 0
|
||||
run: echo "Some files failed the linting checks!" && exit 1
|
||||
2
.github/workflows/freebsd.yml
vendored
2
.github/workflows/freebsd.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Test in FreeBSD VM
|
||||
uses: cross-platform-actions/action@v0.19.1
|
||||
uses: cross-platform-actions/action@v0.21.1
|
||||
timeout-minutes: 180
|
||||
with:
|
||||
operating_system: freebsd
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -44,3 +44,7 @@ packagecache
|
||||
*.out
|
||||
*.app
|
||||
/.direnv/
|
||||
|
||||
# Nix
|
||||
result
|
||||
result-*
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
FROM debian:sid
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 libplayerctl-dev && \
|
||||
apt-get clean
|
||||
RUN apt update && \
|
||||
apt install -y \
|
||||
build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev \
|
||||
wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev \
|
||||
libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev \
|
||||
libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev \
|
||||
libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev \
|
||||
libxkbregistry-dev libxkbregistry0 libplayerctl-dev sudo python3-venv python3-pip && \
|
||||
apt clean
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
# Waybar [](LICENSE) [](https://paypal.me/ARouillard)<br>
|
||||
|
||||
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
|
||||
> 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>
|
||||
> Available in [all major distributions](https://github.com/Alexays/Waybar/wiki/Installation)<br>
|
||||
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
|
||||
|
||||
#### Current features
|
||||
- Sway (Workspaces, Binding mode, Focused window name)
|
||||
- River (Mapping mode, Tags, Focused window name)
|
||||
- Hyprland (Focused window name)
|
||||
- Hyprland (Window Icons, Workspaces, Focused window name)
|
||||
- DWL (Tags) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc)
|
||||
- Tray [#21](https://github.com/Alexays/Waybar/issues/21)
|
||||
- Local time
|
||||
@@ -17,6 +16,7 @@
|
||||
- Network
|
||||
- Bluetooth
|
||||
- Pulseaudio
|
||||
- Privacy Info
|
||||
- Wireplumber
|
||||
- Disk
|
||||
- Memory
|
||||
|
||||
99
flake.lock
generated
99
flake.lock
generated
@@ -1,32 +1,13 @@
|
||||
{
|
||||
"nodes": {
|
||||
"devshell": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1692523566,
|
||||
"narHash": "sha256-VDJDihK6jNebVw9y3qKCVD6+6QaC/x8kxZzL4MaIPPY=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "d208c58e2f7afef838add5f18a9936b12a71d695",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -35,47 +16,13 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1689068808,
|
||||
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1677383253,
|
||||
"narHash": "sha256-UfpzWfSxkfXHnb4boXZNaKsAcUrZT9Hw+tao1oZxd08=",
|
||||
"lastModified": 1704538339,
|
||||
"narHash": "sha256-1734d3mQuux9ySvwf6axRWZRBhtcZA9Q8eftD6EZg6U=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9952d6bc395f5841262b006fbace8dd7e143b634",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1692638711,
|
||||
"narHash": "sha256-J0LgSFgJVGCC1+j5R2QndadWI1oumusg6hCtYAzLID4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "91a22f76cd1716f9d0149e8a5c68424bb691de15",
|
||||
"rev": "46ae0210ce163b3cba6c7da08840c1d63de9c701",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -87,40 +34,8 @@
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devshell": "devshell",
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
101
flake.nix
101
flake.nix
@@ -1,28 +1,22 @@
|
||||
{
|
||||
description = "Highly customizable Wayland bar for Sway and Wlroots based compositors.";
|
||||
description = "Highly customizable Wayland bar for Sway and Wlroots based compositors";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
devshell.url = "github:numtide/devshell";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
flake-compat = {
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, flake-utils, devshell, nixpkgs, flake-compat }:
|
||||
outputs = { self, nixpkgs, ... }:
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
genSystems = lib.genAttrs [
|
||||
genSystems = func: lib.genAttrs [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
];
|
||||
|
||||
pkgsFor = genSystems (system:
|
||||
import nixpkgs {
|
||||
inherit system;
|
||||
});
|
||||
]
|
||||
(system: func (import nixpkgs { inherit system; }));
|
||||
|
||||
mkDate = longDate: (lib.concatStringsSep "-" [
|
||||
(builtins.substring 0 4 longDate)
|
||||
@@ -31,60 +25,43 @@
|
||||
]);
|
||||
in
|
||||
{
|
||||
devShells = genSystems
|
||||
(pkgs:
|
||||
{
|
||||
default =
|
||||
pkgs.mkShell
|
||||
{
|
||||
name = "waybar-shell";
|
||||
|
||||
# inherit attributes from upstream nixpkgs derivation
|
||||
inherit (pkgs.waybar) buildInputs depsBuildBuild depsBuildBuildPropagated depsBuildTarget
|
||||
depsBuildTargetPropagated depsHostHost depsHostHostPropagated depsTargetTarget
|
||||
depsTargetTargetPropagated propagatedBuildInputs propagatedNativeBuildInputs strictDeps;
|
||||
|
||||
# overrides for local development
|
||||
nativeBuildInputs = pkgs.waybar.nativeBuildInputs ++ (with pkgs; [
|
||||
clang-tools
|
||||
gdb
|
||||
]);
|
||||
};
|
||||
});
|
||||
|
||||
overlays.default = final: prev: {
|
||||
waybar = final.callPackage ./nix/default.nix {
|
||||
version = prev.waybar.version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
# take the first "version: '...'" from meson.build
|
||||
version =
|
||||
(builtins.head (builtins.split "'"
|
||||
(builtins.elemAt
|
||||
(builtins.split " version: '" (builtins.readFile ./meson.build))
|
||||
2)))
|
||||
+ "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
};
|
||||
};
|
||||
packages = genSystems
|
||||
(system:
|
||||
(self.overlays.default pkgsFor.${system} pkgsFor.${system})
|
||||
// {
|
||||
default = self.packages.${system}.waybar;
|
||||
});
|
||||
} //
|
||||
flake-utils.lib.eachDefaultSystem (system: {
|
||||
devShell =
|
||||
let pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
|
||||
overlays = [ devshell.overlays.default ];
|
||||
};
|
||||
in
|
||||
pkgs.devshell.mkShell {
|
||||
imports = [ "${pkgs.devshell.extraModulesDir}/language/c.nix" ];
|
||||
|
||||
devshell.packages = with pkgs; [
|
||||
clang-tools
|
||||
gdb
|
||||
# from nativeBuildInputs
|
||||
gnumake
|
||||
meson
|
||||
ninja
|
||||
pkg-config
|
||||
scdoc
|
||||
] ++ (map lib.getDev [
|
||||
# from buildInputs
|
||||
wayland wlroots gtkmm3 libsigcxx jsoncpp spdlog gtk-layer-shell howard-hinnant-date libxkbcommon
|
||||
# optional dependencies
|
||||
gobject-introspection glib playerctl python3.pkgs.pygobject3
|
||||
libevdev libinput libjack2 libmpdclient playerctl libnl
|
||||
libpulseaudio sndio sway libdbusmenu-gtk3 udev upower wireplumber
|
||||
|
||||
# from propagated build inputs?
|
||||
at-spi2-atk atkmm cairo cairomm catch2 fmt_8 fontconfig
|
||||
gdk-pixbuf glibmm gtk3 harfbuzz pango pangomm wayland-protocols
|
||||
]);
|
||||
|
||||
env = with pkgs; [
|
||||
{ name = "CPLUS_INCLUDE_PATH"; prefix = "$DEVSHELL_DIR/include"; }
|
||||
{ name = "PKG_CONFIG_PATH"; prefix = "$DEVSHELL_DIR/lib/pkgconfig"; }
|
||||
{ name = "PKG_CONFIG_PATH"; prefix = "$DEVSHELL_DIR/share/pkgconfig"; }
|
||||
{ name = "PATH"; prefix = "${wayland.bin}/bin"; }
|
||||
{ name = "LIBRARY_PATH"; prefix = "${lib.getLib sndio}/lib"; }
|
||||
{ name = "LIBRARY_PATH"; prefix = "${lib.getLib zlib}/lib"; }
|
||||
{ name = "LIBRARY_PATH"; prefix = "${lib.getLib howard-hinnant-date}/lib"; }
|
||||
];
|
||||
};
|
||||
});
|
||||
packages = genSystems (pkgs:
|
||||
let packages = self.overlays.default pkgs pkgs;
|
||||
in packages // {
|
||||
default = packages.waybar;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class ALabel : public AModule {
|
||||
bool alt_ = false;
|
||||
std::string default_format_;
|
||||
|
||||
bool handleToggle(GdkEventButton *const &e) override;
|
||||
//todo bool handleToggle(GdkEventButton *const &e) override;
|
||||
virtual std::string getState(uint8_t value, bool lesser = false);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <glibmm/dispatcher.h>
|
||||
#include <glibmm/markup.h>
|
||||
#include <gtkmm/eventbox.h>
|
||||
//#include <gtkmm/eventbox.h>
|
||||
#include <json/json.h>
|
||||
|
||||
#include "IModule.hpp"
|
||||
@@ -11,12 +11,15 @@ namespace waybar {
|
||||
|
||||
class AModule : public IModule {
|
||||
public:
|
||||
static constexpr const char *MODULE_CLASS = "module";
|
||||
|
||||
virtual ~AModule();
|
||||
auto update() -> void override;
|
||||
virtual auto refresh(int) -> void{};
|
||||
operator Gtk::Widget &() override;
|
||||
auto doAction(const std::string &name) -> void override;
|
||||
|
||||
/// Emitting on this dispatcher triggers a update() call
|
||||
Glib::Dispatcher dp;
|
||||
|
||||
protected:
|
||||
@@ -27,25 +30,25 @@ class AModule : public IModule {
|
||||
|
||||
enum SCROLL_DIR { NONE, UP, DOWN, LEFT, RIGHT };
|
||||
|
||||
SCROLL_DIR getScrollDir(GdkEventScroll *e);
|
||||
// SCROLL_DIR getScrollDir(GdkEventScroll *e);
|
||||
bool tooltipEnabled();
|
||||
|
||||
const std::string name_;
|
||||
const Json::Value &config_;
|
||||
Gtk::EventBox event_box_;
|
||||
// Gtk::EventBox event_box_;
|
||||
|
||||
virtual bool handleToggle(GdkEventButton *const &ev);
|
||||
virtual bool handleScroll(GdkEventScroll *);
|
||||
virtual bool handleRelease(GdkEventButton *const &ev);
|
||||
// virtual bool handleToggle(GdkEventButton *const &ev);
|
||||
// virtual bool handleScroll(GdkEventScroll *);
|
||||
// virtual bool handleRelease(GdkEventButton *const &ev);
|
||||
|
||||
private:
|
||||
bool handleUserEvent(GdkEventButton *const &ev);
|
||||
|
||||
// bool handleUserEvent(GdkEventButton *const &ev);
|
||||
const bool isTooltip;
|
||||
std::vector<int> pid_;
|
||||
gdouble distance_scrolled_y_;
|
||||
gdouble distance_scrolled_x_;
|
||||
std::map<std::string, std::string> eventActionMap_;
|
||||
static const inline std::map<std::pair<uint, GdkEventType>, std::string> eventMap_{
|
||||
/* static const inline std::map<std::pair<uint, GdkEventType>, std::string> eventMap_{
|
||||
{std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"},
|
||||
{std::make_pair(1, GdkEventType::GDK_BUTTON_RELEASE), "on-click-release"},
|
||||
{std::make_pair(1, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click"},
|
||||
@@ -65,7 +68,7 @@ class AModule : public IModule {
|
||||
{std::make_pair(9, GdkEventType::GDK_BUTTON_PRESS), "on-click-forward"},
|
||||
{std::make_pair(9, GdkEventType::GDK_BUTTON_RELEASE), "on-click-forward-release"},
|
||||
{std::make_pair(9, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-forward"},
|
||||
{std::make_pair(9, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-forward"}};
|
||||
{std::make_pair(9, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-forward"}};*/
|
||||
};
|
||||
|
||||
} // namespace waybar
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <gdkmm/monitor.h>
|
||||
#include <glibmm/refptr.h>
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/centerbox.h>
|
||||
#include <gtkmm/cssprovider.h>
|
||||
#include <gtkmm/main.h>
|
||||
#include <gtkmm/window.h>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "AModule.hpp"
|
||||
#include "group.hpp"
|
||||
//#include "group.hpp"
|
||||
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||
|
||||
namespace waybar {
|
||||
@@ -53,34 +48,18 @@ class BarIpcClient;
|
||||
}
|
||||
#endif // HAVE_SWAY
|
||||
|
||||
class BarSurface {
|
||||
protected:
|
||||
BarSurface() = default;
|
||||
|
||||
public:
|
||||
virtual void setExclusiveZone(bool enable) = 0;
|
||||
virtual void setLayer(bar_layer layer) = 0;
|
||||
virtual void setMargins(const struct bar_margins &margins) = 0;
|
||||
virtual void setPassThrough(bool enable) = 0;
|
||||
virtual void setPosition(const std::string_view &position) = 0;
|
||||
virtual void setSize(uint32_t width, uint32_t height) = 0;
|
||||
virtual void commit(){};
|
||||
|
||||
virtual ~BarSurface() = default;
|
||||
};
|
||||
|
||||
class Bar {
|
||||
public:
|
||||
using bar_mode_map = std::map<std::string_view, struct bar_mode>;
|
||||
using bar_mode_map = std::map<std::string, struct bar_mode>;
|
||||
static const bar_mode_map PRESET_MODES;
|
||||
static const std::string_view MODE_DEFAULT;
|
||||
static const std::string_view MODE_INVISIBLE;
|
||||
static const std::string MODE_DEFAULT;
|
||||
static const std::string MODE_INVISIBLE;
|
||||
|
||||
Bar(struct waybar_output *w_output, const Json::Value &);
|
||||
Bar(const Bar &) = delete;
|
||||
~Bar();
|
||||
|
||||
void setMode(const std::string_view &);
|
||||
void setMode(const std::string &mode);
|
||||
void setVisible(bool visible);
|
||||
void toggle();
|
||||
void handleSignal(int);
|
||||
@@ -89,8 +68,10 @@ class Bar {
|
||||
Json::Value config;
|
||||
struct wl_surface *surface;
|
||||
bool visible = true;
|
||||
bool vertical = false;
|
||||
Gtk::Window window;
|
||||
Glib::RefPtr<Gdk::Surface> gdk_surface_;
|
||||
Gtk::Orientation orientation = Gtk::Orientation::HORIZONTAL;
|
||||
Gtk::PositionType position = Gtk::PositionType::TOP;
|
||||
|
||||
int x_global;
|
||||
int y_global;
|
||||
@@ -100,13 +81,15 @@ class Bar {
|
||||
#endif
|
||||
|
||||
private:
|
||||
void onMap(GdkEventAny *);
|
||||
void onMap();
|
||||
auto setupWidgets() -> void;
|
||||
void getModules(const Factory &, const std::string &, waybar::Group *);
|
||||
// void getModules(const Factory &, const std::string &, waybar::Group *);
|
||||
void setupAltFormatKeyForModule(const std::string &module_name);
|
||||
void setupAltFormatKeyForModuleList(const char *module_list_name);
|
||||
void setMode(const bar_mode &);
|
||||
void onConfigure(GdkEventConfigure *ev);
|
||||
void setPassThrough(bool passthrough);
|
||||
void setPosition(Gtk::PositionType position);
|
||||
void onConfigure(int width, int height);
|
||||
void configureGlobalOffset(int width, int height);
|
||||
void onOutputGeometryChanged();
|
||||
|
||||
@@ -115,12 +98,13 @@ class Bar {
|
||||
std::string last_mode_{MODE_DEFAULT};
|
||||
|
||||
struct bar_margins margins_;
|
||||
uint32_t width_, height_;
|
||||
bool passthrough_;
|
||||
|
||||
std::unique_ptr<BarSurface> surface_impl_;
|
||||
Gtk::Box left_;
|
||||
Gtk::Box center_;
|
||||
Gtk::Box right_;
|
||||
Gtk::Box box_;
|
||||
Gtk::CenterBox box_;
|
||||
std::vector<std::shared_ptr<waybar::AModule>> modules_left_;
|
||||
std::vector<std::shared_ptr<waybar::AModule>> modules_center_;
|
||||
std::vector<std::shared_ptr<waybar::AModule>> modules_right_;
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <gdk/gdk.h>
|
||||
#include <gdk/gdkwayland.h>
|
||||
#include <wayland-client.h>
|
||||
#include <gdk/wayland/gdkwayland.h>
|
||||
|
||||
#include "bar.hpp"
|
||||
#include "config.hpp"
|
||||
#include "util/portal.hpp"
|
||||
|
||||
struct zwlr_layer_shell_v1;
|
||||
struct zwp_idle_inhibitor_v1;
|
||||
struct zwp_idle_inhibit_manager_v1;
|
||||
|
||||
@@ -25,7 +21,6 @@ class Client {
|
||||
Glib::RefPtr<Gdk::Display> gdk_display;
|
||||
struct wl_display *wl_display = nullptr;
|
||||
struct wl_registry *registry = nullptr;
|
||||
struct zwlr_layer_shell_v1 *layer_shell = nullptr;
|
||||
struct zxdg_output_manager_v1 *xdg_output_manager = nullptr;
|
||||
struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr;
|
||||
std::vector<std::unique_ptr<Bar>> bars;
|
||||
@@ -34,6 +29,7 @@ class Client {
|
||||
|
||||
private:
|
||||
Client() = default;
|
||||
Glib::RefPtr<Gio::ListModel> monitors_;
|
||||
const std::string getStyle(const std::string &style, std::optional<Appearance> appearance);
|
||||
void bindInterfaces();
|
||||
void handleOutput(struct waybar_output &output);
|
||||
@@ -51,7 +47,6 @@ class Client {
|
||||
void handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor);
|
||||
void handleDeferredMonitorRemoval(Glib::RefPtr<Gdk::Monitor> monitor);
|
||||
|
||||
Glib::RefPtr<Gtk::StyleContext> style_context_;
|
||||
Glib::RefPtr<Gtk::CssProvider> css_provider_;
|
||||
std::unique_ptr<Portal> portal;
|
||||
std::list<struct waybar_output> outputs_;
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
#ifdef HAVE_UPOWER
|
||||
#include "modules/upower/upower.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_PIPEWIRE
|
||||
#include "modules/privacy/privacy.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_LIBPULSE
|
||||
#include "modules/pulseaudio.hpp"
|
||||
#endif
|
||||
@@ -90,7 +93,11 @@
|
||||
#ifdef HAVE_LIBCAVA
|
||||
#include "modules/cava.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_SYSTEMD_MONITOR
|
||||
#include "modules/systemd_failed_units.hpp"
|
||||
#endif
|
||||
#include "bar.hpp"
|
||||
#include "modules/cffi.hpp"
|
||||
#include "modules/custom.hpp"
|
||||
#include "modules/image.hpp"
|
||||
#include "modules/temperature.hpp"
|
||||
@@ -101,7 +108,7 @@ namespace waybar {
|
||||
class Factory {
|
||||
public:
|
||||
Factory(const Bar& bar, const Json::Value& config);
|
||||
AModule* makeModule(const std::string& name) const;
|
||||
AModule* makeModule(const std::string& name, const std::string& pos) const;
|
||||
|
||||
private:
|
||||
const Bar& bar_;
|
||||
|
||||
@@ -19,7 +19,7 @@ class Group : public AModule {
|
||||
virtual Gtk::Box& getBox();
|
||||
void addWidget(Gtk::Widget& widget);
|
||||
|
||||
bool handleMouseHover(GdkEventCrossing* const& e);
|
||||
// bool handleMouseHover(GdkEventCrossing* const& e);
|
||||
|
||||
protected:
|
||||
Gtk::Box box;
|
||||
|
||||
60
include/modules/cffi.hpp
Normal file
60
include/modules/cffi.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "AModule.hpp"
|
||||
#include "util/command.hpp"
|
||||
#include "util/json.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
namespace ffi {
|
||||
extern "C" {
|
||||
typedef struct wbcffi_module wbcffi_module;
|
||||
|
||||
typedef struct {
|
||||
wbcffi_module* obj;
|
||||
const char* waybar_version;
|
||||
//todo GtkContainer* (*get_root_widget)(wbcffi_module*);
|
||||
void (*queue_update)(wbcffi_module*);
|
||||
} wbcffi_init_info;
|
||||
|
||||
struct wbcffi_config_entry {
|
||||
const char* key;
|
||||
const char* value;
|
||||
};
|
||||
}
|
||||
} // namespace ffi
|
||||
|
||||
class CFFI : public AModule {
|
||||
public:
|
||||
CFFI(const std::string&, const std::string&, const Json::Value&);
|
||||
virtual ~CFFI();
|
||||
|
||||
virtual auto refresh(int signal) -> void override;
|
||||
virtual auto doAction(const std::string& name) -> void override;
|
||||
virtual auto update() -> void override;
|
||||
|
||||
private:
|
||||
///
|
||||
void* cffi_instance_ = nullptr;
|
||||
|
||||
typedef void*(InitFn)(const ffi::wbcffi_init_info* init_info,
|
||||
const ffi::wbcffi_config_entry* config_entries, size_t config_entries_len);
|
||||
typedef void(DenitFn)(void* instance);
|
||||
typedef void(RefreshFn)(void* instance, int signal);
|
||||
typedef void(DoActionFn)(void* instance, const char* name);
|
||||
typedef void(UpdateFn)(void* instance);
|
||||
|
||||
// FFI hooks
|
||||
struct {
|
||||
std::function<InitFn> init = nullptr;
|
||||
std::function<DenitFn> deinit = nullptr;
|
||||
std::function<RefreshFn> refresh = [](void*, int) {};
|
||||
std::function<DoActionFn> doAction = [](void*, const char*) {};
|
||||
std::function<UpdateFn> update = [](void*) {};
|
||||
} hooks_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
@@ -6,38 +6,26 @@
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
const std::string kCalendarPlaceholder = "calendar";
|
||||
const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list";
|
||||
|
||||
enum class WeeksSide {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
HIDDEN,
|
||||
};
|
||||
const std::string kCldPlaceholder{"calendar"};
|
||||
const std::string kTZPlaceholder{"tz_list"};
|
||||
|
||||
enum class CldMode { MONTH, YEAR };
|
||||
enum class WS { LEFT, RIGHT, HIDDEN };
|
||||
|
||||
class Clock final : public ALabel {
|
||||
public:
|
||||
Clock(const std::string&, const Json::Value&);
|
||||
virtual ~Clock() = default;
|
||||
auto update() -> void override;
|
||||
auto doAction(const std::string& name) -> void override;
|
||||
auto doAction(const std::string&) -> void override;
|
||||
|
||||
private:
|
||||
util::SleeperThread thread_;
|
||||
std::locale locale_;
|
||||
std::vector<const date::time_zone*> time_zones_;
|
||||
int current_time_zone_idx_;
|
||||
bool is_calendar_in_tooltip_;
|
||||
bool is_timezoned_list_in_tooltip_;
|
||||
|
||||
auto first_day_of_week() -> date::weekday;
|
||||
const date::time_zone* current_timezone();
|
||||
auto timezones_text(std::chrono::system_clock::time_point now) -> std::string;
|
||||
|
||||
/*Calendar properties*/
|
||||
WeeksSide cldWPos_{WeeksSide::HIDDEN};
|
||||
const std::locale locale_;
|
||||
// tooltip
|
||||
const std::string tlpFmt_;
|
||||
std::string tlpText_{""}; // tooltip text to print
|
||||
// Calendar
|
||||
const bool cldInTooltip_; // calendar in tooltip
|
||||
/*
|
||||
0 - calendar.format.months
|
||||
1 - calendar.format.weekdays
|
||||
@@ -47,28 +35,37 @@ class Clock final : public ALabel {
|
||||
5 - tooltip-format
|
||||
*/
|
||||
std::map<int, std::string const> fmtMap_;
|
||||
uint cldMonCols_{3}; // calendar count month columns
|
||||
int cldWnLen_{3}; // calendar week number length
|
||||
const int cldMonColLen_{20}; // calendar month column length
|
||||
WS cldWPos_{WS::HIDDEN}; // calendar week side to print
|
||||
months cldCurrShift_{0}; // calendar months shift
|
||||
year_month_day cldYearShift_; // calendar Year mode. Cached ymd
|
||||
std::string cldYearCached_; // calendar Year mode. Cached calendar
|
||||
year_month cldMonShift_; // calendar Month mode. Cached ym
|
||||
std::string cldMonCached_; // calendar Month mode. Cached calendar
|
||||
day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
|
||||
std::string cldText_{""}; // calendar text to print
|
||||
CldMode cldMode_{CldMode::MONTH};
|
||||
uint cldMonCols_{3}; // Count of the month in the row
|
||||
int cldMonColLen_{20}; // Length of the month column
|
||||
int cldWnLen_{3}; // Length of the week number
|
||||
date::year_month_day cldYearShift_;
|
||||
date::year_month cldMonShift_;
|
||||
date::months cldCurrShift_{0};
|
||||
date::months cldShift_{0};
|
||||
std::string cldYearCached_{};
|
||||
std::string cldMonCached_{};
|
||||
date::day cldBaseDay_{0};
|
||||
/*Calendar functions*/
|
||||
auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd,
|
||||
const date::time_zone* tz) -> const std::string;
|
||||
/*Clock actions*/
|
||||
auto get_calendar(const year_month_day& today, const year_month_day& ymd, const time_zone* tz)
|
||||
-> const std::string;
|
||||
|
||||
// time zoned time in tooltip
|
||||
const bool tzInTooltip_; // if need to print time zones text
|
||||
std::vector<const time_zone*> tzList_; // time zones list
|
||||
int tzCurrIdx_; // current time zone index for tzList_
|
||||
std::string tzText_{""}; // time zones text to print
|
||||
util::SleeperThread thread_;
|
||||
|
||||
auto getTZtext(sys_seconds now) -> std::string;
|
||||
auto first_day_of_week() -> weekday;
|
||||
// Module actions
|
||||
void cldModeSwitch();
|
||||
void cldShift_up();
|
||||
void cldShift_down();
|
||||
void tz_up();
|
||||
void tz_down();
|
||||
|
||||
// ModuleActionMap
|
||||
// Module Action Map
|
||||
static inline std::map<const std::string, void (waybar::modules::Clock::*const)()> actionMap_{
|
||||
{"mode", &waybar::modules::Clock::cldModeSwitch},
|
||||
{"shift_up", &waybar::modules::Clock::cldShift_up},
|
||||
@@ -76,4 +73,5 @@ class Clock final : public ALabel {
|
||||
{"tz_up", &waybar::modules::Clock::tz_up},
|
||||
{"tz_down", &waybar::modules::Clock::tz_down}};
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace waybar::modules {
|
||||
|
||||
class Custom : public ALabel {
|
||||
public:
|
||||
Custom(const std::string&, const std::string&, const Json::Value&);
|
||||
Custom(const std::string&, const std::string&, const Json::Value&, const std::string&);
|
||||
virtual ~Custom();
|
||||
auto update() -> void override;
|
||||
void refresh(int /*signal*/) override;
|
||||
@@ -26,10 +26,11 @@ class Custom : public ALabel {
|
||||
void parseOutputRaw();
|
||||
void parseOutputJson();
|
||||
void handleEvent();
|
||||
bool handleScroll(GdkEventScroll* e) override;
|
||||
bool handleToggle(GdkEventButton* const& e) override;
|
||||
//todo bool handleScroll(GdkEventScroll* e) override;
|
||||
//todo bool handleToggle(GdkEventButton* const& e) override;
|
||||
|
||||
const std::string name_;
|
||||
const std::string output_name_;
|
||||
std::string text_;
|
||||
std::string id_;
|
||||
std::string alt_;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include "util/json.hpp"
|
||||
@@ -25,16 +23,16 @@ class IPC {
|
||||
void registerForIPC(const std::string&, EventHandler*);
|
||||
void unregisterForIPC(EventHandler*);
|
||||
|
||||
std::string getSocket1Reply(const std::string& rq);
|
||||
static std::string getSocket1Reply(const std::string& rq);
|
||||
Json::Value getSocket1JsonReply(const std::string& rq);
|
||||
|
||||
private:
|
||||
void startIPC();
|
||||
void parseIPC(const std::string&);
|
||||
|
||||
std::mutex callbackMutex;
|
||||
util::JsonParser parser_;
|
||||
std::list<std::pair<std::string, EventHandler*>> callbacks;
|
||||
std::mutex m_callbackMutex;
|
||||
util::JsonParser m_parser;
|
||||
std::list<std::pair<std::string, EventHandler*>> m_callbacks;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<IPC> gIPC;
|
||||
|
||||
@@ -34,82 +34,82 @@ class WindowCreationPayload {
|
||||
std::string window_class, std::string window_title);
|
||||
WindowCreationPayload(Json::Value const& client_data);
|
||||
|
||||
int increment_time_spent_uncreated();
|
||||
bool is_empty(Workspaces& workspace_manager);
|
||||
bool repr_is_ready() const { return std::holds_alternative<Repr>(window_); }
|
||||
int incrementTimeSpentUncreated();
|
||||
bool isEmpty(Workspaces& workspace_manager);
|
||||
bool reprIsReady() const { return std::holds_alternative<Repr>(m_window); }
|
||||
std::string repr(Workspaces& workspace_manager);
|
||||
|
||||
std::string workspace_name() const { return workspace_name_; }
|
||||
WindowAddress addr() const { return window_address_; }
|
||||
std::string getWorkspaceName() const { return m_workspaceName; }
|
||||
WindowAddress getAddress() const { return m_windowAddress; }
|
||||
|
||||
void move_to_worksace(std::string& new_workspace_name);
|
||||
void moveToWorksace(std::string& new_workspace_name);
|
||||
|
||||
private:
|
||||
void clear_addr();
|
||||
void clear_workspace_name();
|
||||
void clearAddr();
|
||||
void clearWorkspaceName();
|
||||
|
||||
using Repr = std::string;
|
||||
using ClassAndTitle = std::pair<std::string, std::string>;
|
||||
std::variant<Repr, ClassAndTitle> window_;
|
||||
std::variant<Repr, ClassAndTitle> m_window;
|
||||
|
||||
WindowAddress window_address_;
|
||||
std::string workspace_name_;
|
||||
WindowAddress m_windowAddress;
|
||||
std::string m_workspaceName;
|
||||
|
||||
int time_spent_uncreated_ = 0;
|
||||
int m_timeSpentUncreated = 0;
|
||||
};
|
||||
|
||||
class Workspace {
|
||||
public:
|
||||
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
|
||||
const Json::Value& clients_data = Json::Value::nullRef);
|
||||
std::string& select_icon(std::map<std::string, std::string>& icons_map);
|
||||
Gtk::Button& button() { return button_; };
|
||||
std::string& selectIcon(std::map<std::string, std::string>& icons_map);
|
||||
Gtk::Button& button() { return m_button; };
|
||||
|
||||
int id() const { return id_; };
|
||||
std::string name() const { return name_; };
|
||||
std::string output() const { return output_; };
|
||||
bool active() const { return active_; };
|
||||
bool is_special() const { return is_special_; };
|
||||
bool is_persistent() const { return is_persistent_; };
|
||||
bool is_visible() const { return is_visible_; };
|
||||
bool is_empty() const { return windows_ == 0; };
|
||||
bool is_urgent() const { return is_urgent_; };
|
||||
int id() const { return m_id; };
|
||||
std::string name() const { return m_name; };
|
||||
std::string output() const { return m_output; };
|
||||
bool isActive() const { return m_active; };
|
||||
bool isSpecial() const { return m_isSpecial; };
|
||||
bool isPersistent() const { return m_isPersistent; };
|
||||
bool isVisible() const { return m_isVisible; };
|
||||
bool isEmpty() const { return m_windows == 0; };
|
||||
bool isUrgent() const { return m_isUrgent; };
|
||||
|
||||
bool handle_clicked(GdkEventButton* bt) const;
|
||||
void set_active(bool value = true) { active_ = value; };
|
||||
void set_persistent(bool value = true) { is_persistent_ = value; };
|
||||
void set_urgent(bool value = true) { is_urgent_ = value; };
|
||||
void set_visible(bool value = true) { is_visible_ = value; };
|
||||
void set_windows(uint value) { windows_ = value; };
|
||||
void set_name(std::string const& value) { name_ = value; };
|
||||
bool contains_window(WindowAddress const& addr) const { return window_map_.contains(addr); }
|
||||
void insert_window(WindowCreationPayload create_window_paylod);
|
||||
std::string remove_window(WindowAddress const& addr);
|
||||
void initialize_window_map(const Json::Value& clients_data);
|
||||
bool handleClicked(GdkEventButton* bt) const;
|
||||
void setActive(bool value = true) { m_active = value; };
|
||||
void setPersistent(bool value = true) { m_isPersistent = value; };
|
||||
void setUrgent(bool value = true) { m_isUrgent = value; };
|
||||
void setVisible(bool value = true) { m_isVisible = value; };
|
||||
void setWindows(uint value) { m_windows = value; };
|
||||
void setName(std::string const& value) { m_name = value; };
|
||||
bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); }
|
||||
void insertWindow(WindowCreationPayload create_window_paylod);
|
||||
std::string removeWindow(WindowAddress const& addr);
|
||||
void initializeWindowMap(const Json::Value& clients_data);
|
||||
|
||||
bool on_window_opened(WindowCreationPayload const& create_window_paylod);
|
||||
std::optional<std::string> close_window(WindowAddress const& addr);
|
||||
bool onWindowOpened(WindowCreationPayload const& create_window_paylod);
|
||||
std::optional<std::string> closeWindow(WindowAddress const& addr);
|
||||
|
||||
void update(const std::string& format, const std::string& icon);
|
||||
|
||||
private:
|
||||
Workspaces& workspace_manager_;
|
||||
Workspaces& m_workspaceManager;
|
||||
|
||||
int id_;
|
||||
std::string name_;
|
||||
std::string output_;
|
||||
uint windows_;
|
||||
bool active_ = false;
|
||||
bool is_special_ = false;
|
||||
bool is_persistent_ = false;
|
||||
bool is_urgent_ = false;
|
||||
bool is_visible_ = false;
|
||||
int m_id;
|
||||
std::string m_name;
|
||||
std::string m_output;
|
||||
uint m_windows;
|
||||
bool m_active = false;
|
||||
bool m_isSpecial = false;
|
||||
bool m_isPersistent = false;
|
||||
bool m_isUrgent = false;
|
||||
bool m_isVisible = false;
|
||||
|
||||
std::map<WindowAddress, std::string> window_map_;
|
||||
std::map<WindowAddress, std::string> m_windowMap;
|
||||
|
||||
Gtk::Button button_;
|
||||
Gtk::Box content_;
|
||||
Gtk::Label label_;
|
||||
Gtk::Button m_button;
|
||||
Gtk::Box m_content;
|
||||
Gtk::Label m_label;
|
||||
};
|
||||
|
||||
class Workspaces : public AModule, public EventHandler {
|
||||
@@ -119,85 +119,96 @@ class Workspaces : public AModule, public EventHandler {
|
||||
void update() override;
|
||||
void init();
|
||||
|
||||
auto all_outputs() const -> bool { return all_outputs_; }
|
||||
auto show_special() const -> bool { return show_special_; }
|
||||
auto active_only() const -> bool { return active_only_; }
|
||||
auto allOutputs() const -> bool { return m_allOutputs; }
|
||||
auto showSpecial() const -> bool { return m_showSpecial; }
|
||||
auto activeOnly() const -> bool { return m_activeOnly; }
|
||||
|
||||
auto get_bar_output() const -> std::string { return bar_.output->name; }
|
||||
auto getBarOutput() const -> std::string { return m_bar.output->name; }
|
||||
|
||||
std::string get_rewrite(std::string window_class, std::string window_title);
|
||||
std::string& get_window_separator() { return format_window_separator_; }
|
||||
bool is_workspace_ignored(std::string const& workspace_name);
|
||||
std::string getRewrite(std::string window_class, std::string window_title);
|
||||
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
|
||||
bool isWorkspaceIgnored(std::string const& workspace_name);
|
||||
|
||||
bool window_rewrite_config_uses_title() const { return any_window_rewrite_rule_uses_title_; }
|
||||
bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; }
|
||||
|
||||
private:
|
||||
void onEvent(const std::string& e) override;
|
||||
void update_window_count();
|
||||
void sort_workspaces();
|
||||
void create_workspace(Json::Value const& workspace_data,
|
||||
Json::Value const& clients_data = Json::Value::nullRef);
|
||||
void remove_workspace(std::string const& name);
|
||||
void set_urgent_workspace(std::string const& windowaddress);
|
||||
void parse_config(const Json::Value& config);
|
||||
void register_ipc();
|
||||
void updateWindowCount();
|
||||
void sortWorkspaces();
|
||||
void createWorkspace(Json::Value const& workspace_data,
|
||||
Json::Value const& clients_data = Json::Value::nullRef);
|
||||
void removeWorkspace(std::string const& name);
|
||||
void setUrgentWorkspace(std::string const& windowaddress);
|
||||
void parseConfig(const Json::Value& config);
|
||||
void registerIpc();
|
||||
|
||||
// workspace events
|
||||
void on_workspace_activated(std::string const& payload);
|
||||
void on_workspace_destroyed(std::string const& payload);
|
||||
void on_workspace_created(std::string const& payload);
|
||||
void on_workspace_moved(std::string const& payload);
|
||||
void on_workspace_renamed(std::string const& payload);
|
||||
void onWorkspaceActivated(std::string const& payload);
|
||||
void onWorkspaceDestroyed(std::string const& payload);
|
||||
void onWorkspaceCreated(std::string const& workspaceName,
|
||||
Json::Value const& clientsData = Json::Value::nullRef);
|
||||
void onWorkspaceMoved(std::string const& payload);
|
||||
void onWorkspaceRenamed(std::string const& payload);
|
||||
|
||||
// monitor events
|
||||
void on_monitor_focused(std::string const& payload);
|
||||
void onMonitorFocused(std::string const& payload);
|
||||
|
||||
// window events
|
||||
void on_window_opened(std::string const& payload);
|
||||
void on_window_closed(std::string const& payload);
|
||||
void on_window_moved(std::string const& payload);
|
||||
void onWindowOpened(std::string const& payload);
|
||||
void onWindowClosed(std::string const& addr);
|
||||
void onWindowMoved(std::string const& payload);
|
||||
|
||||
void on_window_title_event(std::string const& payload);
|
||||
void onWindowTitleEvent(std::string const& payload);
|
||||
|
||||
int window_rewrite_priority_function(std::string const& window_rule);
|
||||
int windowRewritePriorityFunction(std::string const& window_rule);
|
||||
|
||||
bool all_outputs_ = false;
|
||||
bool show_special_ = false;
|
||||
bool active_only_ = false;
|
||||
void doUpdate();
|
||||
|
||||
enum class SORT_METHOD { ID, NAME, NUMBER, DEFAULT };
|
||||
util::EnumParser<SORT_METHOD> enum_parser_;
|
||||
SORT_METHOD sort_by_ = SORT_METHOD::DEFAULT;
|
||||
std::map<std::string, SORT_METHOD> sort_map_ = {{"ID", SORT_METHOD::ID},
|
||||
{"NAME", SORT_METHOD::NAME},
|
||||
{"NUMBER", SORT_METHOD::NUMBER},
|
||||
{"DEFAULT", SORT_METHOD::DEFAULT}};
|
||||
void extendOrphans(int workspaceId, Json::Value const& clientsJson);
|
||||
void registerOrphanWindow(WindowCreationPayload create_window_paylod);
|
||||
|
||||
void fill_persistent_workspaces();
|
||||
void create_persistent_workspaces();
|
||||
std::vector<std::string> persistent_workspaces_to_create_;
|
||||
bool persistent_created_ = false;
|
||||
bool m_allOutputs = false;
|
||||
bool m_showSpecial = false;
|
||||
bool m_activeOnly = false;
|
||||
|
||||
std::string format_;
|
||||
// Map for windows stored in workspaces not present in the current bar.
|
||||
// This happens when the user has multiple monitors (hence, multiple bars)
|
||||
// and doesn't share windows accross bars (a.k.a `all-outputs` = false)
|
||||
std::map<WindowAddress, std::string> m_orphanWindowMap;
|
||||
|
||||
std::map<std::string, std::string> icons_map_;
|
||||
util::RegexCollection window_rewrite_rules_;
|
||||
bool any_window_rewrite_rule_uses_title_ = false;
|
||||
std::string format_window_separator_;
|
||||
enum class SortMethod { ID, NAME, NUMBER, DEFAULT };
|
||||
util::EnumParser<SortMethod> m_enumParser;
|
||||
SortMethod m_sortBy = SortMethod::DEFAULT;
|
||||
std::map<std::string, SortMethod> m_sortMap = {{"ID", SortMethod::ID},
|
||||
{"NAME", SortMethod::NAME},
|
||||
{"NUMBER", SortMethod::NUMBER},
|
||||
{"DEFAULT", SortMethod::DEFAULT}};
|
||||
|
||||
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::vector<WindowCreationPayload> windows_to_create_;
|
||||
void fillPersistentWorkspaces();
|
||||
void createPersistentWorkspaces();
|
||||
std::vector<std::string> m_persistentWorkspacesToCreate;
|
||||
bool m_persistentCreated = false;
|
||||
|
||||
std::vector<std::regex> ignore_workspaces_;
|
||||
std::string m_format;
|
||||
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
Gtk::Box box_;
|
||||
std::map<std::string, std::string> m_iconsMap;
|
||||
util::RegexCollection m_windowRewriteRules;
|
||||
bool m_anyWindowRewriteRuleUsesTitle = false;
|
||||
std::string m_formatWindowSeparator;
|
||||
|
||||
bool m_withIcon;
|
||||
uint64_t m_monitorId;
|
||||
std::string m_activeWorkspaceName;
|
||||
std::vector<std::unique_ptr<Workspace>> m_workspaces;
|
||||
std::vector<std::pair<Json::Value, Json::Value>> m_workspacesToCreate;
|
||||
std::vector<std::string> m_workspacesToRemove;
|
||||
std::vector<WindowCreationPayload> m_windowsToCreate;
|
||||
|
||||
std::vector<std::regex> m_ignoreWorkspaces;
|
||||
|
||||
std::mutex m_mutex;
|
||||
const Bar& m_bar;
|
||||
Gtk::Box m_box;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
||||
@@ -19,7 +19,7 @@ class IdleInhibitor : public ALabel {
|
||||
static bool status;
|
||||
|
||||
private:
|
||||
bool handleToggle(GdkEventButton* const& e) override;
|
||||
//todo bool handleToggle(GdkEventButton* const& e) override;
|
||||
void toggleStatus();
|
||||
|
||||
const Bar& bar_;
|
||||
|
||||
41
include/modules/privacy/privacy.hpp
Normal file
41
include/modules/privacy/privacy.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "gtkmm/box.h"
|
||||
#include "modules/privacy/privacy_item.hpp"
|
||||
#include "util/pipewire/pipewire_backend.hpp"
|
||||
#include "util/pipewire/privacy_node_info.hpp"
|
||||
|
||||
using waybar::util::PipewireBackend::PrivacyNodeInfo;
|
||||
|
||||
namespace waybar::modules::privacy {
|
||||
|
||||
class Privacy : public AModule {
|
||||
public:
|
||||
Privacy(const std::string &, const Json::Value &, const std::string &pos);
|
||||
auto update() -> void override;
|
||||
|
||||
void onPrivacyNodesChanged();
|
||||
|
||||
private:
|
||||
std::list<PrivacyNodeInfo *> nodes_screenshare; // Screen is being shared
|
||||
std::list<PrivacyNodeInfo *> nodes_audio_in; // Application is using the microphone
|
||||
std::list<PrivacyNodeInfo *> nodes_audio_out; // Application is outputting audio
|
||||
|
||||
std::mutex mutex_;
|
||||
sigc::connection visibility_conn;
|
||||
|
||||
// Config
|
||||
Gtk::Box box_;
|
||||
uint iconSpacing = 4;
|
||||
uint iconSize = 20;
|
||||
uint transition_duration = 250;
|
||||
|
||||
std::shared_ptr<util::PipewireBackend::PipewireBackend> backend = nullptr;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::privacy
|
||||
51
include/modules/privacy/privacy_item.hpp
Normal file
51
include/modules/privacy/privacy_item.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <json/value.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "gtkmm/box.h"
|
||||
#include "gtkmm/image.h"
|
||||
#include "gtkmm/revealer.h"
|
||||
#include "util/pipewire/privacy_node_info.hpp"
|
||||
|
||||
using waybar::util::PipewireBackend::PrivacyNodeInfo;
|
||||
using waybar::util::PipewireBackend::PrivacyNodeType;
|
||||
|
||||
namespace waybar::modules::privacy {
|
||||
|
||||
class PrivacyItem : public Gtk::Revealer {
|
||||
public:
|
||||
PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_,
|
||||
std::list<PrivacyNodeInfo *> *nodes, const std::string &pos, const uint icon_size,
|
||||
const uint transition_duration);
|
||||
|
||||
enum PrivacyNodeType privacy_type;
|
||||
|
||||
void set_in_use(bool in_use);
|
||||
|
||||
private:
|
||||
std::list<PrivacyNodeInfo *> *nodes;
|
||||
|
||||
sigc::connection signal_conn;
|
||||
|
||||
Gtk::Box tooltip_window;
|
||||
|
||||
bool init = false;
|
||||
bool in_use = false;
|
||||
|
||||
// Config
|
||||
std::string iconName = "image-missing-symbolic";
|
||||
bool tooltip = true;
|
||||
uint tooltipIconSize = 24;
|
||||
|
||||
Gtk::Box box_;
|
||||
Gtk::Image icon_;
|
||||
|
||||
void update_tooltip();
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::privacy
|
||||
30
include/modules/systemd_failed_units.hpp
Normal file
30
include/modules/systemd_failed_units.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <giomm/dbusproxy.h>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class SystemdFailedUnits : public ALabel {
|
||||
public:
|
||||
SystemdFailedUnits(const std::string&, const Json::Value&);
|
||||
virtual ~SystemdFailedUnits();
|
||||
auto update() -> void override;
|
||||
|
||||
private:
|
||||
bool hide_on_ok;
|
||||
std::string format_ok;
|
||||
|
||||
bool update_pending;
|
||||
std::string last_status;
|
||||
uint32_t nr_failed_system, nr_failed_user;
|
||||
Glib::RefPtr<Gio::DBus::Proxy> system_proxy, user_proxy;
|
||||
|
||||
void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name,
|
||||
const Glib::VariantContainerBase &arguments);
|
||||
void updateData();
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
||||
@@ -72,6 +72,7 @@ class UPower : public AModule {
|
||||
std::unique_ptr<UPowerTooltip> upower_tooltip;
|
||||
std::string lastStatus;
|
||||
bool showAltText;
|
||||
bool showIcon = true;
|
||||
bool upowerRunning;
|
||||
guint upowerWatcher_id;
|
||||
std::string nativePath_;
|
||||
|
||||
@@ -14,7 +14,7 @@ class User : public AIconLabel {
|
||||
virtual ~User() = default;
|
||||
auto update() -> void override;
|
||||
|
||||
bool handleToggle(GdkEventButton* const& e) override;
|
||||
//todo bool handleToggle(GdkEventButton* const& e) override;
|
||||
|
||||
private:
|
||||
util::SleeperThread thread_;
|
||||
|
||||
@@ -52,7 +52,7 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
|
||||
using slot_t = decltype(std::declval<signal_t>().make_slot());
|
||||
using arg_tuple_t = std::tuple<std::decay_t<Args>...>;
|
||||
// ensure that unwrapped methods are not accessible
|
||||
using signal_t::emit_reverse;
|
||||
using signal_t::emit;
|
||||
using signal_t::make_slot;
|
||||
|
||||
void handle_event() {
|
||||
|
||||
@@ -66,7 +66,7 @@ inline int close(FILE* fp, pid_t pid) {
|
||||
return stat;
|
||||
}
|
||||
|
||||
inline FILE* open(const std::string& cmd, int& pid) {
|
||||
inline FILE* open(const std::string& cmd, int& pid, const std::string& output_name) {
|
||||
if (cmd == "") return nullptr;
|
||||
int fd[2];
|
||||
// Open the pipe with the close-on-exec flag set, so it will not be inherited
|
||||
@@ -109,6 +109,9 @@ inline FILE* open(const std::string& cmd, int& pid) {
|
||||
::close(fd[0]);
|
||||
dup2(fd[1], 1);
|
||||
setpgid(child_pid, child_pid);
|
||||
if (output_name != "") {
|
||||
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
|
||||
}
|
||||
execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
||||
exit(0);
|
||||
} else {
|
||||
@@ -118,9 +121,9 @@ inline FILE* open(const std::string& cmd, int& pid) {
|
||||
return fdopen(fd[0], "r");
|
||||
}
|
||||
|
||||
inline struct res exec(const std::string& cmd) {
|
||||
inline struct res exec(const std::string& cmd, const std::string& output_name) {
|
||||
int pid;
|
||||
auto fp = command::open(cmd, pid);
|
||||
auto fp = command::open(cmd, pid, output_name);
|
||||
if (!fp) return {-1, ""};
|
||||
auto output = command::read(fp);
|
||||
auto stat = command::close(fp, pid);
|
||||
@@ -129,7 +132,7 @@ inline struct res exec(const std::string& cmd) {
|
||||
|
||||
inline struct res execNoRead(const std::string& cmd) {
|
||||
int pid;
|
||||
auto fp = command::open(cmd, pid);
|
||||
auto fp = command::open(cmd, pid, "");
|
||||
if (!fp) return {-1, ""};
|
||||
auto stat = command::close(fp, pid);
|
||||
return {WEXITSTATUS(stat), ""};
|
||||
|
||||
@@ -1,34 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <chrono>
|
||||
|
||||
#if HAVE_CHRONO_TIMEZONES
|
||||
#include <chrono>
|
||||
#include <format>
|
||||
|
||||
/* Compatibility layer for <date/tz.h> on top of C++20 <chrono> */
|
||||
namespace date {
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
namespace literals {
|
||||
using std::chrono::last;
|
||||
}
|
||||
|
||||
inline auto format(const std::string& spec, const auto& ztime) {
|
||||
return spec.empty() ? "" : std::vformat("{:L" + spec + "}", std::make_format_args(ztime));
|
||||
}
|
||||
|
||||
inline auto format(const std::locale& loc, const std::string& spec, const auto& ztime) {
|
||||
return spec.empty() ? "" : std::vformat(loc, "{:L" + spec + "}", std::make_format_args(ztime));
|
||||
}
|
||||
|
||||
} // namespace date
|
||||
|
||||
#else
|
||||
#include <date/tz.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <regex>
|
||||
#endif
|
||||
|
||||
// Date
|
||||
namespace date {
|
||||
#if HAVE_CHRONO_TIMEZONES
|
||||
using namespace std::chrono;
|
||||
using namespace std;
|
||||
#else
|
||||
|
||||
using system_clock = std::chrono::system_clock;
|
||||
using seconds = std::chrono::seconds;
|
||||
|
||||
template <typename T>
|
||||
inline auto format(const char* spec, const T& arg) {
|
||||
return date::format(std::regex_replace(spec, std::regex("\\{:L|\\}"), ""), arg);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto format(const std::locale& loc, const char* spec, const T& arg) {
|
||||
return date::format(loc, std::regex_replace(spec, std::regex("\\{:L|\\}"), ""), arg);
|
||||
}
|
||||
|
||||
constexpr decltype(auto) operator""d(unsigned long long d) noexcept {
|
||||
return date::operator""_d(d); // very verbose, but it works
|
||||
}
|
||||
#endif
|
||||
} // namespace date
|
||||
|
||||
// Format
|
||||
namespace waybar::util::date::format {
|
||||
#if HAVE_CHRONO_TIMEZONES
|
||||
using namespace std;
|
||||
#else
|
||||
using namespace fmt;
|
||||
#endif
|
||||
} // namespace waybar::util::date::format
|
||||
|
||||
#if not HAVE_CHRONO_TIMEZONES
|
||||
template <typename Duration, typename TimeZonePtr>
|
||||
struct fmt::formatter<date::zoned_time<Duration, TimeZonePtr>> {
|
||||
std::string_view specs;
|
||||
@@ -58,3 +76,6 @@ struct fmt::formatter<date::zoned_time<Duration, TimeZonePtr>> {
|
||||
return fmt::format_to(ctx.out(), "{}", date::format(fmt::to_string(specs), ztime));
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
using namespace date;
|
||||
|
||||
@@ -92,7 +92,7 @@ struct formatter<pow_format> {
|
||||
template <>
|
||||
struct formatter<Glib::ustring> : formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const Glib::ustring& value, FormatContext& ctx) {
|
||||
auto format(const Glib::ustring& value, FormatContext& ctx) const {
|
||||
return formatter<std::string>::format(static_cast<std::string>(value), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
40
include/util/pipewire/pipewire_backend.hpp
Normal file
40
include/util/pipewire/pipewire_backend.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "util/backend_common.hpp"
|
||||
#include "util/pipewire/privacy_node_info.hpp"
|
||||
|
||||
namespace waybar::util::PipewireBackend {
|
||||
|
||||
class PipewireBackend {
|
||||
private:
|
||||
pw_thread_loop* mainloop_;
|
||||
pw_context* context_;
|
||||
pw_core* core_;
|
||||
|
||||
spa_hook registry_listener;
|
||||
|
||||
/* Hack to keep constructor inaccessible but still public.
|
||||
* This is required to be able to use std::make_shared.
|
||||
* It is important to keep this class only accessible via a reference-counted
|
||||
* pointer because the destructor will manually free memory, and this could be
|
||||
* a problem with C++20's copy and move semantics.
|
||||
*/
|
||||
struct private_constructor_tag {};
|
||||
|
||||
public:
|
||||
std::mutex mutex_;
|
||||
|
||||
pw_registry* registry;
|
||||
|
||||
sigc::signal<void> privacy_nodes_changed_signal_event;
|
||||
|
||||
std::unordered_map<uint32_t, PrivacyNodeInfo*> privacy_nodes;
|
||||
|
||||
static std::shared_ptr<PipewireBackend> getInstance();
|
||||
|
||||
PipewireBackend(private_constructor_tag tag);
|
||||
~PipewireBackend();
|
||||
};
|
||||
} // namespace waybar::util::PipewireBackend
|
||||
62
include/util/pipewire/privacy_node_info.hpp
Normal file
62
include/util/pipewire/privacy_node_info.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/gtk_icon.hpp"
|
||||
|
||||
namespace waybar::util::PipewireBackend {
|
||||
|
||||
enum PrivacyNodeType {
|
||||
PRIVACY_NODE_TYPE_NONE,
|
||||
PRIVACY_NODE_TYPE_VIDEO_INPUT,
|
||||
PRIVACY_NODE_TYPE_AUDIO_INPUT,
|
||||
PRIVACY_NODE_TYPE_AUDIO_OUTPUT
|
||||
};
|
||||
|
||||
class PrivacyNodeInfo {
|
||||
public:
|
||||
PrivacyNodeType type = PRIVACY_NODE_TYPE_NONE;
|
||||
uint32_t id;
|
||||
uint32_t client_id;
|
||||
enum pw_node_state state = PW_NODE_STATE_IDLE;
|
||||
std::string media_class;
|
||||
std::string media_name;
|
||||
std::string node_name;
|
||||
std::string application_name;
|
||||
|
||||
std::string pipewire_access_portal_app_id;
|
||||
std::string application_icon_name;
|
||||
|
||||
struct spa_hook object_listener;
|
||||
struct spa_hook proxy_listener;
|
||||
|
||||
void *data;
|
||||
|
||||
std::string get_name() {
|
||||
const std::vector<std::string *> names{&application_name, &node_name};
|
||||
std::string name = "Unknown Application";
|
||||
for (auto &name_ : names) {
|
||||
if (name_ != nullptr && name_->length() > 0) {
|
||||
name = *name_;
|
||||
name[0] = toupper(name[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string get_icon_name() {
|
||||
const std::vector<std::string *> names{&application_icon_name, &pipewire_access_portal_app_id,
|
||||
&application_name, &node_name};
|
||||
const std::string name = "application-x-executable-symbolic";
|
||||
for (auto &name_ : names) {
|
||||
if (name_ != nullptr && name_->length() > 0 && DefaultGtkIconThemeWrapper::has_icon(*name_)) {
|
||||
return *name_;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
};
|
||||
} // namespace waybar::util::PipewireBackend
|
||||
@@ -19,7 +19,7 @@ class Portal : private DBus::Proxy {
|
||||
void refreshAppearance();
|
||||
Appearance getAppearance();
|
||||
|
||||
typedef sigc::signal<void, Appearance> type_signal_appearance_changed;
|
||||
typedef sigc::signal<void(Appearance)> type_signal_appearance_changed;
|
||||
type_signal_appearance_changed signal_appearance_changed() { return m_signal_appearance_changed; }
|
||||
|
||||
private:
|
||||
|
||||
37
man/waybar-cffi.5.scd
Normal file
37
man/waybar-cffi.5.scd
Normal file
@@ -0,0 +1,37 @@
|
||||
waybar-cffi(5)
|
||||
# NAME
|
||||
|
||||
waybar - cffi module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *cffi* module gives full control of a GTK widget to a third-party dynamic library, to create more complex modules using different programming languages.
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
Addressed by *cffi/<name>*
|
||||
|
||||
*module_path*: ++
|
||||
typeof: string ++
|
||||
The path to the dynamic library to load to control the widget.
|
||||
|
||||
Some additional configuration may be required depending on the cffi dynamic library being used.
|
||||
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
## C example:
|
||||
|
||||
An example module written in C can be found at https://github.com/Alexays/Waybar/resources/custom_modules/cffi_example/
|
||||
|
||||
Waybar config to enable the module:
|
||||
```
|
||||
"cffi/c_example": {
|
||||
"module_path": ".config/waybar/cffi/wb_cffi_example.so"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# STYLE
|
||||
|
||||
The classes and IDs are managed by the cffi dynamic library.
|
||||
@@ -85,7 +85,7 @@ $XDG_CONFIG_HOME/waybar/config ++
|
||||
:[ same as format
|
||||
:[ Tooltip on hover
|
||||
|
||||
View all valid format options in *strftime(3)* or have a look <https://fmt.dev/latest/syntax.html#chrono-specs>
|
||||
View all valid format options in *strftime(3)* or have a look https://en.cppreference.com/w/cpp/chrono/duration/formatter
|
||||
|
||||
2. Addressed by *clock: calendar*
|
||||
[- *Option*
|
||||
@@ -156,7 +156,7 @@ View all valid format options in *strftime(3)* or have a look <https://fmt.dev/l
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
- *{calendar}*: Current month calendar
|
||||
- *{timezoned_time_list}*: List of time in the rest timezones, if more than one timezone is set in the config
|
||||
- *{tz_list}*: List of time in the rest timezones, if more than one timezone is set in the config
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Addressed by *hyprland/workspaces*
|
||||
*window-rewrite*: ++
|
||||
typeof: object ++
|
||||
Regex rules to map window class to an icon or preferred method of representation for a workspace's window.
|
||||
Keys are the rules, while the values are the methods of representation.
|
||||
Keys are the rules, while the values are the methods of representation. Values may use the placeholders {class} and {title} to use the window's original class and/or title respectively.
|
||||
Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching.
|
||||
|
||||
*window-rewrite-default*:
|
||||
|
||||
85
man/waybar-privacy.5.scd
Normal file
85
man/waybar-privacy.5.scd
Normal file
@@ -0,0 +1,85 @@
|
||||
waybar-privacy(5)
|
||||
|
||||
# NAME
|
||||
|
||||
waybar - privacy module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *privacy* module displays if any application is capturing audio, sharing ++
|
||||
the screen or playing audio.
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
*icon-spacing*: ++
|
||||
typeof: integer ++
|
||||
default: 4 ++
|
||||
The spacing between each privacy icon.
|
||||
|
||||
*icon-size*: ++
|
||||
typeof: integer ++
|
||||
default: 20 ++
|
||||
The size of each privacy icon.
|
||||
|
||||
*transition-duration*: ++
|
||||
typeof: integer ++
|
||||
default: 250 ++
|
||||
Option to disable tooltip on hover.
|
||||
|
||||
*modules* ++
|
||||
typeof: array of objects ++
|
||||
default: [{"type": "screenshare"}, {"type": "audio-in"}] ++
|
||||
Which privacy modules to monitor. See *MODULES CONFIGURATION* for++
|
||||
more information.
|
||||
|
||||
# MODULES CONFIGURATION
|
||||
|
||||
*type*: ++
|
||||
typeof: string ++
|
||||
values: "screenshare", "audio-in", "audio-out" ++
|
||||
Specifies which module to use and configure.
|
||||
|
||||
*tooltip*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
Option to disable tooltip on hover.
|
||||
|
||||
*tooltip-icon-size*: ++
|
||||
typeof: integer ++
|
||||
default: 24 ++
|
||||
The size of each icon in the tooltip.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
```
|
||||
"privacy": {
|
||||
"icon-spacing": 4,
|
||||
"icon-size": 18,
|
||||
"transition-duration": 250,
|
||||
"modules": [
|
||||
{
|
||||
"type": "screenshare",
|
||||
"tooltip": true,
|
||||
"tooltip-icon-size": 24
|
||||
},
|
||||
{
|
||||
"type": "audio-out",
|
||||
"tooltip": true,
|
||||
"tooltip-icon-size": 24
|
||||
},
|
||||
{
|
||||
"type": "audio-in",
|
||||
"tooltip": true,
|
||||
"tooltip-icon-size": 24
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#privacy*
|
||||
- *#privacy-item*
|
||||
- *#privacy-item.screenshare*
|
||||
- *#privacy-item.audio-in*
|
||||
- *#privacy-item.audio-out*
|
||||
63
man/waybar-systemd-failed-units.5.scd
Normal file
63
man/waybar-systemd-failed-units.5.scd
Normal file
@@ -0,0 +1,63 @@
|
||||
waybar-systemd-failed-units(5)
|
||||
|
||||
# NAME
|
||||
|
||||
waybar - systemd failed units monitor module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *systemd-failed-units* module displays the number of failed systemd units.
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
Addressed by *systemd-failed-units*
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: *{nr_failed} failed* ++
|
||||
The format, how information should be displayed. This format is used when other formats aren't specified.
|
||||
|
||||
*format-ok*: ++
|
||||
typeof: string ++
|
||||
This format is used when there is no failing units.
|
||||
|
||||
*user*: ++
|
||||
typeof: bool ++
|
||||
default: *true* ++
|
||||
Option to count user systemd units.
|
||||
|
||||
*system*: ++
|
||||
typeof: bool ++
|
||||
default: *true* ++
|
||||
Option to count systemwide (PID=1) systemd units.
|
||||
|
||||
*hide-on-ok*: ++
|
||||
typeof: bool ++
|
||||
default: *true* ++
|
||||
Option to hide this module when there is no failing units.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
*{nr_failed_system}*: Number of failed units from systemwide (PID=1) systemd.
|
||||
|
||||
*{nr_failed_user}*: Number of failed units from user systemd.
|
||||
|
||||
*{nr_failed}*: Number of total failed units.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
```
|
||||
"systemd-failed-units": {
|
||||
"hide-on-ok": false,
|
||||
"format": "✗ {nr_failed}",
|
||||
"format-ok": "✓",
|
||||
"system": true,
|
||||
"user": false,
|
||||
}
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#systemd-failed-units*
|
||||
- *#systemd-failed-units.ok*
|
||||
- *#systemd-failed-units.degraded*
|
||||
@@ -57,6 +57,11 @@ compatible devices in the tooltip.
|
||||
typeof: string ++
|
||||
Command to execute when clicked on the module.
|
||||
|
||||
*show-icon*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
Option to disable battery icon.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
*{percentage}*: The battery capacity in percentage
|
||||
@@ -93,6 +98,15 @@ depending on the charging state.
|
||||
"tooltip": true,
|
||||
"tooltip-spacing": 20
|
||||
}
|
||||
```
|
||||
```
|
||||
"upower": {
|
||||
"show-icon": false,
|
||||
"hide-if-empty": true,
|
||||
"tooltip": true,
|
||||
"tooltip-spacing": 20
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
@@ -6,15 +6,15 @@ waybar - configuration file
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The configuration uses the JSON file format and is named *config*.
|
||||
The configuration uses the JSONC file format and is named *config* or *config.jsonc*.
|
||||
|
||||
Valid locations for this file are:
|
||||
|
||||
- *$XDG_CONFIG_HOME/waybar/config*
|
||||
- *~/.config/waybar/config*
|
||||
- *~/waybar/config*
|
||||
- */etc/xdg/waybar/config*
|
||||
- *@sysconfdir@/xdg/waybar/config*
|
||||
- *$XDG_CONFIG_HOME/waybar/*
|
||||
- *~/.config/waybar/*
|
||||
- *~/waybar/*
|
||||
- */etc/xdg/waybar/*
|
||||
- *@sysconfdir@/xdg/waybar/*
|
||||
|
||||
A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config
|
||||
Also, a minimal example configuration can be found at the bottom of this man page.
|
||||
@@ -108,12 +108,6 @@ Also, a minimal example configuration can be found at the bottom of this man pag
|
||||
Option to pass any pointer events to the window under the bar.
|
||||
Intended to be used with either *top* or *overlay* layers and without exclusive zone.
|
||||
|
||||
*gtk-layer-shell* ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
Option to disable the use of gtk-layer-shell for popups.
|
||||
Only functional if compiled with gtk-layer-shell support.
|
||||
|
||||
*ipc* ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
|
||||
440
meson.build
440
meson.build
@@ -1,10 +1,10 @@
|
||||
project(
|
||||
'waybar', 'cpp', 'c',
|
||||
version: '0.9.24',
|
||||
version: '4.1.0',
|
||||
license: 'MIT',
|
||||
meson_version: '>= 0.50.0',
|
||||
meson_version: '>= 1.3.0',
|
||||
default_options : [
|
||||
'cpp_std=c++20',
|
||||
'cpp_std=c++23',
|
||||
'buildtype=release',
|
||||
'default_library=static'
|
||||
],
|
||||
@@ -78,63 +78,19 @@ is_freebsd = host_machine.system() == 'freebsd'
|
||||
is_netbsd = host_machine.system() == 'netbsd'
|
||||
is_openbsd = host_machine.system() == 'openbsd'
|
||||
|
||||
thread_dep = dependency('threads')
|
||||
fmt = dependency('fmt', version : ['>=8.1.1'], fallback : ['fmt', 'fmt_dep'])
|
||||
spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled'])
|
||||
jsoncpp = dependency('jsoncpp', version : ['>=1.9.4'], fallback : ['jsoncpp', 'jsoncpp_dep'])
|
||||
wayland_client = dependency('wayland-client')
|
||||
wayland_cursor = dependency('wayland-cursor')
|
||||
wayland_protos = dependency('wayland-protocols')
|
||||
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
|
||||
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
|
||||
giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or
|
||||
get_option('logind').enabled() or
|
||||
get_option('upower_glib').enabled() or
|
||||
get_option('mpris').enabled()))
|
||||
jsoncpp = dependency('jsoncpp', version : ['>=1.9.2'], fallback : ['jsoncpp', 'jsoncpp_dep'])
|
||||
sigcpp = dependency('sigc++-2.0')
|
||||
libinotify = dependency('libinotify', required: false)
|
||||
libepoll = dependency('epoll-shim', required: false)
|
||||
libinput = dependency('libinput', required: get_option('libinput'))
|
||||
libnl = dependency('libnl-3.0', required: get_option('libnl'))
|
||||
libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
|
||||
upower_glib = dependency('upower-glib', required: get_option('upower_glib'))
|
||||
playerctl = dependency('playerctl', version : ['>=2.0.0'], required: get_option('mpris'))
|
||||
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
|
||||
libudev = dependency('libudev', required: get_option('libudev'))
|
||||
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
||||
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
||||
xkbregistry = dependency('xkbregistry')
|
||||
libjack = dependency('jack', required: get_option('jack'))
|
||||
libwireplumber = dependency('wireplumber-0.4', required: get_option('wireplumber'))
|
||||
sigcpp = dependency('sigc++-3.0', version: ['>=3.4.0'])
|
||||
gtkmm = dependency('gtkmm-4.0', version : ['>=4.12.0'])
|
||||
giounix = dependency('gio-unix-2.0', version: ['>=2.76.4'])
|
||||
spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled'])
|
||||
|
||||
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||
if libsndio.found()
|
||||
if not compiler.has_function('sioctl_open', prefix: '#include <sndio.h>', dependencies: libsndio)
|
||||
if get_option('sndio').enabled()
|
||||
error('libsndio is too old, required >=1.7.0')
|
||||
else
|
||||
warning('libsndio is too old, required >=1.7.0')
|
||||
libsndio = dependency('', required: false)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
gtk_layer_shell = dependency('gtk-layer-shell-0',
|
||||
required: get_option('gtk-layer-shell'),
|
||||
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
|
||||
systemd = dependency('systemd', required: get_option('systemd'))
|
||||
|
||||
cpp_lib_chrono = compiler.compute_int('__cpp_lib_chrono', prefix : '#include <chrono>')
|
||||
have_chrono_timezones = cpp_lib_chrono >= 201907
|
||||
if have_chrono_timezones
|
||||
tz_dep = declare_dependency()
|
||||
else
|
||||
tz_dep = dependency('date',
|
||||
required: false,
|
||||
default_options : [ 'use_system_tzdb=true' ],
|
||||
modules : [ 'date::date', 'date::date-tz' ],
|
||||
fallback: [ 'date', 'tz_dep' ])
|
||||
endif
|
||||
gtk_layer_shell = dependency('gtk4-layer-shell-0',
|
||||
version : ['>=1.0.2'],
|
||||
fallback : ['gtk4-layer-shell', 'gtk_layer_shell'],
|
||||
default_options : ['introspection=false', 'vapi=false'])
|
||||
|
||||
prefix = get_option('prefix')
|
||||
sysconfdir = get_option('sysconfdir')
|
||||
@@ -143,269 +99,32 @@ conf_data.set('prefix', prefix)
|
||||
|
||||
add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'cpp')
|
||||
|
||||
if systemd.found()
|
||||
user_units_dir = systemd.get_pkgconfig_variable('systemduserunitdir')
|
||||
|
||||
configure_file(
|
||||
configuration: conf_data,
|
||||
input: './resources/waybar.service.in',
|
||||
output: '@BASENAME@',
|
||||
install_dir: user_units_dir
|
||||
)
|
||||
endif
|
||||
|
||||
src_files = files(
|
||||
'src/factory.cpp',
|
||||
'src/AModule.cpp',
|
||||
'src/ALabel.cpp',
|
||||
'src/AIconLabel.cpp',
|
||||
'src/AAppIconLabel.cpp',
|
||||
'src/modules/custom.cpp',
|
||||
'src/modules/disk.cpp',
|
||||
'src/modules/idle_inhibitor.cpp',
|
||||
'src/modules/image.cpp',
|
||||
'src/modules/load.cpp',
|
||||
'src/modules/temperature.cpp',
|
||||
'src/modules/user.cpp',
|
||||
'src/ASlider.cpp',
|
||||
'src/main.cpp',
|
||||
'src/bar.cpp',
|
||||
'src/client.cpp',
|
||||
'src/config.cpp',
|
||||
'src/group.cpp',
|
||||
'src/util/portal.cpp',
|
||||
'src/util/enum.cpp',
|
||||
'src/util/prepare_for_sleep.cpp',
|
||||
'src/util/ustring_clen.cpp',
|
||||
'src/util/sanitize_str.cpp',
|
||||
'src/util/rewrite_string.cpp',
|
||||
'src/util/gtk_icon.cpp',
|
||||
'src/util/regex_collection.cpp'
|
||||
'src/main.cpp',
|
||||
'src/client.cpp',
|
||||
'src/bar.cpp',
|
||||
'src/config.cpp',
|
||||
'src/util/portal.cpp',
|
||||
'src/util/prepare_for_sleep.cpp',
|
||||
# 'src/AModule.cpp',
|
||||
)
|
||||
|
||||
inc_dirs = ['include']
|
||||
|
||||
if is_linux
|
||||
add_project_arguments('-DHAVE_CPU_LINUX', language: 'cpp')
|
||||
add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp')
|
||||
src_files += files(
|
||||
'src/modules/battery.cpp',
|
||||
'src/modules/cpu.cpp',
|
||||
'src/modules/cpu_frequency/common.cpp',
|
||||
'src/modules/cpu_frequency/linux.cpp',
|
||||
'src/modules/cpu_usage/common.cpp',
|
||||
'src/modules/cpu_usage/linux.cpp',
|
||||
'src/modules/memory/common.cpp',
|
||||
'src/modules/memory/linux.cpp',
|
||||
)
|
||||
elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd
|
||||
add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp')
|
||||
add_project_arguments('-DHAVE_MEMORY_BSD', language: 'cpp')
|
||||
src_files += files(
|
||||
'src/modules/cpu.cpp',
|
||||
'src/modules/cpu_frequency/bsd.cpp',
|
||||
'src/modules/cpu_frequency/common.cpp',
|
||||
'src/modules/cpu_usage/bsd.cpp',
|
||||
'src/modules/cpu_usage/common.cpp',
|
||||
'src/modules/memory/bsd.cpp',
|
||||
'src/modules/memory/common.cpp',
|
||||
)
|
||||
if is_freebsd
|
||||
src_files += files(
|
||||
'src/modules/battery.cpp',
|
||||
)
|
||||
endif
|
||||
endif
|
||||
|
||||
add_project_arguments('-DHAVE_SWAY', language: 'cpp')
|
||||
src_files += [
|
||||
'src/modules/sway/ipc/client.cpp',
|
||||
'src/modules/sway/bar.cpp',
|
||||
'src/modules/sway/mode.cpp',
|
||||
'src/modules/sway/language.cpp',
|
||||
'src/modules/sway/window.cpp',
|
||||
'src/modules/sway/workspaces.cpp',
|
||||
'src/modules/sway/scratchpad.cpp'
|
||||
]
|
||||
|
||||
if true
|
||||
add_project_arguments('-DHAVE_WLR', language: 'cpp')
|
||||
src_files += 'src/modules/wlr/taskbar.cpp'
|
||||
src_files += 'src/modules/wlr/workspace_manager.cpp'
|
||||
src_files += 'src/modules/wlr/workspace_manager_binding.cpp'
|
||||
endif
|
||||
|
||||
if true
|
||||
add_project_arguments('-DHAVE_RIVER', language: 'cpp')
|
||||
src_files += 'src/modules/river/mode.cpp'
|
||||
src_files += 'src/modules/river/tags.cpp'
|
||||
src_files += 'src/modules/river/window.cpp'
|
||||
src_files += 'src/modules/river/layout.cpp'
|
||||
endif
|
||||
|
||||
if true
|
||||
add_project_arguments('-DHAVE_DWL', language: 'cpp')
|
||||
src_files += 'src/modules/dwl/tags.cpp'
|
||||
endif
|
||||
|
||||
if true
|
||||
add_project_arguments('-DHAVE_HYPRLAND', language: 'cpp')
|
||||
src_files += 'src/modules/hyprland/backend.cpp'
|
||||
src_files += 'src/modules/hyprland/window.cpp'
|
||||
src_files += 'src/modules/hyprland/language.cpp'
|
||||
src_files += 'src/modules/hyprland/submap.cpp'
|
||||
src_files += 'src/modules/hyprland/workspaces.cpp'
|
||||
endif
|
||||
|
||||
if libnl.found() and libnlgen.found()
|
||||
add_project_arguments('-DHAVE_LIBNL', language: 'cpp')
|
||||
src_files += 'src/modules/network.cpp'
|
||||
endif
|
||||
|
||||
if (giounix.found() and not get_option('logind').disabled())
|
||||
add_project_arguments('-DHAVE_GAMEMODE', language: 'cpp')
|
||||
src_files += 'src/modules/gamemode.cpp'
|
||||
endif
|
||||
|
||||
if (upower_glib.found() and giounix.found() and not get_option('logind').disabled())
|
||||
add_project_arguments('-DHAVE_UPOWER', language: 'cpp')
|
||||
src_files += 'src/modules/upower/upower.cpp'
|
||||
src_files += 'src/modules/upower/upower_tooltip.cpp'
|
||||
endif
|
||||
|
||||
if (playerctl.found() and giounix.found() and not get_option('logind').disabled())
|
||||
add_project_arguments('-DHAVE_MPRIS', language: 'cpp')
|
||||
src_files += 'src/modules/mpris/mpris.cpp'
|
||||
endif
|
||||
|
||||
if libpulse.found()
|
||||
add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp')
|
||||
src_files += 'src/modules/pulseaudio.cpp'
|
||||
src_files += 'src/modules/pulseaudio_slider.cpp'
|
||||
src_files += 'src/util/audio_backend.cpp'
|
||||
endif
|
||||
|
||||
if libjack.found()
|
||||
add_project_arguments('-DHAVE_LIBJACK', language: 'cpp')
|
||||
src_files += 'src/modules/jack.cpp'
|
||||
endif
|
||||
|
||||
if libwireplumber.found()
|
||||
add_project_arguments('-DHAVE_LIBWIREPLUMBER', language: 'cpp')
|
||||
src_files += 'src/modules/wireplumber.cpp'
|
||||
endif
|
||||
|
||||
if dbusmenu_gtk.found()
|
||||
add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp')
|
||||
src_files += files(
|
||||
'src/modules/sni/tray.cpp',
|
||||
'src/modules/sni/watcher.cpp',
|
||||
'src/modules/sni/host.cpp',
|
||||
'src/modules/sni/item.cpp'
|
||||
)
|
||||
endif
|
||||
|
||||
if libudev.found() and (is_linux or libepoll.found())
|
||||
add_project_arguments('-DHAVE_LIBUDEV', language: 'cpp')
|
||||
src_files += 'src/modules/backlight.cpp'
|
||||
src_files += 'src/modules/backlight_slider.cpp'
|
||||
src_files += 'src/util/backlight_backend.cpp'
|
||||
endif
|
||||
|
||||
if libevdev.found() and (is_linux or libepoll.found()) and libinput.found() and (is_linux or libinotify.found())
|
||||
add_project_arguments('-DHAVE_LIBEVDEV', language: 'cpp')
|
||||
add_project_arguments('-DHAVE_LIBINPUT', language: 'cpp')
|
||||
src_files += 'src/modules/keyboard_state.cpp'
|
||||
endif
|
||||
|
||||
if libmpdclient.found()
|
||||
add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp')
|
||||
src_files += 'src/modules/mpd/mpd.cpp'
|
||||
src_files += 'src/modules/mpd/state.cpp'
|
||||
endif
|
||||
|
||||
if gtk_layer_shell.found()
|
||||
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
|
||||
endif
|
||||
|
||||
if libsndio.found()
|
||||
add_project_arguments('-DHAVE_LIBSNDIO', language: 'cpp')
|
||||
src_files += 'src/modules/sndio.cpp'
|
||||
endif
|
||||
|
||||
if (giounix.found() and not get_option('logind').disabled())
|
||||
add_project_arguments('-DHAVE_GIO_UNIX', language: 'cpp')
|
||||
src_files += 'src/modules/inhibitor.cpp'
|
||||
src_files += 'src/modules/bluetooth.cpp'
|
||||
endif
|
||||
|
||||
if get_option('rfkill').enabled() and is_linux
|
||||
add_project_arguments('-DWANT_RFKILL', language: 'cpp')
|
||||
src_files += files(
|
||||
'src/util/rfkill.cpp'
|
||||
)
|
||||
endif
|
||||
|
||||
if have_chrono_timezones
|
||||
add_project_arguments('-DHAVE_CHRONO_TIMEZONES', language: 'cpp')
|
||||
src_files += 'src/modules/clock.cpp'
|
||||
elif tz_dep.found()
|
||||
add_project_arguments('-DHAVE_LIBDATE', language: 'cpp')
|
||||
src_files += 'src/modules/clock.cpp'
|
||||
else
|
||||
src_files += 'src/modules/simpleclock.cpp'
|
||||
endif
|
||||
|
||||
if get_option('experimental')
|
||||
add_project_arguments('-DUSE_EXPERIMENTAL', language: 'cpp')
|
||||
endif
|
||||
|
||||
cava = dependency('cava',
|
||||
version : '>=0.9.1',
|
||||
required: get_option('cava'),
|
||||
fallback : ['cava', 'cava_dep'],
|
||||
not_found_message: 'cava is not found. Building waybar without cava')
|
||||
|
||||
if cava.found()
|
||||
add_project_arguments('-DHAVE_LIBCAVA', language: 'cpp')
|
||||
src_files += 'src/modules/cava.cpp'
|
||||
endif
|
||||
|
||||
subdir('protocol')
|
||||
|
||||
executable(
|
||||
'waybar',
|
||||
src_files,
|
||||
[src_files],
|
||||
dependencies: [
|
||||
thread_dep,
|
||||
client_protos,
|
||||
wayland_client,
|
||||
fmt,
|
||||
spdlog,
|
||||
gtk_layer_shell,
|
||||
gtkmm,
|
||||
giounix,
|
||||
sigcpp,
|
||||
jsoncpp,
|
||||
wayland_client,
|
||||
wayland_cursor,
|
||||
gtkmm,
|
||||
dbusmenu_gtk,
|
||||
giounix,
|
||||
libinput,
|
||||
libnl,
|
||||
libnlgen,
|
||||
upower_glib,
|
||||
playerctl,
|
||||
libpulse,
|
||||
libjack,
|
||||
libwireplumber,
|
||||
libudev,
|
||||
libinotify,
|
||||
libepoll,
|
||||
libmpdclient,
|
||||
libevdev,
|
||||
gtk_layer_shell,
|
||||
libsndio,
|
||||
tz_dep,
|
||||
xkbregistry,
|
||||
cava
|
||||
client_protos,
|
||||
spdlog
|
||||
],
|
||||
include_directories: inc_dirs,
|
||||
install: true,
|
||||
@@ -416,110 +135,3 @@ install_data(
|
||||
'./resources/style.css',
|
||||
install_dir: sysconfdir + '/xdg/waybar'
|
||||
)
|
||||
|
||||
scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
|
||||
|
||||
if scdoc.found()
|
||||
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
|
||||
sh = find_program('sh', native: true)
|
||||
|
||||
main_manpage = configure_file(
|
||||
input: 'man/waybar.5.scd.in',
|
||||
output: 'waybar.5.scd',
|
||||
configuration: {
|
||||
'sysconfdir': join_paths(prefix, sysconfdir)
|
||||
}
|
||||
)
|
||||
|
||||
main_manpage_path = join_paths(meson.build_root(), '@0@'.format(main_manpage))
|
||||
|
||||
mandir = get_option('mandir')
|
||||
man_files = [
|
||||
main_manpage_path,
|
||||
'waybar-backlight.5.scd',
|
||||
'waybar-backlight-slider.5.scd',
|
||||
'waybar-battery.5.scd',
|
||||
'waybar-cava.5.scd',
|
||||
'waybar-clock.5.scd',
|
||||
'waybar-cpu.5.scd',
|
||||
'waybar-custom.5.scd',
|
||||
'waybar-disk.5.scd',
|
||||
'waybar-gamemode.5.scd',
|
||||
'waybar-idle-inhibitor.5.scd',
|
||||
'waybar-image.5.scd',
|
||||
'waybar-keyboard-state.5.scd',
|
||||
'waybar-memory.5.scd',
|
||||
'waybar-mpd.5.scd',
|
||||
'waybar-mpris.5.scd',
|
||||
'waybar-network.5.scd',
|
||||
'waybar-pulseaudio.5.scd',
|
||||
'waybar-pulseaudio-slider.5.scd',
|
||||
'waybar-river-mode.5.scd',
|
||||
'waybar-river-tags.5.scd',
|
||||
'waybar-river-window.5.scd',
|
||||
'waybar-river-layout.5.scd',
|
||||
'waybar-sway-language.5.scd',
|
||||
'waybar-sway-mode.5.scd',
|
||||
'waybar-sway-scratchpad.5.scd',
|
||||
'waybar-sway-window.5.scd',
|
||||
'waybar-sway-workspaces.5.scd',
|
||||
'waybar-temperature.5.scd',
|
||||
'waybar-tray.5.scd',
|
||||
'waybar-states.5.scd',
|
||||
'waybar-wlr-taskbar.5.scd',
|
||||
'waybar-wlr-workspaces.5.scd',
|
||||
'waybar-bluetooth.5.scd',
|
||||
'waybar-sndio.5.scd',
|
||||
'waybar-upower.5.scd',
|
||||
'waybar-wireplumber.5.scd',
|
||||
'waybar-dwl-tags.5.scd',
|
||||
]
|
||||
|
||||
if (giounix.found() and not get_option('logind').disabled())
|
||||
man_files += 'waybar-inhibitor.5.scd'
|
||||
endif
|
||||
|
||||
foreach file : man_files
|
||||
path = '@0@'.format(file)
|
||||
basename = path.split('/')[-1]
|
||||
|
||||
topic = basename.split('.')[-3]
|
||||
section = basename.split('.')[-2]
|
||||
output = '@0@.@1@'.format(topic, section)
|
||||
|
||||
custom_target(
|
||||
output,
|
||||
# drops the 'man' if `path` is an absolute path
|
||||
input: join_paths('man', path),
|
||||
output: output,
|
||||
command: [
|
||||
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
|
||||
],
|
||||
install: true,
|
||||
install_dir: '@0@/man@1@'.format(mandir, section)
|
||||
)
|
||||
endforeach
|
||||
endif
|
||||
|
||||
catch2 = dependency(
|
||||
'catch2',
|
||||
version: '>=2.0.0',
|
||||
fallback: ['catch2', 'catch2_dep'],
|
||||
required: get_option('tests'),
|
||||
)
|
||||
if catch2.found()
|
||||
subdir('test')
|
||||
endif
|
||||
|
||||
clangtidy = find_program('clang-tidy', required: false)
|
||||
|
||||
if clangtidy.found()
|
||||
run_target(
|
||||
'tidy',
|
||||
command: [
|
||||
clangtidy,
|
||||
'-checks=*,-fuchsia-default-arguments',
|
||||
'-p', meson.build_root()
|
||||
] + src_files)
|
||||
endif
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev s
|
||||
option('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features')
|
||||
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
|
||||
option('upower_glib', type: 'feature', value: 'auto', description: 'Enable support for upower')
|
||||
option('pipewire', type: 'feature', value: 'auto', description: 'Enable support for pipewire')
|
||||
option('mpris', type: 'feature', value: 'auto', description: 'Enable support for mpris')
|
||||
option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit')
|
||||
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
|
||||
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
||||
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
|
||||
option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support')
|
||||
option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL')
|
||||
option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio')
|
||||
option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind')
|
||||
|
||||
@@ -1,20 +1,30 @@
|
||||
{ lib
|
||||
, pkgs
|
||||
, waybar
|
||||
, version
|
||||
}:
|
||||
|
||||
waybar.overrideAttrs (prev: {
|
||||
let
|
||||
catch2_3 = {
|
||||
src = pkgs.fetchFromGitHub
|
||||
{
|
||||
owner = "catchorg";
|
||||
repo = "Catch2";
|
||||
rev = "v3.5.1";
|
||||
hash = "sha256-OyYNUfnu6h1+MfCF8O+awQ4Usad0qrdCtdZhYgOY+Vw=";
|
||||
};
|
||||
};
|
||||
in
|
||||
(waybar.overrideAttrs (oldAttrs: rec {
|
||||
inherit version;
|
||||
# version = "0.9.17";
|
||||
|
||||
src = lib.cleanSourceWith {
|
||||
filter = name: type:
|
||||
let
|
||||
baseName = baseNameOf (toString name);
|
||||
in
|
||||
! (
|
||||
lib.hasSuffix ".nix" baseName
|
||||
);
|
||||
filter = name: type: type != "regular" || !lib.hasSuffix ".nix" name;
|
||||
src = lib.cleanSource ../.;
|
||||
};
|
||||
})
|
||||
).override {
|
||||
catch2_3 = pkgs.catch2_3.overrideAttrs (oldAttrs: {
|
||||
version = "3.5.1";
|
||||
src = catch2_3.src;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ client_protocols = [
|
||||
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
|
||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
|
||||
['wlr-layer-shell-unstable-v1.xml'],
|
||||
['wlr-foreign-toplevel-management-unstable-v1.xml'],
|
||||
['ext-workspace-unstable-v1.xml'],
|
||||
['river-status-unstable-v1.xml'],
|
||||
|
||||
@@ -1,311 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_layer_shell_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2017 Drew DeVault
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this
|
||||
software and its documentation for any purpose is hereby granted
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that copyright notice and this permission
|
||||
notice appear in supporting documentation, and that the name of
|
||||
the copyright holders not be used in advertising or publicity
|
||||
pertaining to distribution of the software without specific,
|
||||
written prior permission. The copyright holders make no
|
||||
representations about the suitability of this software for any
|
||||
purpose. It is provided "as is" without express or implied
|
||||
warranty.
|
||||
|
||||
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<interface name="zwlr_layer_shell_v1" version="3">
|
||||
<description summary="create surfaces that are layers of the desktop">
|
||||
Clients can use this interface to assign the surface_layer role to
|
||||
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||
rendered with a defined z-depth respective to each other. They may also be
|
||||
anchored to the edges and corners of a screen and specify input handling
|
||||
semantics. This interface should be suitable for the implementation of
|
||||
many desktop shell components, and a broad number of other applications
|
||||
that interact with the desktop.
|
||||
</description>
|
||||
|
||||
<request name="get_layer_surface">
|
||||
<description summary="create a layer_surface from a surface">
|
||||
Create a layer surface for an existing surface. This assigns the role of
|
||||
layer_surface, or raises a protocol error if another role is already
|
||||
assigned.
|
||||
|
||||
Creating a layer surface from a wl_surface which has a buffer attached
|
||||
or committed is a client error, and any attempts by a client to attach
|
||||
or manipulate a buffer prior to the first layer_surface.configure call
|
||||
must also be treated as errors.
|
||||
|
||||
You may pass NULL for output to allow the compositor to decide which
|
||||
output to use. Generally this will be the one that the user most
|
||||
recently interacted with.
|
||||
|
||||
Clients can specify a namespace that defines the purpose of the layer
|
||||
surface.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
|
||||
<arg name="np" type="string" summary="namespace for the layer surface"/>
|
||||
</request>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="role" value="0" summary="wl_surface has another role"/>
|
||||
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
|
||||
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
|
||||
</enum>
|
||||
|
||||
<enum name="layer">
|
||||
<description summary="available layers for surfaces">
|
||||
These values indicate which layers a surface can be rendered in. They
|
||||
are ordered by z depth, bottom-most first. Traditional shell surfaces
|
||||
will typically be rendered between the bottom and top layers.
|
||||
Fullscreen shell surfaces are typically rendered at the top layer.
|
||||
Multiple surfaces can share a single layer, and ordering within a
|
||||
single layer is undefined.
|
||||
</description>
|
||||
|
||||
<entry name="background" value="0"/>
|
||||
<entry name="bottom" value="1"/>
|
||||
<entry name="top" value="2"/>
|
||||
<entry name="overlay" value="3"/>
|
||||
</enum>
|
||||
|
||||
<!-- Version 3 additions -->
|
||||
|
||||
<request name="destroy" type="destructor" since="3">
|
||||
<description summary="destroy the layer_shell object">
|
||||
This request indicates that the client will not use the layer_shell
|
||||
object any more. Objects that have been created through this instance
|
||||
are not affected.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_layer_surface_v1" version="3">
|
||||
<description summary="layer metadata interface">
|
||||
An interface that may be implemented by a wl_surface, for surfaces that
|
||||
are designed to be rendered as a layer of a stacked desktop-like
|
||||
environment.
|
||||
|
||||
Layer surface state (layer, size, anchor, exclusive zone,
|
||||
margin, interactivity) is double-buffered, and will be applied at the
|
||||
time wl_surface.commit of the corresponding wl_surface is called.
|
||||
</description>
|
||||
|
||||
<request name="set_size">
|
||||
<description summary="sets the size of the surface">
|
||||
Sets the size of the surface in surface-local coordinates. The
|
||||
compositor will display the surface centered with respect to its
|
||||
anchors.
|
||||
|
||||
If you pass 0 for either value, the compositor will assign it and
|
||||
inform you of the assignment in the configure event. You must set your
|
||||
anchor to opposite edges in the dimensions you omit; not doing so is a
|
||||
protocol error. Both values are 0 by default.
|
||||
|
||||
Size is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="width" type="uint"/>
|
||||
<arg name="height" type="uint"/>
|
||||
</request>
|
||||
|
||||
<request name="set_anchor">
|
||||
<description summary="configures the anchor point of the surface">
|
||||
Requests that the compositor anchor the surface to the specified edges
|
||||
and corners. If two orthogonal edges are specified (e.g. 'top' and
|
||||
'left'), then the anchor point will be the intersection of the edges
|
||||
(e.g. the top left corner of the output); otherwise the anchor point
|
||||
will be centered on that edge, or in the center if none is specified.
|
||||
|
||||
Anchor is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="anchor" type="uint" enum="anchor"/>
|
||||
</request>
|
||||
|
||||
<request name="set_exclusive_zone">
|
||||
<description summary="configures the exclusive geometry of this surface">
|
||||
Requests that the compositor avoids occluding an area with other
|
||||
surfaces. The compositor's use of this information is
|
||||
implementation-dependent - do not assume that this region will not
|
||||
actually be occluded.
|
||||
|
||||
A positive value is only meaningful if the surface is anchored to one
|
||||
edge or an edge and both perpendicular edges. If the surface is not
|
||||
anchored, anchored to only two perpendicular edges (a corner), anchored
|
||||
to only two parallel edges or anchored to all edges, a positive value
|
||||
will be treated the same as zero.
|
||||
|
||||
A positive zone is the distance from the edge in surface-local
|
||||
coordinates to consider exclusive.
|
||||
|
||||
Surfaces that do not wish to have an exclusive zone may instead specify
|
||||
how they should interact with surfaces that do. If set to zero, the
|
||||
surface indicates that it would like to be moved to avoid occluding
|
||||
surfaces with a positive exclusive zone. If set to -1, the surface
|
||||
indicates that it would not like to be moved to accommodate for other
|
||||
surfaces, and the compositor should extend it all the way to the edges
|
||||
it is anchored to.
|
||||
|
||||
For example, a panel might set its exclusive zone to 10, so that
|
||||
maximized shell surfaces are not shown on top of it. A notification
|
||||
might set its exclusive zone to 0, so that it is moved to avoid
|
||||
occluding the panel, but shell surfaces are shown underneath it. A
|
||||
wallpaper or lock screen might set their exclusive zone to -1, so that
|
||||
they stretch below or over the panel.
|
||||
|
||||
The default value is 0.
|
||||
|
||||
Exclusive zone is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="zone" type="int"/>
|
||||
</request>
|
||||
|
||||
<request name="set_margin">
|
||||
<description summary="sets a margin from the anchor point">
|
||||
Requests that the surface be placed some distance away from the anchor
|
||||
point on the output, in surface-local coordinates. Setting this value
|
||||
for edges you are not anchored to has no effect.
|
||||
|
||||
The exclusive zone includes the margin.
|
||||
|
||||
Margin is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="top" type="int"/>
|
||||
<arg name="right" type="int"/>
|
||||
<arg name="bottom" type="int"/>
|
||||
<arg name="left" type="int"/>
|
||||
</request>
|
||||
|
||||
<request name="set_keyboard_interactivity">
|
||||
<description summary="requests keyboard events">
|
||||
Set to 1 to request that the seat send keyboard events to this layer
|
||||
surface. For layers below the shell surface layer, the seat will use
|
||||
normal focus semantics. For layers above the shell surface layers, the
|
||||
seat will always give exclusive keyboard focus to the top-most layer
|
||||
which has keyboard interactivity set to true.
|
||||
|
||||
Layer surfaces receive pointer, touch, and tablet events normally. If
|
||||
you do not want to receive them, set the input region on your surface
|
||||
to an empty region.
|
||||
|
||||
Events is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="keyboard_interactivity" type="uint"/>
|
||||
</request>
|
||||
|
||||
<request name="get_popup">
|
||||
<description summary="assign this layer_surface as an xdg_popup parent">
|
||||
This assigns an xdg_popup's parent to this layer_surface. This popup
|
||||
should have been created via xdg_surface::get_popup with the parent set
|
||||
to NULL, and this request must be invoked before committing the popup's
|
||||
initial state.
|
||||
|
||||
See the documentation of xdg_popup for more details about what an
|
||||
xdg_popup is and how it is used.
|
||||
</description>
|
||||
<arg name="popup" type="object" interface="xdg_popup"/>
|
||||
</request>
|
||||
|
||||
<request name="ack_configure">
|
||||
<description summary="ack a configure event">
|
||||
When a configure event is received, if a client commits the
|
||||
surface in response to the configure event, then the client
|
||||
must make an ack_configure request sometime before the commit
|
||||
request, passing along the serial of the configure event.
|
||||
|
||||
If the client receives multiple configure events before it
|
||||
can respond to one, it only has to ack the last configure event.
|
||||
|
||||
A client is not required to commit immediately after sending
|
||||
an ack_configure request - it may even ack_configure several times
|
||||
before its next surface commit.
|
||||
|
||||
A client may send multiple ack_configure requests before committing, but
|
||||
only the last request sent before a commit indicates which configure
|
||||
event the client really is responding to.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="the serial from the configure event"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the layer_surface">
|
||||
This request destroys the layer surface.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="configure">
|
||||
<description summary="suggest a surface change">
|
||||
The configure event asks the client to resize its surface.
|
||||
|
||||
Clients should arrange their surface for the new states, and then send
|
||||
an ack_configure request with the serial sent in this configure event at
|
||||
some point before committing the new surface.
|
||||
|
||||
The client is free to dismiss all but the last configure event it
|
||||
received.
|
||||
|
||||
The width and height arguments specify the size of the window in
|
||||
surface-local coordinates.
|
||||
|
||||
The size is a hint, in the sense that the client is free to ignore it if
|
||||
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
|
||||
resize in steps of NxM pixels). If the client picks a smaller size and
|
||||
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
|
||||
surface will be centered on this axis.
|
||||
|
||||
If the width or height arguments are zero, it means the client should
|
||||
decide its own window dimension.
|
||||
</description>
|
||||
<arg name="serial" type="uint"/>
|
||||
<arg name="width" type="uint"/>
|
||||
<arg name="height" type="uint"/>
|
||||
</event>
|
||||
|
||||
<event name="closed">
|
||||
<description summary="surface should be closed">
|
||||
The closed event is sent by the compositor when the surface will no
|
||||
longer be shown. The output may have been destroyed or the user may
|
||||
have asked for it to be removed. Further changes to the surface will be
|
||||
ignored. The client should destroy the resource after receiving this
|
||||
event, and create a new surface if they so choose.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
|
||||
<entry name="invalid_size" value="1" summary="size is invalid"/>
|
||||
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
|
||||
</enum>
|
||||
|
||||
<enum name="anchor" bitfield="true">
|
||||
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
|
||||
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
|
||||
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
||||
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
||||
</enum>
|
||||
|
||||
<!-- Version 2 additions -->
|
||||
|
||||
<request name="set_layer" since="2">
|
||||
<description summary="change the layer of the surface">
|
||||
Change the layer that the surface is rendered on.
|
||||
|
||||
Layer is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
||||
1
resources/custom_modules/cffi_example/.gitignore
vendored
Normal file
1
resources/custom_modules/cffi_example/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.cache/
|
||||
38
resources/custom_modules/cffi_example/README.md
Normal file
38
resources/custom_modules/cffi_example/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# C FFI module
|
||||
|
||||
A C FFI module is a dynamic library that exposes standard C functions and
|
||||
constants, that Waybar can load and execute to create custom advanced widgets.
|
||||
|
||||
Most language can implement the required functions and constants (C, C++, Rust,
|
||||
Go, Python, ...), meaning you can develop custom modules using your language of
|
||||
choice, as long as there's GTK bindings.
|
||||
|
||||
Symbols to implement are documented in the
|
||||
[waybar_cffi_module.h](waybar_cffi_module.h) file.
|
||||
|
||||
# Usage
|
||||
|
||||
## Building this module
|
||||
|
||||
```bash
|
||||
meson setup build
|
||||
meson compile -C build
|
||||
```
|
||||
|
||||
## Load the module
|
||||
|
||||
Edit your waybar config:
|
||||
```json
|
||||
{
|
||||
// ...
|
||||
"modules-center": [
|
||||
// ...
|
||||
"cffi/c_example"
|
||||
],
|
||||
// ...
|
||||
"cffi/c_example": {
|
||||
// Path to the compiled dynamic library file
|
||||
"module_path": "resources/custom_modules/cffi_example/build/wb_cffi_example.so"
|
||||
}
|
||||
}
|
||||
```
|
||||
70
resources/custom_modules/cffi_example/main.c
Normal file
70
resources/custom_modules/cffi_example/main.c
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
#include "waybar_cffi_module.h"
|
||||
|
||||
typedef struct {
|
||||
wbcffi_module* waybar_module;
|
||||
GtkBox* container;
|
||||
GtkButton* button;
|
||||
} ExampleMod;
|
||||
|
||||
// This static variable is shared between all instances of this module
|
||||
static int instance_count = 0;
|
||||
|
||||
void onclicked(GtkButton* button) {
|
||||
char text[256];
|
||||
snprintf(text, 256, "Dice throw result: %d", rand() % 6 + 1);
|
||||
gtk_button_set_label(button, text);
|
||||
}
|
||||
|
||||
// You must
|
||||
const size_t wbcffi_version = 1;
|
||||
|
||||
void* wbcffi_init(const wbcffi_init_info* init_info, const wbcffi_config_entry* config_entries,
|
||||
size_t config_entries_len) {
|
||||
printf("cffi_example: init config:\n");
|
||||
for (size_t i = 0; i < config_entries_len; i++) {
|
||||
printf(" %s = %s\n", config_entries[i].key, config_entries[i].value);
|
||||
}
|
||||
|
||||
// Allocate the instance object
|
||||
ExampleMod* inst = malloc(sizeof(ExampleMod));
|
||||
inst->waybar_module = init_info->obj;
|
||||
|
||||
GtkContainer* root = init_info->get_root_widget(init_info->obj);
|
||||
|
||||
// Add a container for displaying the next widgets
|
||||
inst->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5));
|
||||
gtk_container_add(GTK_CONTAINER(root), GTK_WIDGET(inst->container));
|
||||
|
||||
// Add a label
|
||||
GtkLabel* label = GTK_LABEL(gtk_label_new("[Example C FFI Module:"));
|
||||
gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(label));
|
||||
|
||||
// Add a button
|
||||
inst->button = GTK_BUTTON(gtk_button_new_with_label("click me !"));
|
||||
g_signal_connect(inst->button, "clicked", G_CALLBACK(onclicked), NULL);
|
||||
gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(inst->button));
|
||||
|
||||
// Add a label
|
||||
label = GTK_LABEL(gtk_label_new("]"));
|
||||
gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(label));
|
||||
|
||||
// Return instance object
|
||||
printf("cffi_example inst=%p: init success ! (%d total instances)\n", inst, ++instance_count);
|
||||
return inst;
|
||||
}
|
||||
|
||||
void wbcffi_deinit(void* instance) {
|
||||
printf("cffi_example inst=%p: free memory\n", instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void wbcffi_update(void* instance) { printf("cffi_example inst=%p: Update request\n", instance); }
|
||||
|
||||
void wbcffi_refresh(void* instance, int signal) {
|
||||
printf("cffi_example inst=%p: Received refresh signal %d\n", instance, signal);
|
||||
}
|
||||
|
||||
void wbcffi_doaction(void* instance, const char* name) {
|
||||
printf("cffi_example inst=%p: doAction(%s)\n", instance, name);
|
||||
}
|
||||
13
resources/custom_modules/cffi_example/meson.build
Normal file
13
resources/custom_modules/cffi_example/meson.build
Normal file
@@ -0,0 +1,13 @@
|
||||
project(
|
||||
'waybar_cffi_example', 'c',
|
||||
version: '0.1.0',
|
||||
license: 'MIT',
|
||||
)
|
||||
|
||||
shared_library('wb_cffi_example',
|
||||
['main.c'],
|
||||
dependencies: [
|
||||
dependency('gtk+-3.0', version : ['>=3.22.0'])
|
||||
],
|
||||
name_prefix: ''
|
||||
)
|
||||
89
resources/custom_modules/cffi_example/waybar_cffi_module.h
Normal file
89
resources/custom_modules/cffi_example/waybar_cffi_module.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// Waybar ABI version. 1 is the latest version
|
||||
extern const size_t wbcffi_version;
|
||||
|
||||
/// Private Waybar CFFI module
|
||||
typedef struct wbcffi_module wbcffi_module;
|
||||
|
||||
/// Waybar module information
|
||||
typedef struct {
|
||||
/// Waybar CFFI object pointer
|
||||
wbcffi_module* obj;
|
||||
|
||||
/// Waybar version string
|
||||
const char* waybar_version;
|
||||
|
||||
/// Returns the waybar widget allocated for this module
|
||||
/// @param obj Waybar CFFI object pointer
|
||||
GtkContainer* (*get_root_widget)(wbcffi_module* obj);
|
||||
|
||||
/// Queues a request for calling wbcffi_update() on the next GTK main event
|
||||
/// loop iteration
|
||||
/// @param obj Waybar CFFI object pointer
|
||||
void (*queue_update)(wbcffi_module*);
|
||||
} wbcffi_init_info;
|
||||
|
||||
/// Config key-value pair
|
||||
typedef struct {
|
||||
/// Entry key
|
||||
const char* key;
|
||||
/// Entry value as string. JSON object and arrays are serialized.
|
||||
const char* value;
|
||||
} wbcffi_config_entry;
|
||||
|
||||
/// Module init/new function, called on module instantiation
|
||||
///
|
||||
/// MANDATORY CFFI function
|
||||
///
|
||||
/// @param init_info Waybar module information
|
||||
/// @param config_entries Flat representation of the module JSON config. The data only available
|
||||
/// during wbcffi_init call.
|
||||
/// @param config_entries_len Number of entries in `config_entries`
|
||||
///
|
||||
/// @return A untyped pointer to module data, NULL if the module failed to load.
|
||||
void* wbcffi_init(const wbcffi_init_info* init_info, const wbcffi_config_entry* config_entries,
|
||||
size_t config_entries_len);
|
||||
|
||||
/// Module deinit/delete function, called when Waybar is closed or when the module is removed
|
||||
///
|
||||
/// MANDATORY CFFI function
|
||||
///
|
||||
/// @param instance Module instance data (as returned by `wbcffi_init`)
|
||||
void wbcffi_deinit(void* instance);
|
||||
|
||||
/// Called from the GTK main event loop, to update the UI
|
||||
///
|
||||
/// Optional CFFI function
|
||||
///
|
||||
/// @param instance Module instance data (as returned by `wbcffi_init`)
|
||||
/// @param action_name Action name
|
||||
void wbcffi_update(void* instance);
|
||||
|
||||
/// Called when Waybar receives a POSIX signal and forwards it to each module
|
||||
///
|
||||
/// Optional CFFI function
|
||||
///
|
||||
/// @param instance Module instance data (as returned by `wbcffi_init`)
|
||||
/// @param signal Signal ID
|
||||
void wbcffi_refresh(void* instance, int signal);
|
||||
|
||||
/// Called on module action (see
|
||||
/// https://github.com/Alexays/Waybar/wiki/Configuration#module-actions-config)
|
||||
///
|
||||
/// Optional CFFI function
|
||||
///
|
||||
/// @param instance Module instance data (as returned by `wbcffi_init`)
|
||||
/// @param action_name Action name
|
||||
void wbcffi_doaction(void* instance, const char* action_name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
6
resources/icons/meson.build
Normal file
6
resources/icons/meson.build
Normal file
@@ -0,0 +1,6 @@
|
||||
gnome = import('gnome')
|
||||
|
||||
app_resources += gnome.compile_resources('icon-resources',
|
||||
'waybar_icons.gresource.xml',
|
||||
c_name: 'waybar_icons'
|
||||
)
|
||||
4
resources/icons/waybar-privacy-audio-input-symbolic.svg
Normal file
4
resources/icons/waybar-privacy-audio-input-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m 8 0 c -1.660156 0 -3 1.339844 -3 3 v 5 c 0 1.660156 1.339844 3 3 3 s 3 -1.339844 3 -3 v -5 c 0 -1.660156 -1.339844 -3 -3 -3 z m -6 6 v 2.011719 c 0 2.964843 2.164062 5.429687 5 5.90625 v 2.082031 h 2 v -2.082031 c 2.835938 -0.476563 5 -2.941407 5 -5.90625 v -2.011719 h -1.5 v 2.011719 c 0 2.5 -1.992188 4.488281 -4.5 4.488281 s -4.5 -1.988281 -4.5 -4.488281 v -2.011719 z m 0 0" fill="#2e3436"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 546 B |
2
resources/icons/waybar-privacy-audio-output-symbolic.svg
Normal file
2
resources/icons/waybar-privacy-audio-output-symbolic.svg
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix color-interpolation-filters="sRGB" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.5"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.7"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.35"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><path d="m 1.972656 3.773438 c -0.132812 0.007812 -0.257812 0.070312 -0.34375 0.167968 c -1.066406 1.210938 -1.628906 2.585938 -1.628906 3.984375 c 0 1.402344 0.558594 2.8125 1.617188 4.105469 c 0.175781 0.214844 0.488281 0.246094 0.703124 0.070312 c 0.214844 -0.175781 0.246094 -0.488281 0.070313 -0.703124 c -0.945313 -1.152344 -1.390625 -2.332032 -1.390625 -3.472657 c 0 -1.136719 0.441406 -2.261719 1.378906 -3.324219 c 0.183594 -0.207031 0.164063 -0.523437 -0.042968 -0.707031 c -0.101563 -0.085937 -0.230469 -0.128906 -0.363282 -0.121093 z m 11.984375 0 c -0.105469 0.011718 -0.207031 0.054687 -0.285156 0.121093 c -0.207031 0.183594 -0.226563 0.5 -0.042969 0.710938 c 0.933594 1.058593 1.375 2.183593 1.375 3.320312 c 0 1.140625 -0.445312 2.320313 -1.386718 3.472657 c -0.175782 0.214843 -0.144532 0.527343 0.070312 0.703124 c 0.210938 0.175782 0.527344 0.144532 0.703125 -0.070312 c 1.058594 -1.292969 1.613281 -2.703125 1.613281 -4.101562 c 0 -1.402344 -0.558594 -2.777344 -1.625 -3.988282 c -0.109375 -0.121094 -0.265625 -0.183594 -0.421875 -0.167968 z m -8.101562 0.164062 c -0.480469 0.023438 -0.855469 0.417969 -0.855469 0.898438 v 6.359374 c 0 0.675782 0.742188 1.085938 1.3125 0.730469 l 5.265625 -3.246093 c 0.507813 -0.3125 0.507813 -1.046876 0.003906 -1.363282 l -5.203125 -3.246094 c -0.15625 -0.097656 -0.339844 -0.144531 -0.523437 -0.132812 z m -2.359375 1.050781 c -0.160156 -0.003906 -0.3125 0.066407 -0.410156 0.195313 c -0.679688 0.886718 -1.070313 1.824218 -1.078126 2.792968 c -0.011718 0.964844 0.363282 1.921876 1.085938 2.835938 c 0.167969 0.214844 0.484375 0.25 0.699219 0.078125 c 0.214843 -0.167969 0.253906 -0.484375 0.082031 -0.699219 c -0.617188 -0.777344 -0.875 -1.5 -0.871094 -2.207031 c 0.007813 -0.703125 0.289063 -1.425781 0.875 -2.191406 c 0.167969 -0.21875 0.128906 -0.53125 -0.09375 -0.703125 c -0.082031 -0.0625 -0.183594 -0.097656 -0.289062 -0.101563 z m 9.015625 0 c -0.109375 0.003907 -0.210938 0.039063 -0.292969 0.105469 c -0.21875 0.164062 -0.261719 0.480469 -0.09375 0.699219 c 0.585938 0.765625 0.867188 1.488281 0.875 2.195312 c 0.007812 0.703125 -0.253906 1.425781 -0.867188 2.203125 c -0.171874 0.214844 -0.136718 0.53125 0.082032 0.703125 c 0.214844 0.167969 0.527344 0.132813 0.699218 -0.082031 c 0.722657 -0.914062 1.097657 -1.871094 1.085938 -2.835938 c -0.011719 -0.96875 -0.398438 -1.90625 -1.078125 -2.792968 c -0.097656 -0.128906 -0.253906 -0.199219 -0.410156 -0.195313 z m 0 0" fill="#222222"/><g mask="url(#b)"><g clip-path="url(#c)" transform="matrix(1 0 0 1 -600 -1044)"><path d="m 550 182 c -0.351562 0.003906 -0.695312 0.101562 -1 0.28125 v 3.4375 c 0.304688 0.179688 0.648438 0.277344 1 0.28125 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 c -0.339844 0 -0.679688 0.058594 -1 0.175781 v 6.824219 h 4 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#d)"><g clip-path="url(#e)" transform="matrix(1 0 0 1 -600 -1044)"><path d="m 569 182 v 4 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 v 7 h 3 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#f)"><g clip-path="url(#g)" transform="matrix(1 0 0 1 -600 -1044)"><path d="m 573 182.269531 v 3.449219 c 0.613281 -0.355469 0.996094 -1.007812 1 -1.71875 c 0 -0.714844 -0.382812 -1.375 -1 -1.730469 z m 0 4.90625 v 6.824219 h 2 v -4 c 0 -1.269531 -0.800781 -2.402344 -2 -2.824219 z m 0 0"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
7
resources/icons/waybar-privacy-screen-share-symbolic.svg
Normal file
7
resources/icons/waybar-privacy-screen-share-symbolic.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#2e3436">
|
||||
<path d="m 3 0 c -1.660156 0 -3 1.339844 -3 3 v 7 c 0 1.660156 1.339844 3 3 3 h 10 c 1.660156 0 3 -1.339844 3 -3 v -1 c 0 -0.550781 -0.449219 -1 -1 -1 s -1 0.449219 -1 1 v 1 c 0 0.554688 -0.445312 1 -1 1 h -10 c -0.554688 0 -1 -0.445312 -1 -1 v -7 c 0 -0.554688 0.445312 -1 1 -1 h 4 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 7 0 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 h 2.585938 l -5.292969 5.289062 c -0.1875 0.191407 -0.292969 0.445313 -0.292969 0.710938 s 0.105469 0.519531 0.292969 0.707031 c 0.390625 0.390625 1.023437 0.390625 1.414062 0 l 5.292969 -5.292969 v 2.585938 c 0 0.550781 0.449219 1 1 1 s 1 -0.449219 1 -1 v -5 c 0 -0.085938 -0.011719 -0.171875 -0.035156 -0.257812 c -0.023438 -0.085938 -0.054688 -0.167969 -0.101563 -0.242188 c -0.042969 -0.074219 -0.09375 -0.144531 -0.15625 -0.207031 c 0 0 0 0 0 -0.003907 c -0.015625 -0.011718 -0.03125 -0.023437 -0.046875 -0.035156 c -0.054687 -0.046875 -0.117187 -0.089844 -0.183594 -0.128906 c -0.035156 -0.015625 -0.074218 -0.03125 -0.113281 -0.046875 c -0.050781 -0.0195312 -0.101562 -0.0351562 -0.15625 -0.0507812 c -0.039062 -0.0078126 -0.082031 -0.0117188 -0.121093 -0.015625 c -0.027344 -0.0039063 -0.058594 -0.00781255 -0.085938 -0.0117188 z m -5 14 c -1.105469 0 -2 0.894531 -2 2 h 10 c 0 -1.105469 -0.894531 -2 -2 -2 z m 0 0"/>
|
||||
<path d="m 3 3 v 7 h 10 v -4.171875 l -3.585938 3.585937 c -0.773437 0.773438 -2.054687 0.773438 -2.828124 0 c -0.773438 -0.773437 -0.773438 -2.058593 0 -2.832031 l 3.585937 -3.582031 z m 0 0" fill-opacity="0.34902"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
8
resources/icons/waybar_icons.gresource.xml
Normal file
8
resources/icons/waybar_icons.gresource.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/fr/arouillard/waybar/icons/scalable/actions">
|
||||
<file preprocess="xml-stripblanks">waybar-privacy-audio-input-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">waybar-privacy-audio-output-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks">waybar-privacy-screen-share-symbolic.svg</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
@@ -278,3 +278,24 @@ label:focus {
|
||||
#scratchpad.empty {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#privacy {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#privacy-item {
|
||||
padding: 0 5px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#privacy-item.screenshare {
|
||||
background-color: #cf5700;
|
||||
}
|
||||
|
||||
#privacy-item.audio-in {
|
||||
background-color: #1ca000;
|
||||
}
|
||||
|
||||
#privacy-item.audio-out {
|
||||
background-color: #0069d4;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
||||
if (!id.empty()) {
|
||||
label_.get_style_context()->add_class(id);
|
||||
}
|
||||
label_.get_style_context()->add_class(MODULE_CLASS);
|
||||
event_box_.add(label_);
|
||||
if (config_["max-length"].isUInt()) {
|
||||
label_.set_max_width_chars(config_["max-length"].asInt());
|
||||
|
||||
@@ -10,6 +10,7 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
||||
bool enable_click, bool enable_scroll)
|
||||
: name_(std::move(name)),
|
||||
config_(std::move(config)),
|
||||
isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true},
|
||||
distance_scrolled_y_(0.0),
|
||||
distance_scrolled_x_(0.0) {
|
||||
// Configure module action Map
|
||||
@@ -189,9 +190,7 @@ bool AModule::handleScroll(GdkEventScroll* e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AModule::tooltipEnabled() {
|
||||
return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true;
|
||||
}
|
||||
bool AModule::tooltipEnabled() { return isTooltip; }
|
||||
|
||||
AModule::operator Gtk::Widget&() { return event_box_; }
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ ASlider::ASlider(const Json::Value& config, const std::string& name, const std::
|
||||
if (!id.empty()) {
|
||||
scale_.get_style_context()->add_class(id);
|
||||
}
|
||||
scale_.get_style_context()->add_class(MODULE_CLASS);
|
||||
event_box_.add(scale_);
|
||||
scale_.signal_value_changed().connect(sigc::mem_fun(*this, &ASlider::onValueChanged));
|
||||
|
||||
|
||||
674
src/bar.cpp
674
src/bar.cpp
@@ -1,16 +1,11 @@
|
||||
#ifdef HAVE_GTK_LAYER_SHELL
|
||||
#include <gtk-layer-shell.h>
|
||||
#endif
|
||||
#include "bar.hpp"
|
||||
|
||||
#include <gtk4-layer-shell.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "bar.hpp"
|
||||
#include "client.hpp"
|
||||
#include "factory.hpp"
|
||||
#include "group.hpp"
|
||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||
//#include "factory.hpp"
|
||||
//#include "group.hpp"
|
||||
|
||||
#ifdef HAVE_SWAY
|
||||
#include "modules/sway/bar.hpp"
|
||||
@@ -25,9 +20,6 @@ static constexpr const char* MIN_WIDTH_MSG =
|
||||
|
||||
static constexpr const char* BAR_SIZE_MSG = "Bar configured (width: {}, height: {}) for output: {}";
|
||||
|
||||
static constexpr const char* SIZE_DEFINED =
|
||||
"{} size is defined in the config file so it will stay like that";
|
||||
|
||||
const Bar::bar_mode_map Bar::PRESET_MODES = { //
|
||||
{"default",
|
||||
{// Special mode to hold the global bar configuration
|
||||
@@ -60,8 +52,8 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { //
|
||||
.passthrough = true,
|
||||
.visible = true}}};
|
||||
|
||||
const std::string_view Bar::MODE_DEFAULT = "default";
|
||||
const std::string_view Bar::MODE_INVISIBLE = "invisible";
|
||||
const std::string Bar::MODE_DEFAULT = "default";
|
||||
const std::string Bar::MODE_INVISIBLE = "invisible";
|
||||
const std::string_view DEFAULT_BAR_ID = "bar-0";
|
||||
|
||||
/* Deserializer for enum bar_layer */
|
||||
@@ -93,11 +85,37 @@ void from_json(const Json::Value& j, bar_mode& m) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Deserializer for enum Gtk::PositionType */
|
||||
void from_json(const Json::Value& j, Gtk::PositionType& pos) {
|
||||
if (j == "left") {
|
||||
pos = Gtk::PositionType::LEFT;
|
||||
} else if (j == "right") {
|
||||
pos = Gtk::PositionType::RIGHT;
|
||||
} else if (j == "top") {
|
||||
pos = Gtk::PositionType::TOP;
|
||||
} else if (j == "bottom") {
|
||||
pos = Gtk::PositionType::BOTTOM;
|
||||
}
|
||||
}
|
||||
|
||||
Glib::ustring to_string(Gtk::PositionType pos) {
|
||||
switch (pos) {
|
||||
case Gtk::PositionType::LEFT:
|
||||
return "left";
|
||||
case Gtk::PositionType::RIGHT:
|
||||
return "right";
|
||||
case Gtk::PositionType::TOP:
|
||||
return "top";
|
||||
case Gtk::PositionType::BOTTOM:
|
||||
return "bottom";
|
||||
}
|
||||
}
|
||||
|
||||
/* Deserializer for JSON Object -> map<string compatible type, Value>
|
||||
* Assumes that all the values in the object are deserializable to the same type.
|
||||
*/
|
||||
template <typename Key, typename Value,
|
||||
typename = std::enable_if_t<std::is_convertible<std::string_view, Key>::value>>
|
||||
typename = std::enable_if_t<std::is_convertible<std::string, Key>::value>>
|
||||
void from_json(const Json::Value& j, std::map<Key, Value>& m) {
|
||||
if (j.isObject()) {
|
||||
for (auto it = j.begin(); it != j.end(); ++it) {
|
||||
@@ -106,404 +124,37 @@ void from_json(const Json::Value& j, std::map<Key, Value>& m) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_GTK_LAYER_SHELL
|
||||
struct GLSSurfaceImpl : public BarSurface, public sigc::trackable {
|
||||
GLSSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} {
|
||||
output_name_ = output.name;
|
||||
// this has to be executed before GtkWindow.realize
|
||||
gtk_layer_init_for_window(window_.gobj());
|
||||
gtk_layer_set_keyboard_interactivity(window.gobj(), FALSE);
|
||||
gtk_layer_set_monitor(window_.gobj(), output.monitor->gobj());
|
||||
gtk_layer_set_namespace(window_.gobj(), "waybar");
|
||||
|
||||
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &GLSSurfaceImpl::onMap));
|
||||
window.signal_configure_event().connect_notify(
|
||||
sigc::mem_fun(*this, &GLSSurfaceImpl::onConfigure));
|
||||
}
|
||||
|
||||
void setExclusiveZone(bool enable) override {
|
||||
if (enable) {
|
||||
gtk_layer_auto_exclusive_zone_enable(window_.gobj());
|
||||
} else {
|
||||
gtk_layer_set_exclusive_zone(window_.gobj(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void setMargins(const struct bar_margins& margins) override {
|
||||
gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_LEFT, margins.left);
|
||||
gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, margins.right);
|
||||
gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_TOP, margins.top);
|
||||
gtk_layer_set_margin(window_.gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, margins.bottom);
|
||||
}
|
||||
|
||||
void setLayer(bar_layer value) override {
|
||||
auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM;
|
||||
if (value == bar_layer::TOP) {
|
||||
layer = GTK_LAYER_SHELL_LAYER_TOP;
|
||||
} else if (value == bar_layer::OVERLAY) {
|
||||
layer = GTK_LAYER_SHELL_LAYER_OVERLAY;
|
||||
}
|
||||
gtk_layer_set_layer(window_.gobj(), layer);
|
||||
}
|
||||
|
||||
void setPassThrough(bool enable) override {
|
||||
passthrough_ = enable;
|
||||
auto gdk_window = window_.get_window();
|
||||
if (gdk_window) {
|
||||
Cairo::RefPtr<Cairo::Region> region;
|
||||
if (enable) {
|
||||
region = Cairo::Region::create();
|
||||
}
|
||||
gdk_window->input_shape_combine_region(region, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void setPosition(const std::string_view& position) override {
|
||||
auto unanchored = GTK_LAYER_SHELL_EDGE_BOTTOM;
|
||||
vertical_ = false;
|
||||
if (position == "bottom") {
|
||||
unanchored = GTK_LAYER_SHELL_EDGE_TOP;
|
||||
} else if (position == "left") {
|
||||
unanchored = GTK_LAYER_SHELL_EDGE_RIGHT;
|
||||
vertical_ = true;
|
||||
} else if (position == "right") {
|
||||
vertical_ = true;
|
||||
unanchored = GTK_LAYER_SHELL_EDGE_LEFT;
|
||||
}
|
||||
for (auto edge : {GTK_LAYER_SHELL_EDGE_LEFT, GTK_LAYER_SHELL_EDGE_RIGHT,
|
||||
GTK_LAYER_SHELL_EDGE_TOP, GTK_LAYER_SHELL_EDGE_BOTTOM}) {
|
||||
gtk_layer_set_anchor(window_.gobj(), edge, unanchored != edge);
|
||||
}
|
||||
|
||||
// Disable anchoring for other edges too if the width
|
||||
// or the height has been set to a value other than 'auto'
|
||||
// otherwise the bar will use all space
|
||||
if (vertical_ && height_ > 1) {
|
||||
gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, false);
|
||||
gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_TOP, false);
|
||||
} else if (!vertical_ && width_ > 1) {
|
||||
gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_LEFT, false);
|
||||
gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, false);
|
||||
}
|
||||
}
|
||||
|
||||
void setSize(uint32_t width, uint32_t height) override {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
window_.set_size_request(width_, height_);
|
||||
};
|
||||
|
||||
private:
|
||||
Gtk::Window& window_;
|
||||
std::string output_name_;
|
||||
uint32_t width_;
|
||||
uint32_t height_;
|
||||
bool passthrough_ = false;
|
||||
bool vertical_ = false;
|
||||
|
||||
void onMap(GdkEventAny* ev) { setPassThrough(passthrough_); }
|
||||
|
||||
void onConfigure(GdkEventConfigure* ev) {
|
||||
/*
|
||||
* GTK wants new size for the window.
|
||||
* Actual resizing and management of the exclusve zone is handled within the gtk-layer-shell
|
||||
* code. This event handler only updates stored size of the window and prints some warnings.
|
||||
*
|
||||
* Note: forced resizing to a window smaller than required by GTK would not work with
|
||||
* gtk-layer-shell.
|
||||
*/
|
||||
if (vertical_) {
|
||||
if (width_ > 1 && ev->width > static_cast<int>(width_)) {
|
||||
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
|
||||
}
|
||||
} else {
|
||||
if (height_ > 1 && ev->height > static_cast<int>(height_)) {
|
||||
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
|
||||
}
|
||||
}
|
||||
width_ = ev->width;
|
||||
height_ = ev->height;
|
||||
spdlog::info(BAR_SIZE_MSG, width_, height_, output_name_);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
struct RawSurfaceImpl : public BarSurface, public sigc::trackable {
|
||||
RawSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} {
|
||||
output_ = gdk_wayland_monitor_get_wl_output(output.monitor->gobj());
|
||||
output_name_ = output.name;
|
||||
|
||||
window.signal_realize().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::onRealize));
|
||||
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &RawSurfaceImpl::onMap));
|
||||
window.signal_configure_event().connect_notify(
|
||||
sigc::mem_fun(*this, &RawSurfaceImpl::onConfigure));
|
||||
|
||||
if (window.get_realized()) {
|
||||
onRealize();
|
||||
}
|
||||
}
|
||||
|
||||
void setExclusiveZone(bool enable) override {
|
||||
exclusive_zone_ = enable;
|
||||
if (layer_surface_) {
|
||||
auto zone = 0;
|
||||
if (enable) {
|
||||
// exclusive zone already includes margin for anchored edge,
|
||||
// only opposite margin should be added
|
||||
if ((anchor_ & VERTICAL_ANCHOR) == VERTICAL_ANCHOR) {
|
||||
zone += width_;
|
||||
zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) ? margins_.right : margins_.left;
|
||||
} else {
|
||||
zone += height_;
|
||||
zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) ? margins_.bottom : margins_.top;
|
||||
}
|
||||
}
|
||||
spdlog::debug("Set exclusive zone {} for output {}", zone, output_name_);
|
||||
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_.get(), zone);
|
||||
}
|
||||
}
|
||||
|
||||
void setLayer(bar_layer layer) override {
|
||||
layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
|
||||
if (layer == bar_layer::TOP) {
|
||||
layer_ = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
|
||||
} else if (layer == bar_layer::OVERLAY) {
|
||||
layer_ = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
|
||||
}
|
||||
// updating already mapped window
|
||||
if (layer_surface_) {
|
||||
if (zwlr_layer_surface_v1_get_version(layer_surface_.get()) >=
|
||||
ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION) {
|
||||
zwlr_layer_surface_v1_set_layer(layer_surface_.get(), layer_);
|
||||
} else {
|
||||
spdlog::warn("Unable to change layer: layer-shell implementation is too old");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setMargins(const struct bar_margins& margins) override {
|
||||
margins_ = margins;
|
||||
// updating already mapped window
|
||||
if (layer_surface_) {
|
||||
zwlr_layer_surface_v1_set_margin(layer_surface_.get(), margins_.top, margins_.right,
|
||||
margins_.bottom, margins_.left);
|
||||
}
|
||||
}
|
||||
|
||||
void setPassThrough(bool enable) override {
|
||||
passthrough_ = enable;
|
||||
/* GTK overwrites any region changes applied directly to the wl_surface,
|
||||
* thus the same GTK region API as in the GLS impl has to be used. */
|
||||
auto gdk_window = window_.get_window();
|
||||
if (gdk_window) {
|
||||
Cairo::RefPtr<Cairo::Region> region;
|
||||
if (enable) {
|
||||
region = Cairo::Region::create();
|
||||
}
|
||||
gdk_window->input_shape_combine_region(region, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void setPosition(const std::string_view& position) override {
|
||||
anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
||||
if (position == "bottom") {
|
||||
anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
||||
} else if (position == "left") {
|
||||
anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
|
||||
} else if (position == "right") {
|
||||
anchor_ = VERTICAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
||||
}
|
||||
|
||||
// updating already mapped window
|
||||
if (layer_surface_) {
|
||||
zwlr_layer_surface_v1_set_anchor(layer_surface_.get(), anchor_);
|
||||
}
|
||||
}
|
||||
|
||||
void setSize(uint32_t width, uint32_t height) override {
|
||||
configured_width_ = width_ = width;
|
||||
configured_height_ = height_ = height;
|
||||
// layer_shell.configure handler should update exclusive zone if size changes
|
||||
window_.set_size_request(width, height);
|
||||
};
|
||||
|
||||
void commit() override {
|
||||
if (surface_) {
|
||||
wl_surface_commit(surface_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr static uint8_t VERTICAL_ANCHOR =
|
||||
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
||||
constexpr static uint8_t HORIZONTAL_ANCHOR =
|
||||
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
||||
|
||||
template <auto fn>
|
||||
using deleter_fn = std::integral_constant<decltype(fn), fn>;
|
||||
using layer_surface_ptr =
|
||||
std::unique_ptr<zwlr_layer_surface_v1, deleter_fn<zwlr_layer_surface_v1_destroy>>;
|
||||
|
||||
Gtk::Window& window_;
|
||||
std::string output_name_;
|
||||
uint32_t configured_width_ = 0;
|
||||
uint32_t configured_height_ = 0;
|
||||
uint32_t width_ = 0;
|
||||
uint32_t height_ = 0;
|
||||
uint8_t anchor_ = HORIZONTAL_ANCHOR | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
||||
bool exclusive_zone_ = true;
|
||||
bool passthrough_ = false;
|
||||
struct bar_margins margins_;
|
||||
|
||||
zwlr_layer_shell_v1_layer layer_ = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
|
||||
struct wl_output* output_ = nullptr; // owned by GTK
|
||||
struct wl_surface* surface_ = nullptr; // owned by GTK
|
||||
layer_surface_ptr layer_surface_;
|
||||
|
||||
void onRealize() {
|
||||
auto gdk_window = window_.get_window()->gobj();
|
||||
gdk_wayland_window_set_use_custom_surface(gdk_window);
|
||||
}
|
||||
|
||||
void onMap(GdkEventAny* ev) {
|
||||
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
|
||||
.configure = onSurfaceConfigure,
|
||||
.closed = onSurfaceClosed,
|
||||
};
|
||||
auto client = Client::inst();
|
||||
auto gdk_window = window_.get_window()->gobj();
|
||||
surface_ = gdk_wayland_window_get_wl_surface(gdk_window);
|
||||
|
||||
layer_surface_.reset(zwlr_layer_shell_v1_get_layer_surface(client->layer_shell, surface_,
|
||||
output_, layer_, "waybar"));
|
||||
|
||||
zwlr_layer_surface_v1_add_listener(layer_surface_.get(), &layer_surface_listener, this);
|
||||
zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_.get(), false);
|
||||
zwlr_layer_surface_v1_set_anchor(layer_surface_.get(), anchor_);
|
||||
zwlr_layer_surface_v1_set_margin(layer_surface_.get(), margins_.top, margins_.right,
|
||||
margins_.bottom, margins_.left);
|
||||
|
||||
setSurfaceSize(width_, height_);
|
||||
setExclusiveZone(exclusive_zone_);
|
||||
setPassThrough(passthrough_);
|
||||
|
||||
commit();
|
||||
wl_display_roundtrip(client->wl_display);
|
||||
}
|
||||
|
||||
void onConfigure(GdkEventConfigure* ev) {
|
||||
/*
|
||||
* GTK wants new size for the window.
|
||||
*
|
||||
* Prefer configured size if it's non-default.
|
||||
* If the size is not set and the window is smaller than requested by GTK, request resize from
|
||||
* layer surface.
|
||||
*/
|
||||
auto tmp_height = height_;
|
||||
auto tmp_width = width_;
|
||||
if (ev->height > static_cast<int>(height_)) {
|
||||
// Default minimal value
|
||||
if (height_ > 1) {
|
||||
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
|
||||
}
|
||||
if (configured_height_ > 1) {
|
||||
spdlog::info(SIZE_DEFINED, "Height");
|
||||
} else {
|
||||
tmp_height = ev->height;
|
||||
}
|
||||
}
|
||||
if (ev->width > static_cast<int>(width_)) {
|
||||
// Default minimal value
|
||||
if (width_ > 1) {
|
||||
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
|
||||
}
|
||||
if (configured_width_ > 1) {
|
||||
spdlog::info(SIZE_DEFINED, "Width");
|
||||
} else {
|
||||
tmp_width = ev->width;
|
||||
}
|
||||
}
|
||||
if (tmp_width != width_ || tmp_height != height_) {
|
||||
setSurfaceSize(tmp_width, tmp_height);
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
void setSurfaceSize(uint32_t width, uint32_t height) {
|
||||
/* If the client is anchored to two opposite edges, layer_surface.configure will return
|
||||
* size without margins for the axis.
|
||||
* layer_surface.set_size, however, expects size with margins for the anchored axis.
|
||||
* This is not specified by wlr-layer-shell and based on actual behavior of sway.
|
||||
*
|
||||
* If the size for unanchored axis is not set (0), change request to 1 to avoid automatic
|
||||
* assignment by the compositor.
|
||||
*/
|
||||
if ((anchor_ & VERTICAL_ANCHOR) == VERTICAL_ANCHOR) {
|
||||
width = width > 0 ? width : 1;
|
||||
if (height > 1) {
|
||||
height += margins_.top + margins_.bottom;
|
||||
}
|
||||
} else {
|
||||
height = height > 0 ? height : 1;
|
||||
if (width > 1) {
|
||||
width += margins_.right + margins_.left;
|
||||
}
|
||||
}
|
||||
spdlog::debug("Set surface size {}x{} for output {}", width, height, output_name_);
|
||||
zwlr_layer_surface_v1_set_size(layer_surface_.get(), width, height);
|
||||
}
|
||||
|
||||
static void onSurfaceConfigure(void* data, struct zwlr_layer_surface_v1* surface, uint32_t serial,
|
||||
uint32_t width, uint32_t height) {
|
||||
auto o = static_cast<RawSurfaceImpl*>(data);
|
||||
if (width != o->width_ || height != o->height_) {
|
||||
o->width_ = width;
|
||||
o->height_ = height;
|
||||
o->window_.set_size_request(o->width_, o->height_);
|
||||
o->window_.resize(o->width_, o->height_);
|
||||
o->setExclusiveZone(o->exclusive_zone_);
|
||||
spdlog::info(BAR_SIZE_MSG, o->width_ == 1 ? "auto" : std::to_string(o->width_),
|
||||
o->height_ == 1 ? "auto" : std::to_string(o->height_), o->output_name_);
|
||||
o->commit();
|
||||
}
|
||||
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
||||
}
|
||||
|
||||
static void onSurfaceClosed(void* data, struct zwlr_layer_surface_v1* /* surface */) {
|
||||
auto o = static_cast<RawSurfaceImpl*>(data);
|
||||
o->layer_surface_.reset();
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace waybar
|
||||
|
||||
waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||
: output(w_output),
|
||||
config(w_config),
|
||||
window{Gtk::WindowType::WINDOW_TOPLEVEL},
|
||||
window{Gtk::Window()},
|
||||
x_global(0),
|
||||
y_global(0),
|
||||
margins_{.top = 0, .right = 0, .bottom = 0, .left = 0},
|
||||
left_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||
center_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||
right_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||
box_(Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||
left_(Gtk::Orientation::HORIZONTAL, 0),
|
||||
center_(Gtk::Orientation::HORIZONTAL, 0),
|
||||
right_(Gtk::Orientation::HORIZONTAL, 0),
|
||||
box_{} {
|
||||
window.set_title("waybar");
|
||||
window.set_name("waybar");
|
||||
window.set_decorated(false);
|
||||
window.set_child(box_);
|
||||
window.get_style_context()->add_class(output->name);
|
||||
window.get_style_context()->add_class(config["name"].asString());
|
||||
window.get_style_context()->add_class(config["position"].asString());
|
||||
|
||||
auto position = config["position"].asString();
|
||||
from_json(config["position"], position);
|
||||
orientation = (position == Gtk::PositionType::LEFT || position == Gtk::PositionType::RIGHT)
|
||||
? Gtk::Orientation::VERTICAL
|
||||
: Gtk::Orientation::HORIZONTAL;
|
||||
|
||||
if (position == "right" || position == "left") {
|
||||
left_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
|
||||
center_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
|
||||
right_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
|
||||
box_ = Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0);
|
||||
vertical = true;
|
||||
}
|
||||
window.get_style_context()->add_class(to_string(position));
|
||||
|
||||
left_.set_orientation(orientation);
|
||||
center_.set_orientation(orientation);
|
||||
right_.set_orientation(orientation);
|
||||
box_.set_orientation(orientation);
|
||||
|
||||
left_.get_style_context()->add_class("modules-left");
|
||||
center_.get_style_context()->add_class("modules-center");
|
||||
@@ -516,8 +167,8 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||
right_.set_spacing(spacing);
|
||||
}
|
||||
|
||||
uint32_t height = config["height"].isUInt() ? config["height"].asUInt() : 0;
|
||||
uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0;
|
||||
height_ = config["height"].isUInt() ? config["height"].asUInt() : 0;
|
||||
width_ = config["width"].isUInt() ? config["width"].asUInt() : 0;
|
||||
|
||||
if (config["margin-top"].isInt() || config["margin-right"].isInt() ||
|
||||
config["margin-bottom"].isInt() || config["margin-left"].isInt()) {
|
||||
@@ -564,25 +215,26 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||
margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps};
|
||||
}
|
||||
|
||||
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
|
||||
output->monitor->property_geometry().signal_changed().connect(
|
||||
sigc::mem_fun(*this, &Bar::onOutputGeometryChanged));
|
||||
|
||||
#ifdef HAVE_GTK_LAYER_SHELL
|
||||
bool use_gls = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true;
|
||||
if (use_gls) {
|
||||
surface_impl_ = std::make_unique<GLSSurfaceImpl>(window, *output);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
surface_impl_ = std::make_unique<RawSurfaceImpl>(window, *output);
|
||||
}
|
||||
// this has to be executed before GtkWindow.realize
|
||||
auto* gtk_window = window.gobj();
|
||||
gtk_layer_init_for_window(gtk_window);
|
||||
gtk_layer_set_keyboard_mode(gtk_window, GTK_LAYER_SHELL_KEYBOARD_MODE_NONE);
|
||||
gtk_layer_set_monitor(gtk_window, output->monitor->gobj());
|
||||
gtk_layer_set_namespace(gtk_window, "waybar");
|
||||
|
||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, margins_.left);
|
||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right);
|
||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_TOP, margins_.top);
|
||||
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, margins_.bottom);
|
||||
|
||||
window.set_size_request(width_, height_);
|
||||
|
||||
surface_impl_->setMargins(margins_);
|
||||
surface_impl_->setSize(width, height);
|
||||
// Position needs to be set after calculating the height due to the
|
||||
// GTK layer shell anchors logic relying on the dimensions of the bar.
|
||||
surface_impl_->setPosition(position);
|
||||
setPosition(position);
|
||||
|
||||
/* Read custom modes if available */
|
||||
if (auto modes = config.get("modes", {}); modes.isObject()) {
|
||||
@@ -602,7 +254,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
||||
window.signal_map().connect(sigc::mem_fun(*this, &Bar::onMap));
|
||||
|
||||
#if HAVE_SWAY
|
||||
if (auto ipc = config["ipc"]; ipc.isBool() && ipc.asBool()) {
|
||||
@@ -622,7 +274,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||
#endif
|
||||
|
||||
setupWidgets();
|
||||
window.show_all();
|
||||
window.show();
|
||||
|
||||
if (spdlog::should_log(spdlog::level::debug)) {
|
||||
// Unfortunately, this function isn't in the C++ bindings, so we have to call the C version.
|
||||
@@ -638,7 +290,7 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||
/* Need to define it here because of forward declared members */
|
||||
waybar::Bar::~Bar() = default;
|
||||
|
||||
void waybar::Bar::setMode(const std::string_view& mode) {
|
||||
void waybar::Bar::setMode(const std::string& mode) {
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
auto style = window.get_style_context();
|
||||
@@ -659,9 +311,23 @@ void waybar::Bar::setMode(const std::string_view& mode) {
|
||||
}
|
||||
|
||||
void waybar::Bar::setMode(const struct bar_mode& mode) {
|
||||
surface_impl_->setLayer(mode.layer);
|
||||
surface_impl_->setExclusiveZone(mode.exclusive);
|
||||
surface_impl_->setPassThrough(mode.passthrough);
|
||||
auto* gtk_window = window.gobj();
|
||||
|
||||
auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM;
|
||||
if (mode.layer == bar_layer::TOP) {
|
||||
layer = GTK_LAYER_SHELL_LAYER_TOP;
|
||||
} else if (mode.layer == bar_layer::OVERLAY) {
|
||||
layer = GTK_LAYER_SHELL_LAYER_OVERLAY;
|
||||
}
|
||||
gtk_layer_set_layer(gtk_window, layer);
|
||||
|
||||
if (mode.exclusive) {
|
||||
gtk_layer_auto_exclusive_zone_enable(gtk_window);
|
||||
} else {
|
||||
gtk_layer_set_exclusive_zone(gtk_window, 0);
|
||||
}
|
||||
|
||||
setPassThrough(passthrough_ = mode.passthrough);
|
||||
|
||||
if (mode.visible) {
|
||||
window.get_style_context()->remove_class("hidden");
|
||||
@@ -670,16 +336,69 @@ void waybar::Bar::setMode(const struct bar_mode& mode) {
|
||||
window.get_style_context()->add_class("hidden");
|
||||
window.set_opacity(0);
|
||||
}
|
||||
surface_impl_->commit();
|
||||
}
|
||||
|
||||
void waybar::Bar::onMap(GdkEventAny*) {
|
||||
void waybar::Bar::setPassThrough(bool passthrough) {
|
||||
if (gdk_surface_) {
|
||||
Cairo::RefPtr<Cairo::Region> region;
|
||||
if (passthrough) {
|
||||
region = Cairo::Region::create();
|
||||
}
|
||||
gdk_surface_->set_input_region(region);
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::Bar::setPosition(Gtk::PositionType position) {
|
||||
std::array<gboolean, GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER> anchors;
|
||||
anchors.fill(TRUE);
|
||||
|
||||
auto orientation = (position == Gtk::PositionType::LEFT || position == Gtk::PositionType::RIGHT)
|
||||
? Gtk::Orientation::VERTICAL
|
||||
: Gtk::Orientation::HORIZONTAL;
|
||||
|
||||
switch (position) {
|
||||
case Gtk::PositionType::LEFT:
|
||||
anchors[GTK_LAYER_SHELL_EDGE_RIGHT] = FALSE;
|
||||
break;
|
||||
case Gtk::PositionType::RIGHT:
|
||||
anchors[GTK_LAYER_SHELL_EDGE_LEFT] = FALSE;
|
||||
break;
|
||||
case Gtk::PositionType::BOTTOM:
|
||||
anchors[GTK_LAYER_SHELL_EDGE_TOP] = FALSE;
|
||||
break;
|
||||
default: /* Gtk::POS_TOP */
|
||||
anchors[GTK_LAYER_SHELL_EDGE_BOTTOM] = FALSE;
|
||||
break;
|
||||
};
|
||||
// Disable anchoring for other edges too if the width
|
||||
// or the height has been set to a value other than 'auto'
|
||||
// otherwise the bar will use all space
|
||||
uint32_t configured_width = config["width"].isUInt() ? config["width"].asUInt() : 0;
|
||||
uint32_t configured_height = config["height"].isUInt() ? config["height"].asUInt() : 0;
|
||||
if (orientation == Gtk::Orientation::VERTICAL && configured_height > 1) {
|
||||
anchors[GTK_LAYER_SHELL_EDGE_TOP] = FALSE;
|
||||
anchors[GTK_LAYER_SHELL_EDGE_BOTTOM] = FALSE;
|
||||
} else if (orientation == Gtk::Orientation::HORIZONTAL && configured_width > 1) {
|
||||
anchors[GTK_LAYER_SHELL_EDGE_LEFT] = FALSE;
|
||||
anchors[GTK_LAYER_SHELL_EDGE_RIGHT] = FALSE;
|
||||
}
|
||||
|
||||
for (auto edge : {GTK_LAYER_SHELL_EDGE_LEFT, GTK_LAYER_SHELL_EDGE_RIGHT, GTK_LAYER_SHELL_EDGE_TOP,
|
||||
GTK_LAYER_SHELL_EDGE_BOTTOM}) {
|
||||
gtk_layer_set_anchor(window.gobj(), edge, anchors[edge]);
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::Bar::onMap() {
|
||||
/*
|
||||
* Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor).
|
||||
*/
|
||||
auto gdk_window = window.get_window()->gobj();
|
||||
surface = gdk_wayland_window_get_wl_surface(gdk_window);
|
||||
configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window));
|
||||
gdk_surface_ = window.get_surface();
|
||||
surface = gdk_wayland_surface_get_wl_surface(gdk_surface_->gobj());
|
||||
configureGlobalOffset(gdk_surface_->get_width(), gdk_surface_->get_height());
|
||||
gdk_surface_->signal_layout().connect(sigc::mem_fun(*this, &Bar::onConfigure));
|
||||
|
||||
setPassThrough(passthrough_);
|
||||
}
|
||||
|
||||
void waybar::Bar::setVisible(bool value) {
|
||||
@@ -739,6 +458,8 @@ void waybar::Bar::handleSignal(int signal) {
|
||||
}
|
||||
}
|
||||
|
||||
// todo gtkmm4
|
||||
/*
|
||||
void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
|
||||
waybar::Group* group = nullptr) {
|
||||
auto module_list = group ? config[pos]["modules"] : config[pos];
|
||||
@@ -754,13 +475,13 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
|
||||
auto class_name = hash_pos != std::string::npos ? ref.substr(hash_pos + 1) : "";
|
||||
|
||||
auto vertical = (group ? group->getBox().get_orientation() : box_.get_orientation()) ==
|
||||
Gtk::ORIENTATION_VERTICAL;
|
||||
Gtk::Orientation::VERTICAL;
|
||||
|
||||
auto group_module = new waybar::Group(id_name, class_name, config[ref], vertical);
|
||||
getModules(factory, ref, group_module);
|
||||
module = group_module;
|
||||
} else {
|
||||
module = factory.makeModule(ref);
|
||||
module = factory.makeModule(ref, pos);
|
||||
}
|
||||
|
||||
std::shared_ptr<AModule> module_sp(module);
|
||||
@@ -791,72 +512,95 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
auto waybar::Bar::setupWidgets() -> void {
|
||||
window.add(box_);
|
||||
box_.pack_start(left_, false, false);
|
||||
box_.set_start_widget(left_);
|
||||
if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) {
|
||||
box_.set_center_widget(center_);
|
||||
} else {
|
||||
box_.pack_start(center_, true, false);
|
||||
box_.set_start_widget(center_);
|
||||
}
|
||||
box_.pack_end(right_, false, false);
|
||||
|
||||
box_.set_end_widget(right_);
|
||||
// Convert to button code for every module that is used.
|
||||
setupAltFormatKeyForModuleList("modules-left");
|
||||
setupAltFormatKeyForModuleList("modules-right");
|
||||
setupAltFormatKeyForModuleList("modules-center");
|
||||
|
||||
Factory factory(*this, config);
|
||||
getModules(factory, "modules-left");
|
||||
getModules(factory, "modules-center");
|
||||
getModules(factory, "modules-right");
|
||||
// todo gtkmm4
|
||||
// Factory factory(*this, config);
|
||||
// getModules(factory, "modules-left");
|
||||
// getModules(factory, "modules-center");
|
||||
// getModules(factory, "modules-right");
|
||||
for (auto const& module : modules_left_) {
|
||||
left_.pack_start(*module, false, false);
|
||||
left_.prepend(*module);
|
||||
}
|
||||
for (auto const& module : modules_center_) {
|
||||
center_.pack_start(*module, false, false);
|
||||
center_.prepend(*module);
|
||||
}
|
||||
std::reverse(modules_right_.begin(), modules_right_.end());
|
||||
for (auto const& module : modules_right_) {
|
||||
right_.pack_end(*module, false, false);
|
||||
right_.append(*module);
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
|
||||
configureGlobalOffset(ev->width, ev->height);
|
||||
void waybar::Bar::onConfigure(int width, int height) {
|
||||
/*
|
||||
* GTK wants new size for the window.
|
||||
* Actual resizing and management of the exclusve zone is handled within the gtk-layer-shell
|
||||
* code. This event handler only updates stored size of the window and prints some warnings.
|
||||
*
|
||||
* Note: forced resizing to a window smaller than required by GTK would not work with
|
||||
* gtk-layer-shell.
|
||||
*/
|
||||
if (orientation == Gtk::Orientation::VERTICAL) {
|
||||
if (width_ > 1 && width > static_cast<int>(width_)) {
|
||||
spdlog::warn(MIN_WIDTH_MSG, width_, width);
|
||||
}
|
||||
} else {
|
||||
if (height_ > 1 && height > static_cast<int>(height_)) {
|
||||
spdlog::warn(MIN_HEIGHT_MSG, height_, height);
|
||||
}
|
||||
}
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
|
||||
configureGlobalOffset(width, height);
|
||||
spdlog::info(BAR_SIZE_MSG, width, height, output->name);
|
||||
}
|
||||
|
||||
void waybar::Bar::configureGlobalOffset(int width, int height) {
|
||||
auto monitor_geometry = *output->monitor->property_geometry().get_value().gobj();
|
||||
auto position = config["position"].asString();
|
||||
int x;
|
||||
int y;
|
||||
if (position == "bottom") {
|
||||
if (width + margins_.left + margins_.right >= monitor_geometry.width)
|
||||
switch (position) {
|
||||
case Gtk::PositionType::BOTTOM:
|
||||
if (width + margins_.left + margins_.right >= monitor_geometry.width)
|
||||
x = margins_.left;
|
||||
else
|
||||
x = (monitor_geometry.width - width) / 2;
|
||||
y = monitor_geometry.height - height - margins_.bottom;
|
||||
break;
|
||||
case Gtk::PositionType::LEFT:
|
||||
x = margins_.left;
|
||||
else
|
||||
x = (monitor_geometry.width - width) / 2;
|
||||
y = monitor_geometry.height - height - margins_.bottom;
|
||||
} else if (position == "left") {
|
||||
x = margins_.left;
|
||||
if (height + margins_.top + margins_.bottom >= monitor_geometry.height)
|
||||
if (height + margins_.top + margins_.bottom >= monitor_geometry.height)
|
||||
y = margins_.top;
|
||||
else
|
||||
y = (monitor_geometry.height - height) / 2;
|
||||
break;
|
||||
case Gtk::PositionType::RIGHT:
|
||||
x = monitor_geometry.width - width - margins_.right;
|
||||
if (height + margins_.top + margins_.bottom >= monitor_geometry.height)
|
||||
y = margins_.top;
|
||||
else
|
||||
y = (monitor_geometry.height - height) / 2;
|
||||
break;
|
||||
default: /* Gtk::PositionType::TOP */
|
||||
if (width + margins_.left + margins_.right >= monitor_geometry.width)
|
||||
x = margins_.left;
|
||||
else
|
||||
x = (monitor_geometry.width - width) / 2;
|
||||
y = margins_.top;
|
||||
else
|
||||
y = (monitor_geometry.height - height) / 2;
|
||||
} else if (position == "right") {
|
||||
x = monitor_geometry.width - width - margins_.right;
|
||||
if (height + margins_.top + margins_.bottom >= monitor_geometry.height)
|
||||
y = margins_.top;
|
||||
else
|
||||
y = (monitor_geometry.height - height) / 2;
|
||||
} else {
|
||||
// position is top
|
||||
if (width + margins_.left + margins_.right >= monitor_geometry.width)
|
||||
x = margins_.left;
|
||||
else
|
||||
x = (monitor_geometry.width - width) / 2;
|
||||
y = margins_.top;
|
||||
break;
|
||||
}
|
||||
|
||||
x_global = x + monitor_geometry.x;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
#include "client.hpp"
|
||||
|
||||
#include <gtk4-layer-shell.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "gtkmm/icontheme.h"
|
||||
#include "gtkmm/application.h"
|
||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "util/clara.hpp"
|
||||
#include "util/format.hpp"
|
||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||
|
||||
waybar::Client *waybar::Client::inst() {
|
||||
static auto c = new Client();
|
||||
@@ -17,13 +19,8 @@ waybar::Client *waybar::Client::inst() {
|
||||
void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint32_t name,
|
||||
const char *interface, uint32_t version) {
|
||||
auto client = static_cast<Client *>(data);
|
||||
if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||
// limit version to a highest supported by the client protocol file
|
||||
version = std::min<uint32_t>(version, zwlr_layer_shell_v1_interface.version);
|
||||
client->layer_shell = static_cast<struct zwlr_layer_shell_v1 *>(
|
||||
wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version));
|
||||
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
|
||||
version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
|
||||
if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
|
||||
version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
|
||||
client->xdg_output_manager = static_cast<struct zxdg_output_manager_v1 *>(wl_registry_bind(
|
||||
registry, name, &zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION));
|
||||
} else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) {
|
||||
@@ -180,14 +177,15 @@ const std::string waybar::Client::getStyle(const std::string &style,
|
||||
|
||||
auto waybar::Client::setupCss(const std::string &css_file) -> void {
|
||||
css_provider_ = Gtk::CssProvider::create();
|
||||
style_context_ = Gtk::StyleContext::create();
|
||||
|
||||
// Load our css file, wherever that may be hiding
|
||||
if (!css_provider_->load_from_path(css_file)) {
|
||||
throw std::runtime_error("Can't open style file");
|
||||
try {
|
||||
css_provider_->load_from_path(css_file);
|
||||
} catch (const Glib::Error& e) {
|
||||
spdlog::error("{}", e.what());
|
||||
}
|
||||
// there's always only one screen
|
||||
style_context_->add_provider_for_screen(Gdk::Screen::get_default(), css_provider_,
|
||||
Gtk::StyleContext::add_provider_for_display(Gdk::Display::get_default(), css_provider_,
|
||||
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
}
|
||||
|
||||
@@ -199,17 +197,29 @@ void waybar::Client::bindInterfaces() {
|
||||
};
|
||||
wl_registry_add_listener(registry, ®istry_listener, this);
|
||||
wl_display_roundtrip(wl_display);
|
||||
if (layer_shell == nullptr || xdg_output_manager == nullptr) {
|
||||
|
||||
if (!gtk_layer_is_supported()) {
|
||||
throw std::runtime_error("The Wayland compositor does not support wlr-layer-shell protocol");
|
||||
}
|
||||
|
||||
if (xdg_output_manager == nullptr) {
|
||||
throw std::runtime_error("Failed to acquire required resources.");
|
||||
}
|
||||
// add existing outputs and subscribe to updates
|
||||
for (auto i = 0; i < gdk_display->get_n_monitors(); ++i) {
|
||||
auto monitor = gdk_display->get_monitor(i);
|
||||
handleMonitorAdded(monitor);
|
||||
// auto monitors{gdk_display->get_monitors()};
|
||||
for (guint i{0}; i < monitors_->get_n_items(); ++i) {
|
||||
handleMonitorAdded(std::dynamic_pointer_cast<Gdk::Monitor>(monitors_->get_object(i)));
|
||||
}
|
||||
gdk_display->signal_monitor_added().connect(sigc::mem_fun(*this, &Client::handleMonitorAdded));
|
||||
gdk_display->signal_monitor_removed().connect(
|
||||
sigc::mem_fun(*this, &Client::handleMonitorRemoved));
|
||||
|
||||
monitors_->signal_items_changed().connect([=, this](const guint& position, const guint& removed, const guint& added){
|
||||
for (auto i{removed}; i > 0; --i) {
|
||||
handleMonitorRemoved(std::dynamic_pointer_cast<Gdk::Monitor>(monitors_->get_object(position + i)));
|
||||
}
|
||||
|
||||
for (auto i{added}; i > 0; --i) {
|
||||
handleMonitorAdded(std::dynamic_pointer_cast<Gdk::Monitor>(monitors_->get_object(position + i)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int waybar::Client::main(int argc, char *argv[]) {
|
||||
@@ -242,8 +252,8 @@ int waybar::Client::main(int argc, char *argv[]) {
|
||||
if (!log_level.empty()) {
|
||||
spdlog::set_level(spdlog::level::from_str(log_level));
|
||||
}
|
||||
gtk_app = Gtk::Application::create(argc, argv, "fr.arouillard.waybar",
|
||||
Gio::APPLICATION_HANDLES_COMMAND_LINE);
|
||||
gtk_app = Gtk::Application::create("fr.arouillard.waybar",
|
||||
Gio::Application::Flags::HANDLES_COMMAND_LINE);
|
||||
gdk_display = Gdk::Display::get_default();
|
||||
if (!gdk_display) {
|
||||
throw std::runtime_error("Can't find display");
|
||||
@@ -251,7 +261,13 @@ int waybar::Client::main(int argc, char *argv[]) {
|
||||
if (!GDK_IS_WAYLAND_DISPLAY(gdk_display->gobj())) {
|
||||
throw std::runtime_error("Bar need to run under Wayland");
|
||||
}
|
||||
|
||||
// Initialize Waybars GTK resources with our custom icons
|
||||
auto theme{Gtk::IconTheme::get_for_display(gdk_display)};
|
||||
theme->add_resource_path("/fr/arouillard/waybar/icons");
|
||||
|
||||
wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj());
|
||||
monitors_ = gdk_display->get_monitors();
|
||||
config.load(config_opt);
|
||||
if (!portal) {
|
||||
portal = std::make_unique<waybar::Portal>();
|
||||
@@ -266,6 +282,7 @@ int waybar::Client::main(int argc, char *argv[]) {
|
||||
gtk_app->hold();
|
||||
gtk_app->run();
|
||||
bars.clear();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
|
||||
waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {}
|
||||
|
||||
waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||
waybar::AModule* waybar::Factory::makeModule(const std::string& name,
|
||||
const std::string& pos) const {
|
||||
try {
|
||||
auto hash_pos = name.find('#');
|
||||
auto ref = name.substr(0, hash_pos);
|
||||
@@ -30,6 +31,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||
return new waybar::modules::upower::UPower(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_PIPEWIRE
|
||||
if (ref == "privacy") {
|
||||
return new waybar::modules::privacy::Privacy(id, config_[name], pos);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_MPRIS
|
||||
if (ref == "mpris") {
|
||||
return new waybar::modules::mpris::Mpris(id, config_[name]);
|
||||
@@ -194,12 +200,20 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||
if (ref == "cava") {
|
||||
return new waybar::modules::Cava(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_SYSTEMD_MONITOR
|
||||
if (ref == "systemd-failed-units") {
|
||||
return new waybar::modules::SystemdFailedUnits(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
if (ref == "temperature") {
|
||||
return new waybar::modules::Temperature(id, config_[name]);
|
||||
}
|
||||
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
||||
return new waybar::modules::Custom(ref.substr(7), id, config_[name]);
|
||||
return new waybar::modules::Custom(ref.substr(7), id, config_[name], bar_.output->name);
|
||||
}
|
||||
if (ref.compare(0, 5, "cffi/") == 0 && ref.size() > 5) {
|
||||
return new waybar::modules::CFFI(ref.substr(5), id, config_[name]);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());
|
||||
|
||||
@@ -108,11 +108,11 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
delete client;
|
||||
return ret;
|
||||
} catch (const Glib::Error& e) {
|
||||
spdlog::error("{}", static_cast<std::string>(e.what()));
|
||||
return 1;
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("{}", e.what());
|
||||
return 1;
|
||||
} catch (const Glib::Exception& e) {
|
||||
spdlog::error("{}", static_cast<std::string>(e.what()));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
119
src/modules/cffi.cpp
Normal file
119
src/modules/cffi.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "modules/cffi.hpp"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <json/value.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& config)
|
||||
: AModule(config, name, id, true, true) {
|
||||
const auto dynlib_path = config_["module_path"].asString();
|
||||
if (dynlib_path.empty()) {
|
||||
throw std::runtime_error{"Missing or empty 'module_path' in module config"};
|
||||
}
|
||||
|
||||
void* handle = dlopen(dynlib_path.c_str(), RTLD_LAZY);
|
||||
if (handle == nullptr) {
|
||||
throw std::runtime_error{std::string{"Failed to load CFFI module: "} + dlerror()};
|
||||
}
|
||||
|
||||
// Fetch ABI version
|
||||
auto wbcffi_version = reinterpret_cast<size_t*>(dlsym(handle, "wbcffi_version"));
|
||||
if (wbcffi_version == nullptr) {
|
||||
throw std::runtime_error{std::string{"Missing wbcffi_version function: "} + dlerror()};
|
||||
}
|
||||
|
||||
// Fetch functions
|
||||
if (*wbcffi_version == 1) {
|
||||
// Mandatory functions
|
||||
hooks_.init = reinterpret_cast<InitFn*>(dlsym(handle, "wbcffi_init"));
|
||||
if (!hooks_.init) {
|
||||
throw std::runtime_error{std::string{"Missing wbcffi_init function: "} + dlerror()};
|
||||
}
|
||||
hooks_.deinit = reinterpret_cast<DenitFn*>(dlsym(handle, "wbcffi_deinit"));
|
||||
if (!hooks_.init) {
|
||||
throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()};
|
||||
}
|
||||
// Optional functions
|
||||
if (auto fn = reinterpret_cast<UpdateFn*>(dlsym(handle, "wbcffi_update"))) {
|
||||
hooks_.update = fn;
|
||||
}
|
||||
if (auto fn = reinterpret_cast<RefreshFn*>(dlsym(handle, "wbcffi_refresh"))) {
|
||||
hooks_.refresh = fn;
|
||||
}
|
||||
if (auto fn = reinterpret_cast<DoActionFn*>(dlsym(handle, "wbcffi_doaction"))) {
|
||||
hooks_.doAction = fn;
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error{"Unknown wbcffi_version " + std::to_string(*wbcffi_version)};
|
||||
}
|
||||
|
||||
// Prepare init() arguments
|
||||
// Convert JSON values to string
|
||||
std::vector<std::string> config_entries_stringstor;
|
||||
const auto& keys = config.getMemberNames();
|
||||
for (size_t i = 0; i < keys.size(); i++) {
|
||||
const auto& value = config[keys[i]];
|
||||
if (value.isConvertibleTo(Json::ValueType::stringValue)) {
|
||||
config_entries_stringstor.push_back(config[keys[i]].asString());
|
||||
} else {
|
||||
config_entries_stringstor.push_back(config[keys[i]].toStyledString());
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare config_entries array
|
||||
std::vector<ffi::wbcffi_config_entry> config_entries;
|
||||
for (size_t i = 0; i < keys.size(); i++) {
|
||||
config_entries.push_back({keys[i].c_str(), config_entries_stringstor[i].c_str()});
|
||||
}
|
||||
|
||||
ffi::wbcffi_init_info init_info = {
|
||||
.obj = (ffi::wbcffi_module*)this,
|
||||
.waybar_version = VERSION,
|
||||
.get_root_widget =
|
||||
[](ffi::wbcffi_module* obj) {
|
||||
return dynamic_cast<Gtk::Container*>(&((CFFI*)obj)->event_box_)->gobj();
|
||||
},
|
||||
.queue_update = [](ffi::wbcffi_module* obj) { ((CFFI*)obj)->dp.emit(); },
|
||||
};
|
||||
|
||||
// Call init
|
||||
cffi_instance_ = hooks_.init(&init_info, config_entries.data(), config_entries.size());
|
||||
|
||||
// Handle init failures
|
||||
if (cffi_instance_ == nullptr) {
|
||||
throw std::runtime_error{"Failed to initialize C ABI module"};
|
||||
}
|
||||
}
|
||||
|
||||
CFFI::~CFFI() {
|
||||
if (cffi_instance_ != nullptr) {
|
||||
hooks_.deinit(cffi_instance_);
|
||||
}
|
||||
}
|
||||
|
||||
auto CFFI::update() -> void {
|
||||
assert(cffi_instance_ != nullptr);
|
||||
hooks_.update(cffi_instance_);
|
||||
|
||||
// Execute the on-update command set in config
|
||||
AModule::update();
|
||||
}
|
||||
|
||||
auto CFFI::refresh(int signal) -> void {
|
||||
assert(cffi_instance_ != nullptr);
|
||||
hooks_.refresh(cffi_instance_, signal);
|
||||
}
|
||||
|
||||
auto CFFI::doAction(const std::string& name) -> void {
|
||||
assert(cffi_instance_ != nullptr);
|
||||
if (!name.empty()) {
|
||||
hooks_.doAction(cffi_instance_, name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace waybar::modules
|
||||
@@ -1,325 +1,286 @@
|
||||
#include "modules/clock.hpp"
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
#include "util/ustring_clen.hpp"
|
||||
|
||||
#ifdef HAVE_LANGINFO_1STDAY
|
||||
#include <langinfo.h>
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
namespace fmt_lib = waybar::util::date::format;
|
||||
|
||||
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
|
||||
current_time_zone_idx_{0},
|
||||
is_calendar_in_tooltip_{false},
|
||||
is_timezoned_list_in_tooltip_{false} {
|
||||
locale_{std::locale(config_["locale"].isString() ? config_["locale"].asString() : "")},
|
||||
tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""},
|
||||
cldInTooltip_{tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos},
|
||||
tzInTooltip_{tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos},
|
||||
tzCurrIdx_{0} {
|
||||
tlpText_ = tlpFmt_;
|
||||
|
||||
if (config_["timezones"].isArray() && !config_["timezones"].empty()) {
|
||||
for (const auto& zone_name : config_["timezones"]) {
|
||||
if (!zone_name.isString()) continue;
|
||||
if (zone_name.asString().empty())
|
||||
// local time should be shown
|
||||
time_zones_.push_back(date::current_zone());
|
||||
tzList_.push_back(current_zone());
|
||||
else
|
||||
try {
|
||||
time_zones_.push_back(date::locate_zone(zone_name.asString()));
|
||||
tzList_.push_back(locate_zone(zone_name.asString()));
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::warn("Timezone: {0}. {1}", zone_name.asString(), e.what());
|
||||
}
|
||||
}
|
||||
} else if (config_["timezone"].isString()) {
|
||||
if (config_["timezone"].asString().empty())
|
||||
time_zones_.push_back(date::current_zone());
|
||||
// local time should be shown
|
||||
tzList_.push_back(current_zone());
|
||||
else
|
||||
try {
|
||||
time_zones_.push_back(date::locate_zone(config_["timezone"].asString()));
|
||||
tzList_.push_back(locate_zone(config_["timezone"].asString()));
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::warn("Timezone: {0}. {1}", config_["timezone"].asString(), e.what());
|
||||
}
|
||||
}
|
||||
if (!tzList_.size()) tzList_.push_back(current_zone());
|
||||
|
||||
// If all timezones are parsed and no one is good
|
||||
if (!time_zones_.size()) {
|
||||
time_zones_.push_back(date::current_zone());
|
||||
}
|
||||
|
||||
// Check if a particular placeholder is present in the tooltip format, to know what to calculate
|
||||
// on update.
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
std::string trimmed_format{config_["tooltip-format"].asString()};
|
||||
fmtMap_.insert({5, trimmed_format});
|
||||
trimmed_format.erase(std::remove_if(trimmed_format.begin(), trimmed_format.end(),
|
||||
[](unsigned char x) { return std::isspace(x); }),
|
||||
trimmed_format.end());
|
||||
|
||||
if (trimmed_format.find("{" + kCalendarPlaceholder + "}") != std::string::npos) {
|
||||
is_calendar_in_tooltip_ = true;
|
||||
}
|
||||
if (trimmed_format.find("{" + KTimezonedTimeListPlaceholder + "}") != std::string::npos) {
|
||||
is_timezoned_list_in_tooltip_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Calendar configuration
|
||||
if (is_calendar_in_tooltip_) {
|
||||
if (config_[kCalendarPlaceholder]["weeks-pos"].isString()) {
|
||||
if (config_[kCalendarPlaceholder]["weeks-pos"].asString() == "left") {
|
||||
cldWPos_ = WeeksSide::LEFT;
|
||||
} else if (config_[kCalendarPlaceholder]["weeks-pos"].asString() == "right") {
|
||||
cldWPos_ = WeeksSide::RIGHT;
|
||||
}
|
||||
}
|
||||
if (config_[kCalendarPlaceholder]["format"]["months"].isString())
|
||||
fmtMap_.insert({0, config_[kCalendarPlaceholder]["format"]["months"].asString()});
|
||||
else
|
||||
fmtMap_.insert({0, "{}"});
|
||||
if (config_[kCalendarPlaceholder]["format"]["days"].isString())
|
||||
fmtMap_.insert({2, config_[kCalendarPlaceholder]["format"]["days"].asString()});
|
||||
else
|
||||
fmtMap_.insert({2, "{}"});
|
||||
if (config_[kCalendarPlaceholder]["format"]["weeks"].isString() &&
|
||||
cldWPos_ != WeeksSide::HIDDEN) {
|
||||
fmtMap_.insert(
|
||||
{4, std::regex_replace(config_[kCalendarPlaceholder]["format"]["weeks"].asString(),
|
||||
std::regex("\\{\\}"),
|
||||
(first_day_of_week() == date::Monday) ? "{:%W}" : "{:%U}")});
|
||||
Glib::ustring tmp{std::regex_replace(fmtMap_[4], std::regex("</?[^>]+>|\\{.*\\}"), "")};
|
||||
cldWnLen_ += tmp.size();
|
||||
} else {
|
||||
if (cldWPos_ != WeeksSide::HIDDEN)
|
||||
fmtMap_.insert({4, (first_day_of_week() == date::Monday) ? "{:%W}" : "{:%U}"});
|
||||
else
|
||||
cldWnLen_ = 0;
|
||||
}
|
||||
if (config_[kCalendarPlaceholder]["format"]["weekdays"].isString())
|
||||
fmtMap_.insert({1, config_[kCalendarPlaceholder]["format"]["weekdays"].asString()});
|
||||
else
|
||||
fmtMap_.insert({1, "{}"});
|
||||
if (config_[kCalendarPlaceholder]["format"]["today"].isString()) {
|
||||
fmtMap_.insert({3, config_[kCalendarPlaceholder]["format"]["today"].asString()});
|
||||
cldBaseDay_ =
|
||||
date::year_month_day{date::floor<date::days>(std::chrono::system_clock::now())}.day();
|
||||
} else
|
||||
fmtMap_.insert({3, "{}"});
|
||||
if (config_[kCalendarPlaceholder]["mode"].isString()) {
|
||||
const std::string cfgMode{(config_[kCalendarPlaceholder]["mode"].isString())
|
||||
? config_[kCalendarPlaceholder]["mode"].asString()
|
||||
: "month"};
|
||||
const std::map<std::string, const CldMode&> monthModes{{"month", CldMode::MONTH},
|
||||
{"year", CldMode::YEAR}};
|
||||
// Calendar properties
|
||||
if (cldInTooltip_) {
|
||||
if (config_[kCldPlaceholder]["mode"].isString()) {
|
||||
const std::string cfgMode{config_[kCldPlaceholder]["mode"].asString()};
|
||||
const std::map<std::string_view, const CldMode&> monthModes{{"month", CldMode::MONTH},
|
||||
{"year", CldMode::YEAR}};
|
||||
if (monthModes.find(cfgMode) != monthModes.end())
|
||||
cldMode_ = monthModes.at(cfgMode);
|
||||
else
|
||||
spdlog::warn(
|
||||
"Clock calendar configuration \"mode\"\"\" \"{0}\" is not recognized. Mode = \"month\" "
|
||||
"is using instead",
|
||||
"Clock calendar configuration mode \"{0}\" is not recognized. Mode = \"month\" is "
|
||||
"using instead",
|
||||
cfgMode);
|
||||
}
|
||||
if (config_[kCalendarPlaceholder]["mode-mon-col"].isInt()) {
|
||||
cldMonCols_ = config_[kCalendarPlaceholder]["mode-mon-col"].asInt();
|
||||
if (cldMonCols_ == 0u || 12 % cldMonCols_ != 0u) {
|
||||
cldMonCols_ = 3u;
|
||||
if (config_[kCldPlaceholder]["weeks-pos"].isString()) {
|
||||
if (config_[kCldPlaceholder]["weeks-pos"].asString() == "left") cldWPos_ = WS::LEFT;
|
||||
if (config_[kCldPlaceholder]["weeks-pos"].asString() == "right") cldWPos_ = WS::RIGHT;
|
||||
}
|
||||
if (config_[kCldPlaceholder]["format"]["months"].isString())
|
||||
fmtMap_.insert({0, config_[kCldPlaceholder]["format"]["months"].asString()});
|
||||
else
|
||||
fmtMap_.insert({0, "{}"});
|
||||
if (config_[kCldPlaceholder]["format"]["weekdays"].isString())
|
||||
fmtMap_.insert({1, config_[kCldPlaceholder]["format"]["weekdays"].asString()});
|
||||
else
|
||||
fmtMap_.insert({1, "{}"});
|
||||
|
||||
if (config_[kCldPlaceholder]["format"]["days"].isString())
|
||||
fmtMap_.insert({2, config_[kCldPlaceholder]["format"]["days"].asString()});
|
||||
else
|
||||
fmtMap_.insert({2, "{}"});
|
||||
if (config_[kCldPlaceholder]["format"]["today"].isString()) {
|
||||
fmtMap_.insert({3, config_[kCldPlaceholder]["format"]["today"].asString()});
|
||||
cldBaseDay_ =
|
||||
year_month_day{
|
||||
floor<days>(zoned_time{current_zone(), system_clock::now()}.get_local_time())}
|
||||
.day();
|
||||
} else
|
||||
fmtMap_.insert({3, "{}"});
|
||||
if (config_[kCldPlaceholder]["format"]["weeks"].isString() && cldWPos_ != WS::HIDDEN) {
|
||||
fmtMap_.insert({4, std::regex_replace(config_[kCldPlaceholder]["format"]["weeks"].asString(),
|
||||
std::regex("\\{\\}"),
|
||||
(first_day_of_week() == Monday) ? "{:%W}" : "{:%U}")});
|
||||
Glib::ustring tmp{std::regex_replace(fmtMap_[4], std::regex("</?[^>]+>|\\{.*\\}"), "")};
|
||||
cldWnLen_ += tmp.size();
|
||||
} else {
|
||||
if (cldWPos_ != WS::HIDDEN)
|
||||
fmtMap_.insert({4, (first_day_of_week() == Monday) ? "{:%W}" : "{:%U}"});
|
||||
else
|
||||
cldWnLen_ = 0;
|
||||
}
|
||||
if (config_[kCldPlaceholder]["mode-mon-col"].isInt()) {
|
||||
cldMonCols_ = config_[kCldPlaceholder]["mode-mon-col"].asInt();
|
||||
if (cldMonCols_ == 0u || (12 % cldMonCols_) != 0u) {
|
||||
spdlog::warn(
|
||||
"Clock calendar configuration \"mode-mon-col\" = {0} must be one of [1, 2, 3, 4, 6, "
|
||||
"12]. Value 3 is using instead",
|
||||
"Clock calendar configuration mode-mon-col = {0} must be one of [1, 2, 3, 4, 6, 12]. "
|
||||
"Value 3 is using instead",
|
||||
cldMonCols_);
|
||||
cldMonCols_ = 3u;
|
||||
}
|
||||
} else
|
||||
cldMonCols_ = 1;
|
||||
if (config_[kCalendarPlaceholder]["on-scroll"].isInt()) {
|
||||
cldShift_ = date::months{config_[kCalendarPlaceholder]["on-scroll"].asInt()};
|
||||
if (config_[kCldPlaceholder]["on-scroll"].isInt()) {
|
||||
event_box_.add_events(Gdk::LEAVE_NOTIFY_MASK);
|
||||
event_box_.signal_leave_notify_event().connect([this](GdkEventCrossing*) {
|
||||
cldCurrShift_ = date::months{0};
|
||||
cldCurrShift_ = months{0};
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (config_["locale"].isString())
|
||||
locale_ = std::locale(config_["locale"].asString());
|
||||
else
|
||||
locale_ = std::locale("");
|
||||
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
auto now = std::chrono::system_clock::now();
|
||||
/* difference with projected wakeup time */
|
||||
auto diff = now.time_since_epoch() % interval_;
|
||||
/* sleep until the next projected time */
|
||||
thread_.sleep_for(interval_ - diff);
|
||||
thread_.sleep_for(interval_ - system_clock::now().time_since_epoch() % interval_);
|
||||
};
|
||||
}
|
||||
|
||||
const date::time_zone* waybar::modules::Clock::current_timezone() {
|
||||
return time_zones_[current_time_zone_idx_];
|
||||
}
|
||||
|
||||
auto waybar::modules::Clock::update() -> void {
|
||||
const auto* tz{current_timezone()};
|
||||
const date::zoned_time now{
|
||||
tz,
|
||||
date::floor<std::chrono::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 tz{tzList_[tzCurrIdx_]};
|
||||
const zoned_time now{tz, floor<seconds>(system_clock::now())};
|
||||
|
||||
label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now));
|
||||
label_.set_markup(fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(now)));
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
const std::string tz_text{(is_timezoned_list_in_tooltip_) ? timezones_text(now.get_sys_time())
|
||||
: ""};
|
||||
const std::string cld_text{(is_calendar_in_tooltip_) ? get_calendar(today, shiftedDay, tz)
|
||||
: ""};
|
||||
const year_month_day today{floor<days>(now.get_local_time())};
|
||||
const auto shiftedDay{today + cldCurrShift_};
|
||||
const zoned_time shiftedNow{
|
||||
tz, local_days(shiftedDay) + (now.get_local_time() - floor<days>(now.get_local_time()))};
|
||||
|
||||
const std::string text{fmt::format(locale_, fmt::runtime(fmtMap_[5]), shiftedNow,
|
||||
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), tz_text),
|
||||
fmt::arg(kCalendarPlaceholder.c_str(), cld_text))};
|
||||
label_.set_tooltip_markup(text);
|
||||
if (tzInTooltip_) tzText_ = getTZtext(now.get_sys_time());
|
||||
if (cldInTooltip_) cldText_ = get_calendar(today, shiftedDay, tz);
|
||||
if (tzInTooltip_ || cldInTooltip_) {
|
||||
// std::vformat doesn't support named arguments.
|
||||
tlpText_ = std::regex_replace(tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_);
|
||||
tlpText_ =
|
||||
std::regex_replace(tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_);
|
||||
}
|
||||
|
||||
tlpText_ = fmt_lib::vformat(locale_, tlpText_, fmt_lib::make_format_args(shiftedNow));
|
||||
|
||||
label_.set_tooltip_markup(tlpText_);
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
auto waybar::modules::Clock::doAction(const std::string& name) -> void {
|
||||
if ((actionMap_[name])) {
|
||||
(this->*actionMap_[name])();
|
||||
update();
|
||||
} else
|
||||
spdlog::error("Clock. Unsupported action \"{0}\"", name);
|
||||
auto waybar::modules::Clock::getTZtext(sys_seconds now) -> std::string {
|
||||
if (tzList_.size() == 1) return "";
|
||||
|
||||
std::stringstream os;
|
||||
for (size_t tz_idx{0}; tz_idx < tzList_.size(); ++tz_idx) {
|
||||
if (static_cast<int>(tz_idx) == tzCurrIdx_) continue;
|
||||
auto zt{zoned_time{tzList_[tz_idx], now}};
|
||||
os << fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(zt)) << '\n';
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
// The number of weeks in calendar month layout plus 1 more for calendar titles
|
||||
const unsigned cldRowsInMonth(const date::year_month& ym, const date::weekday& firstdow) {
|
||||
using namespace date;
|
||||
return static_cast<unsigned>(
|
||||
ceil<weeks>((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count()) +
|
||||
2;
|
||||
const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) {
|
||||
return 2u + ceil<weeks>((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count();
|
||||
}
|
||||
|
||||
auto cldGetWeekForLine(const date::year_month& ym, const date::weekday& firstdow,
|
||||
unsigned const line) -> const date::year_month_weekday {
|
||||
unsigned index = line - 2;
|
||||
auto sd = date::sys_days{ym / 1};
|
||||
if (date::weekday{sd} == firstdow) ++index;
|
||||
auto ymdw = ym / firstdow[index];
|
||||
return ymdw;
|
||||
auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line)
|
||||
-> const year_month_weekday {
|
||||
unsigned index{line - 2};
|
||||
if (weekday{ym / 1} == firstdow) ++index;
|
||||
return ym / firstdow[index];
|
||||
}
|
||||
|
||||
auto getCalendarLine(const date::year_month_day& currDate, const date::year_month ym,
|
||||
const unsigned line, const date::weekday& firstdow,
|
||||
const std::locale* const locale_) -> std::string {
|
||||
using namespace date::literals;
|
||||
std::ostringstream res;
|
||||
auto getCalendarLine(const year_month_day& currDate, const year_month ym, const unsigned line,
|
||||
const weekday& firstdow, const std::locale* const locale_) -> std::string {
|
||||
std::ostringstream os;
|
||||
|
||||
switch (line) {
|
||||
// Print month and year title
|
||||
case 0: {
|
||||
// Output month and year title
|
||||
res << date::format(*locale_, "%B %Y", ym);
|
||||
os << date::format(*locale_, "{:L%B %Y}", ym);
|
||||
break;
|
||||
}
|
||||
// Print weekday names title
|
||||
case 1: {
|
||||
// Output weekday names title
|
||||
auto wd{firstdow};
|
||||
Glib::ustring wdStr;
|
||||
Glib::ustring::size_type wdLen{0};
|
||||
int clen{0};
|
||||
do {
|
||||
Glib::ustring wd_ustring{date::format(*locale_, "%a", wd)};
|
||||
auto clen{ustring_clen(wd_ustring)};
|
||||
auto wd_len{wd_ustring.length()};
|
||||
wdStr = date::format(*locale_, "{:L%a}", wd);
|
||||
clen = ustring_clen(wdStr);
|
||||
wdLen = wdStr.length();
|
||||
while (clen > 2) {
|
||||
wd_ustring = wd_ustring.substr(0, wd_len - 1);
|
||||
--wd_len;
|
||||
clen = ustring_clen(wd_ustring);
|
||||
wdStr = wdStr.substr(0, wdLen - 1);
|
||||
--wdLen;
|
||||
clen = ustring_clen(wdStr);
|
||||
}
|
||||
const std::string pad(2 - clen, ' ');
|
||||
|
||||
if (wd != firstdow) res << ' ';
|
||||
if (wd != firstdow) os << ' ';
|
||||
|
||||
res << pad << wd_ustring;
|
||||
os << pad << wdStr;
|
||||
} while (++wd != firstdow);
|
||||
break;
|
||||
}
|
||||
// Print first week prefixed with spaces if necessary
|
||||
case 2: {
|
||||
// Output first week prefixed with spaces if necessary
|
||||
auto wd = date::weekday{ym / 1};
|
||||
res << std::string(static_cast<unsigned>((wd - firstdow).count()) * 3, ' ');
|
||||
auto wd{weekday{ym / 1}};
|
||||
os << std::string((wd - firstdow).count() * 3, ' ');
|
||||
|
||||
if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / 1_d)
|
||||
res << date::format("%e", 1_d);
|
||||
if (currDate != ym / 1d)
|
||||
os << date::format(*locale_, "{:L%e}", 1d);
|
||||
else
|
||||
res << "{today}";
|
||||
|
||||
auto d = 2_d;
|
||||
os << "{today}";
|
||||
|
||||
auto d{2d};
|
||||
while (++wd != firstdow) {
|
||||
if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / d)
|
||||
res << date::format(" %e", d);
|
||||
if (currDate != ym / d)
|
||||
os << date::format(*locale_, " {:L%e}", d);
|
||||
else
|
||||
res << " {today}";
|
||||
os << " {today}";
|
||||
|
||||
++d;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Print non-first week
|
||||
default: {
|
||||
// Output a non-first week:
|
||||
auto ymdw{cldGetWeekForLine(ym, firstdow, line)};
|
||||
if (ymdw.ok()) {
|
||||
auto d = date::year_month_day{ymdw}.day();
|
||||
auto const e = (ym / last).day();
|
||||
auto wd = firstdow;
|
||||
auto ymdTmp{cldGetWeekForLine(ym, firstdow, line)};
|
||||
if (ymdTmp.ok()) {
|
||||
auto d{year_month_day{ymdTmp}.day()};
|
||||
const auto dlast{(ym / last).day()};
|
||||
auto wd{firstdow};
|
||||
|
||||
if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / d)
|
||||
res << date::format("%e", d);
|
||||
if (currDate != ym / d)
|
||||
os << date::format(*locale_, "{:L%e}", d);
|
||||
else
|
||||
res << "{today}";
|
||||
os << "{today}";
|
||||
|
||||
while (++wd != firstdow && ++d <= e) {
|
||||
if (currDate.year() != ym.year() || currDate.month() != ym.month() || currDate != ym / d)
|
||||
res << date::format(" %e", d);
|
||||
while (++wd != firstdow && ++d <= dlast) {
|
||||
if (currDate != ym / d)
|
||||
os << date::format(*locale_, " {:L%e}", d);
|
||||
else
|
||||
res << " {today}";
|
||||
os << " {today}";
|
||||
}
|
||||
// Append row with spaces if the week did not complete
|
||||
res << std::string(static_cast<unsigned>((firstdow - wd).count()) * 3, ' ');
|
||||
// Append row with spaces if the week was not completed
|
||||
os << std::string((firstdow - wd).count() * 3, ' ');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res.str();
|
||||
return os.str();
|
||||
}
|
||||
|
||||
auto waybar::modules::Clock::get_calendar(const date::year_month_day& today,
|
||||
const date::year_month_day& ymd,
|
||||
const date::time_zone* tz) -> const std::string {
|
||||
auto waybar::modules::Clock::get_calendar(const year_month_day& today, const year_month_day& ymd,
|
||||
const time_zone* tz) -> const std::string {
|
||||
const auto firstdow{first_day_of_week()};
|
||||
const auto maxRows{12 / cldMonCols_};
|
||||
const auto ym{ymd.year() / ymd.month()};
|
||||
const auto y{ymd.year()};
|
||||
const auto d{ymd.day()};
|
||||
const auto firstdow = first_day_of_week();
|
||||
const auto maxRows{12 / cldMonCols_};
|
||||
|
||||
std::ostringstream os;
|
||||
std::ostringstream tmp;
|
||||
|
||||
if (cldMode_ == CldMode::YEAR) {
|
||||
if (y / date::month{1} / 1 == cldYearShift_)
|
||||
if (y / month{1} / 1 == cldYearShift_)
|
||||
if (d == cldBaseDay_ || (uint)cldBaseDay_ == 0u)
|
||||
return cldYearCached_;
|
||||
else
|
||||
cldBaseDay_ = d;
|
||||
else
|
||||
cldYearShift_ = y / date::month{1} / 1;
|
||||
cldYearShift_ = y / month{1} / 1;
|
||||
}
|
||||
if (cldMode_ == CldMode::MONTH) {
|
||||
if (ym == cldMonShift_)
|
||||
@@ -330,67 +291,75 @@ auto waybar::modules::Clock::get_calendar(const date::year_month_day& today,
|
||||
else
|
||||
cldMonShift_ = ym;
|
||||
}
|
||||
|
||||
// Pad object
|
||||
const std::string pads(cldWnLen_, ' ');
|
||||
// Compute number of lines needed for each calendar month
|
||||
unsigned ml[12]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
|
||||
|
||||
for (auto& m : ml) {
|
||||
if (cldMode_ == CldMode::YEAR || m == static_cast<unsigned>(ymd.month()))
|
||||
m = cldRowsInMonth(y / date::month{m}, firstdow);
|
||||
m = cldRowsInMonth(y / month{m}, firstdow);
|
||||
else
|
||||
m = 0u;
|
||||
}
|
||||
|
||||
for (auto row{0u}; row < maxRows; ++row) {
|
||||
const auto lines = *std::max_element(std::begin(ml) + (row * cldMonCols_),
|
||||
std::begin(ml) + ((row + 1) * cldMonCols_));
|
||||
const auto lines{*std::max_element(std::begin(ml) + (row * cldMonCols_),
|
||||
std::begin(ml) + ((row + 1) * cldMonCols_))};
|
||||
for (auto line{0u}; line < lines; ++line) {
|
||||
for (auto col{0u}; col < cldMonCols_; ++col) {
|
||||
const auto mon{date::month{row * cldMonCols_ + col + 1}};
|
||||
const auto mon{month{row * cldMonCols_ + col + 1}};
|
||||
if (cldMode_ == CldMode::YEAR || y / mon == ym) {
|
||||
date::year_month ymTmp{y / mon};
|
||||
if (col != 0 && cldMode_ == CldMode::YEAR) os << " ";
|
||||
const year_month ymTmp{y / mon};
|
||||
if (col != 0 && cldMode_ == CldMode::YEAR) os << std::string(3, ' ');
|
||||
|
||||
// Week numbers on the left
|
||||
if (cldWPos_ == WeeksSide::LEFT && line > 0) {
|
||||
if (cldWPos_ == WS::LEFT && line > 0) {
|
||||
if (line > 1) {
|
||||
if (line < ml[static_cast<unsigned>(ymTmp.month()) - 1u])
|
||||
os << fmt::format(fmt::runtime(fmtMap_[4]),
|
||||
(line == 2)
|
||||
? date::zoned_seconds{tz, date::local_days{ymTmp / 1}}
|
||||
: date::zoned_seconds{tz, date::local_days{cldGetWeekForLine(
|
||||
ymTmp, firstdow, line)}})
|
||||
if (line < ml[(unsigned)ymTmp.month() - 1u]) {
|
||||
os << fmt_lib::vformat(
|
||||
locale_, fmtMap_[4],
|
||||
fmt_lib::make_format_args(
|
||||
(line == 2)
|
||||
? static_cast<const date::zoned_seconds&&>(
|
||||
zoned_seconds{tz, local_days{ymTmp / 1}})
|
||||
: static_cast<const date::zoned_seconds&&>(zoned_seconds{
|
||||
tz, local_days{cldGetWeekForLine(ymTmp, firstdow, line)}})))
|
||||
<< ' ';
|
||||
else
|
||||
os << std::string(cldWnLen_, ' ');
|
||||
} else
|
||||
os << pads;
|
||||
}
|
||||
}
|
||||
|
||||
os << fmt::format(
|
||||
fmt::runtime((cldWPos_ != WeeksSide::LEFT || line == 0) ? "{:<{}}" : "{:>{}}"),
|
||||
getCalendarLine(today, ymTmp, line, firstdow, &locale_),
|
||||
(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)));
|
||||
os << Glib::ustring::format((cldWPos_ != WS::LEFT || line == 0) ? std::left : std::right,
|
||||
std::setfill(L' '),
|
||||
std::setw(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)),
|
||||
getCalendarLine(today, ymTmp, line, firstdow, &locale_));
|
||||
|
||||
// Week numbers on the right
|
||||
if (cldWPos_ == WeeksSide ::RIGHT && line > 0) {
|
||||
if (cldWPos_ == WS::RIGHT && line > 0) {
|
||||
if (line > 1) {
|
||||
if (line < ml[static_cast<unsigned>(ymTmp.month()) - 1u])
|
||||
if (line < ml[(unsigned)ymTmp.month() - 1u])
|
||||
os << ' '
|
||||
<< fmt::format(fmt::runtime(fmtMap_[4]),
|
||||
(line == 2)
|
||||
? date::zoned_seconds{tz, date::local_days{ymTmp / 1}}
|
||||
: date::zoned_seconds{tz, date::local_days{cldGetWeekForLine(
|
||||
ymTmp, firstdow, line)}});
|
||||
<< fmt_lib::vformat(
|
||||
locale_, fmtMap_[4],
|
||||
fmt_lib::make_format_args(
|
||||
(line == 2) ? static_cast<const date::zoned_seconds&&>(
|
||||
zoned_seconds{tz, local_days{ymTmp / 1}})
|
||||
: static_cast<const date::zoned_seconds&&>(
|
||||
zoned_seconds{tz, local_days{cldGetWeekForLine(
|
||||
ymTmp, firstdow, line)}})));
|
||||
else
|
||||
os << std::string(cldWnLen_, ' ');
|
||||
os << pads;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply user formats to calendar
|
||||
// Apply user's formats
|
||||
if (line < 2)
|
||||
tmp << fmt::format(fmt::runtime(fmtMap_[line]), os.str());
|
||||
tmp << fmt_lib::vformat(
|
||||
locale_, fmtMap_[line],
|
||||
fmt_lib::make_format_args(static_cast<const std::string_view&&>(os.str())));
|
||||
else
|
||||
tmp << os.str();
|
||||
// Clear ostringstream
|
||||
@@ -400,10 +369,13 @@ auto waybar::modules::Clock::get_calendar(const date::year_month_day& today,
|
||||
if (row + 1u != maxRows && cldMode_ == CldMode::YEAR) tmp << '\n';
|
||||
}
|
||||
|
||||
os << fmt::format( // Apply days format
|
||||
fmt::runtime(fmt::format(fmt::runtime(fmtMap_[2]), tmp.str())),
|
||||
// Apply today format
|
||||
fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", d))));
|
||||
os << std::regex_replace(
|
||||
fmt_lib::vformat(locale_, fmtMap_[2],
|
||||
fmt_lib::make_format_args(static_cast<const std::string_view&&>(tmp.str()))),
|
||||
std::regex("\\{today\\}"),
|
||||
fmt_lib::vformat(locale_, fmtMap_[3],
|
||||
fmt_lib::make_format_args(
|
||||
static_cast<const std::string_view&&>(date::format("{:L%e}", d)))));
|
||||
|
||||
if (cldMode_ == CldMode::YEAR)
|
||||
cldYearCached_ = os.str();
|
||||
@@ -413,50 +385,34 @@ auto waybar::modules::Clock::get_calendar(const date::year_month_day& today,
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/*Clock actions*/
|
||||
// Actions handler
|
||||
auto waybar::modules::Clock::doAction(const std::string& name) -> void {
|
||||
if (actionMap_[name]) {
|
||||
(this->*actionMap_[name])();
|
||||
} else
|
||||
spdlog::error("Clock. Unsupported action \"{0}\"", name);
|
||||
}
|
||||
|
||||
// Module actions
|
||||
void waybar::modules::Clock::cldModeSwitch() {
|
||||
cldMode_ = (cldMode_ == CldMode::YEAR) ? CldMode::MONTH : CldMode::YEAR;
|
||||
}
|
||||
void waybar::modules::Clock::cldShift_up() {
|
||||
cldCurrShift_ += ((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_;
|
||||
cldCurrShift_ += (months)((cldMode_ == CldMode::YEAR) ? 12 : 1);
|
||||
}
|
||||
void waybar::modules::Clock::cldShift_down() {
|
||||
cldCurrShift_ -= ((cldMode_ == CldMode::YEAR) ? 12 : 1) * cldShift_;
|
||||
cldCurrShift_ -= (months)((cldMode_ == CldMode::YEAR) ? 12 : 1);
|
||||
}
|
||||
void waybar::modules::Clock::tz_up() {
|
||||
auto nr_zones = time_zones_.size();
|
||||
|
||||
if (nr_zones == 1) return;
|
||||
|
||||
size_t new_idx = current_time_zone_idx_ + 1;
|
||||
current_time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx;
|
||||
const auto tzSize{tzList_.size()};
|
||||
if (tzSize == 1) return;
|
||||
size_t newIdx{tzCurrIdx_ + 1lu};
|
||||
tzCurrIdx_ = (newIdx == tzSize) ? 0 : newIdx;
|
||||
}
|
||||
void waybar::modules::Clock::tz_down() {
|
||||
auto nr_zones = time_zones_.size();
|
||||
|
||||
if (nr_zones == 1) return;
|
||||
|
||||
current_time_zone_idx_ = current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1;
|
||||
}
|
||||
|
||||
auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point now)
|
||||
-> std::string {
|
||||
if (time_zones_.size() == 1) {
|
||||
return "";
|
||||
}
|
||||
std::stringstream os;
|
||||
for (size_t time_zone_idx = 0; time_zone_idx < time_zones_.size(); ++time_zone_idx) {
|
||||
if (static_cast<int>(time_zone_idx) == current_time_zone_idx_) {
|
||||
continue;
|
||||
}
|
||||
const date::time_zone* timezone = time_zones_[time_zone_idx];
|
||||
if (!timezone) {
|
||||
timezone = date::current_zone();
|
||||
}
|
||||
auto ztime = date::zoned_time{timezone, date::floor<std::chrono::seconds>(now)};
|
||||
os << fmt::format(locale_, fmt::runtime(format_), ztime) << '\n';
|
||||
}
|
||||
return os.str();
|
||||
const auto tzSize{tzList_.size()};
|
||||
if (tzSize == 1) return;
|
||||
tzCurrIdx_ = (tzCurrIdx_ == 0) ? tzSize - 1 : tzCurrIdx_ - 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LANGINFO_1STDAY
|
||||
@@ -468,17 +424,16 @@ using deleting_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
|
||||
#endif
|
||||
|
||||
// Computations done similarly to Linux cal utility.
|
||||
auto waybar::modules::Clock::first_day_of_week() -> date::weekday {
|
||||
auto waybar::modules::Clock::first_day_of_week() -> weekday {
|
||||
#ifdef HAVE_LANGINFO_1STDAY
|
||||
deleting_unique_ptr<std::remove_pointer<locale_t>::type, freelocale> posix_locale{
|
||||
newlocale(LC_ALL, locale_.name().c_str(), nullptr)};
|
||||
if (posix_locale) {
|
||||
const int i = (std::intptr_t)nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get());
|
||||
auto ymd = date::year(i / 10000) / (i / 100 % 100) / (i % 100);
|
||||
auto wd = date::weekday(ymd);
|
||||
uint8_t j = *nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, posix_locale.get());
|
||||
return wd + date::days(j - 1);
|
||||
const auto i{(int)((std::intptr_t)nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get()))};
|
||||
const weekday wd{year_month_day{year(i / 10000) / month(i / 100 % 100) / day(i % 100)}};
|
||||
const auto j{(uint8_t)*nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, posix_locale.get())};
|
||||
return wd + days{j - 1};
|
||||
}
|
||||
#endif
|
||||
return date::Sunday;
|
||||
return Sunday;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,9 @@ std::vector<std::tuple<size_t, size_t>> waybar::modules::CpuUsage::parseCpuinfo(
|
||||
|
||||
size_t idle_time = 0;
|
||||
size_t total_time = 0;
|
||||
if (times.size() >= 4) {
|
||||
idle_time = times[3];
|
||||
if (times.size() >= 5) {
|
||||
// idle + iowait
|
||||
idle_time = times[3] + times[4];
|
||||
total_time = std::accumulate(times.begin(), times.end(), 0);
|
||||
}
|
||||
cpuinfo.emplace_back(idle_time, total_time);
|
||||
|
||||
@@ -5,15 +5,17 @@
|
||||
#include "util/scope_guard.hpp"
|
||||
|
||||
waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
|
||||
const Json::Value& config)
|
||||
const Json::Value& config, const std::string& output_name)
|
||||
: ALabel(config, "custom-" + name, id, "{}"),
|
||||
name_(name),
|
||||
output_name_(output_name),
|
||||
id_(id),
|
||||
percentage_(0),
|
||||
fp_(nullptr),
|
||||
pid_(-1) {
|
||||
dp.emit();
|
||||
if (!config_["signal"].empty() && config_["interval"].empty()) {
|
||||
if (!config_["signal"].empty() && config_["interval"].empty() &&
|
||||
config_["restart-interval"].empty()) {
|
||||
waitingWorker();
|
||||
} else if (interval_.count() > 0) {
|
||||
delayWorker();
|
||||
@@ -42,7 +44,7 @@ void waybar::modules::Custom::delayWorker() {
|
||||
}
|
||||
if (can_update) {
|
||||
if (config_["exec"].isString()) {
|
||||
output_ = util::command::exec(config_["exec"].asString());
|
||||
output_ = util::command::exec(config_["exec"].asString(), output_name_);
|
||||
}
|
||||
dp.emit();
|
||||
}
|
||||
@@ -53,7 +55,7 @@ void waybar::modules::Custom::delayWorker() {
|
||||
void waybar::modules::Custom::continuousWorker() {
|
||||
auto cmd = config_["exec"].asString();
|
||||
pid_ = -1;
|
||||
fp_ = util::command::open(cmd, pid_);
|
||||
fp_ = util::command::open(cmd, pid_, output_name_);
|
||||
if (!fp_) {
|
||||
throw std::runtime_error("Unable to open " + cmd);
|
||||
}
|
||||
@@ -79,7 +81,7 @@ void waybar::modules::Custom::continuousWorker() {
|
||||
if (config_["restart-interval"].isUInt()) {
|
||||
pid_ = -1;
|
||||
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
|
||||
fp_ = util::command::open(cmd, pid_);
|
||||
fp_ = util::command::open(cmd, pid_, output_name_);
|
||||
if (!fp_) {
|
||||
throw std::runtime_error("Unable to open " + cmd);
|
||||
}
|
||||
@@ -112,7 +114,7 @@ void waybar::modules::Custom::waitingWorker() {
|
||||
}
|
||||
if (can_update) {
|
||||
if (config_["exec"].isString()) {
|
||||
output_ = util::command::exec(config_["exec"].asString());
|
||||
output_ = util::command::exec(config_["exec"].asString(), output_name_);
|
||||
}
|
||||
dp.emit();
|
||||
}
|
||||
@@ -174,16 +176,18 @@ auto waybar::modules::Custom::update() -> void {
|
||||
}
|
||||
}
|
||||
}
|
||||
auto classes = label_.get_style_context()->list_classes();
|
||||
auto style = label_.get_style_context();
|
||||
auto classes = style->list_classes();
|
||||
for (auto const& c : classes) {
|
||||
if (c == id_) continue;
|
||||
label_.get_style_context()->remove_class(c);
|
||||
style->remove_class(c);
|
||||
}
|
||||
for (auto const& c : class_) {
|
||||
label_.get_style_context()->add_class(c);
|
||||
style->add_class(c);
|
||||
}
|
||||
label_.get_style_context()->add_class("flat");
|
||||
label_.get_style_context()->add_class("text-button");
|
||||
style->add_class("flat");
|
||||
style->add_class("text-button");
|
||||
style->add_class(MODULE_CLASS);
|
||||
event_box_.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con
|
||||
status_manager_{nullptr},
|
||||
seat_{nullptr},
|
||||
bar_(bar),
|
||||
box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
||||
box_{bar.orientation, 0},
|
||||
output_status_{nullptr} {
|
||||
struct wl_display *display = Client::inst()->wl_display;
|
||||
struct wl_registry *registry = wl_display_get_registry(display);
|
||||
@@ -113,6 +113,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
box_.get_style_context()->add_class(MODULE_CLASS);
|
||||
event_box_.add(box_);
|
||||
|
||||
// Default to 9 tags, cap at 32
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
#include "modules/hyprland/backend.hpp"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
@@ -24,9 +19,9 @@ void IPC::startIPC() {
|
||||
|
||||
std::thread([&]() {
|
||||
// check for hyprland
|
||||
const char* HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
const char* his = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
|
||||
if (!HIS) {
|
||||
if (his == nullptr) {
|
||||
spdlog::warn("Hyprland is not running, Hyprland IPC will not be available.");
|
||||
return;
|
||||
}
|
||||
@@ -45,8 +40,8 @@ void IPC::startIPC() {
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
// socket path
|
||||
std::string socketPath = "/tmp/hypr/" + std::string(HIS) + "/.socket2.sock";
|
||||
// socket path, specified by EventManager of Hyprland
|
||||
std::string socketPath = "/tmp/hypr/" + std::string(his) + "/.socket2.sock";
|
||||
|
||||
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
|
||||
|
||||
@@ -61,10 +56,9 @@ void IPC::startIPC() {
|
||||
|
||||
auto file = fdopen(socketfd, "r");
|
||||
|
||||
while (1) {
|
||||
// read
|
||||
|
||||
while (true) {
|
||||
char buffer[1024]; // Hyprland socket2 events are max 1024 bytes
|
||||
|
||||
auto recievedCharPtr = fgets(buffer, 1024, file);
|
||||
|
||||
if (!recievedCharPtr) {
|
||||
@@ -72,28 +66,21 @@ void IPC::startIPC() {
|
||||
continue;
|
||||
}
|
||||
|
||||
callbackMutex.lock();
|
||||
|
||||
std::string messageRecieved(buffer);
|
||||
|
||||
messageRecieved = messageRecieved.substr(0, messageRecieved.find_first_of('\n'));
|
||||
|
||||
spdlog::debug("hyprland IPC received {}", messageRecieved);
|
||||
|
||||
parseIPC(messageRecieved);
|
||||
|
||||
callbackMutex.unlock();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void IPC::parseIPC(const std::string& ev) {
|
||||
// todo
|
||||
std::string request = ev.substr(0, ev.find_first_of('>'));
|
||||
std::unique_lock lock(m_callbackMutex);
|
||||
|
||||
for (auto& [eventname, handler] : callbacks) {
|
||||
for (auto& [eventname, handler] : m_callbacks) {
|
||||
if (eventname == request) {
|
||||
handler->onEvent(ev);
|
||||
}
|
||||
@@ -104,11 +91,9 @@ void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) {
|
||||
if (!ev_handler) {
|
||||
return;
|
||||
}
|
||||
callbackMutex.lock();
|
||||
|
||||
callbacks.emplace_back(std::make_pair(ev, ev_handler));
|
||||
|
||||
callbackMutex.unlock();
|
||||
std::unique_lock lock(m_callbackMutex);
|
||||
m_callbacks.emplace_back(ev, ev_handler);
|
||||
}
|
||||
|
||||
void IPC::unregisterForIPC(EventHandler* ev_handler) {
|
||||
@@ -116,37 +101,35 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
callbackMutex.lock();
|
||||
std::unique_lock lock(m_callbackMutex);
|
||||
|
||||
for (auto it = callbacks.begin(); it != callbacks.end();) {
|
||||
auto it_current = it;
|
||||
it++;
|
||||
auto& [eventname, handler] = *it_current;
|
||||
for (auto it = m_callbacks.begin(); it != m_callbacks.end();) {
|
||||
auto& [eventname, handler] = *it;
|
||||
if (handler == ev_handler) {
|
||||
callbacks.erase(it_current);
|
||||
m_callbacks.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
callbackMutex.unlock();
|
||||
}
|
||||
|
||||
std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||
// basically hyprctl
|
||||
|
||||
struct addrinfo ai_hints;
|
||||
struct addrinfo* ai_res = NULL;
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
struct addrinfo aiHints;
|
||||
struct addrinfo* aiRes = nullptr;
|
||||
const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (SERVERSOCKET < 0) {
|
||||
if (serverSocket < 0) {
|
||||
spdlog::error("Hyprland IPC: Couldn't open a socket (1)");
|
||||
return "";
|
||||
}
|
||||
|
||||
memset(&ai_hints, 0, sizeof(struct addrinfo));
|
||||
ai_hints.ai_family = AF_UNSPEC;
|
||||
ai_hints.ai_socktype = SOCK_STREAM;
|
||||
memset(&aiHints, 0, sizeof(struct addrinfo));
|
||||
aiHints.ai_family = AF_UNSPEC;
|
||||
aiHints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (getaddrinfo("localhost", NULL, &ai_hints, &ai_res) != 0) {
|
||||
if (getaddrinfo("localhost", nullptr, &aiHints, &aiRes) != 0) {
|
||||
spdlog::error("Hyprland IPC: Couldn't get host (2)");
|
||||
return "";
|
||||
}
|
||||
@@ -173,13 +156,13 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (connect(SERVERSOCKET, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress)) <
|
||||
if (connect(serverSocket, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress)) <
|
||||
0) {
|
||||
spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto sizeWritten = write(SERVERSOCKET, rq.c_str(), rq.length());
|
||||
auto sizeWritten = write(serverSocket, rq.c_str(), rq.length());
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
spdlog::error("Hyprland IPC: Couldn't write (4)");
|
||||
@@ -190,22 +173,22 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||
std::string response;
|
||||
|
||||
do {
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
sizeWritten = read(serverSocket, buffer, 8192);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
spdlog::error("Hyprland IPC: Couldn't read (5)");
|
||||
close(SERVERSOCKET);
|
||||
close(serverSocket);
|
||||
return "";
|
||||
}
|
||||
response.append(buffer, sizeWritten);
|
||||
} while (sizeWritten > 0);
|
||||
|
||||
close(SERVERSOCKET);
|
||||
close(serverSocket);
|
||||
return response;
|
||||
}
|
||||
|
||||
Json::Value IPC::getSocket1JsonReply(const std::string& rq) {
|
||||
return parser_.parse(getSocket1Reply("j/" + rq));
|
||||
return m_parser.parse(getSocket1Reply("j/" + rq));
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
box_.get_style_context()->add_class(MODULE_CLASS);
|
||||
event_box_.add(box_);
|
||||
|
||||
dp.emit();
|
||||
@@ -45,7 +46,7 @@ auto waybar::modules::Image::update() -> void {
|
||||
if (config_["path"].isString()) {
|
||||
path_ = config_["path"].asString();
|
||||
} else if (config_["exec"].isString()) {
|
||||
output_ = util::command::exec(config_["exec"].asString());
|
||||
output_ = util::command::exec(config_["exec"].asString(), "");
|
||||
parseOutputRaw();
|
||||
} else {
|
||||
path_ = "";
|
||||
|
||||
@@ -81,7 +81,7 @@ auto supportsLockStates(const libevdev* dev) -> bool {
|
||||
waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar,
|
||||
const Json::Value& config)
|
||||
: AModule(config, "keyboard-state", id, false, !config["disable-scroll"].asBool()),
|
||||
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||
box_(bar.orientation, 0),
|
||||
numlock_label_(""),
|
||||
capslock_label_(""),
|
||||
numlock_format_(config_["format"].isString() ? config_["format"].asString()
|
||||
@@ -132,6 +132,7 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
box_.get_style_context()->add_class(MODULE_CLASS);
|
||||
event_box_.add(box_);
|
||||
|
||||
if (config_["device-path"].isString()) {
|
||||
|
||||
179
src/modules/privacy/privacy.cpp
Normal file
179
src/modules/privacy/privacy.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
#include "modules/privacy/privacy.hpp"
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <json/value.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "AModule.hpp"
|
||||
#include "gtkmm/image.h"
|
||||
#include "modules/privacy/privacy_item.hpp"
|
||||
|
||||
namespace waybar::modules::privacy {
|
||||
|
||||
using util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT;
|
||||
using util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT;
|
||||
using util::PipewireBackend::PRIVACY_NODE_TYPE_NONE;
|
||||
using util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT;
|
||||
|
||||
Privacy::Privacy(const std::string& id, const Json::Value& config, const std::string& pos)
|
||||
: AModule(config, "privacy", id),
|
||||
nodes_screenshare(),
|
||||
nodes_audio_in(),
|
||||
nodes_audio_out(),
|
||||
visibility_conn(),
|
||||
box_(Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||
box_.set_name(name_);
|
||||
|
||||
event_box_.add(box_);
|
||||
|
||||
// Icon Spacing
|
||||
if (config_["icon-spacing"].isUInt()) {
|
||||
iconSpacing = config_["icon-spacing"].asUInt();
|
||||
}
|
||||
box_.set_spacing(iconSpacing);
|
||||
|
||||
// Icon Size
|
||||
if (config_["icon-size"].isUInt()) {
|
||||
iconSize = config_["icon-size"].asUInt();
|
||||
}
|
||||
|
||||
// Transition Duration
|
||||
if (config_["transition-duration"].isUInt()) {
|
||||
transition_duration = config_["transition-duration"].asUInt();
|
||||
}
|
||||
|
||||
// Initialize each privacy module
|
||||
Json::Value modules = config_["modules"];
|
||||
// Add Screenshare and Mic usage as default modules if none are specified
|
||||
if (!modules.isArray() || modules.size() == 0) {
|
||||
modules = Json::Value(Json::arrayValue);
|
||||
for (auto& type : {"screenshare", "audio-in"}) {
|
||||
Json::Value obj = Json::Value(Json::objectValue);
|
||||
obj["type"] = type;
|
||||
modules.append(obj);
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < modules.size(); i++) {
|
||||
const Json::Value& module_config = modules[i];
|
||||
if (!module_config.isObject() || !module_config["type"].isString()) continue;
|
||||
const std::string type = module_config["type"].asString();
|
||||
if (type == "screenshare") {
|
||||
auto item =
|
||||
Gtk::make_managed<PrivacyItem>(module_config, PRIVACY_NODE_TYPE_VIDEO_INPUT,
|
||||
&nodes_screenshare, pos, iconSize, transition_duration);
|
||||
box_.add(*item);
|
||||
} else if (type == "audio-in") {
|
||||
auto item =
|
||||
Gtk::make_managed<PrivacyItem>(module_config, PRIVACY_NODE_TYPE_AUDIO_INPUT,
|
||||
&nodes_audio_in, pos, iconSize, transition_duration);
|
||||
box_.add(*item);
|
||||
} else if (type == "audio-out") {
|
||||
auto item =
|
||||
Gtk::make_managed<PrivacyItem>(module_config, PRIVACY_NODE_TYPE_AUDIO_OUTPUT,
|
||||
&nodes_audio_out, pos, iconSize, transition_duration);
|
||||
box_.add(*item);
|
||||
}
|
||||
}
|
||||
|
||||
backend = util::PipewireBackend::PipewireBackend::getInstance();
|
||||
backend->privacy_nodes_changed_signal_event.connect(
|
||||
sigc::mem_fun(*this, &Privacy::onPrivacyNodesChanged));
|
||||
|
||||
dp.emit();
|
||||
}
|
||||
|
||||
void Privacy::onPrivacyNodesChanged() {
|
||||
mutex_.lock();
|
||||
nodes_audio_out.clear();
|
||||
nodes_audio_in.clear();
|
||||
nodes_screenshare.clear();
|
||||
|
||||
for (auto& node : backend->privacy_nodes) {
|
||||
switch (node.second->state) {
|
||||
case PW_NODE_STATE_RUNNING:
|
||||
switch (node.second->type) {
|
||||
case PRIVACY_NODE_TYPE_VIDEO_INPUT:
|
||||
nodes_screenshare.push_back(node.second);
|
||||
break;
|
||||
case PRIVACY_NODE_TYPE_AUDIO_INPUT:
|
||||
nodes_audio_in.push_back(node.second);
|
||||
break;
|
||||
case PRIVACY_NODE_TYPE_AUDIO_OUTPUT:
|
||||
nodes_audio_out.push_back(node.second);
|
||||
break;
|
||||
case PRIVACY_NODE_TYPE_NONE:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_.unlock();
|
||||
dp.emit();
|
||||
}
|
||||
|
||||
auto Privacy::update() -> void {
|
||||
mutex_.lock();
|
||||
bool screenshare, audio_in, audio_out;
|
||||
|
||||
for (Gtk::Widget* widget : box_.get_children()) {
|
||||
PrivacyItem* module = dynamic_cast<PrivacyItem*>(widget);
|
||||
if (!module) continue;
|
||||
switch (module->privacy_type) {
|
||||
case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT:
|
||||
screenshare = !nodes_screenshare.empty();
|
||||
module->set_in_use(screenshare);
|
||||
break;
|
||||
case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT:
|
||||
audio_in = !nodes_audio_in.empty();
|
||||
module->set_in_use(audio_in);
|
||||
break;
|
||||
case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT:
|
||||
audio_out = !nodes_audio_out.empty();
|
||||
module->set_in_use(audio_out);
|
||||
break;
|
||||
case util::PipewireBackend::PRIVACY_NODE_TYPE_NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_.unlock();
|
||||
|
||||
// Hide the whole widget if none are in use
|
||||
bool is_visible = screenshare || audio_in || audio_out;
|
||||
if (is_visible != event_box_.get_visible()) {
|
||||
// Disconnect any previous connection so that it doesn't get activated in
|
||||
// the future, hiding the module when it should be visible
|
||||
visibility_conn.disconnect();
|
||||
if (is_visible) {
|
||||
event_box_.set_visible(true);
|
||||
} else {
|
||||
// Hides the widget when all of the privacy_item revealers animations
|
||||
// have finished animating
|
||||
visibility_conn = Glib::signal_timeout().connect(
|
||||
sigc::track_obj(
|
||||
[this] {
|
||||
mutex_.lock();
|
||||
bool screenshare = !nodes_screenshare.empty();
|
||||
bool audio_in = !nodes_audio_in.empty();
|
||||
bool audio_out = !nodes_audio_out.empty();
|
||||
mutex_.unlock();
|
||||
event_box_.set_visible(screenshare || audio_in || audio_out);
|
||||
return false;
|
||||
},
|
||||
*this),
|
||||
transition_duration);
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
AModule::update();
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::privacy
|
||||
163
src/modules/privacy/privacy_item.cpp
Normal file
163
src/modules/privacy/privacy_item.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "modules/privacy/privacy_item.hpp"
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "AModule.hpp"
|
||||
#include "glibmm/main.h"
|
||||
#include "glibmm/priorities.h"
|
||||
#include "gtkmm/enums.h"
|
||||
#include "gtkmm/label.h"
|
||||
#include "gtkmm/revealer.h"
|
||||
#include "gtkmm/tooltip.h"
|
||||
#include "sigc++/adaptors/bind.h"
|
||||
#include "util/gtk_icon.hpp"
|
||||
#include "util/pipewire/privacy_node_info.hpp"
|
||||
|
||||
namespace waybar::modules::privacy {
|
||||
|
||||
PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_,
|
||||
std::list<PrivacyNodeInfo *> *nodes_, const std::string &pos,
|
||||
const uint icon_size, const uint transition_duration)
|
||||
: Gtk::Revealer(),
|
||||
privacy_type(privacy_type_),
|
||||
nodes(nodes_),
|
||||
signal_conn(),
|
||||
tooltip_window(Gtk::ORIENTATION_VERTICAL, 0),
|
||||
box_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||
icon_() {
|
||||
switch (privacy_type) {
|
||||
case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_INPUT:
|
||||
box_.get_style_context()->add_class("audio-in");
|
||||
iconName = "waybar-privacy-audio-input-symbolic";
|
||||
break;
|
||||
case util::PipewireBackend::PRIVACY_NODE_TYPE_AUDIO_OUTPUT:
|
||||
box_.get_style_context()->add_class("audio-out");
|
||||
iconName = "waybar-privacy-audio-output-symbolic";
|
||||
break;
|
||||
case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT:
|
||||
box_.get_style_context()->add_class("screenshare");
|
||||
iconName = "waybar-privacy-screen-share-symbolic";
|
||||
break;
|
||||
default:
|
||||
case util::PipewireBackend::PRIVACY_NODE_TYPE_NONE:
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the reveal transition to not look weird when sliding in
|
||||
if (pos == "modules-left") {
|
||||
set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_SLIDE_RIGHT);
|
||||
} else if (pos == "modules-center") {
|
||||
set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_CROSSFADE);
|
||||
} else if (pos == "modules-right") {
|
||||
set_transition_type(Gtk::REVEALER_TRANSITION_TYPE_SLIDE_LEFT);
|
||||
}
|
||||
set_transition_duration(transition_duration);
|
||||
|
||||
box_.set_name("privacy-item");
|
||||
box_.add(icon_);
|
||||
icon_.set_pixel_size(icon_size);
|
||||
add(box_);
|
||||
|
||||
// Icon Name
|
||||
if (config_["icon-name"].isString()) {
|
||||
iconName = config_["icon-name"].asString();
|
||||
}
|
||||
icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID);
|
||||
|
||||
// Tooltip Icon Size
|
||||
if (config_["tooltip-icon-size"].isUInt()) {
|
||||
tooltipIconSize = config_["tooltip-icon-size"].asUInt();
|
||||
}
|
||||
// Tooltip
|
||||
if (config_["tooltip"].isString()) {
|
||||
tooltip = config_["tooltip"].asBool();
|
||||
}
|
||||
set_has_tooltip(tooltip);
|
||||
if (tooltip) {
|
||||
// Sets the window to use when showing the tooltip
|
||||
update_tooltip();
|
||||
this->signal_query_tooltip().connect(sigc::track_obj(
|
||||
[this](int x, int y, bool keyboard_tooltip, const Glib::RefPtr<Gtk::Tooltip> &tooltip) {
|
||||
tooltip->set_custom(tooltip_window);
|
||||
return true;
|
||||
},
|
||||
*this));
|
||||
}
|
||||
|
||||
// Don't show by default
|
||||
set_reveal_child(true);
|
||||
set_visible(false);
|
||||
}
|
||||
|
||||
void PrivacyItem::update_tooltip() {
|
||||
// Removes all old nodes
|
||||
for (auto child : tooltip_window.get_children()) {
|
||||
delete child;
|
||||
}
|
||||
|
||||
for (auto *node : *nodes) {
|
||||
Gtk::Box *box = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4);
|
||||
|
||||
// Set device icon
|
||||
Gtk::Image *node_icon = new Gtk::Image();
|
||||
node_icon->set_pixel_size(tooltipIconSize);
|
||||
node_icon->set_from_icon_name(node->get_icon_name(), Gtk::ICON_SIZE_INVALID);
|
||||
box->add(*node_icon);
|
||||
|
||||
// Set model
|
||||
Gtk::Label *node_name = new Gtk::Label(node->get_name());
|
||||
box->add(*node_name);
|
||||
|
||||
tooltip_window.add(*box);
|
||||
}
|
||||
|
||||
tooltip_window.show_all();
|
||||
}
|
||||
|
||||
void PrivacyItem::set_in_use(bool in_use) {
|
||||
if (in_use) {
|
||||
update_tooltip();
|
||||
}
|
||||
|
||||
if (this->in_use == in_use && init) return;
|
||||
|
||||
if (init) {
|
||||
// Disconnect any previous connection so that it doesn't get activated in
|
||||
// the future, hiding the module when it should be visible
|
||||
signal_conn.disconnect();
|
||||
|
||||
this->in_use = in_use;
|
||||
guint duration = 0;
|
||||
if (this->in_use) {
|
||||
set_visible(true);
|
||||
} else {
|
||||
set_reveal_child(false);
|
||||
duration = get_transition_duration();
|
||||
}
|
||||
|
||||
signal_conn = Glib::signal_timeout().connect(sigc::track_obj(
|
||||
[this] {
|
||||
if (this->in_use) {
|
||||
set_reveal_child(true);
|
||||
} else {
|
||||
set_visible(false);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
*this),
|
||||
duration);
|
||||
} else {
|
||||
set_visible(false);
|
||||
set_reveal_child(false);
|
||||
}
|
||||
this->init = true;
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::privacy
|
||||
@@ -87,7 +87,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con
|
||||
control_{nullptr},
|
||||
seat_{nullptr},
|
||||
bar_(bar),
|
||||
box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
||||
box_{bar.orientation, 0},
|
||||
output_status_{nullptr} {
|
||||
struct wl_display *display = Client::inst()->wl_display;
|
||||
struct wl_registry *registry = wl_display_get_registry(display);
|
||||
@@ -111,6 +111,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
box_.get_style_context()->add_class(MODULE_CLASS);
|
||||
event_box_.add(box_);
|
||||
|
||||
// Default to 9 tags, cap at 32
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace waybar::modules::SNI {
|
||||
|
||||
Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
: AModule(config, "tray", id),
|
||||
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||
box_(bar.orientation, 0),
|
||||
watcher_(SNI::Watcher::getInstance()),
|
||||
host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1),
|
||||
std::bind(&Tray::onRemove, this, std::placeholders::_1)) {
|
||||
@@ -15,6 +15,7 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
box_.get_style_context()->add_class(MODULE_CLASS);
|
||||
if (config_["spacing"].isUInt()) {
|
||||
box_.set_spacing(config_["spacing"].asUInt());
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ int Workspaces::convertWorkspaceNameToNum(std::string name) {
|
||||
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
|
||||
: AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()),
|
||||
bar_(bar),
|
||||
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||
box_(bar.orientation, 0) {
|
||||
if (config["format-icons"]["high-priority-named"].isArray()) {
|
||||
for (auto &it : config["format-icons"]["high-priority-named"]) {
|
||||
high_priority_named_.push_back(it.asString());
|
||||
@@ -37,6 +37,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
box_.get_style_context()->add_class(MODULE_CLASS);
|
||||
event_box_.add(box_);
|
||||
ipc_.subscribe(R"(["workspace"])");
|
||||
ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent));
|
||||
@@ -314,7 +315,7 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node
|
||||
if (config_["format-icons"][key].isString() && node[key].asBool()) {
|
||||
return config_["format-icons"][key].asString();
|
||||
}
|
||||
} else if (config_["format_icons"]["persistent"].isString() &&
|
||||
} else if (config_["format-icons"]["persistent"].isString() &&
|
||||
node["target_output"].isString()) {
|
||||
return config_["format-icons"]["persistent"].asString();
|
||||
} else if (config_["format-icons"][key].isString()) {
|
||||
|
||||
133
src/modules/systemd_failed_units.cpp
Normal file
133
src/modules/systemd_failed_units.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "modules/systemd_failed_units.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <giomm/dbusproxy.h>
|
||||
#include <glibmm/variant.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
static const unsigned UPDATE_DEBOUNCE_TIME_MS = 1000;
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
SystemdFailedUnits::SystemdFailedUnits(const std::string& id, const Json::Value& config)
|
||||
: ALabel(config, "systemd-failed-units", id, "{nr_failed} failed", 1),
|
||||
hide_on_ok(true),
|
||||
update_pending(false),
|
||||
nr_failed_system(0),
|
||||
nr_failed_user(0),
|
||||
last_status() {
|
||||
if (config["hide-on-ok"].isBool()) {
|
||||
hide_on_ok = config["hide-on-ok"].asBool();
|
||||
}
|
||||
if (config["format-ok"].isString()) {
|
||||
format_ok = config["format-ok"].asString();
|
||||
} else {
|
||||
format_ok = format_;
|
||||
}
|
||||
|
||||
/* Default to enable both "system" and "user". */
|
||||
if (!config["system"].isBool() || config["system"].asBool()) {
|
||||
system_proxy = Gio::DBus::Proxy::create_for_bus_sync(
|
||||
Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.systemd1",
|
||||
"/org/freedesktop/systemd1", "org.freedesktop.DBus.Properties");
|
||||
if (!system_proxy) {
|
||||
throw std::runtime_error("Unable to connect to systemwide systemd DBus!");
|
||||
}
|
||||
system_proxy->signal_signal().connect(sigc::mem_fun(*this, &SystemdFailedUnits::notify_cb));
|
||||
}
|
||||
if (!config["user"].isBool() || config["user"].asBool()) {
|
||||
user_proxy = Gio::DBus::Proxy::create_for_bus_sync(
|
||||
Gio::DBus::BusType::BUS_TYPE_SESSION, "org.freedesktop.systemd1",
|
||||
"/org/freedesktop/systemd1", "org.freedesktop.DBus.Properties");
|
||||
if (!user_proxy) {
|
||||
throw std::runtime_error("Unable to connect to user systemd DBus!");
|
||||
}
|
||||
user_proxy->signal_signal().connect(sigc::mem_fun(*this, &SystemdFailedUnits::notify_cb));
|
||||
}
|
||||
|
||||
updateData();
|
||||
/* Always update for the first time. */
|
||||
dp.emit();
|
||||
}
|
||||
|
||||
SystemdFailedUnits::~SystemdFailedUnits() {
|
||||
if (system_proxy) system_proxy.reset();
|
||||
if (user_proxy) user_proxy.reset();
|
||||
}
|
||||
|
||||
auto SystemdFailedUnits::notify_cb(
|
||||
const Glib::ustring &sender_name,
|
||||
const Glib::ustring &signal_name,
|
||||
const Glib::VariantContainerBase &arguments) -> void {
|
||||
if (signal_name == "PropertiesChanged" && !update_pending) {
|
||||
update_pending = true;
|
||||
/* The fail count may fluctuate due to restarting. */
|
||||
Glib::signal_timeout().connect_once(
|
||||
sigc::mem_fun(*this, &SystemdFailedUnits::updateData),
|
||||
UPDATE_DEBOUNCE_TIME_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void SystemdFailedUnits::updateData() {
|
||||
update_pending = false;
|
||||
|
||||
auto load = [](const char* kind, Glib::RefPtr<Gio::DBus::Proxy> &proxy) -> uint32_t {
|
||||
try {
|
||||
auto parameters = Glib::VariantContainerBase(
|
||||
g_variant_new("(ss)", "org.freedesktop.systemd1.Manager", "NFailedUnits"));
|
||||
Glib::VariantContainerBase data = proxy->call_sync("Get", parameters);
|
||||
if (data && data.is_of_type(Glib::VariantType("(v)"))) {
|
||||
Glib::VariantBase variant;
|
||||
g_variant_get(data.gobj_copy(), "(v)", &variant);
|
||||
if (variant && variant.is_of_type(Glib::VARIANT_TYPE_UINT32)) {
|
||||
uint32_t value = 0;
|
||||
g_variant_get(variant.gobj_copy(), "u", &value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
} catch (Glib::Error& e) {
|
||||
spdlog::error("Failed to get {} failed units: {}", kind, e.what().c_str());
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
if (system_proxy) {
|
||||
nr_failed_system = load("systemwide", system_proxy);
|
||||
}
|
||||
if (user_proxy) {
|
||||
nr_failed_user = load("user", user_proxy);
|
||||
}
|
||||
dp.emit();
|
||||
}
|
||||
|
||||
auto SystemdFailedUnits::update() -> void {
|
||||
uint32_t nr_failed = nr_failed_system + nr_failed_user;
|
||||
|
||||
// Hide if needed.
|
||||
if (nr_failed == 0 && hide_on_ok) {
|
||||
event_box_.set_visible(false);
|
||||
return;
|
||||
}
|
||||
if (!event_box_.get_visible()) {
|
||||
event_box_.set_visible(true);
|
||||
}
|
||||
|
||||
// Set state class.
|
||||
const std::string status = nr_failed == 0 ? "ok" : "degraded";
|
||||
if (!last_status.empty() && label_.get_style_context()->has_class(last_status)) {
|
||||
label_.get_style_context()->remove_class(last_status);
|
||||
}
|
||||
if (!label_.get_style_context()->has_class(status)) {
|
||||
label_.get_style_context()->add_class(status);
|
||||
}
|
||||
last_status = status;
|
||||
|
||||
label_.set_markup(fmt::format(
|
||||
fmt::runtime(nr_failed == 0 ? format_ok : format_),
|
||||
fmt::arg("nr_failed", nr_failed),
|
||||
fmt::arg("nr_failed_system", nr_failed_system),
|
||||
fmt::arg("nr_failed_user", nr_failed_user)));
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::systemd_failed_units
|
||||
@@ -18,7 +18,15 @@ UPower::UPower(const std::string& id, const Json::Value& config)
|
||||
m_Mutex(),
|
||||
client(),
|
||||
showAltText(false) {
|
||||
box_.pack_start(icon_);
|
||||
// Show icon only when "show-icon" isn't set to false
|
||||
if (config_["show-icon"].isBool()) {
|
||||
showIcon = config_["show-icon"].asBool();
|
||||
}
|
||||
|
||||
if (showIcon) {
|
||||
box_.pack_start(icon_);
|
||||
}
|
||||
|
||||
box_.pack_start(label_);
|
||||
box_.set_name(name_);
|
||||
event_box_.add(box_);
|
||||
|
||||
@@ -46,6 +46,7 @@ waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Val
|
||||
}
|
||||
|
||||
waybar::modules::Wireplumber::~Wireplumber() {
|
||||
wp_core_disconnect(wp_core_);
|
||||
g_clear_pointer(&apis_, g_ptr_array_unref);
|
||||
g_clear_object(&om_);
|
||||
g_clear_object(&wp_core_);
|
||||
@@ -81,7 +82,7 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber*
|
||||
auto nick = wp_properties_get(properties, "node.nick");
|
||||
auto description = wp_properties_get(properties, "node.description");
|
||||
|
||||
self->node_name_ = nick ? nick : description;
|
||||
self->node_name_ = nick ? nick : description ? description : "Unknown node name";
|
||||
spdlog::debug("[{}]: Updating node name to: {}", self->name_, self->node_name_);
|
||||
}
|
||||
|
||||
@@ -316,13 +317,6 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) {
|
||||
if (dir == SCROLL_DIR::NONE) {
|
||||
return true;
|
||||
}
|
||||
if (config_["reverse-scrolling"].asInt() == 1) {
|
||||
if (dir == SCROLL_DIR::UP) {
|
||||
dir = SCROLL_DIR::DOWN;
|
||||
} else if (dir == SCROLL_DIR::DOWN) {
|
||||
dir = SCROLL_DIR::UP;
|
||||
}
|
||||
}
|
||||
double max_volume = 1;
|
||||
double step = 1.0 / 100.0;
|
||||
if (config_["scroll-step"].isDouble()) {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "glibmm/fileutils.h"
|
||||
#include "glibmm/refptr.h"
|
||||
#include "util/format.hpp"
|
||||
#include "util/gtk_icon.hpp"
|
||||
#include "util/rewrite_string.hpp"
|
||||
#include "util/string.hpp"
|
||||
|
||||
@@ -182,11 +183,21 @@ bool Task::image_load_icon(Gtk::Image &image, const Glib::RefPtr<Gtk::IconTheme>
|
||||
|
||||
try {
|
||||
pixbuf = icon_theme->load_icon(ret_icon_name, scaled_icon_size, Gtk::ICON_LOOKUP_FORCE_SIZE);
|
||||
spdlog::debug("{} Loaded icon '{}'", repr(), ret_icon_name);
|
||||
} catch (...) {
|
||||
if (Glib::file_test(ret_icon_name, Glib::FILE_TEST_EXISTS))
|
||||
if (Glib::file_test(ret_icon_name, Glib::FILE_TEST_EXISTS)) {
|
||||
pixbuf = load_icon_from_file(ret_icon_name, scaled_icon_size);
|
||||
else
|
||||
pixbuf = {};
|
||||
spdlog::debug("{} Loaded icon from file '{}'", repr(), ret_icon_name);
|
||||
} else {
|
||||
try {
|
||||
pixbuf = DefaultGtkIconThemeWrapper::load_icon(
|
||||
"image-missing", scaled_icon_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
|
||||
spdlog::debug("{} Loaded icon from resource", repr());
|
||||
} catch (...) {
|
||||
pixbuf = {};
|
||||
spdlog::debug("{} Unable to load icon.", repr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pixbuf) {
|
||||
@@ -266,7 +277,7 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar,
|
||||
handle_{tl_handle},
|
||||
seat_{seat},
|
||||
id_{global_id++},
|
||||
content_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0} {
|
||||
content_{bar.orientation, 0} {
|
||||
zwlr_foreign_toplevel_handle_v1_add_listener(handle_, &toplevel_handle_impl, this);
|
||||
|
||||
button.set_relief(Gtk::RELIEF_NONE);
|
||||
@@ -304,6 +315,10 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar,
|
||||
with_icon_ = true;
|
||||
}
|
||||
|
||||
if (app_id_.empty()) {
|
||||
handle_app_id("unknown");
|
||||
}
|
||||
|
||||
/* Strip spaces at the beginning and end of the format strings */
|
||||
format_tooltip_.clear();
|
||||
if (!config_["tooltip"].isBool() || config_["tooltip"].asBool()) {
|
||||
@@ -392,6 +407,11 @@ void Task::hide_if_ignored() {
|
||||
}
|
||||
|
||||
void Task::handle_app_id(const char *app_id) {
|
||||
if (app_id_.empty()) {
|
||||
spdlog::debug(fmt::format("Task ({}) setting app_id to {}", id_, app_id));
|
||||
} else {
|
||||
spdlog::debug(fmt::format("Task ({}) overwriting app_id '{}' with '{}'", id_, app_id_, app_id));
|
||||
}
|
||||
app_id_ = app_id;
|
||||
hide_if_ignored();
|
||||
|
||||
@@ -710,13 +730,14 @@ static const wl_registry_listener registry_listener_impl = {.global = handle_glo
|
||||
Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Value &config)
|
||||
: waybar::AModule(config, "taskbar", id, false, false),
|
||||
bar_(bar),
|
||||
box_{bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0},
|
||||
box_{bar.orientation, 0},
|
||||
manager_{nullptr},
|
||||
seat_{nullptr} {
|
||||
box_.set_name("taskbar");
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
box_.get_style_context()->add_class(MODULE_CLASS);
|
||||
box_.get_style_context()->add_class("empty");
|
||||
event_box_.add(box_);
|
||||
|
||||
|
||||
@@ -21,9 +21,7 @@ std::map<std::string, std::string> Workspace::icons_map_;
|
||||
|
||||
WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar,
|
||||
const Json::Value &config)
|
||||
: waybar::AModule(config, "workspaces", id, false, false),
|
||||
bar_(bar),
|
||||
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||
: waybar::AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.orientation, 0) {
|
||||
auto config_sort_by_name = config_["sort-by-name"];
|
||||
if (config_sort_by_name.isBool()) {
|
||||
sort_by_name_ = config_sort_by_name.asBool();
|
||||
@@ -54,6 +52,7 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
box_.get_style_context()->add_class(MODULE_CLASS);
|
||||
event_box_.add(box_);
|
||||
|
||||
add_registry_listener(this);
|
||||
|
||||
@@ -28,7 +28,7 @@ EnumType EnumParser<EnumType>::parseStringToEnum(const std::string& str,
|
||||
std::map<std::string, EnumType> capitalizedEnumMap;
|
||||
std::transform(
|
||||
enumMap.begin(), enumMap.end(), std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()),
|
||||
[this](const auto& pair) { return std::make_pair(capitalize(pair.first), pair.second); });
|
||||
[](const auto& pair) { return std::make_pair(capitalize(pair.first), pair.second); });
|
||||
|
||||
// Return enum match of string
|
||||
auto it = capitalizedEnumMap.find(uppercaseStr);
|
||||
@@ -40,6 +40,6 @@ EnumType EnumParser<EnumType>::parseStringToEnum(const std::string& str,
|
||||
|
||||
// Explicit instantiations for specific EnumType types you intend to use
|
||||
// Add explicit instantiations for all relevant EnumType types
|
||||
template struct EnumParser<modules::hyprland::Workspaces::SORT_METHOD>;
|
||||
template struct EnumParser<modules::hyprland::Workspaces::SortMethod>;
|
||||
|
||||
} // namespace waybar::util
|
||||
|
||||
155
src/util/pipewire_backend.cpp
Normal file
155
src/util/pipewire_backend.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "util/pipewire/pipewire_backend.hpp"
|
||||
|
||||
#include "util/pipewire/privacy_node_info.hpp"
|
||||
|
||||
namespace waybar::util::PipewireBackend {
|
||||
|
||||
static void get_node_info(void *data_, const struct pw_node_info *info) {
|
||||
PrivacyNodeInfo *p_node_info = static_cast<PrivacyNodeInfo *>(data_);
|
||||
PipewireBackend *backend = (PipewireBackend *)p_node_info->data;
|
||||
|
||||
p_node_info->state = info->state;
|
||||
|
||||
const struct spa_dict_item *item;
|
||||
spa_dict_for_each(item, info->props) {
|
||||
if (strcmp(item->key, PW_KEY_CLIENT_ID) == 0) {
|
||||
p_node_info->client_id = strtoul(item->value, NULL, 10);
|
||||
} else if (strcmp(item->key, PW_KEY_MEDIA_NAME) == 0) {
|
||||
p_node_info->media_name = item->value;
|
||||
} else if (strcmp(item->key, PW_KEY_NODE_NAME) == 0) {
|
||||
p_node_info->node_name = item->value;
|
||||
} else if (strcmp(item->key, PW_KEY_APP_NAME) == 0) {
|
||||
p_node_info->application_name = item->value;
|
||||
} else if (strcmp(item->key, "pipewire.access.portal.app_id") == 0) {
|
||||
p_node_info->pipewire_access_portal_app_id = item->value;
|
||||
} else if (strcmp(item->key, PW_KEY_APP_ICON_NAME) == 0) {
|
||||
p_node_info->application_icon_name = item->value;
|
||||
}
|
||||
}
|
||||
|
||||
backend->privacy_nodes_changed_signal_event.emit();
|
||||
}
|
||||
|
||||
static const struct pw_node_events node_events = {
|
||||
.version = PW_VERSION_NODE_EVENTS,
|
||||
.info = get_node_info,
|
||||
};
|
||||
|
||||
static void proxy_destroy(void *data) {
|
||||
PrivacyNodeInfo *node = (PrivacyNodeInfo *)data;
|
||||
|
||||
spa_hook_remove(&node->proxy_listener);
|
||||
spa_hook_remove(&node->object_listener);
|
||||
}
|
||||
|
||||
static const struct pw_proxy_events proxy_events = {
|
||||
.version = PW_VERSION_PROXY_EVENTS,
|
||||
.destroy = proxy_destroy,
|
||||
};
|
||||
|
||||
static void registry_event_global(void *_data, uint32_t id, uint32_t permissions, const char *type,
|
||||
uint32_t version, const struct spa_dict *props) {
|
||||
if (!props || strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return;
|
||||
|
||||
const char *lookup_str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
|
||||
if (!lookup_str) return;
|
||||
std::string media_class = lookup_str;
|
||||
enum PrivacyNodeType media_type = PRIVACY_NODE_TYPE_NONE;
|
||||
if (media_class == "Stream/Input/Video") {
|
||||
media_type = PRIVACY_NODE_TYPE_VIDEO_INPUT;
|
||||
} else if (media_class == "Stream/Input/Audio") {
|
||||
media_type = PRIVACY_NODE_TYPE_AUDIO_INPUT;
|
||||
} else if (media_class == "Stream/Output/Audio") {
|
||||
media_type = PRIVACY_NODE_TYPE_AUDIO_OUTPUT;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
PipewireBackend *backend = static_cast<PipewireBackend *>(_data);
|
||||
struct pw_proxy *proxy =
|
||||
(pw_proxy *)pw_registry_bind(backend->registry, id, type, version, sizeof(PrivacyNodeInfo));
|
||||
|
||||
if (!proxy) return;
|
||||
|
||||
PrivacyNodeInfo *p_node_info = (PrivacyNodeInfo *)pw_proxy_get_user_data(proxy);
|
||||
p_node_info->id = id;
|
||||
p_node_info->data = backend;
|
||||
p_node_info->type = media_type;
|
||||
p_node_info->media_class = media_class;
|
||||
|
||||
pw_proxy_add_listener(proxy, &p_node_info->proxy_listener, &proxy_events, p_node_info);
|
||||
|
||||
pw_proxy_add_object_listener(proxy, &p_node_info->object_listener, &node_events, p_node_info);
|
||||
|
||||
backend->privacy_nodes.insert_or_assign(id, p_node_info);
|
||||
}
|
||||
|
||||
static void registry_event_global_remove(void *_data, uint32_t id) {
|
||||
auto backend = static_cast<PipewireBackend *>(_data);
|
||||
|
||||
backend->mutex_.lock();
|
||||
auto iter = backend->privacy_nodes.find(id);
|
||||
if (iter != backend->privacy_nodes.end()) {
|
||||
backend->privacy_nodes.erase(id);
|
||||
}
|
||||
backend->mutex_.unlock();
|
||||
|
||||
backend->privacy_nodes_changed_signal_event.emit();
|
||||
}
|
||||
|
||||
static const struct pw_registry_events registry_events = {
|
||||
.version = PW_VERSION_REGISTRY_EVENTS,
|
||||
.global = registry_event_global,
|
||||
.global_remove = registry_event_global_remove,
|
||||
};
|
||||
|
||||
PipewireBackend::PipewireBackend(private_constructor_tag tag)
|
||||
: mainloop_(nullptr), context_(nullptr), core_(nullptr) {
|
||||
pw_init(nullptr, nullptr);
|
||||
mainloop_ = pw_thread_loop_new("waybar", nullptr);
|
||||
if (mainloop_ == nullptr) {
|
||||
throw std::runtime_error("pw_thread_loop_new() failed.");
|
||||
}
|
||||
context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0);
|
||||
if (context_ == nullptr) {
|
||||
throw std::runtime_error("pa_context_new() failed.");
|
||||
}
|
||||
core_ = pw_context_connect(context_, nullptr, 0);
|
||||
if (core_ == nullptr) {
|
||||
throw std::runtime_error("pw_context_connect() failed");
|
||||
}
|
||||
registry = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0);
|
||||
|
||||
spa_zero(registry_listener);
|
||||
pw_registry_add_listener(registry, ®istry_listener, ®istry_events, this);
|
||||
if (pw_thread_loop_start(mainloop_) < 0) {
|
||||
throw std::runtime_error("pw_thread_loop_start() failed.");
|
||||
}
|
||||
}
|
||||
|
||||
PipewireBackend::~PipewireBackend() {
|
||||
if (registry != nullptr) {
|
||||
pw_proxy_destroy((struct pw_proxy *)registry);
|
||||
}
|
||||
|
||||
spa_zero(registry_listener);
|
||||
|
||||
if (core_ != nullptr) {
|
||||
pw_core_disconnect(core_);
|
||||
}
|
||||
|
||||
if (context_ != nullptr) {
|
||||
pw_context_destroy(context_);
|
||||
}
|
||||
|
||||
if (mainloop_ != nullptr) {
|
||||
pw_thread_loop_stop(mainloop_);
|
||||
pw_thread_loop_destroy(mainloop_);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<PipewireBackend> PipewireBackend::getInstance() {
|
||||
private_constructor_tag tag;
|
||||
return std::make_shared<PipewireBackend>(tag);
|
||||
}
|
||||
} // namespace waybar::util::PipewireBackend
|
||||
@@ -36,7 +36,7 @@ auto fmt::formatter<waybar::Appearance>::format(waybar::Appearance c, format_con
|
||||
}
|
||||
|
||||
waybar::Portal::Portal()
|
||||
: DBus::Proxy(DBus::Connection::get_sync(DBus::BusType::BUS_TYPE_SESSION), PORTAL_BUS_NAME,
|
||||
: DBus::Proxy(DBus::Connection::get_sync(DBus::BusType::SESSION), PORTAL_BUS_NAME,
|
||||
PORTAL_OBJ_PATH, PORTAL_INTERFACE),
|
||||
currentMode(Appearance::UNKNOWN) {
|
||||
refreshAppearance();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[wrap-file]
|
||||
directory = Catch2-3.4.0
|
||||
source_url = https://github.com/catchorg/Catch2/archive/v3.4.0.tar.gz
|
||||
source_filename = Catch2-3.4.0.tar.gz
|
||||
source_hash = 122928b814b75717316c71af69bd2b43387643ba076a6ec16e7882bfb2dfacbb
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.4.0-1/Catch2-3.4.0.tar.gz
|
||||
wrapdb_version = 3.4.0-1
|
||||
directory = Catch2-3.5.1
|
||||
source_url = https://github.com/catchorg/Catch2/archive/v3.5.1.tar.gz
|
||||
source_filename = Catch2-3.5.1.tar.gz
|
||||
source_hash = 49c3ca7a68f1c8ec71307736bc6ed14fec21631707e1be9af45daf4037e75a08
|
||||
# source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.4.0-1/Catch2-3.4.0.tar.gz
|
||||
# wrapdb_version = 3.4.0-1
|
||||
|
||||
[provide]
|
||||
catch2 = catch2_dep
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
[wrap-file]
|
||||
directory = gtk-layer-shell-0.4.0
|
||||
source_filename = gtk-layer-shell-0.4.0.tar.gz
|
||||
source_hash = 52fd74d3161fefa5528585ca5a523c3150934961f2284ad010ae54336dad097e
|
||||
source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.4.0/gtk-layer-shell-0.4.0.tar.gz
|
||||
9
subprojects/gtk4-layer-shell.wrap
Normal file
9
subprojects/gtk4-layer-shell.wrap
Normal file
@@ -0,0 +1,9 @@
|
||||
[wrap-file]
|
||||
directory = gtk4-layer-shell-1.0.2
|
||||
source_filename = gtk4-layer-shell-1.0.2.tar.gz
|
||||
source_hash = 55fa593241cad200fcb2b647b4dcc0d5d5516792e21a5b65662f010e224d7a73
|
||||
#source_hash = a3a827845612fa15de30734eb29c01db329c32f8e279d8bc5251facc69220b86
|
||||
source_url = https://github.com/wmww/gtk4-layer-shell/archive/v1.0.2/gtk4-layer-shell-1.0.2.tar.gz
|
||||
|
||||
[provide]
|
||||
gtk-layer-shell = gtk_layer_shell
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "util/date.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
@@ -20,13 +19,13 @@
|
||||
#endif
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
namespace fmt_lib = waybar::util::date::format;
|
||||
/*
|
||||
* Check that the date/time formatter with locale and timezone support is working as expected.
|
||||
*/
|
||||
|
||||
const date::zoned_time<std::chrono::seconds> TEST_TIME = date::zoned_time{
|
||||
"UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s};
|
||||
const zoned_time<std::chrono::seconds> TEST_TIME{
|
||||
"UTC", local_days{Monday[1] / January / 2022} + 13h + 4min + 5s};
|
||||
|
||||
/*
|
||||
* Check if the date formatted with LC_TIME=en_US is within expectations.
|
||||
@@ -52,10 +51,11 @@ static const bool LC_TIME_is_sane = []() {
|
||||
TEST_CASE("Format UTC time", "[clock][util]") {
|
||||
const auto loc = std::locale("C");
|
||||
const auto tm = TEST_TIME;
|
||||
|
||||
CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||
CHECK(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC");
|
||||
CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
||||
#if not HAVE_CHRONO_TIMEZONES
|
||||
CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified
|
||||
#endif
|
||||
CHECK(fmt_lib::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC");
|
||||
CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103130405");
|
||||
|
||||
if (!LC_TIME_is_sane) {
|
||||
SKIP("Locale support check failed, skip tests");
|
||||
@@ -66,11 +66,15 @@ TEST_CASE("Format UTC time", "[clock][util]") {
|
||||
try {
|
||||
const auto loc = std::locale("en_US.UTF-8");
|
||||
|
||||
CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||
#if not HAVE_CHRONO_TIMEZONES
|
||||
CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt_lib::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||
Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM"));
|
||||
CHECK(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 01:04:05 PM");
|
||||
CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
||||
CHECK(fmt_lib::format(loc, "{:%x %X}", tm) == "01/03/2022 01:04:05 PM");
|
||||
#else
|
||||
CHECK(fmt_lib::format(loc, "{:%F %r}", tm) == "2022-01-03 01:04:05 PM");
|
||||
#endif
|
||||
CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103130405");
|
||||
} catch (const std::runtime_error &) {
|
||||
WARN("Locale en_US not found, skip tests");
|
||||
}
|
||||
@@ -79,11 +83,15 @@ TEST_CASE("Format UTC time", "[clock][util]") {
|
||||
try {
|
||||
const auto loc = std::locale("en_GB.UTF-8");
|
||||
|
||||
CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||
#if not HAVE_CHRONO_TIMEZONES
|
||||
CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt_lib::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||
Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05"));
|
||||
CHECK(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 13:04:05");
|
||||
CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
||||
CHECK(fmt_lib::format(loc, "{:%x %X}", tm) == "03/01/22 13:04:05");
|
||||
#else
|
||||
CHECK(fmt_lib::format(loc, "{:%F %T}", tm) == "2022-01-03 13:04:05");
|
||||
#endif
|
||||
CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103130405");
|
||||
} catch (const std::runtime_error &) {
|
||||
WARN("Locale en_GB not found, skip tests");
|
||||
}
|
||||
@@ -92,11 +100,15 @@ TEST_CASE("Format UTC time", "[clock][util]") {
|
||||
try {
|
||||
const auto loc = std::locale::global(std::locale("en_US.UTF-8"));
|
||||
|
||||
CHECK(fmt::format("{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
|
||||
#if not HAVE_CHRONO_TIMEZONES
|
||||
CHECK(fmt_lib::format("{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt_lib::format("{:%c}", tm), // HowardHinnant/date#704
|
||||
Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM"));
|
||||
CHECK(fmt::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM");
|
||||
CHECK(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
|
||||
CHECK(fmt_lib::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM");
|
||||
#else
|
||||
CHECK(fmt_lib::format("{:%F %r}", tm) == "2022-01-03 01:04:05 PM");
|
||||
#endif
|
||||
CHECK(fmt_lib::format("{:%Y%m%d%H%M%S}", tm) == "20220103130405");
|
||||
|
||||
std::locale::global(loc);
|
||||
} catch (const std::runtime_error &) {
|
||||
@@ -107,11 +119,13 @@ TEST_CASE("Format UTC time", "[clock][util]") {
|
||||
|
||||
TEST_CASE("Format zoned time", "[clock][util]") {
|
||||
const auto loc = std::locale("C");
|
||||
const auto tm = date::zoned_time{"America/New_York", TEST_TIME};
|
||||
const auto tm = zoned_time{"America/New_York", TEST_TIME};
|
||||
|
||||
CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||
CHECK(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST");
|
||||
CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
||||
#if not HAVE_CHRONO_TIMEZONES
|
||||
CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified
|
||||
#endif
|
||||
CHECK(fmt_lib::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST");
|
||||
CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103080405");
|
||||
|
||||
if (!LC_TIME_is_sane) {
|
||||
SKIP("Locale support check failed, skip tests");
|
||||
@@ -122,11 +136,15 @@ TEST_CASE("Format zoned time", "[clock][util]") {
|
||||
try {
|
||||
const auto loc = std::locale("en_US.UTF-8");
|
||||
|
||||
CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||
#if not HAVE_CHRONO_TIMEZONES
|
||||
CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt_lib::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM"));
|
||||
CHECK(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 08:04:05 AM");
|
||||
CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
||||
CHECK(fmt_lib::format(loc, "{:%x %X}", tm) == "01/03/2022 08:04:05 AM");
|
||||
#else
|
||||
CHECK(fmt_lib::format(loc, "{:%F %r}", tm) == "2022-01-03 08:04:05 AM");
|
||||
#endif
|
||||
CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103080405");
|
||||
} catch (const std::runtime_error &) {
|
||||
WARN("Locale en_US not found, skip tests");
|
||||
}
|
||||
@@ -135,11 +153,15 @@ TEST_CASE("Format zoned time", "[clock][util]") {
|
||||
try {
|
||||
const auto loc = std::locale("en_GB.UTF-8");
|
||||
|
||||
CHECK(fmt::format(loc, "{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||
#if not HAVE_CHRONO_TIMEZONES
|
||||
CHECK(fmt_lib::format(loc, "{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt_lib::format(loc, "{:%c}", tm), // HowardHinnant/date#704
|
||||
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05"));
|
||||
CHECK(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 08:04:05");
|
||||
CHECK(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
||||
CHECK(fmt_lib::format(loc, "{:%x %X}", tm) == "03/01/22 08:04:05");
|
||||
#else
|
||||
CHECK(fmt_lib::format(loc, "{:%F %T}", tm) == "2022-01-03 08:04:05");
|
||||
#endif
|
||||
CHECK(fmt_lib::format(loc, "{:%Y%m%d%H%M%S}", tm) == "20220103080405");
|
||||
} catch (const std::runtime_error &) {
|
||||
WARN("Locale en_GB not found, skip tests");
|
||||
}
|
||||
@@ -148,11 +170,15 @@ TEST_CASE("Format zoned time", "[clock][util]") {
|
||||
try {
|
||||
const auto loc = std::locale::global(std::locale("en_US.UTF-8"));
|
||||
|
||||
CHECK(fmt::format("{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
|
||||
#if not HAVE_CHRONO_TIMEZONES
|
||||
CHECK(fmt_lib::format("{}", tm).empty()); // no format specified
|
||||
CHECK_THAT(fmt_lib::format("{:%c}", tm), // HowardHinnant/date#704
|
||||
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM"));
|
||||
CHECK(fmt::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM");
|
||||
CHECK(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
|
||||
CHECK(fmt_lib::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM");
|
||||
#else
|
||||
CHECK(fmt_lib::format("{:%F %r}", tm) == "2022-01-03 08:04:05 AM");
|
||||
#endif
|
||||
CHECK(fmt_lib::format("{:%Y%m%d%H%M%S}", tm) == "20220103080405");
|
||||
|
||||
std::locale::global(loc);
|
||||
} catch (const std::runtime_error &) {
|
||||
|
||||
Reference in New Issue
Block a user