mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
Compare commits
384 Commits
Author | SHA1 | Date | |
---|---|---|---|
f0bead34d4 | |||
d6bd440027 | |||
c692d7bf64 | |||
2045aac5b0 | |||
a92223c316 | |||
24d03d13ce | |||
b3b5d8f9ab | |||
86850f5c7a | |||
0bc5314e08 | |||
1d6cfe7ce6 | |||
2a3ebc12de | |||
1938bb5d28 | |||
a35861b3b9 | |||
8b512e7b22 | |||
fb083f93dc | |||
f795e7a308 | |||
21abd4f9f9 | |||
f724cc3f9d | |||
bfbb2f9a40 | |||
91357f210d | |||
3e48551f25 | |||
c05f41d732 | |||
4d59de42af | |||
6e296838e4 | |||
e00e36981e | |||
4136ffaecb | |||
bd199e414b | |||
531bdfb8bb | |||
c1ea7626b9 | |||
995802e8ae | |||
0079092699 | |||
b5c686c0dd | |||
4c4d09992e | |||
9218968d2f | |||
a08967e008 | |||
272c638f7e | |||
57ad7f9536 | |||
2a76d8e5b9 | |||
c5babb4c44 | |||
328575a721 | |||
ea9078d887 | |||
b1833b1f36 | |||
53e89dace7 | |||
3cbcef61cf | |||
22084691ff | |||
f4afa59861 | |||
ce8c13788a | |||
b74f3c7aaa | |||
2111865efe | |||
94d6ae9741 | |||
e6760bf9dd | |||
7671ccfbc6 | |||
459541ed89 | |||
da3d9533d1 | |||
8db1996ccc | |||
cfef78a5bc | |||
60fa5e9f67 | |||
cea59ddc6c | |||
3730793197 | |||
d2b4076ac8 | |||
e63e3a0ca9 | |||
99d370d9ed | |||
80b2b29a77 | |||
27ad9ec267 | |||
9eb6c4e296 | |||
748c6125d0 | |||
235861fd3d | |||
5e9bbe5c76 | |||
74fa131ebe | |||
2c7cb0e9d4 | |||
ce8ae5bf17 | |||
062e7bb9b4 | |||
3acd31c3e9 | |||
456e06c4b5 | |||
a2751cfcd6 | |||
d9cc995405 | |||
00a2ebf00d | |||
c2f98d07ef | |||
833dcc1bb8 | |||
8c24e26f0e | |||
56b4a11a9c | |||
1111763251 | |||
769858fbb4 | |||
2695815bcc | |||
49afb87e34 | |||
5250123dcb | |||
c0b3e9ee35 | |||
454ba610f4 | |||
3718902b9d | |||
9f0a14c22b | |||
781da93f3d | |||
8f4f67f69f | |||
8be5bab8ad | |||
d02e23c759 | |||
d2b22c6ec5 | |||
ed898cd211 | |||
1a1c617520 | |||
253222d31c | |||
51e6fc6250 | |||
af1668dfd0 | |||
cf5877073a | |||
bd567800c9 | |||
6477e539d0 | |||
242e19a07d | |||
0e53c37d6b | |||
3030850b22 | |||
92cc01f401 | |||
d48eebd4d3 | |||
eb705533b5 | |||
3cf027fc56 | |||
09120caf17 | |||
73495df377 | |||
fd417c0805 | |||
afa590f781 | |||
df36ac3408 | |||
ebdf575d45 | |||
a4d27ea806 | |||
a10266ceee | |||
31137c30fb | |||
c374c412d3 | |||
2fec1d4907 | |||
be28ee3d7e | |||
2d7e21ed7d | |||
930a3e168b | |||
7948d03d25 | |||
ff61e7bf4e | |||
64849f52c9 | |||
1374b0fce4 | |||
1ceaff27c2 | |||
527017baca | |||
f330e51472 | |||
411c6f4b4b | |||
e8e8ccb6cf | |||
a24f2d72a7 | |||
ffa458223d | |||
aa8bd51952 | |||
05dbfe261a | |||
1f591e36f1 | |||
d0677c1801 | |||
c18c6b080a | |||
7240611d87 | |||
e1045381fe | |||
e660a3634d | |||
54e04b5a30 | |||
662a250705 | |||
f72c1a54d3 | |||
6b221133c2 | |||
d01fda6fae | |||
692b90c995 | |||
dea2d721eb | |||
72a2ada82c | |||
6156a62294 | |||
d4d35e2f89 | |||
a58988ea9d | |||
0ada5ac8b0 | |||
1421163df3 | |||
9d5f0e45c0 | |||
45e44e03bd | |||
830c5cd5d0 | |||
90f206f92a | |||
59e7f1974c | |||
97ae2ff343 | |||
3d63080346 | |||
cb842d9d50 | |||
a7e6330078 | |||
93807b0b3e | |||
6e73c58e60 | |||
209225e381 | |||
7746328daa | |||
c7d475ee86 | |||
4ed13df092 | |||
33c3ab35a8 | |||
4dfea72db0 | |||
504132dc55 | |||
debbfccf07 | |||
56ec72c31c | |||
27c6c96b37 | |||
8551c4bbe3 | |||
58362abfaf | |||
2abeba2b52 | |||
bfa3adcfd6 | |||
2db6fc8b1b | |||
c2dd296d31 | |||
5b0c5ea9ce | |||
c7bb0ae0af | |||
b2f90dffe1 | |||
f86dff60e6 | |||
1db3c55b48 | |||
35254ee834 | |||
9a0013cb10 | |||
cca5227210 | |||
cf9d98a0be | |||
015409acaf | |||
2b735f44bc | |||
8fa5d9b838 | |||
0012bcbd74 | |||
b8322c4b4b | |||
07050cf354 | |||
ddf3e11240 | |||
1ca660460a | |||
0898236586 | |||
c3e91cd228 | |||
c500c7d9a1 | |||
5da45ece9d | |||
024777a5bc | |||
9758833027 | |||
9a958f6848 | |||
9e03bb61c7 | |||
710f89599e | |||
d1700bf202 | |||
e1b31db42b | |||
52e9f624be | |||
e75eafcb34 | |||
6558a156b3 | |||
faf8954712 | |||
e58f1fd3e0 | |||
6b83360e76 | |||
03ca8de6d7 | |||
ac193ae669 | |||
38d2815425 | |||
79f21c0d7b | |||
0306c97173 | |||
8a82cdff16 | |||
29bdff5314 | |||
eb017347b8 | |||
912d7f8588 | |||
5647146ac0 | |||
af2a3f8bda | |||
55e83f90d1 | |||
0d94853613 | |||
120c68e014 | |||
4deb6d812d | |||
bc201fd0eb | |||
d2ff116c92 | |||
e3342467fc | |||
ce10ce0d5e | |||
4a929240df | |||
33d13af6d1 | |||
90878a5c98 | |||
0d27949f0a | |||
f6322d2dd1 | |||
330d166c82 | |||
5f2dd99e6d | |||
8b03e38594 | |||
5944989a8a | |||
58a399b9af | |||
dcd75b3b40 | |||
17f91391b6 | |||
061f4550f4 | |||
fd24d7bcf6 | |||
51670f0506 | |||
4e930ba50a | |||
8839a86afe | |||
f4bfe777d9 | |||
59e57ab9a0 | |||
f00f30a5ae | |||
40bc2e96db | |||
9ac9dc368e | |||
39c170bf10 | |||
5fea01300c | |||
b181cd04b6 | |||
d786f9a0e6 | |||
c5910ae19a | |||
ed6467e785 | |||
43c3ca1d38 | |||
97f0d6fa42 | |||
b8a68b8085 | |||
8881b9a6ef | |||
e8942feefc | |||
a23d58e900 | |||
16d5619f3b | |||
bcee4e15d3 | |||
b7bd06ad8f | |||
e50c246601 | |||
ee504b826d | |||
848ae1f818 | |||
406eb0ee9a | |||
112d481ae7 | |||
872cd6083d | |||
8dc78e4e40 | |||
e662b8c624 | |||
e0451816e2 | |||
e2e59a52df | |||
123ed36739 | |||
c64058c947 | |||
56d46e62c1 | |||
89a57f6722 | |||
4336f10b29 | |||
a7979a3e56 | |||
bfed2114e4 | |||
f65a372855 | |||
6f3fe6d339 | |||
c287b0c82b | |||
5b1cd65e20 | |||
99ed2bb7fa | |||
ddd5b4e157 | |||
e9e5780aae | |||
061ad13082 | |||
77bea7c182 | |||
c2ab2e6d19 | |||
11239a4900 | |||
95b5348c24 | |||
9616df58da | |||
7b115913de | |||
4029c5423f | |||
3996764880 | |||
60821257ac | |||
e14005a6aa | |||
15dbe8965e | |||
decc5bcd68 | |||
92870cab2a | |||
4cb2cc9f21 | |||
02df861829 | |||
23eaffc04b | |||
714451e4f9 | |||
4cd6024f07 | |||
8b5f42d934 | |||
b65c976bc1 | |||
5e7c9378df | |||
a9569e7d5c | |||
318a6e0969 | |||
a1d046b2e7 | |||
c7b09eea11 | |||
bc8517fd08 | |||
9439e4183c | |||
8fc8bb40bf | |||
d906080f26 | |||
04d66de866 | |||
699f732146 | |||
f437bf96e3 | |||
fc9a390977 | |||
56a45e962b | |||
48d2759df5 | |||
1116ff0d67 | |||
0c04aea108 | |||
a44622aa9f | |||
3117aefdf3 | |||
24a8332b62 | |||
84e7689521 | |||
0708573fa4 | |||
08d472d1b1 | |||
c35f91ed7a | |||
9c3af1b6ad | |||
17b60bc737 | |||
c1f92d2a3c | |||
72f478c195 | |||
5128a5d9f3 | |||
36aa22189b | |||
d10d9b8202 | |||
e57899c0c5 | |||
249c0aad73 | |||
18a4f87a59 | |||
458c03bf95 | |||
1b4a7b02f4 | |||
fb2ac8a765 | |||
13100326b0 | |||
ca0d35286d | |||
f3a049c6df | |||
074b7c4b99 | |||
b24fd35add | |||
c27dab9379 | |||
6857691679 | |||
a475be7cf7 | |||
00c11c64ca | |||
4e2305639b | |||
e0f29dbf71 | |||
36d3d511d6 | |||
ae9fb57790 | |||
b8ee448e71 | |||
632058a4f6 | |||
d25278f710 | |||
2dfd64e1c9 | |||
3c182c9ca9 | |||
e094480684 | |||
ccce2b700b | |||
41dea6e46c | |||
65c3f0a132 | |||
e6262b870c | |||
823ed887ab | |||
3bf815f6de | |||
c1cda1553a | |||
f6ee90e5ba | |||
d5c400c0cc | |||
a650c7d90c |
9
.github/workflows/freebsd.yml
vendored
9
.github/workflows/freebsd.yml
vendored
@ -4,14 +4,14 @@ on: [ push, pull_request ]
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
clang:
|
clang:
|
||||||
# Run actions in a FreeBSD vm on the macos-10.15 runner
|
# Run actions in a FreeBSD VM on the macos-12 runner
|
||||||
# https://github.com/actions/runner/issues/385 - for FreeBSD runner support
|
# https://github.com/actions/runner/issues/385 - for FreeBSD runner support
|
||||||
# https://github.com/actions/virtual-environments/issues/4060 - for lack of VirtualBox on MacOS 11 runners
|
# https://github.com/actions/virtual-environments/issues/4060 - for lack of VirtualBox on MacOS 11 runners
|
||||||
runs-on: macos-10.15
|
runs-on: macos-12
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Test in FreeBSD VM
|
- name: Test in FreeBSD VM
|
||||||
uses: vmactions/freebsd-vm@v0.1.6 # aka FreeBSD 13.0
|
uses: vmactions/freebsd-vm@v0
|
||||||
with:
|
with:
|
||||||
mem: 2048
|
mem: 2048
|
||||||
usesh: true
|
usesh: true
|
||||||
@ -21,7 +21,8 @@ jobs:
|
|||||||
pkg install -y git # subprojects/date
|
pkg install -y git # subprojects/date
|
||||||
pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \
|
pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \
|
||||||
libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \
|
libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \
|
||||||
pkgconf pulseaudio scdoc sndio spdlog wayland-protocols
|
pkgconf pulseaudio scdoc sndio spdlog wayland-protocols upower \
|
||||||
|
libinotify
|
||||||
run: |
|
run: |
|
||||||
meson build -Dman-pages=enabled
|
meson build -Dman-pages=enabled
|
||||||
ninja -C build
|
ninja -C build
|
||||||
|
1
.github/workflows/linux.yml
vendored
1
.github/workflows/linux.yml
vendored
@ -12,6 +12,7 @@ jobs:
|
|||||||
- debian
|
- debian
|
||||||
- fedora
|
- fedora
|
||||||
- opensuse
|
- opensuse
|
||||||
|
- gentoo
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,6 +2,8 @@
|
|||||||
*~
|
*~
|
||||||
vgcore.*
|
vgcore.*
|
||||||
/.vscode
|
/.vscode
|
||||||
|
/.idea
|
||||||
|
/.cache
|
||||||
*.swp
|
*.swp
|
||||||
packagecache
|
packagecache
|
||||||
/subprojects/**/
|
/subprojects/**/
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon tzdata
|
RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon tzdata playerctl-dev
|
||||||
|
@ -3,4 +3,4 @@
|
|||||||
FROM archlinux:base-devel
|
FROM archlinux:base-devel
|
||||||
|
|
||||||
RUN pacman -Syu --noconfirm && \
|
RUN pacman -Syu --noconfirm && \
|
||||||
pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm libxkbcommon
|
pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
FROM debian:sid
|
FROM debian:sid
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 && \
|
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 libplayerctl-dev && \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
@ -8,5 +8,6 @@ RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \
|
|||||||
'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \
|
'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \
|
||||||
'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \
|
'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \
|
||||||
'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \
|
'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \
|
||||||
'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' && \
|
'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' \
|
||||||
|
'pkgconfig(playerctl)' && \
|
||||||
dnf clean all -y
|
dnf clean all -y
|
||||||
|
11
Dockerfiles/gentoo
Normal file
11
Dockerfiles/gentoo
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# vim: ft=Dockerfile
|
||||||
|
|
||||||
|
FROM gentoo/stage3:latest
|
||||||
|
|
||||||
|
RUN export FEATURES="-ipc-sandbox -network-sandbox -pid-sandbox -sandbox -usersandbox" && \
|
||||||
|
emerge --sync && \
|
||||||
|
eselect news read --quiet new 1>/dev/null 2>&1 && \
|
||||||
|
emerge --verbose --update --deep --with-bdeps=y --backtrack=30 --newuse @world && \
|
||||||
|
USE="wayland gtk3 gtk -doc X" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols =dev-cpp/gtkmm-3.24.6 x11-libs/libxkbcommon \
|
||||||
|
x11-libs/gtk+:3 dev-libs/libdbusmenu dev-libs/libnl sys-power/upower media-libs/libpulse dev-libs/libevdev media-libs/libmpdclient \
|
||||||
|
media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl
|
@ -6,4 +6,4 @@ RUN zypper -n up && \
|
|||||||
zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \
|
zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \
|
||||||
zypper -n refresh && \
|
zypper -n refresh && \
|
||||||
zypper -n install -t pattern devel_C_C++ && \
|
zypper -n install -t pattern devel_C_C++ && \
|
||||||
zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc
|
zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc playerctl-devel
|
||||||
|
@ -7,18 +7,23 @@
|
|||||||
|
|
||||||
#### Current features
|
#### Current features
|
||||||
- Sway (Workspaces, Binding mode, Focused window name)
|
- Sway (Workspaces, Binding mode, Focused window name)
|
||||||
|
- River (Mapping mode, Tags, Focused window name)
|
||||||
|
- Hyprland (Focused window name)
|
||||||
- Tray [#21](https://github.com/Alexays/Waybar/issues/21)
|
- Tray [#21](https://github.com/Alexays/Waybar/issues/21)
|
||||||
- Local time
|
- Local time
|
||||||
- Battery
|
- Battery
|
||||||
|
- UPower
|
||||||
- Network
|
- Network
|
||||||
- Bluetooth
|
- Bluetooth
|
||||||
- Pulseaudio
|
- Pulseaudio
|
||||||
|
- Wireplumber
|
||||||
- Disk
|
- Disk
|
||||||
- Memory
|
- Memory
|
||||||
- Cpu load average
|
- Cpu load average
|
||||||
- Temperature
|
- Temperature
|
||||||
- MPD
|
- MPD
|
||||||
- Custom scripts
|
- Custom scripts
|
||||||
|
- Custom image
|
||||||
- Multiple output configuration
|
- Multiple output configuration
|
||||||
- And many more customizations
|
- And many more customizations
|
||||||
|
|
||||||
@ -70,6 +75,7 @@ libmpdclient [MPD module]
|
|||||||
libsndio [sndio module]
|
libsndio [sndio module]
|
||||||
libevdev [KeyboardState module]
|
libevdev [KeyboardState module]
|
||||||
xkbregistry
|
xkbregistry
|
||||||
|
upower [UPower battery module]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Build dependencies**
|
**Build dependencies**
|
||||||
@ -103,6 +109,7 @@ sudo apt install \
|
|||||||
libspdlog-dev \
|
libspdlog-dev \
|
||||||
libwayland-dev \
|
libwayland-dev \
|
||||||
scdoc \
|
scdoc \
|
||||||
|
upower \
|
||||||
libxkbregistry-dev
|
libxkbregistry-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -48,10 +48,10 @@ class AModule : public IModule {
|
|||||||
{std::make_pair(3, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-right"},
|
{std::make_pair(3, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-right"},
|
||||||
{std::make_pair(8, GdkEventType::GDK_BUTTON_PRESS), "on-click-backward"},
|
{std::make_pair(8, GdkEventType::GDK_BUTTON_PRESS), "on-click-backward"},
|
||||||
{std::make_pair(8, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-backward"},
|
{std::make_pair(8, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-backward"},
|
||||||
{std::make_pair(8, GdkEventType::GDK_2BUTTON_PRESS), "on-triple-click-backward"},
|
{std::make_pair(8, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-backward"},
|
||||||
{std::make_pair(9, GdkEventType::GDK_BUTTON_PRESS), "on-click-forward"},
|
{std::make_pair(9, GdkEventType::GDK_BUTTON_PRESS), "on-click-forward"},
|
||||||
{std::make_pair(9, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-forward"},
|
{std::make_pair(9, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-forward"},
|
||||||
{std::make_pair(9, GdkEventType::GDK_2BUTTON_PRESS), "on-triple-click-forward"}};
|
{std::make_pair(9, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-forward"}};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar
|
} // namespace waybar
|
||||||
|
@ -14,6 +14,7 @@ namespace waybar {
|
|||||||
class Config {
|
class Config {
|
||||||
public:
|
public:
|
||||||
static const std::vector<std::string> CONFIG_DIRS;
|
static const std::vector<std::string> CONFIG_DIRS;
|
||||||
|
static const char *CONFIG_PATH_ENV;
|
||||||
|
|
||||||
/* Try to find any of provided names in the supported set of config directories */
|
/* Try to find any of provided names in the supported set of config directories */
|
||||||
static std::optional<std::string> findConfigPath(
|
static std::optional<std::string> findConfigPath(
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#ifdef HAVE_SWAY
|
#ifdef HAVE_SWAY
|
||||||
#include "modules/sway/language.hpp"
|
#include "modules/sway/language.hpp"
|
||||||
#include "modules/sway/mode.hpp"
|
#include "modules/sway/mode.hpp"
|
||||||
|
#include "modules/sway/scratchpad.hpp"
|
||||||
#include "modules/sway/window.hpp"
|
#include "modules/sway/window.hpp"
|
||||||
#include "modules/sway/workspaces.hpp"
|
#include "modules/sway/workspaces.hpp"
|
||||||
#endif
|
#endif
|
||||||
@ -17,10 +18,17 @@
|
|||||||
#include "modules/wlr/workspace_manager.hpp"
|
#include "modules/wlr/workspace_manager.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_RIVER
|
#ifdef HAVE_RIVER
|
||||||
|
#include "modules/river/mode.hpp"
|
||||||
#include "modules/river/tags.hpp"
|
#include "modules/river/tags.hpp"
|
||||||
#include "modules/river/window.hpp"
|
#include "modules/river/window.hpp"
|
||||||
#endif
|
#endif
|
||||||
#if defined(__linux__) && !defined(NO_FILESYSTEM)
|
#ifdef HAVE_HYPRLAND
|
||||||
|
#include "modules/hyprland/backend.hpp"
|
||||||
|
#include "modules/hyprland/language.hpp"
|
||||||
|
#include "modules/hyprland/submap.hpp"
|
||||||
|
#include "modules/hyprland/window.hpp"
|
||||||
|
#endif
|
||||||
|
#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM))
|
||||||
#include "modules/battery.hpp"
|
#include "modules/battery.hpp"
|
||||||
#endif
|
#endif
|
||||||
#if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD)
|
#if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD)
|
||||||
@ -34,6 +42,9 @@
|
|||||||
#ifdef HAVE_DBUSMENU
|
#ifdef HAVE_DBUSMENU
|
||||||
#include "modules/sni/tray.hpp"
|
#include "modules/sni/tray.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_MPRIS
|
||||||
|
#include "modules/mpris/mpris.hpp"
|
||||||
|
#endif
|
||||||
#ifdef HAVE_LIBNL
|
#ifdef HAVE_LIBNL
|
||||||
#include "modules/network.hpp"
|
#include "modules/network.hpp"
|
||||||
#endif
|
#endif
|
||||||
@ -62,9 +73,17 @@
|
|||||||
#include "modules/bluetooth.hpp"
|
#include "modules/bluetooth.hpp"
|
||||||
#include "modules/inhibitor.hpp"
|
#include "modules/inhibitor.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_LIBJACK
|
||||||
|
#include "modules/jack.hpp"
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LIBWIREPLUMBER
|
||||||
|
#include "modules/wireplumber.hpp"
|
||||||
|
#endif
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "modules/custom.hpp"
|
#include "modules/custom.hpp"
|
||||||
|
#include "modules/image.hpp"
|
||||||
#include "modules/temperature.hpp"
|
#include "modules/temperature.hpp"
|
||||||
|
#include "modules/user.hpp"
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
|
@ -18,12 +18,14 @@ class Backlight : public ALabel {
|
|||||||
class BacklightDev {
|
class BacklightDev {
|
||||||
public:
|
public:
|
||||||
BacklightDev() = default;
|
BacklightDev() = default;
|
||||||
BacklightDev(std::string name, int actual, int max);
|
BacklightDev(std::string name, int actual, int max, bool powered);
|
||||||
std::string_view name() const;
|
std::string_view name() const;
|
||||||
int get_actual() const;
|
int get_actual() const;
|
||||||
void set_actual(int actual);
|
void set_actual(int actual);
|
||||||
int get_max() const;
|
int get_max() const;
|
||||||
void set_max(int max);
|
void set_max(int max);
|
||||||
|
bool get_powered() const;
|
||||||
|
void set_powered(bool powered);
|
||||||
friend inline bool operator==(const BacklightDev &lhs, const BacklightDev &rhs) {
|
friend inline bool operator==(const BacklightDev &lhs, const BacklightDev &rhs) {
|
||||||
return lhs.name_ == rhs.name_ && lhs.actual_ == rhs.actual_ && lhs.max_ == rhs.max_;
|
return lhs.name_ == rhs.name_ && lhs.actual_ == rhs.actual_ && lhs.max_ == rhs.max_;
|
||||||
}
|
}
|
||||||
@ -32,6 +34,7 @@ class Backlight : public ALabel {
|
|||||||
std::string name_;
|
std::string name_;
|
||||||
int actual_ = 1;
|
int actual_ = 1;
|
||||||
int max_ = 1;
|
int max_ = 1;
|
||||||
|
bool powered_ = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#endif
|
#endif
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#if defined(__linux__)
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -25,21 +25,23 @@ class Clock : public ALabel {
|
|||||||
std::locale locale_;
|
std::locale locale_;
|
||||||
std::vector<const date::time_zone*> time_zones_;
|
std::vector<const date::time_zone*> time_zones_;
|
||||||
int current_time_zone_idx_;
|
int current_time_zone_idx_;
|
||||||
date::year_month_day cached_calendar_ymd_ = date::January / 1 / 0;
|
date::year_month_day calendar_cached_ymd_{date::January / 1 / 0};
|
||||||
std::string cached_calendar_text_;
|
date::months calendar_shift_{0}, calendar_shift_init_{0};
|
||||||
|
std::string calendar_cached_text_;
|
||||||
bool is_calendar_in_tooltip_;
|
bool is_calendar_in_tooltip_;
|
||||||
bool is_timezoned_list_in_tooltip_;
|
bool is_timezoned_list_in_tooltip_;
|
||||||
|
|
||||||
bool handleScroll(GdkEventScroll* e);
|
bool handleScroll(GdkEventScroll* e);
|
||||||
|
|
||||||
|
std::string fmt_str_weeks_;
|
||||||
|
std::string fmt_str_calendar_;
|
||||||
|
int fmt_weeks_left_pad_{0};
|
||||||
auto calendar_text(const waybar_time& wtime) -> std::string;
|
auto calendar_text(const waybar_time& wtime) -> std::string;
|
||||||
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
|
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
|
||||||
auto first_day_of_week() -> date::weekday;
|
auto first_day_of_week() -> date::weekday;
|
||||||
const date::time_zone* current_timezone();
|
const date::time_zone* current_timezone();
|
||||||
auto print_iso_weeknum(std::ostream& os, int weeknum) -> void;
|
|
||||||
bool is_timezone_fixed();
|
bool is_timezone_fixed();
|
||||||
auto timezones_text(std::chrono::system_clock::time_point* now) -> std::string;
|
auto timezones_text(std::chrono::system_clock::time_point* now) -> std::string;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace modules
|
} // namespace modules
|
||||||
} // namespace waybar
|
} // namespace waybar
|
||||||
|
@ -30,6 +30,7 @@ class Custom : public ALabel {
|
|||||||
|
|
||||||
const std::string name_;
|
const std::string name_;
|
||||||
std::string text_;
|
std::string text_;
|
||||||
|
std::string id_;
|
||||||
std::string alt_;
|
std::string alt_;
|
||||||
std::string tooltip_;
|
std::string tooltip_;
|
||||||
std::vector<std::string> class_;
|
std::vector<std::string> class_;
|
||||||
|
36
include/modules/hyprland/backend.hpp
Normal file
36
include/modules/hyprland/backend.hpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
class EventHandler {
|
||||||
|
public:
|
||||||
|
virtual void onEvent(const std::string& ev) = 0;
|
||||||
|
virtual ~EventHandler() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPC {
|
||||||
|
public:
|
||||||
|
IPC() { startIPC(); }
|
||||||
|
|
||||||
|
void registerForIPC(const std::string&, EventHandler*);
|
||||||
|
void unregisterForIPC(EventHandler*);
|
||||||
|
|
||||||
|
std::string getSocket1Reply(const std::string& rq);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void startIPC();
|
||||||
|
void parseIPC(const std::string&);
|
||||||
|
|
||||||
|
std::mutex callbackMutex;
|
||||||
|
std::list<std::pair<std::string, EventHandler*>> callbacks;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<IPC> gIPC;
|
||||||
|
inline bool modulesReady = false;
|
||||||
|
}; // namespace waybar::modules::hyprland
|
29
include/modules/hyprland/language.hpp
Normal file
29
include/modules/hyprland/language.hpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "modules/hyprland/backend.hpp"
|
||||||
|
#include "util/json.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
class Language : public waybar::ALabel, public EventHandler {
|
||||||
|
public:
|
||||||
|
Language(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
|
~Language();
|
||||||
|
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onEvent(const std::string&);
|
||||||
|
|
||||||
|
void initLanguage();
|
||||||
|
std::string getShortFrom(const std::string&);
|
||||||
|
|
||||||
|
std::mutex mutex_;
|
||||||
|
const Bar& bar_;
|
||||||
|
util::JsonParser parser_;
|
||||||
|
std::string layoutName_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules::hyprland
|
26
include/modules/hyprland/submap.hpp
Normal file
26
include/modules/hyprland/submap.hpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "modules/hyprland/backend.hpp"
|
||||||
|
#include "util/json.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
class Submap : public waybar::ALabel, public EventHandler {
|
||||||
|
public:
|
||||||
|
Submap(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
|
~Submap();
|
||||||
|
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onEvent(const std::string&);
|
||||||
|
|
||||||
|
std::mutex mutex_;
|
||||||
|
const Bar& bar_;
|
||||||
|
util::JsonParser parser_;
|
||||||
|
std::string submap_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules::hyprland
|
31
include/modules/hyprland/window.hpp
Normal file
31
include/modules/hyprland/window.hpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "modules/hyprland/backend.hpp"
|
||||||
|
#include "util/json.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
class Window : public waybar::ALabel, public EventHandler {
|
||||||
|
public:
|
||||||
|
Window(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
|
~Window();
|
||||||
|
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int getActiveWorkspaceID(std::string);
|
||||||
|
std::string getLastWindowTitle(int);
|
||||||
|
void onEvent(const std::string&);
|
||||||
|
|
||||||
|
bool separate_outputs;
|
||||||
|
std::mutex mutex_;
|
||||||
|
const Bar& bar_;
|
||||||
|
util::JsonParser parser_;
|
||||||
|
std::string lastView;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules::hyprland
|
@ -20,6 +20,7 @@ class IdleInhibitor : public ALabel {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool handleToggle(GdkEventButton* const& e);
|
bool handleToggle(GdkEventButton* const& e);
|
||||||
|
void toggleStatus();
|
||||||
|
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
struct zwp_idle_inhibitor_v1* idle_inhibitor_;
|
struct zwp_idle_inhibitor_v1* idle_inhibitor_;
|
||||||
|
34
include/modules/image.hpp
Normal file
34
include/modules/image.hpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <gtkmm/image.h>
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "util/command.hpp"
|
||||||
|
#include "util/json.hpp"
|
||||||
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class Image : public AModule {
|
||||||
|
public:
|
||||||
|
Image(const std::string&, const std::string&, const Json::Value&);
|
||||||
|
auto update() -> void;
|
||||||
|
void refresh(int /*signal*/);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void delayWorker();
|
||||||
|
void handleEvent();
|
||||||
|
|
||||||
|
Gtk::Image image_;
|
||||||
|
std::string path_;
|
||||||
|
int size_;
|
||||||
|
int interval_;
|
||||||
|
|
||||||
|
util::SleeperThread thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
44
include/modules/jack.hpp
Normal file
44
include/modules/jack.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <jack/jack.h>
|
||||||
|
#include <jack/thread.h>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class JACK : public ALabel {
|
||||||
|
public:
|
||||||
|
JACK(const std::string &, const Json::Value &);
|
||||||
|
~JACK() = default;
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
int bufSize(jack_nframes_t size);
|
||||||
|
int sampleRate(jack_nframes_t rate);
|
||||||
|
int xrun();
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string JACKState();
|
||||||
|
|
||||||
|
jack_client_t *client_;
|
||||||
|
jack_nframes_t bufsize_;
|
||||||
|
jack_nframes_t samplerate_;
|
||||||
|
unsigned int xruns_;
|
||||||
|
float load_;
|
||||||
|
bool running_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::string state_;
|
||||||
|
util::SleeperThread thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
||||||
|
|
||||||
|
int bufSizeCallback(jack_nframes_t size, void *obj);
|
||||||
|
int sampleRateCallback(jack_nframes_t rate, void *obj);
|
||||||
|
int xrunCallback(void *obj);
|
||||||
|
void shutdownCallback(void *obj);
|
@ -1,19 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#if FMT_VERSION < 60000
|
|
||||||
#include <fmt/time.h>
|
|
||||||
#else
|
|
||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
#endif
|
|
||||||
#include <gtkmm/label.h>
|
#include <gtkmm/label.h>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "AModule.hpp"
|
#include "AModule.hpp"
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libevdev/libevdev.h>
|
#include <libevdev/libevdev.h>
|
||||||
|
#include <libinput.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
@ -25,6 +23,8 @@ class KeyboardState : public AModule {
|
|||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
auto tryAddDevice(const std::string&) -> void;
|
||||||
|
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
Gtk::Label numlock_label_;
|
Gtk::Label numlock_label_;
|
||||||
Gtk::Label capslock_label_;
|
Gtk::Label capslock_label_;
|
||||||
@ -36,11 +36,12 @@ class KeyboardState : public AModule {
|
|||||||
const std::chrono::seconds interval_;
|
const std::chrono::seconds interval_;
|
||||||
std::string icon_locked_;
|
std::string icon_locked_;
|
||||||
std::string icon_unlocked_;
|
std::string icon_unlocked_;
|
||||||
|
std::string devices_path_;
|
||||||
|
|
||||||
int fd_;
|
struct libinput* libinput_;
|
||||||
libevdev* dev_;
|
std::unordered_map<std::string, struct libinput_device*> libinput_devices_;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread libinput_thread_, hotplug_thread_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
@ -41,6 +41,7 @@ class MPD : public ALabel {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string getTag(mpd_tag_type type, unsigned idx = 0) const;
|
std::string getTag(mpd_tag_type type, unsigned idx = 0) const;
|
||||||
|
std::string getFilename() const;
|
||||||
void setLabel();
|
void setLabel();
|
||||||
std::string getStateIcon() const;
|
std::string getStateIcon() const;
|
||||||
std::string getOptionIcon(std::string optionName, bool activated) const;
|
std::string getOptionIcon(std::string optionName, bool activated) const;
|
||||||
|
68
include/modules/mpris/mpris.hpp
Normal file
68
include/modules/mpris/mpris.hpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "gtkmm/box.h"
|
||||||
|
#include "gtkmm/label.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <playerctl/playerctl.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::mpris {
|
||||||
|
|
||||||
|
class Mpris : public AModule {
|
||||||
|
public:
|
||||||
|
Mpris(const std::string&, const Json::Value&);
|
||||||
|
~Mpris();
|
||||||
|
auto update() -> void;
|
||||||
|
bool handleToggle(GdkEventButton* const&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static auto onPlayerNameAppeared(PlayerctlPlayerManager*, PlayerctlPlayerName*, gpointer) -> void;
|
||||||
|
static auto onPlayerNameVanished(PlayerctlPlayerManager*, PlayerctlPlayerName*, gpointer) -> void;
|
||||||
|
static auto onPlayerPlay(PlayerctlPlayer*, gpointer) -> void;
|
||||||
|
static auto onPlayerPause(PlayerctlPlayer*, gpointer) -> void;
|
||||||
|
static auto onPlayerStop(PlayerctlPlayer*, gpointer) -> void;
|
||||||
|
static auto onPlayerMetadata(PlayerctlPlayer*, GVariant*, gpointer) -> void;
|
||||||
|
|
||||||
|
struct PlayerInfo {
|
||||||
|
std::string name;
|
||||||
|
PlayerctlPlaybackStatus status;
|
||||||
|
std::string status_string;
|
||||||
|
|
||||||
|
std::optional<std::string> artist;
|
||||||
|
std::optional<std::string> album;
|
||||||
|
std::optional<std::string> title;
|
||||||
|
std::optional<std::string> length; // as HH:MM:SS
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getPlayerInfo() -> std::optional<PlayerInfo>;
|
||||||
|
auto getIcon(const Json::Value&, const std::string&) -> std::string;
|
||||||
|
|
||||||
|
Gtk::Box box_;
|
||||||
|
Gtk::Label label_;
|
||||||
|
|
||||||
|
// config
|
||||||
|
std::string format_;
|
||||||
|
std::string format_playing_;
|
||||||
|
std::string format_paused_;
|
||||||
|
std::string format_stopped_;
|
||||||
|
std::chrono::seconds interval_;
|
||||||
|
std::string player_;
|
||||||
|
std::vector<std::string> ignored_players_;
|
||||||
|
|
||||||
|
PlayerctlPlayerManager* manager;
|
||||||
|
PlayerctlPlayer* player;
|
||||||
|
std::string lastStatus;
|
||||||
|
std::string lastPlayer;
|
||||||
|
|
||||||
|
util::SleeperThread thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules::mpris
|
28
include/modules/river/mode.hpp
Normal file
28
include/modules/river/mode.hpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "river-status-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
namespace waybar::modules::river {
|
||||||
|
|
||||||
|
class Mode : public waybar::ALabel {
|
||||||
|
public:
|
||||||
|
Mode(const std::string &, const waybar::Bar &, const Json::Value &);
|
||||||
|
~Mode();
|
||||||
|
|
||||||
|
// Handlers for wayland events
|
||||||
|
void handle_mode(const char *mode);
|
||||||
|
|
||||||
|
struct zriver_status_manager_v1 *status_manager_;
|
||||||
|
struct wl_seat *seat_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const waybar::Bar &bar_;
|
||||||
|
std::string mode_;
|
||||||
|
struct zriver_seat_status_v1 *seat_status_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace waybar::modules::river */
|
@ -1,11 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#if FMT_VERSION < 60000
|
|
||||||
#include <fmt/time.h>
|
|
||||||
#else
|
|
||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
#endif
|
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
|
35
include/modules/sway/scratchpad.hpp
Normal file
35
include/modules/sway/scratchpad.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtkmm/label.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "client.hpp"
|
||||||
|
#include "modules/sway/ipc/client.hpp"
|
||||||
|
#include "util/json.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::sway {
|
||||||
|
class Scratchpad : public ALabel {
|
||||||
|
public:
|
||||||
|
Scratchpad(const std::string&, const Json::Value&);
|
||||||
|
~Scratchpad() = default;
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto getTree() -> void;
|
||||||
|
auto onCmd(const struct Ipc::ipc_response&) -> void;
|
||||||
|
auto onEvent(const struct Ipc::ipc_response&) -> void;
|
||||||
|
|
||||||
|
std::string tooltip_format_;
|
||||||
|
bool show_empty_;
|
||||||
|
bool tooltip_enabled_;
|
||||||
|
std::string tooltip_text_;
|
||||||
|
int count_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
Ipc ipc_;
|
||||||
|
util::JsonParser parser_;
|
||||||
|
};
|
||||||
|
} // namespace waybar::modules::sway
|
@ -21,10 +21,9 @@ class Window : public AIconLabel, public sigc::trackable {
|
|||||||
private:
|
private:
|
||||||
void onEvent(const struct Ipc::ipc_response&);
|
void onEvent(const struct Ipc::ipc_response&);
|
||||||
void onCmd(const struct Ipc::ipc_response&);
|
void onCmd(const struct Ipc::ipc_response&);
|
||||||
std::tuple<std::size_t, int, std::string, std::string, std::string> getFocusedNode(
|
std::tuple<std::size_t, int, std::string, std::string, std::string, std::string> getFocusedNode(
|
||||||
const Json::Value& nodes, std::string& output);
|
const Json::Value& nodes, std::string& output);
|
||||||
void getTree();
|
void getTree();
|
||||||
std::string rewriteTitle(const std::string& title);
|
|
||||||
void updateAppIconName();
|
void updateAppIconName();
|
||||||
void updateAppIcon();
|
void updateAppIcon();
|
||||||
|
|
||||||
@ -35,6 +34,7 @@ class Window : public AIconLabel, public sigc::trackable {
|
|||||||
std::string app_class_;
|
std::string app_class_;
|
||||||
std::string old_app_id_;
|
std::string old_app_id_;
|
||||||
std::size_t app_nb_;
|
std::size_t app_nb_;
|
||||||
|
std::string shell_;
|
||||||
unsigned app_icon_size_{24};
|
unsigned app_icon_size_{24};
|
||||||
bool update_app_icon_{true};
|
bool update_app_icon_{true};
|
||||||
std::string app_icon_name_;
|
std::string app_icon_name_;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "glibconfig.h"
|
#include "glibconfig.h"
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <libupower-glib/upower.h>
|
#include <libupower-glib/upower.h>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "gtkmm/box.h"
|
#include "gtkmm/box.h"
|
||||||
#include "gtkmm/label.h"
|
#include "gtkmm/label.h"
|
||||||
#include "gtkmm/window.h"
|
#include "gtkmm/window.h"
|
||||||
|
34
include/modules/user.hpp
Normal file
34
include/modules/user.hpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/chrono.h>
|
||||||
|
#include <gdkmm/pixbuf.h>
|
||||||
|
#include <glibmm/refptr.h>
|
||||||
|
|
||||||
|
#include "AIconLabel.hpp"
|
||||||
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
class User : public AIconLabel {
|
||||||
|
public:
|
||||||
|
User(const std::string&, const Json::Value&);
|
||||||
|
~User() = default;
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
bool handleToggle(GdkEventButton* const& e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
util::SleeperThread thread_;
|
||||||
|
|
||||||
|
static constexpr inline int defaultUserImageWidth_ = 20;
|
||||||
|
static constexpr inline int defaultUserImageHeight_ = 20;
|
||||||
|
|
||||||
|
long uptime_as_seconds();
|
||||||
|
std::string get_user_login() const;
|
||||||
|
std::string get_user_home_dir() const;
|
||||||
|
std::string get_default_user_avatar_path() const;
|
||||||
|
void init_default_user_avatar(int width, int height);
|
||||||
|
void init_user_avatar(const std::string& path, int width, int height);
|
||||||
|
void init_avatar(const Json::Value& config);
|
||||||
|
void init_update_worker();
|
||||||
|
};
|
||||||
|
} // namespace waybar::modules
|
39
include/modules/wireplumber.hpp
Normal file
39
include/modules/wireplumber.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <wp/wp.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class Wireplumber : public ALabel {
|
||||||
|
public:
|
||||||
|
Wireplumber(const std::string&, const Json::Value&);
|
||||||
|
~Wireplumber();
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loadRequiredApiModules();
|
||||||
|
void prepare();
|
||||||
|
void activatePlugins();
|
||||||
|
static void updateVolume(waybar::modules::Wireplumber* self);
|
||||||
|
static void updateNodeName(waybar::modules::Wireplumber* self);
|
||||||
|
static uint32_t getDefaultNodeId(waybar::modules::Wireplumber* self);
|
||||||
|
static void onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
|
||||||
|
static void onObjectManagerInstalled(waybar::modules::Wireplumber* self);
|
||||||
|
|
||||||
|
WpCore* wp_core_;
|
||||||
|
GPtrArray* apis_;
|
||||||
|
WpObjectManager* om_;
|
||||||
|
uint32_t pending_plugins_;
|
||||||
|
bool muted_;
|
||||||
|
double volume_;
|
||||||
|
uint32_t node_id_{0};
|
||||||
|
std::string node_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
@ -74,6 +74,10 @@ class Task {
|
|||||||
std::string app_id_;
|
std::string app_id_;
|
||||||
uint32_t state_ = 0;
|
uint32_t state_ = 0;
|
||||||
|
|
||||||
|
int32_t drag_start_x;
|
||||||
|
int32_t drag_start_y;
|
||||||
|
int32_t drag_start_button = -1;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string repr() const;
|
std::string repr() const;
|
||||||
std::string state_string(bool = false) const;
|
std::string state_string(bool = false) const;
|
||||||
@ -105,6 +109,12 @@ class Task {
|
|||||||
|
|
||||||
/* Callbacks for Gtk events */
|
/* Callbacks for Gtk events */
|
||||||
bool handle_clicked(GdkEventButton *);
|
bool handle_clicked(GdkEventButton *);
|
||||||
|
bool handle_button_release(GdkEventButton *);
|
||||||
|
bool handle_motion_notify(GdkEventMotion *);
|
||||||
|
void handle_drag_data_get(const Glib::RefPtr<Gdk::DragContext> &context,
|
||||||
|
Gtk::SelectionData &selection_data, guint info, guint time);
|
||||||
|
void handle_drag_data_received(const Glib::RefPtr<Gdk::DragContext> &context, int x, int y,
|
||||||
|
Gtk::SelectionData selection_data, guint info, guint time);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool operator==(const Task &) const;
|
bool operator==(const Task &) const;
|
||||||
|
@ -152,6 +152,7 @@ class WorkspaceManager : public AModule {
|
|||||||
|
|
||||||
bool sort_by_name_ = true;
|
bool sort_by_name_ = true;
|
||||||
bool sort_by_coordinates_ = true;
|
bool sort_by_coordinates_ = true;
|
||||||
|
bool sort_by_number_ = false;
|
||||||
bool all_outputs_ = false;
|
bool all_outputs_ = false;
|
||||||
bool active_only_ = false;
|
bool active_only_ = false;
|
||||||
bool creation_delayed_ = false;
|
bool creation_delayed_ = false;
|
||||||
|
@ -56,9 +56,10 @@ struct formatter<pow_format> {
|
|||||||
fraction /= base;
|
fraction /= base;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto max_width = 4 // coeff in {:.3g} format
|
auto number_width = 5 // coeff in {:.1f} format
|
||||||
+ 1 // prefix from units array
|
+ s.binary_; // potential 4th digit before the decimal point
|
||||||
+ s.binary_ // for the 'i' in GiB.
|
auto max_width = number_width + 1 // prefix from units array
|
||||||
|
+ s.binary_ // for the 'i' in GiB.
|
||||||
+ s.unit_.length();
|
+ s.unit_.length();
|
||||||
|
|
||||||
const char* format;
|
const char* format;
|
||||||
@ -69,15 +70,16 @@ struct formatter<pow_format> {
|
|||||||
case '<':
|
case '<':
|
||||||
return format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width);
|
return format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width);
|
||||||
case '=':
|
case '=':
|
||||||
format = "{coefficient:<4.3g}{padding}{prefix}{unit}";
|
format = "{coefficient:<{number_width}.1f}{padding}{prefix}{unit}";
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
default:
|
default:
|
||||||
format = "{coefficient:.3g}{prefix}{unit}";
|
format = "{coefficient:.1f}{prefix}{unit}";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return format_to(
|
return format_to(
|
||||||
ctx.out(), format, fmt::arg("coefficient", fraction),
|
ctx.out(), format, fmt::arg("coefficient", fraction),
|
||||||
|
fmt::arg("number_width", number_width),
|
||||||
fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : "")),
|
fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : "")),
|
||||||
fmt::arg("unit", s.unit_),
|
fmt::arg("unit", s.unit_),
|
||||||
fmt::arg("padding", pow ? ""
|
fmt::arg("padding", pow ? ""
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/ostream.h>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
|
#if (FMT_VERSION >= 90000)
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<Json::Value> : ostream_formatter {};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace waybar::util {
|
namespace waybar::util {
|
||||||
|
|
||||||
struct JsonParser {
|
struct JsonParser {
|
||||||
|
8
include/util/rewrite_title.hpp
Normal file
8
include/util/rewrite_title.hpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace waybar::util {
|
||||||
|
std::string rewriteTitle(const std::string&, const Json::Value&);
|
||||||
|
}
|
6
include/util/sanitize_str.hpp
Normal file
6
include/util/sanitize_str.hpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace waybar::util {
|
||||||
|
std::string sanitize_string(std::string str);
|
||||||
|
} // namespace waybar::util
|
@ -37,8 +37,8 @@ The *backlight* module displays the current backlight level.
|
|||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
*states*: ++
|
*states*: ++
|
||||||
typeof: array ++
|
typeof: object ++
|
||||||
A number of backlight states which get activated on certain brightness levels.
|
A number of backlight states which get activated on certain brightness levels. See *waybar-states(5)*.
|
||||||
|
|
||||||
*on-click*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
|
@ -33,13 +33,13 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
The interval in which the information gets polled.
|
The interval in which the information gets polled.
|
||||||
|
|
||||||
*states*: ++
|
*states*: ++
|
||||||
typeof: array ++
|
typeof: object ++
|
||||||
A number of battery states which get activated on certain capacity levels. See *waybar-states(5)*.
|
A number of battery states which get activated on certain capacity levels. See *waybar-states(5)*.
|
||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {capacity}% ++
|
default: {capacity}% ++
|
||||||
The format, how the time should be displayed.
|
The format, how information should be displayed.
|
||||||
|
|
||||||
*format-time*: ++
|
*format-time*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
@ -114,9 +114,10 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
|||||||
|
|
||||||
The *battery* module allows you to define how time should be formatted via *format-time*.
|
The *battery* module allows you to define how time should be formatted via *format-time*.
|
||||||
|
|
||||||
The two arguments are:
|
The three arguments are:
|
||||||
*{H}*: Hours
|
*{H}*: Hours
|
||||||
*{M}*: Minutes
|
*{M}*: Minutes
|
||||||
|
*{m}*: Zero-padded minutes
|
||||||
|
|
||||||
# CUSTOM FORMATS
|
# CUSTOM FORMATS
|
||||||
|
|
||||||
|
@ -42,6 +42,12 @@ Addressed by *bluetooth*
|
|||||||
typeof: string ++
|
typeof: string ++
|
||||||
This format is used when the displayed controller is connected to at least 1 device.
|
This format is used when the displayed controller is connected to at least 1 device.
|
||||||
|
|
||||||
|
*format-icons*: ++
|
||||||
|
typeof: array/object ++
|
||||||
|
Based on the current battery percentage (see section *EXPERIMENTAL BATTERY PERCENTAGE FEATURE*), the corresponding icon gets selected. ++
|
||||||
|
The order is *low* to *high*. Will only show the current battery percentage icon in the *\*-connected-battery* config options. ++
|
||||||
|
Or by the state if it is an object. It will fall back to the enabled state if its derivatives are not defined (on, off, connected).
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
@ -115,6 +121,8 @@ Addressed by *bluetooth*
|
|||||||
|
|
||||||
*{status}*: Status of the bluetooth device.
|
*{status}*: Status of the bluetooth device.
|
||||||
|
|
||||||
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
*{num_connections}*: Number of connections the displayed controller has.
|
*{num_connections}*: Number of connections the displayed controller has.
|
||||||
|
|
||||||
*{controller_address}*: Address of the displayed controller.
|
*{controller_address}*: Address of the displayed controller.
|
||||||
@ -129,7 +137,7 @@ Addressed by *bluetooth*
|
|||||||
|
|
||||||
*{device_alias}*: Alias of the displayed device.
|
*{device_alias}*: Alias of the displayed device.
|
||||||
|
|
||||||
*{device_enumerate}*: Show a list of all connected devices, each on a seperate line. Define the format of each device with the *tooltip-format-enumerate-connected* ++
|
*{device_enumerate}*: Show a list of all connected devices, each on a separate line. Define the format of each device with the *tooltip-format-enumerate-connected* ++
|
||||||
and/or *tooltip-format-enumerate-connected-battery* config options. Can only be used in the tooltip related format options.
|
and/or *tooltip-format-enumerate-connected-battery* config options. Can only be used in the tooltip related format options.
|
||||||
|
|
||||||
# EXPERIMENTAL BATTERY PERCENTAGE FEATURE
|
# EXPERIMENTAL BATTERY PERCENTAGE FEATURE
|
||||||
|
@ -42,7 +42,7 @@ The *cpu* module displays the current cpu utilization.
|
|||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
*states*: ++
|
*states*: ++
|
||||||
typeof: array ++
|
typeof: object ++
|
||||||
A number of cpu usage states which get activated on certain usage levels. See *waybar-states(5)*.
|
A number of cpu usage states which get activated on certain usage levels. See *waybar-states(5)*.
|
||||||
|
|
||||||
*on-click*: ++
|
*on-click*: ++
|
||||||
|
@ -32,7 +32,7 @@ Addressed by *disk*
|
|||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
*states*: ++
|
*states*: ++
|
||||||
typeof: array ++
|
typeof: object ++
|
||||||
A number of disk utilization states which get activated on certain percentage thresholds (percentage_used). See *waybar-states(5)*.
|
A number of disk utilization states which get activated on certain percentage thresholds (percentage_used). See *waybar-states(5)*.
|
||||||
|
|
||||||
*max-length*: ++
|
*max-length*: ++
|
||||||
|
@ -23,7 +23,7 @@ Feral Gamemode optimizations.
|
|||||||
|
|
||||||
*tooltip*: ++
|
*tooltip*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
defualt: true ++
|
default: true ++
|
||||||
Option to disable tooltip on hover.
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
*tooltip-format*: ++
|
*tooltip-format*: ++
|
||||||
|
43
man/waybar-hyprland-language.5.scd
Normal file
43
man/waybar-hyprland-language.5.scd
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
waybar-hyprland-language(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - hyprland language module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *language* module displays the currently selected language.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *hyprland/language*
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {} ++
|
||||||
|
The format, how information should be displayed. On {} the currently selected language is displayed.
|
||||||
|
|
||||||
|
*format-<lang>* ++
|
||||||
|
typeof: string++
|
||||||
|
Provide an alternative name to display per language where <lang> is the language of your choosing. Can be passed multiple times with multiple languages as shown by the example below.
|
||||||
|
|
||||||
|
*keyboard-name*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"hyprland/language": {
|
||||||
|
"format": "Lang: {}"
|
||||||
|
"format-en": "AMERICA, HELL YEAH!"
|
||||||
|
"format-tr": "As bayrakları"
|
||||||
|
"keyboard-name": "at-translated-set-2-keyboard"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#language*
|
82
man/waybar-hyprland-submap.5.scd
Normal file
82
man/waybar-hyprland-submap.5.scd
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
waybar-hyprland-submap(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - hyprland submap module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *submap* module displays the currently active submap similar to *sway/mode*.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *hyprland/submap*
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {} ++
|
||||||
|
The format, how information should be displayed. On {} the currently active submap is displayed.
|
||||||
|
|
||||||
|
*rotate*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
|
*max-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The maximum length in character the module should display.
|
||||||
|
|
||||||
|
*min-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The minimum length in characters the module should take up.
|
||||||
|
|
||||||
|
*align*: ++
|
||||||
|
typeof: float ++
|
||||||
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when clicked on the module.
|
||||||
|
|
||||||
|
*on-click-middle*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when middle-clicked on the module using mousewheel.
|
||||||
|
|
||||||
|
*on-click-right*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when you right clicked on the module.
|
||||||
|
|
||||||
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
*on-scroll-up*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling up on the module.
|
||||||
|
|
||||||
|
*on-scroll-down*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling down on the module.
|
||||||
|
|
||||||
|
*smooth-scrolling-threshold*: ++
|
||||||
|
typeof: double ++
|
||||||
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"hyprland/submap": {
|
||||||
|
"format": "✌️ {}",
|
||||||
|
"max-length": 8,
|
||||||
|
"tooltip": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#submap*
|
50
man/waybar-hyprland-window.5.scd
Normal file
50
man/waybar-hyprland-window.5.scd
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
waybar-hyprland-window(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - hyprland window module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *window* module displays the title of the currently focused window in Hyprland.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *hyprland/window*
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {} ++
|
||||||
|
The format, how information should be displayed. On {} the current window title is displayed.
|
||||||
|
|
||||||
|
*rewrite*: ++
|
||||||
|
typeof: object ++
|
||||||
|
Rules to rewrite window title. See *rewrite rules*.
|
||||||
|
|
||||||
|
# REWRITE RULES
|
||||||
|
|
||||||
|
*rewrite* is an object where keys are regular expressions and values are
|
||||||
|
rewrite rules if the expression matches. Rules may contain references to
|
||||||
|
captures of the expression.
|
||||||
|
|
||||||
|
Regular expression and replacement follow ECMA-script rules.
|
||||||
|
|
||||||
|
If no expression matches, the title is left unchanged.
|
||||||
|
|
||||||
|
Invalid expressions (e.g., mismatched parentheses) are skipped.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"hyprland/window": {
|
||||||
|
"format": "{}",
|
||||||
|
"rewrite": {
|
||||||
|
"(.*) - Mozilla Firefox": "🌎 $1",
|
||||||
|
"(.*) - zsh": "> [$1]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#window*
|
@ -63,6 +63,11 @@ screensaving, also known as "presentation mode".
|
|||||||
typeof: double ++
|
typeof: double ++
|
||||||
Threshold to be used when scrolling.
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
*start-activated*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: *false* ++
|
||||||
|
Whether the inhibit should be activated when starting waybar.
|
||||||
|
|
||||||
*timeout*: ++
|
*timeout*: ++
|
||||||
typeof: double ++
|
typeof: double ++
|
||||||
The number of minutes the inhibit should last.
|
The number of minutes the inhibit should last.
|
||||||
@ -72,6 +77,14 @@ screensaving, also known as "presentation mode".
|
|||||||
default: true ++
|
default: true ++
|
||||||
Option to disable tooltip on hover.
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
*tooltip-format-activated*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the inhibit is activated.
|
||||||
|
|
||||||
|
*tooltip-format-deactivated*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the inhibit is deactivated.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{status}*: status (*activated* or *deactivated*)
|
*{status}*: status (*activated* or *deactivated*)
|
||||||
|
72
man/waybar-image.5.scd
Normal file
72
man/waybar-image.5.scd
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
waybar-custom(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - image module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *image* module displays an image from a path.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *custom/<name>*
|
||||||
|
|
||||||
|
*path*: ++
|
||||||
|
typeof: string ++
|
||||||
|
The path to the image.
|
||||||
|
|
||||||
|
*size*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The width/height to render the image.
|
||||||
|
|
||||||
|
*interval*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The interval (in seconds) to re-render the image.
|
||||||
|
This is useful if the contents of *path* changes.
|
||||||
|
If no *interval* is defined, the image will only be rendered once.
|
||||||
|
|
||||||
|
*signal*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The signal number used to update the module.
|
||||||
|
This can be used instead of *interval* if the file changes irregularly.
|
||||||
|
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when clicked on the module.
|
||||||
|
|
||||||
|
*on-click-middle*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when middle-clicked on the module using mousewheel.
|
||||||
|
|
||||||
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
*on-scroll-up*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling up on the module.
|
||||||
|
|
||||||
|
*on-scroll-down*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling down on the module.
|
||||||
|
|
||||||
|
*smooth-scrolling-threshold*: ++
|
||||||
|
typeof: double ++
|
||||||
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Spotify:
|
||||||
|
|
||||||
|
## mpd:
|
||||||
|
|
||||||
|
```
|
||||||
|
"image/album-art": {
|
||||||
|
"path": "/tmp/mpd_art",
|
||||||
|
"size": 32,
|
||||||
|
"interval": 5,
|
||||||
|
"on-click": "mpc toggle"
|
||||||
|
}
|
||||||
|
```
|
@ -6,7 +6,7 @@ waybar - inhibitor module
|
|||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
The *inhibitor* module allows to take an inhibitor lock that logind provides.
|
The *inhibitor* module allows one to take an inhibitor lock that logind provides.
|
||||||
See *systemd-inhibit*(1) for more information.
|
See *systemd-inhibit*(1) for more information.
|
||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
112
man/waybar-jack.5.scd
Normal file
112
man/waybar-jack.5.scd
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
waybar-jack(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - JACK module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *jack* module displays the current state of the JACK server.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *jack*
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: *{load}%* ++
|
||||||
|
The format, how information should be displayed. This format is used when other formats aren't specified.
|
||||||
|
|
||||||
|
*format-connected*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the module is connected to the JACK server.
|
||||||
|
|
||||||
|
*format-disconnected*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the module is not connected to the JACK server.
|
||||||
|
|
||||||
|
*format-xrun*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used for one polling interval, when the JACK server reports an xrun.
|
||||||
|
|
||||||
|
*realtime*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: *true* ++
|
||||||
|
Option to drop real-time privileges for the JACK client opened by Waybar.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: *true* ++
|
||||||
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
*tooltip-format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: *{bufsize}/{samplerate} {latency}ms* ++
|
||||||
|
The format of information displayed in the tooltip.
|
||||||
|
|
||||||
|
*interval*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
default: 1 ++
|
||||||
|
The interval in which the information gets polled.
|
||||||
|
|
||||||
|
*rotate*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
|
*max-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The maximum length in character the module should display.
|
||||||
|
|
||||||
|
*min-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The minimum length in characters the module should take up.
|
||||||
|
|
||||||
|
*align*: ++
|
||||||
|
typeof: float ++
|
||||||
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when clicked on the module.
|
||||||
|
|
||||||
|
*on-click-middle*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when middle-clicked on the module using mousewheel.
|
||||||
|
|
||||||
|
*on-click-right*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when you right clicked on the module.
|
||||||
|
|
||||||
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{load}*: The current CPU load estimated by JACK.
|
||||||
|
|
||||||
|
*{bufsize}*: The size of the JACK buffer.
|
||||||
|
|
||||||
|
*{samplerate}*: The samplerate at which the JACK server is running.
|
||||||
|
|
||||||
|
*{latency}*: The duration, in ms, of the current buffer size.
|
||||||
|
|
||||||
|
*{xruns}*: The number of xruns reported by the JACK server since starting Waybar.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"jack": {
|
||||||
|
"format": "DSP {}%",
|
||||||
|
"format-xrun": "{xruns} xruns",
|
||||||
|
"format-disconnected": "DSP off",
|
||||||
|
"realtime": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#jack*
|
||||||
|
- *#jack.connected*
|
||||||
|
- *#jack.disconnected*
|
||||||
|
- *#jack.xrun*
|
@ -13,6 +13,7 @@ You must be a member of the input group to use this module.
|
|||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
*interval*: ++
|
*interval*: ++
|
||||||
|
Deprecated, this module use event loop now, the interval has no effect.
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
default: 1 ++
|
default: 1 ++
|
||||||
The interval, in seconds, to poll the keyboard state.
|
The interval, in seconds, to poll the keyboard state.
|
||||||
|
@ -32,7 +32,7 @@ Addressed by *memory*
|
|||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
*states*: ++
|
*states*: ++
|
||||||
typeof: array ++
|
typeof: object ++
|
||||||
A number of memory utilization states which get activated on certain percentage thresholds. See *waybar-states(5)*.
|
A number of memory utilization states which get activated on certain percentage thresholds. See *waybar-states(5)*.
|
||||||
|
|
||||||
*max-length*: ++
|
*max-length*: ++
|
||||||
|
103
man/waybar-mpris.5.scd
Normal file
103
man/waybar-mpris.5.scd
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
waybar-mpris(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - MPRIS module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *mpris* module displays currently playing media via libplayerctl.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
*player*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: playerctld ++
|
||||||
|
Name of the MPRIS player to attach to. Using the default value always
|
||||||
|
follows the currenly active player.
|
||||||
|
|
||||||
|
*ignored-players*: ++
|
||||||
|
typeof: []string ++
|
||||||
|
Ignore updates of the listed players, when using playerctld.
|
||||||
|
|
||||||
|
*interval*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Refresh MPRIS information on a timer.
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {player} ({status}) {dynamic} ++
|
||||||
|
The text format.
|
||||||
|
|
||||||
|
*format-[status]*: ++
|
||||||
|
typeof: string ++
|
||||||
|
The status-specific text format.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: play-pause ++
|
||||||
|
Overwrite default action toggles.
|
||||||
|
|
||||||
|
*on-middle-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: previous track ++
|
||||||
|
Overwrite default action toggles.
|
||||||
|
|
||||||
|
*on-right-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: next track ++
|
||||||
|
Overwrite default action toggles.
|
||||||
|
|
||||||
|
*player-icons*: ++
|
||||||
|
typeof: map[string]string
|
||||||
|
Allows setting _{player-icon}_ based on player-name property.
|
||||||
|
|
||||||
|
*status-icons*: ++
|
||||||
|
typeof: map[string]string
|
||||||
|
Allows setting _{status-icon}_ based on player status (playing, paused,
|
||||||
|
stopped).
|
||||||
|
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{player}*: The name of the current media player
|
||||||
|
|
||||||
|
*{status}*: The current status (playing, paused, stopped)
|
||||||
|
|
||||||
|
*{artist}*: The artist of the current track
|
||||||
|
|
||||||
|
*{album}*: The album title of the current track
|
||||||
|
|
||||||
|
*{title}*: The title of the current track
|
||||||
|
|
||||||
|
*{length}*: Length of the track, formatted as HH:MM:SS
|
||||||
|
|
||||||
|
*{dynamic}*: Use _{artist}_, _{album}_, _{title}_ and _{length}_, automatically omit++
|
||||||
|
empty values
|
||||||
|
|
||||||
|
*{player-icon}*: Chooses an icon from _player-icons_ based on _{player}_
|
||||||
|
|
||||||
|
*{status-icon}*: Chooses an icon from _status-icons_ based on _{status}_
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"mpris": {
|
||||||
|
"format": "DEFAULT: {player_icon} {dynamic}",
|
||||||
|
"format-paused": "DEFAULT: {status_icon} <i>{dynamic}</i>",
|
||||||
|
"player-icons": {
|
||||||
|
"default": "▶",
|
||||||
|
"mpv": "🎵"
|
||||||
|
},
|
||||||
|
"status-icons": {
|
||||||
|
"paused": "⏸"
|
||||||
|
},
|
||||||
|
// "ignored-players": ["firefox"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#mpris*
|
||||||
|
- *#mpris.${status}*
|
||||||
|
- *#mpris.${player}*
|
@ -149,10 +149,20 @@ Addressed by *network*
|
|||||||
|
|
||||||
*{bandwidthDownBits}*: Instant down speed in bits/seconds.
|
*{bandwidthDownBits}*: Instant down speed in bits/seconds.
|
||||||
|
|
||||||
|
*{bandwidthTotalBits}*: Instant total speed in bits/seconds.
|
||||||
|
|
||||||
*{bandwidthUpOctets}*: Instant up speed in octets/seconds.
|
*{bandwidthUpOctets}*: Instant up speed in octets/seconds.
|
||||||
|
|
||||||
*{bandwidthDownOctets}*: Instant down speed in octets/seconds.
|
*{bandwidthDownOctets}*: Instant down speed in octets/seconds.
|
||||||
|
|
||||||
|
*{bandwidthTotalOctets}*: Instant total speed in octets/seconds.
|
||||||
|
|
||||||
|
*{bandwidthUpBytes}*: Instant up speed in bytes/seconds.
|
||||||
|
|
||||||
|
*{bandwidthDownBytes}*: Instant down speed in bytes/seconds.
|
||||||
|
|
||||||
|
*{bandwidthTotalBytes}*: Instant total speed in bytes/seconds.
|
||||||
|
|
||||||
*{icon}*: Icon, as defined in *format-icons*.
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
@ -43,8 +43,8 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu
|
|||||||
Positive value to rotate the text label.
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
*states*: ++
|
*states*: ++
|
||||||
typeof: array ++
|
typeof: object ++
|
||||||
A number of volume states which get activated on certain volume levels. See *waybar-states(5)*
|
A number of volume states which get activated on certain volume levels. See *waybar-states(5)*.
|
||||||
|
|
||||||
*max-length*: ++
|
*max-length*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
@ -96,6 +96,15 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu
|
|||||||
default: true ++
|
default: true ++
|
||||||
Option to disable tooltip on hover.
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
*max-volume*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
default: 100 ++
|
||||||
|
The maximum volume that can be set, in percentage.
|
||||||
|
|
||||||
|
*ignored-sinks*: ++
|
||||||
|
typeof: array ++
|
||||||
|
Sinks in this list will not be shown as the active sink by Waybar. Entries should be the sink's description field.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name.
|
*{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name.
|
||||||
|
75
man/waybar-river-mode.5.scd
Normal file
75
man/waybar-river-mode.5.scd
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
waybar-river-mode(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - river mode module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *mode* module displays the current mapping mode of river.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *river/mode*
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {} ++
|
||||||
|
The format, how information should be displayed. On {} data gets inserted.
|
||||||
|
|
||||||
|
*rotate*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
|
*max-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The maximum length in character the module should display.
|
||||||
|
|
||||||
|
*min-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The minimum length in characters the module should take up.
|
||||||
|
|
||||||
|
*align*: ++
|
||||||
|
typeof: float ++
|
||||||
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when clicked on the module.
|
||||||
|
|
||||||
|
*on-click-middle*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when middle-clicked on the module using mousewheel.
|
||||||
|
|
||||||
|
*on-click-right*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when you right clicked on the module.
|
||||||
|
|
||||||
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
*on-scroll-up*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling up on the module.
|
||||||
|
|
||||||
|
*on-scroll-down*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling down on the module.
|
||||||
|
|
||||||
|
*smooth-scrolling-threshold*: ++
|
||||||
|
typeof: double ++
|
||||||
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"river/mode": {
|
||||||
|
"format": " {}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#mode*
|
||||||
|
- *#mode.<mode>*
|
@ -7,14 +7,13 @@ apply a class when the value matches the declared state value.
|
|||||||
|
|
||||||
# STATES
|
# STATES
|
||||||
|
|
||||||
- Every entry (*state*) consists of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
|
Every entry (*state*) consists of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
|
||||||
|
|
||||||
- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the *<name>* of the state.
|
- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the *<name>* of the state.
|
||||||
Each class gets activated when the current capacity is equal or below the configured *<value>*.
|
Each class gets activated when the current value is equal to or less than the configured *<value>* for the *battery* module, or equal to or greater than the configured *<value>* for all other modules.
|
||||||
|
|
||||||
- Also each state can have its own *format*.
|
- Also, each state can have its own *format*.
|
||||||
Those can be configured via *format-<name>*.
|
Those can be configured via *format-<name>*, or if you want to differentiate a bit more, as *format-<status>-<state>*.
|
||||||
Or if you want to differentiate a bit more even as *format-<status>-<state>*.
|
|
||||||
|
|
||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
|
|
||||||
|
64
man/waybar-sway-scratchpad.5.scd
Normal file
64
man/waybar-sway-scratchpad.5.scd
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
waybar-sway-scratchpad(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - sway scratchpad module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *scratchpad* module displays the scratchpad status in Sway
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
Addressed by *sway/scratchpad*
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {icon} {count} ++
|
||||||
|
The format, how information should be displayed.
|
||||||
|
|
||||||
|
*show-empty*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Option to show module when scratchpad is empty.
|
||||||
|
|
||||||
|
*format-icons*: ++
|
||||||
|
typeof: array/object ++
|
||||||
|
Based on the current scratchpad window counts, the corresponding icon gets selected.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
*tooltip-format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {app}: {title} ++
|
||||||
|
The format, how information in the tooltip should be displayed.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
|
*{count}*: Number of windows in the scratchpad.
|
||||||
|
|
||||||
|
*{app}*: Name of the application in the scratchpad.
|
||||||
|
|
||||||
|
*{title}*: Title of the application in the scratchpad.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"sway/scratchpad": {
|
||||||
|
"format": "{icon} {count}",
|
||||||
|
"show-empty": false,
|
||||||
|
"format-icons": ["", ""],
|
||||||
|
"tooltip": true,
|
||||||
|
"tooltip-format": "{app}: {title}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#scratchpad*
|
||||||
|
- *#scratchpad.empty*
|
@ -14,8 +14,8 @@ Addressed by *sway/window*
|
|||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {} ++
|
default: {title} ++
|
||||||
The format, how information should be displayed. On {} data gets inserted.
|
The format, how information should be displayed.
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
@ -80,6 +80,15 @@ Addressed by *sway/window*
|
|||||||
default: 24 ++
|
default: 24 ++
|
||||||
Option to change the size of the application icon.
|
Option to change the size of the application icon.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{title}*: The title of the focused window.
|
||||||
|
|
||||||
|
*{app_id}*: The app_id of the focused window.
|
||||||
|
|
||||||
|
*{shell}*: The shell of the focused window. It's 'xwayland' when the window is
|
||||||
|
running through xwayland, otherwise it's 'xdg-shell'.
|
||||||
|
|
||||||
# REWRITE RULES
|
# REWRITE RULES
|
||||||
|
|
||||||
*rewrite* is an object where keys are regular expressions and values are
|
*rewrite* is an object where keys are regular expressions and values are
|
||||||
|
@ -73,6 +73,10 @@ Addressed by *sway/workspaces*
|
|||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration.
|
Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration.
|
||||||
|
|
||||||
|
*alphabetical_sort*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
Whether to sort workspaces alphabetically. Please note this can make "swaymsg workspace prev/next" move to workspaces inconsistent with the ordering shown in Waybar.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{value}*: Name of the workspace, as defined by sway.
|
*{value}*: Name of the workspace, as defined by sway.
|
||||||
|
@ -33,7 +33,7 @@ compatible devices in the tooltip.
|
|||||||
|
|
||||||
*tooltip*: ++
|
*tooltip*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
defualt: true ++
|
default: true ++
|
||||||
Option to disable tooltip on hover.
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
*tooltip-spacing*: ++
|
*tooltip-spacing*: ++
|
||||||
@ -47,6 +47,10 @@ compatible devices in the tooltip.
|
|||||||
default: 4 ++
|
default: 4 ++
|
||||||
Defines the spacing between the tooltip window edge and the tooltip content.
|
Defines the spacing between the tooltip window edge and the tooltip content.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when clicked on the module.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{percentage}*: The battery capacity in percentage
|
*{percentage}*: The battery capacity in percentage
|
||||||
|
87
man/waybar-wireplumber.5.scd
Normal file
87
man/waybar-wireplumber.5.scd
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
waybar-wireplumber(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - WirePlumber module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *wireplumber* module displays the current volume reported by WirePlumber.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: *{volume}%* ++
|
||||||
|
The format, how information should be displayed. This format is used when other formats aren't specified.
|
||||||
|
|
||||||
|
*format-muted*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the sound is muted.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: *true* ++
|
||||||
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
*tooltip-format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: *{node_name}* ++
|
||||||
|
The format of information displayed in the tooltip.
|
||||||
|
|
||||||
|
*rotate*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
|
*states*: ++
|
||||||
|
typeof: object ++
|
||||||
|
A number of volume states which get activated on certain volume levels. See *waybar-states(5)*.
|
||||||
|
|
||||||
|
*max-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The maximum length in character the module should display.
|
||||||
|
|
||||||
|
*min-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The minimum length in characters the module should take up.
|
||||||
|
|
||||||
|
*align*: ++
|
||||||
|
typeof: float ++
|
||||||
|
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when clicked on the module.
|
||||||
|
|
||||||
|
*on-click-middle*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when middle-clicked on the module using mousewheel.
|
||||||
|
|
||||||
|
*on-click-right*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when you right clicked on the module.
|
||||||
|
|
||||||
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{volume}*: Volume in percentage.
|
||||||
|
|
||||||
|
*{node_name}*: The node's nickname as reported by WirePlumber (*node.nick* property)
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"wireplumber": {
|
||||||
|
"format": "{volume}%",
|
||||||
|
"format-muted": "",
|
||||||
|
"on-click": "helvum"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#wireplumber*
|
||||||
|
- *#wireplumber.muted*
|
@ -33,6 +33,11 @@ Addressed by *wlr/workspaces*
|
|||||||
Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first.
|
Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first.
|
||||||
If both are false - sort by id will be performed.
|
If both are false - sort by id will be performed.
|
||||||
|
|
||||||
|
*sort-by-number*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to true, workspace names will be sorted numerically. Takes presedence over any other sort-by option.
|
||||||
|
|
||||||
*all-outputs*: ++
|
*all-outputs*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
default: false ++
|
||||||
@ -75,7 +80,8 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
|||||||
"5": "",
|
"5": "",
|
||||||
"focused": "",
|
"focused": "",
|
||||||
"default": ""
|
"default": ""
|
||||||
}
|
},
|
||||||
|
"sort-by-number": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -266,11 +266,15 @@ A module group is defined by specifying a module named "group/some-group-name".
|
|||||||
- *waybar-keyboard-state(5)*
|
- *waybar-keyboard-state(5)*
|
||||||
- *waybar-memory(5)*
|
- *waybar-memory(5)*
|
||||||
- *waybar-mpd(5)*
|
- *waybar-mpd(5)*
|
||||||
|
- *waybar-mpris(5)*
|
||||||
- *waybar-network(5)*
|
- *waybar-network(5)*
|
||||||
- *waybar-pulseaudio(5)*
|
- *waybar-pulseaudio(5)*
|
||||||
|
- *waybar-river-mode(5)*
|
||||||
- *waybar-river-tags(5)*
|
- *waybar-river-tags(5)*
|
||||||
|
- *waybar-river-window(5)*
|
||||||
- *waybar-states(5)*
|
- *waybar-states(5)*
|
||||||
- *waybar-sway-mode(5)*
|
- *waybar-sway-mode(5)*
|
||||||
|
- *waybar-sway-scratchpad(5)*
|
||||||
- *waybar-sway-window(5)*
|
- *waybar-sway-window(5)*
|
||||||
- *waybar-sway-workspaces(5)*
|
- *waybar-sway-workspaces(5)*
|
||||||
- *waybar-wlr-taskbar(5)*
|
- *waybar-wlr-taskbar(5)*
|
||||||
|
69
meson.build
69
meson.build
@ -1,6 +1,6 @@
|
|||||||
project(
|
project(
|
||||||
'waybar', 'cpp', 'c',
|
'waybar', 'cpp', 'c',
|
||||||
version: '0.9.13',
|
version: '0.9.17',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>= 0.49.0',
|
meson_version: '>= 0.49.0',
|
||||||
default_options : [
|
default_options : [
|
||||||
@ -79,25 +79,33 @@ is_netbsd = host_machine.system() == 'netbsd'
|
|||||||
is_openbsd = host_machine.system() == 'openbsd'
|
is_openbsd = host_machine.system() == 'openbsd'
|
||||||
|
|
||||||
thread_dep = dependency('threads')
|
thread_dep = dependency('threads')
|
||||||
fmt = dependency('fmt', version : ['>=7.0.0'], fallback : ['fmt', 'fmt_dep'])
|
fmt = dependency('fmt', version : ['>=8.1.1'], fallback : ['fmt', 'fmt_dep'])
|
||||||
spdlog = dependency('spdlog', version : ['>=1.8.5'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true'])
|
spdlog = dependency('spdlog', version : ['>=1.10.0'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=enabled'])
|
||||||
wayland_client = dependency('wayland-client')
|
wayland_client = dependency('wayland-client')
|
||||||
wayland_cursor = dependency('wayland-cursor')
|
wayland_cursor = dependency('wayland-cursor')
|
||||||
wayland_protos = dependency('wayland-protocols')
|
wayland_protos = dependency('wayland-protocols')
|
||||||
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
|
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
|
||||||
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
|
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
|
||||||
giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or get_option('logind').enabled() or get_option('upower_glib').enabled()))
|
giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or
|
||||||
jsoncpp = dependency('jsoncpp')
|
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')
|
sigcpp = dependency('sigc++-2.0')
|
||||||
|
libinotify = dependency('libinotify', required: false)
|
||||||
libepoll = dependency('epoll-shim', required: false)
|
libepoll = dependency('epoll-shim', required: false)
|
||||||
|
libinput = dependency('libinput', required: get_option('libinput'))
|
||||||
libnl = dependency('libnl-3.0', required: get_option('libnl'))
|
libnl = dependency('libnl-3.0', required: get_option('libnl'))
|
||||||
libnlgen = dependency('libnl-genl-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'))
|
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'))
|
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
|
||||||
libudev = dependency('libudev', required: get_option('libudev'))
|
libudev = dependency('libudev', required: get_option('libudev'))
|
||||||
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
||||||
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
||||||
xkbregistry = dependency('xkbregistry')
|
xkbregistry = dependency('xkbregistry')
|
||||||
|
libjack = dependency('jack', required: get_option('jack'))
|
||||||
|
libwireplumber = dependency('wireplumber-0.4', required: get_option('wireplumber'))
|
||||||
|
|
||||||
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||||
if libsndio.found()
|
if libsndio.found()
|
||||||
@ -147,13 +155,17 @@ src_files = files(
|
|||||||
'src/modules/custom.cpp',
|
'src/modules/custom.cpp',
|
||||||
'src/modules/disk.cpp',
|
'src/modules/disk.cpp',
|
||||||
'src/modules/idle_inhibitor.cpp',
|
'src/modules/idle_inhibitor.cpp',
|
||||||
|
'src/modules/image.cpp',
|
||||||
'src/modules/temperature.cpp',
|
'src/modules/temperature.cpp',
|
||||||
|
'src/modules/user.cpp',
|
||||||
'src/main.cpp',
|
'src/main.cpp',
|
||||||
'src/bar.cpp',
|
'src/bar.cpp',
|
||||||
'src/client.cpp',
|
'src/client.cpp',
|
||||||
'src/config.cpp',
|
'src/config.cpp',
|
||||||
'src/group.cpp',
|
'src/group.cpp',
|
||||||
'src/util/ustring_clen.cpp'
|
'src/util/ustring_clen.cpp',
|
||||||
|
'src/util/sanitize_str.cpp',
|
||||||
|
'src/util/rewrite_title.cpp'
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_linux
|
if is_linux
|
||||||
@ -175,6 +187,11 @@ elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd
|
|||||||
'src/modules/memory/bsd.cpp',
|
'src/modules/memory/bsd.cpp',
|
||||||
'src/modules/memory/common.cpp',
|
'src/modules/memory/common.cpp',
|
||||||
)
|
)
|
||||||
|
if is_freebsd
|
||||||
|
src_files += files(
|
||||||
|
'src/modules/battery.cpp',
|
||||||
|
)
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
add_project_arguments('-DHAVE_SWAY', language: 'cpp')
|
add_project_arguments('-DHAVE_SWAY', language: 'cpp')
|
||||||
@ -184,7 +201,8 @@ src_files += [
|
|||||||
'src/modules/sway/mode.cpp',
|
'src/modules/sway/mode.cpp',
|
||||||
'src/modules/sway/language.cpp',
|
'src/modules/sway/language.cpp',
|
||||||
'src/modules/sway/window.cpp',
|
'src/modules/sway/window.cpp',
|
||||||
'src/modules/sway/workspaces.cpp'
|
'src/modules/sway/workspaces.cpp',
|
||||||
|
'src/modules/sway/scratchpad.cpp'
|
||||||
]
|
]
|
||||||
|
|
||||||
if true
|
if true
|
||||||
@ -196,10 +214,19 @@ endif
|
|||||||
|
|
||||||
if true
|
if true
|
||||||
add_project_arguments('-DHAVE_RIVER', language: 'cpp')
|
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/tags.cpp'
|
||||||
src_files += 'src/modules/river/window.cpp'
|
src_files += 'src/modules/river/window.cpp'
|
||||||
endif
|
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'
|
||||||
|
endif
|
||||||
|
|
||||||
if libnl.found() and libnlgen.found()
|
if libnl.found() and libnlgen.found()
|
||||||
add_project_arguments('-DHAVE_LIBNL', language: 'cpp')
|
add_project_arguments('-DHAVE_LIBNL', language: 'cpp')
|
||||||
src_files += 'src/modules/network.cpp'
|
src_files += 'src/modules/network.cpp'
|
||||||
@ -216,11 +243,26 @@ if (upower_glib.found() and giounix.found() and not get_option('logind').disable
|
|||||||
src_files += 'src/modules/upower/upower_tooltip.cpp'
|
src_files += 'src/modules/upower/upower_tooltip.cpp'
|
||||||
endif
|
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()
|
if libpulse.found()
|
||||||
add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp')
|
add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp')
|
||||||
src_files += 'src/modules/pulseaudio.cpp'
|
src_files += 'src/modules/pulseaudio.cpp'
|
||||||
endif
|
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()
|
if dbusmenu_gtk.found()
|
||||||
add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp')
|
add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp')
|
||||||
src_files += files(
|
src_files += files(
|
||||||
@ -236,8 +278,9 @@ if libudev.found() and (is_linux or libepoll.found())
|
|||||||
src_files += 'src/modules/backlight.cpp'
|
src_files += 'src/modules/backlight.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if libevdev.found() and (is_linux or libepoll.found())
|
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_LIBEVDEV', language: 'cpp')
|
||||||
|
add_project_arguments('-DHAVE_LIBINPUT', language: 'cpp')
|
||||||
src_files += 'src/modules/keyboard_state.cpp'
|
src_files += 'src/modules/keyboard_state.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@ -297,11 +340,16 @@ executable(
|
|||||||
gtkmm,
|
gtkmm,
|
||||||
dbusmenu_gtk,
|
dbusmenu_gtk,
|
||||||
giounix,
|
giounix,
|
||||||
|
libinput,
|
||||||
libnl,
|
libnl,
|
||||||
libnlgen,
|
libnlgen,
|
||||||
upower_glib,
|
upower_glib,
|
||||||
|
playerctl,
|
||||||
libpulse,
|
libpulse,
|
||||||
|
libjack,
|
||||||
|
libwireplumber,
|
||||||
libudev,
|
libudev,
|
||||||
|
libinotify,
|
||||||
libepoll,
|
libepoll,
|
||||||
libmpdclient,
|
libmpdclient,
|
||||||
libevdev,
|
libevdev,
|
||||||
@ -345,16 +393,20 @@ if scdoc.found()
|
|||||||
'waybar-cpu.5.scd',
|
'waybar-cpu.5.scd',
|
||||||
'waybar-custom.5.scd',
|
'waybar-custom.5.scd',
|
||||||
'waybar-disk.5.scd',
|
'waybar-disk.5.scd',
|
||||||
|
'waybar-gamemode.5.scd',
|
||||||
'waybar-idle-inhibitor.5.scd',
|
'waybar-idle-inhibitor.5.scd',
|
||||||
'waybar-keyboard-state.5.scd',
|
'waybar-keyboard-state.5.scd',
|
||||||
'waybar-memory.5.scd',
|
'waybar-memory.5.scd',
|
||||||
'waybar-mpd.5.scd',
|
'waybar-mpd.5.scd',
|
||||||
|
'waybar-mpris.5.scd',
|
||||||
'waybar-network.5.scd',
|
'waybar-network.5.scd',
|
||||||
'waybar-pulseaudio.5.scd',
|
'waybar-pulseaudio.5.scd',
|
||||||
|
'waybar-river-mode.5.scd',
|
||||||
'waybar-river-tags.5.scd',
|
'waybar-river-tags.5.scd',
|
||||||
'waybar-river-window.5.scd',
|
'waybar-river-window.5.scd',
|
||||||
'waybar-sway-language.5.scd',
|
'waybar-sway-language.5.scd',
|
||||||
'waybar-sway-mode.5.scd',
|
'waybar-sway-mode.5.scd',
|
||||||
|
'waybar-sway-scratchpad.5.scd',
|
||||||
'waybar-sway-window.5.scd',
|
'waybar-sway-window.5.scd',
|
||||||
'waybar-sway-workspaces.5.scd',
|
'waybar-sway-workspaces.5.scd',
|
||||||
'waybar-temperature.5.scd',
|
'waybar-temperature.5.scd',
|
||||||
@ -395,6 +447,7 @@ endif
|
|||||||
|
|
||||||
catch2 = dependency(
|
catch2 = dependency(
|
||||||
'catch2',
|
'catch2',
|
||||||
|
version: '>=3.0.0',
|
||||||
fallback: ['catch2', 'catch2_dep'],
|
fallback: ['catch2', 'catch2_dep'],
|
||||||
required: get_option('tests'),
|
required: get_option('tests'),
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.')
|
option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.')
|
||||||
|
option('libinput', type: 'feature', value: 'auto', description: 'Enable libinput support for libinput related features')
|
||||||
option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features')
|
option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features')
|
||||||
option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features')
|
option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features')
|
||||||
option('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features')
|
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('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
|
||||||
option('upower_glib', type: 'feature', value: 'auto', description: 'Enable support for upower')
|
option('upower_glib', type: 'feature', value: 'auto', description: 'Enable support for upower')
|
||||||
|
option('mpris', type: 'feature', value: 'auto', description: 'Enable support for mpris')
|
||||||
option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit')
|
option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit')
|
||||||
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
|
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('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
||||||
@ -14,3 +16,5 @@ option('sndio', type: 'feature', value: 'auto', description: 'Enable support for
|
|||||||
option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind')
|
option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind')
|
||||||
option('tests', type: 'feature', value: 'auto', description: 'Enable tests')
|
option('tests', type: 'feature', value: 'auto', description: 'Enable tests')
|
||||||
option('experimental', type : 'boolean', value : false, description: 'Enable experimental features')
|
option('experimental', type : 'boolean', value : false, description: 'Enable experimental features')
|
||||||
|
option('jack', type: 'feature', value: 'auto', description: 'Enable support for JACK')
|
||||||
|
option('wireplumber', type: 'feature', value: 'auto', description: 'Enable support for WirePlumber')
|
@ -16,7 +16,7 @@
|
|||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
</copyright>
|
</copyright>
|
||||||
|
|
||||||
<interface name="zriver_status_manager_v1" version="2">
|
<interface name="zriver_status_manager_v1" version="3">
|
||||||
<description summary="manage river status objects">
|
<description summary="manage river status objects">
|
||||||
A global factory for objects that receive status information specific
|
A global factory for objects that receive status information specific
|
||||||
to river. It could be used to implement, for example, a status bar.
|
to river. It could be used to implement, for example, a status bar.
|
||||||
@ -85,7 +85,7 @@
|
|||||||
</event>
|
</event>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
<interface name="zriver_seat_status_v1" version="1">
|
<interface name="zriver_seat_status_v1" version="3">
|
||||||
<description summary="track seat focus">
|
<description summary="track seat focus">
|
||||||
This interface allows clients to receive information about the current
|
This interface allows clients to receive information about the current
|
||||||
focus of a seat. Note that (un)focused_output events will only be sent
|
focus of a seat. Note that (un)focused_output events will only be sent
|
||||||
@ -121,5 +121,13 @@
|
|||||||
</description>
|
</description>
|
||||||
<arg name="title" type="string" summary="title of the focused view"/>
|
<arg name="title" type="string" summary="title of the focused view"/>
|
||||||
</event>
|
</event>
|
||||||
|
|
||||||
|
<event name="mode" since="3">
|
||||||
|
<description summary="the active mode changed">
|
||||||
|
Sent once on binding the interface and again whenever a new mode
|
||||||
|
is entered (e.g. with riverctl enter-mode foobar).
|
||||||
|
</description>
|
||||||
|
<arg name="name" type="string" summary="name of the mode"/>
|
||||||
|
</event>
|
||||||
</interface>
|
</interface>
|
||||||
</protocol>
|
</protocol>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// "width": 1280, // Waybar width
|
// "width": 1280, // Waybar width
|
||||||
"spacing": 4, // Gaps between modules (4px)
|
"spacing": 4, // Gaps between modules (4px)
|
||||||
// Choose the order of the modules
|
// Choose the order of the modules
|
||||||
"modules-left": ["sway/workspaces", "sway/mode", "custom/media"],
|
"modules-left": ["sway/workspaces", "sway/mode", "sway/scratchpad", "custom/media"],
|
||||||
"modules-center": ["sway/window"],
|
"modules-center": ["sway/window"],
|
||||||
"modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "keyboard-state", "sway/language", "battery", "battery#bat2", "clock", "tray"],
|
"modules-right": ["mpd", "idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "backlight", "keyboard-state", "sway/language", "battery", "battery#bat2", "clock", "tray"],
|
||||||
// Modules configuration
|
// Modules configuration
|
||||||
@ -36,6 +36,13 @@
|
|||||||
"sway/mode": {
|
"sway/mode": {
|
||||||
"format": "<span style=\"italic\">{}</span>"
|
"format": "<span style=\"italic\">{}</span>"
|
||||||
},
|
},
|
||||||
|
"sway/scratchpad": {
|
||||||
|
"format": "{icon} {count}",
|
||||||
|
"show-empty": false,
|
||||||
|
"format-icons": ["", ""],
|
||||||
|
"tooltip": true,
|
||||||
|
"tooltip-format": "{app}: {title}"
|
||||||
|
},
|
||||||
"mpd": {
|
"mpd": {
|
||||||
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ",
|
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ",
|
||||||
"format-disconnected": "Disconnected ",
|
"format-disconnected": "Disconnected ",
|
||||||
|
@ -34,21 +34,28 @@ window#waybar.chromium {
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#workspaces button {
|
button {
|
||||||
padding: 0 5px;
|
|
||||||
background-color: transparent;
|
|
||||||
color: #ffffff;
|
|
||||||
/* Use box-shadow instead of border so the text isn't offset */
|
/* Use box-shadow instead of border so the text isn't offset */
|
||||||
box-shadow: inset 0 -3px transparent;
|
box-shadow: inset 0 -3px transparent;
|
||||||
/* Avoid rounded borders under each workspace name */
|
/* Avoid rounded borders under each button name */
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
|
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
|
||||||
|
button:hover {
|
||||||
|
background: inherit;
|
||||||
|
box-shadow: inset 0 -3px #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button {
|
||||||
|
padding: 0 5px;
|
||||||
|
background-color: transparent;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
#workspaces button:hover {
|
#workspaces button:hover {
|
||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
box-shadow: inset 0 -3px #ffffff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#workspaces button.focused {
|
#workspaces button.focused {
|
||||||
@ -74,10 +81,12 @@ window#waybar.chromium {
|
|||||||
#backlight,
|
#backlight,
|
||||||
#network,
|
#network,
|
||||||
#pulseaudio,
|
#pulseaudio,
|
||||||
|
#wireplumber,
|
||||||
#custom-media,
|
#custom-media,
|
||||||
#tray,
|
#tray,
|
||||||
#mode,
|
#mode,
|
||||||
#idle_inhibitor,
|
#idle_inhibitor,
|
||||||
|
#scratchpad,
|
||||||
#mpd {
|
#mpd {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
@ -168,6 +177,15 @@ label:focus {
|
|||||||
color: #2a5c45;
|
color: #2a5c45;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#wireplumber {
|
||||||
|
background-color: #fff0f5;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wireplumber.muted {
|
||||||
|
background-color: #f53c3c;
|
||||||
|
}
|
||||||
|
|
||||||
#custom-media {
|
#custom-media {
|
||||||
background-color: #66cc99;
|
background-color: #66cc99;
|
||||||
color: #2a5c45;
|
color: #2a5c45;
|
||||||
@ -252,3 +270,11 @@ label:focus {
|
|||||||
#keyboard-state > label.locked {
|
#keyboard-state > label.locked {
|
||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#scratchpad {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#scratchpad.empty {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
@ -181,7 +181,7 @@ struct GLSSurfaceImpl : public BarSurface, public sigc::trackable {
|
|||||||
if (vertical_ && height_ > 1) {
|
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_BOTTOM, false);
|
||||||
gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_TOP, false);
|
gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_TOP, false);
|
||||||
} else if (width_ > 1) {
|
} 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_LEFT, false);
|
||||||
gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, false);
|
gtk_layer_set_anchor(window_.gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, false);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
|
|
||||||
#include <fmt/ostream.h>
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
#include "util/clara.hpp"
|
#include "util/clara.hpp"
|
||||||
|
#include "util/format.hpp"
|
||||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
waybar::Client *waybar::Client::inst() {
|
waybar::Client *waybar::Client::inst() {
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
|
||||||
#include <fmt/ostream.h>
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "util/json.hpp"
|
#include "util/json.hpp"
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
const std::vector<std::string> Config::CONFIG_DIRS = {
|
const std::vector<std::string> Config::CONFIG_DIRS = {
|
||||||
@ -17,12 +19,25 @@ const std::vector<std::string> Config::CONFIG_DIRS = {
|
|||||||
"/etc/xdg/waybar/", SYSCONFDIR "/xdg/waybar/", "./resources/",
|
"/etc/xdg/waybar/", SYSCONFDIR "/xdg/waybar/", "./resources/",
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<std::string> tryExpandPath(const std::string &path) {
|
const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR";
|
||||||
|
|
||||||
|
std::optional<std::string> tryExpandPath(const std::string base, const std::string filename) {
|
||||||
|
fs::path path;
|
||||||
|
|
||||||
|
if (filename != "") {
|
||||||
|
path = fs::path(base) / fs::path(filename);
|
||||||
|
} else {
|
||||||
|
path = fs::path(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::debug("Try expanding: {}", path.string());
|
||||||
|
|
||||||
wordexp_t p;
|
wordexp_t p;
|
||||||
if (wordexp(path.c_str(), &p, 0) == 0) {
|
if (wordexp(path.c_str(), &p, 0) == 0) {
|
||||||
if (access(*p.we_wordv, F_OK) == 0) {
|
if (access(*p.we_wordv, F_OK) == 0) {
|
||||||
std::string result = *p.we_wordv;
|
std::string result = *p.we_wordv;
|
||||||
wordfree(&p);
|
wordfree(&p);
|
||||||
|
spdlog::debug("Found config file: {}", path.string());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
wordfree(&p);
|
wordfree(&p);
|
||||||
@ -32,10 +47,17 @@ std::optional<std::string> tryExpandPath(const std::string &path) {
|
|||||||
|
|
||||||
std::optional<std::string> Config::findConfigPath(const std::vector<std::string> &names,
|
std::optional<std::string> Config::findConfigPath(const std::vector<std::string> &names,
|
||||||
const std::vector<std::string> &dirs) {
|
const std::vector<std::string> &dirs) {
|
||||||
std::vector<std::string> paths;
|
if (const char *dir = std::getenv(Config::CONFIG_PATH_ENV)) {
|
||||||
|
for (const auto &name : names) {
|
||||||
|
if (auto res = tryExpandPath(dir, name); res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto &dir : dirs) {
|
for (const auto &dir : dirs) {
|
||||||
for (const auto &name : names) {
|
for (const auto &name : names) {
|
||||||
if (auto res = tryExpandPath(dir + name); res) {
|
if (auto res = tryExpandPath(dir, name); res) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,11 +91,11 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) {
|
|||||||
if (includes.isArray()) {
|
if (includes.isArray()) {
|
||||||
for (const auto &include : includes) {
|
for (const auto &include : includes) {
|
||||||
spdlog::info("Including resource file: {}", include.asString());
|
spdlog::info("Including resource file: {}", include.asString());
|
||||||
setupConfig(config, tryExpandPath(include.asString()).value_or(""), ++depth);
|
setupConfig(config, tryExpandPath(include.asString(), "").value_or(""), ++depth);
|
||||||
}
|
}
|
||||||
} else if (includes.isString()) {
|
} else if (includes.isString()) {
|
||||||
spdlog::info("Including resource file: {}", includes.asString());
|
spdlog::info("Including resource file: {}", includes.asString());
|
||||||
setupConfig(config, tryExpandPath(includes.asString()).value_or(""), ++depth);
|
setupConfig(config, tryExpandPath(includes.asString(), "").value_or(""), ++depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
auto hash_pos = name.find('#');
|
auto hash_pos = name.find('#');
|
||||||
auto ref = name.substr(0, hash_pos);
|
auto ref = name.substr(0, hash_pos);
|
||||||
auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : "";
|
auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : "";
|
||||||
#if defined(__linux__) && !defined(NO_FILESYSTEM)
|
#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM))
|
||||||
if (ref == "battery") {
|
if (ref == "battery") {
|
||||||
return new waybar::modules::Battery(id, config_[name]);
|
return new waybar::modules::Battery(id, config_[name]);
|
||||||
}
|
}
|
||||||
@ -22,6 +22,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
return new waybar::modules::upower::UPower(id, config_[name]);
|
return new waybar::modules::upower::UPower(id, config_[name]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_MPRIS
|
||||||
|
if (ref == "mpris") {
|
||||||
|
return new waybar::modules::mpris::Mpris(id, config_[name]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef HAVE_SWAY
|
#ifdef HAVE_SWAY
|
||||||
if (ref == "sway/mode") {
|
if (ref == "sway/mode") {
|
||||||
return new waybar::modules::sway::Mode(id, config_[name]);
|
return new waybar::modules::sway::Mode(id, config_[name]);
|
||||||
@ -35,6 +40,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
if (ref == "sway/language") {
|
if (ref == "sway/language") {
|
||||||
return new waybar::modules::sway::Language(id, config_[name]);
|
return new waybar::modules::sway::Language(id, config_[name]);
|
||||||
}
|
}
|
||||||
|
if (ref == "sway/scratchpad") {
|
||||||
|
return new waybar::modules::sway::Scratchpad(id, config_[name]);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_WLR
|
#ifdef HAVE_WLR
|
||||||
if (ref == "wlr/taskbar") {
|
if (ref == "wlr/taskbar") {
|
||||||
@ -47,12 +55,26 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_RIVER
|
#ifdef HAVE_RIVER
|
||||||
|
if (ref == "river/mode") {
|
||||||
|
return new waybar::modules::river::Mode(id, bar_, config_[name]);
|
||||||
|
}
|
||||||
if (ref == "river/tags") {
|
if (ref == "river/tags") {
|
||||||
return new waybar::modules::river::Tags(id, bar_, config_[name]);
|
return new waybar::modules::river::Tags(id, bar_, config_[name]);
|
||||||
}
|
}
|
||||||
if (ref == "river/window") {
|
if (ref == "river/window") {
|
||||||
return new waybar::modules::river::Window(id, bar_, config_[name]);
|
return new waybar::modules::river::Window(id, bar_, config_[name]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_HYPRLAND
|
||||||
|
if (ref == "hyprland/window") {
|
||||||
|
return new waybar::modules::hyprland::Window(id, bar_, config_[name]);
|
||||||
|
}
|
||||||
|
if (ref == "hyprland/language") {
|
||||||
|
return new waybar::modules::hyprland::Language(id, bar_, config_[name]);
|
||||||
|
}
|
||||||
|
if (ref == "hyprland/submap") {
|
||||||
|
return new waybar::modules::hyprland::Submap(id, bar_, config_[name]);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ref == "idle_inhibitor") {
|
if (ref == "idle_inhibitor") {
|
||||||
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
|
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
|
||||||
@ -70,6 +92,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
if (ref == "clock") {
|
if (ref == "clock") {
|
||||||
return new waybar::modules::Clock(id, config_[name]);
|
return new waybar::modules::Clock(id, config_[name]);
|
||||||
}
|
}
|
||||||
|
if (ref == "user") {
|
||||||
|
return new waybar::modules::User(id, config_[name]);
|
||||||
|
}
|
||||||
if (ref == "disk") {
|
if (ref == "disk") {
|
||||||
return new waybar::modules::Disk(id, config_[name]);
|
return new waybar::modules::Disk(id, config_[name]);
|
||||||
}
|
}
|
||||||
@ -115,12 +140,24 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
if (ref == "inhibitor") {
|
if (ref == "inhibitor") {
|
||||||
return new waybar::modules::Inhibitor(id, bar_, config_[name]);
|
return new waybar::modules::Inhibitor(id, bar_, config_[name]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LIBJACK
|
||||||
|
if (ref == "jack") {
|
||||||
|
return new waybar::modules::JACK(id, config_[name]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LIBWIREPLUMBER
|
||||||
|
if (ref == "wireplumber") {
|
||||||
|
return new waybar::modules::Wireplumber(id, config_[name]);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ref == "temperature") {
|
if (ref == "temperature") {
|
||||||
return new waybar::modules::Temperature(id, config_[name]);
|
return new waybar::modules::Temperature(id, config_[name]);
|
||||||
}
|
}
|
||||||
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
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]);
|
||||||
|
} else if (ref.compare(0, 6, "image/") == 0 && ref.size() > 6) {
|
||||||
|
return new waybar::modules::Image(ref.substr(6), id, config_[name]);
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());
|
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());
|
||||||
|
@ -73,8 +73,9 @@ void check_nn(const void *ptr, const char *message = "ptr was null") {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
waybar::modules::Backlight::BacklightDev::BacklightDev(std::string name, int actual, int max)
|
waybar::modules::Backlight::BacklightDev::BacklightDev(std::string name, int actual, int max,
|
||||||
: name_(std::move(name)), actual_(actual), max_(max) {}
|
bool powered)
|
||||||
|
: name_(std::move(name)), actual_(actual), max_(max), powered_(powered) {}
|
||||||
|
|
||||||
std::string_view waybar::modules::Backlight::BacklightDev::name() const { return name_; }
|
std::string_view waybar::modules::Backlight::BacklightDev::name() const { return name_; }
|
||||||
|
|
||||||
@ -86,6 +87,10 @@ int waybar::modules::Backlight::BacklightDev::get_max() const { return max_; }
|
|||||||
|
|
||||||
void waybar::modules::Backlight::BacklightDev::set_max(int max) { max_ = max; }
|
void waybar::modules::Backlight::BacklightDev::set_max(int max) { max_ = max; }
|
||||||
|
|
||||||
|
bool waybar::modules::Backlight::BacklightDev::get_powered() const { return powered_; }
|
||||||
|
|
||||||
|
void waybar::modules::Backlight::BacklightDev::set_powered(bool powered) { powered_ = powered; }
|
||||||
|
|
||||||
waybar::modules::Backlight::Backlight(const std::string &id, const Json::Value &config)
|
waybar::modules::Backlight::Backlight(const std::string &id, const Json::Value &config)
|
||||||
: ALabel(config, "backlight", id, "{percent}%", 2),
|
: ALabel(config, "backlight", id, "{percent}%", 2),
|
||||||
preferred_device_(config["device"].isString() ? config["device"].asString() : "") {
|
preferred_device_(config["device"].isString() ? config["device"].asString() : "") {
|
||||||
@ -172,11 +177,16 @@ auto waybar::modules::Backlight::update() -> void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t percent =
|
if (best->get_powered()) {
|
||||||
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
|
event_box_.show();
|
||||||
label_.set_markup(fmt::format(format_, fmt::arg("percent", std::to_string(percent)),
|
const uint8_t percent =
|
||||||
fmt::arg("icon", getIcon(percent))));
|
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
|
||||||
getState(percent);
|
label_.set_markup(fmt::format(format_, fmt::arg("percent", std::to_string(percent)),
|
||||||
|
fmt::arg("icon", getIcon(percent))));
|
||||||
|
getState(percent);
|
||||||
|
} else {
|
||||||
|
event_box_.hide();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!previous_best_.has_value()) {
|
if (!previous_best_.has_value()) {
|
||||||
return;
|
return;
|
||||||
@ -215,6 +225,7 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last,
|
|||||||
|
|
||||||
const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr);
|
const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr);
|
||||||
const char *max = udev_device_get_sysattr_value(dev, "max_brightness");
|
const char *max = udev_device_get_sysattr_value(dev, "max_brightness");
|
||||||
|
const char *power = udev_device_get_sysattr_value(dev, "bl_power");
|
||||||
|
|
||||||
auto found =
|
auto found =
|
||||||
std::find_if(first, last, [name](const auto &device) { return device.name() == name; });
|
std::find_if(first, last, [name](const auto &device) { return device.name() == name; });
|
||||||
@ -225,10 +236,14 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last,
|
|||||||
if (max != nullptr) {
|
if (max != nullptr) {
|
||||||
found->set_max(std::stoi(max));
|
found->set_max(std::stoi(max));
|
||||||
}
|
}
|
||||||
|
if (power != nullptr) {
|
||||||
|
found->set_powered(std::stoi(power) == 0);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const int actual_int = actual == nullptr ? 0 : std::stoi(actual);
|
const int actual_int = actual == nullptr ? 0 : std::stoi(actual);
|
||||||
const int max_int = max == nullptr ? 0 : std::stoi(max);
|
const int max_int = max == nullptr ? 0 : std::stoi(max);
|
||||||
*inserter = BacklightDev{name, actual_int, max_int};
|
const bool power_bool = power == nullptr ? true : std::stoi(power) == 0;
|
||||||
|
*inserter = BacklightDev{name, actual_int, max_int, power_bool};
|
||||||
++inserter;
|
++inserter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
#include "modules/battery.hpp"
|
#include "modules/battery.hpp"
|
||||||
|
#if defined(__FreeBSD__)
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#endif
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config)
|
waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config)
|
||||||
: ALabel(config, "battery", id, "{capacity}%", 60) {
|
: ALabel(config, "battery", id, "{capacity}%", 60) {
|
||||||
|
#if defined(__linux__)
|
||||||
battery_watch_fd_ = inotify_init1(IN_CLOEXEC);
|
battery_watch_fd_ = inotify_init1(IN_CLOEXEC);
|
||||||
if (battery_watch_fd_ == -1) {
|
if (battery_watch_fd_ == -1) {
|
||||||
throw std::runtime_error("Unable to listen batteries.");
|
throw std::runtime_error("Unable to listen batteries.");
|
||||||
@ -19,11 +23,12 @@ waybar::modules::Battery::Battery(const std::string& id, const Json::Value& conf
|
|||||||
if (global_watch < 0) {
|
if (global_watch < 0) {
|
||||||
throw std::runtime_error("Could not watch for battery plug/unplug");
|
throw std::runtime_error("Could not watch for battery plug/unplug");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
worker();
|
worker();
|
||||||
}
|
}
|
||||||
|
|
||||||
waybar::modules::Battery::~Battery() {
|
waybar::modules::Battery::~Battery() {
|
||||||
|
#if defined(__linux__)
|
||||||
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
||||||
|
|
||||||
if (global_watch >= 0) {
|
if (global_watch >= 0) {
|
||||||
@ -39,9 +44,16 @@ waybar::modules::Battery::~Battery() {
|
|||||||
batteries_.erase(it);
|
batteries_.erase(it);
|
||||||
}
|
}
|
||||||
close(battery_watch_fd_);
|
close(battery_watch_fd_);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Battery::worker() {
|
void waybar::modules::Battery::worker() {
|
||||||
|
#if defined(__FreeBSD__)
|
||||||
|
thread_timer_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
thread_timer_.sleep_for(interval_);
|
||||||
|
};
|
||||||
|
#else
|
||||||
thread_timer_ = [this] {
|
thread_timer_ = [this] {
|
||||||
// Make sure we eventually update the list of batteries even if we miss an
|
// Make sure we eventually update the list of batteries even if we miss an
|
||||||
// inotify event for some reason
|
// inotify event for some reason
|
||||||
@ -68,9 +80,11 @@ void waybar::modules::Battery::worker() {
|
|||||||
refreshBatteries();
|
refreshBatteries();
|
||||||
dp.emit();
|
dp.emit();
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Battery::refreshBatteries() {
|
void waybar::modules::Battery::refreshBatteries() {
|
||||||
|
#if defined(__linux__)
|
||||||
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
||||||
// Mark existing list of batteries as not necessarily found
|
// Mark existing list of batteries as not necessarily found
|
||||||
std::map<fs::path, bool> check_map;
|
std::map<fs::path, bool> check_map;
|
||||||
@ -93,6 +107,15 @@ void waybar::modules::Battery::refreshBatteries() {
|
|||||||
std::ifstream(node.path() / "type") >> type;
|
std::ifstream(node.path() / "type") >> type;
|
||||||
|
|
||||||
if (!type.compare("Battery")) {
|
if (!type.compare("Battery")) {
|
||||||
|
// Ignore non-system power supplies unless explicitly requested
|
||||||
|
if (!bat_defined && fs::exists(node.path() / "scope")) {
|
||||||
|
std::string scope;
|
||||||
|
std::ifstream(node.path() / "scope") >> scope;
|
||||||
|
if (g_ascii_strcasecmp(scope.data(), "device") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
check_map[node.path()] = true;
|
check_map[node.path()] = true;
|
||||||
auto search = batteries_.find(node.path());
|
auto search = batteries_.find(node.path());
|
||||||
if (search == batteries_.end()) {
|
if (search == batteries_.end()) {
|
||||||
@ -108,7 +131,7 @@ void waybar::modules::Battery::refreshBatteries() {
|
|||||||
}
|
}
|
||||||
auto adap_defined = config_["adapter"].isString();
|
auto adap_defined = config_["adapter"].isString();
|
||||||
if (((adap_defined && dir_name == config_["adapter"].asString()) || !adap_defined) &&
|
if (((adap_defined && dir_name == config_["adapter"].asString()) || !adap_defined) &&
|
||||||
fs::exists(node.path() / "online")) {
|
(fs::exists(node.path() / "online") || fs::exists(node.path() / "status"))) {
|
||||||
adapter_ = node.path();
|
adapter_ = node.path();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,6 +158,7 @@ void waybar::modules::Battery::refreshBatteries() {
|
|||||||
batteries_.erase(check.first);
|
batteries_.erase(check.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown > Full > Not charging > Discharging > Charging
|
// Unknown > Full > Not charging > Discharging > Charging
|
||||||
@ -156,105 +180,35 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
|||||||
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
std::lock_guard<std::mutex> guard(battery_list_mutex_);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
uint32_t total_power = 0; // μW
|
#if defined(__FreeBSD__)
|
||||||
uint32_t total_energy = 0; // μWh
|
/* Allocate state of battery units reported via ACPI. */
|
||||||
uint32_t total_energy_full = 0;
|
int battery_units = 0;
|
||||||
uint32_t total_energy_full_design = 0;
|
size_t battery_units_size = sizeof battery_units;
|
||||||
uint32_t total_capacity{0};
|
if (sysctlbyname("hw.acpi.battery.units", &battery_units, &battery_units_size, NULL, 0) != 0) {
|
||||||
std::string status = "Unknown";
|
throw std::runtime_error("sysctl hw.acpi.battery.units failed");
|
||||||
for (auto const& item : batteries_) {
|
}
|
||||||
auto bat = item.first;
|
|
||||||
uint32_t power_now;
|
|
||||||
uint32_t energy_full;
|
|
||||||
uint32_t energy_now;
|
|
||||||
uint32_t energy_full_design;
|
|
||||||
uint32_t capacity{0};
|
|
||||||
std::string _status;
|
|
||||||
std::getline(std::ifstream(bat / "status"), _status);
|
|
||||||
|
|
||||||
// Some battery will report current and charge in μA/μAh.
|
if (battery_units < 0) {
|
||||||
// Scale these by the voltage to get μW/μWh.
|
throw std::runtime_error("No battery units");
|
||||||
if (fs::exists(bat / "current_now") || fs::exists(bat / "current_avg")) {
|
}
|
||||||
uint32_t voltage_now;
|
|
||||||
uint32_t current_now;
|
|
||||||
uint32_t charge_now;
|
|
||||||
uint32_t charge_full;
|
|
||||||
uint32_t charge_full_design;
|
|
||||||
// Some batteries have only *_avg, not *_now
|
|
||||||
if (fs::exists(bat / "voltage_now"))
|
|
||||||
std::ifstream(bat / "voltage_now") >> voltage_now;
|
|
||||||
else
|
|
||||||
std::ifstream(bat / "voltage_avg") >> voltage_now;
|
|
||||||
if (fs::exists(bat / "current_now"))
|
|
||||||
std::ifstream(bat / "current_now") >> current_now;
|
|
||||||
else
|
|
||||||
std::ifstream(bat / "current_avg") >> current_now;
|
|
||||||
std::ifstream(bat / "charge_full") >> charge_full;
|
|
||||||
std::ifstream(bat / "charge_full_design") >> charge_full_design;
|
|
||||||
if (fs::exists(bat / "charge_now"))
|
|
||||||
std::ifstream(bat / "charge_now") >> charge_now;
|
|
||||||
else {
|
|
||||||
// charge_now is missing on some systems, estimate using capacity.
|
|
||||||
uint32_t capacity;
|
|
||||||
std::ifstream(bat / "capacity") >> capacity;
|
|
||||||
charge_now = (capacity * charge_full) / 100;
|
|
||||||
}
|
|
||||||
power_now = ((uint64_t)current_now * (uint64_t)voltage_now) / 1000000;
|
|
||||||
energy_now = ((uint64_t)charge_now * (uint64_t)voltage_now) / 1000000;
|
|
||||||
energy_full = ((uint64_t)charge_full * (uint64_t)voltage_now) / 1000000;
|
|
||||||
energy_full_design = ((uint64_t)charge_full_design * (uint64_t)voltage_now) / 1000000;
|
|
||||||
} // Gamepads such as PS Dualshock provide the only capacity
|
|
||||||
else if (fs::exists(bat / "energy_now") && fs::exists(bat / "energy_full")) {
|
|
||||||
std::ifstream(bat / "power_now") >> power_now;
|
|
||||||
std::ifstream(bat / "energy_now") >> energy_now;
|
|
||||||
std::ifstream(bat / "energy_full") >> energy_full;
|
|
||||||
std::ifstream(bat / "energy_full_design") >> energy_full_design;
|
|
||||||
} else {
|
|
||||||
std::ifstream(bat / "capacity") >> capacity;
|
|
||||||
power_now = 0;
|
|
||||||
energy_now = 0;
|
|
||||||
energy_full = 0;
|
|
||||||
energy_full_design = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show the "smallest" status among all batteries
|
int capacity;
|
||||||
if (status_gt(status, _status)) {
|
size_t size_capacity = sizeof capacity;
|
||||||
status = _status;
|
if (sysctlbyname("hw.acpi.battery.life", &capacity, &size_capacity, NULL, 0) != 0) {
|
||||||
}
|
throw std::runtime_error("sysctl hw.acpi.battery.life failed");
|
||||||
total_power += power_now;
|
|
||||||
total_energy += energy_now;
|
|
||||||
total_energy_full += energy_full;
|
|
||||||
total_energy_full_design += energy_full_design;
|
|
||||||
total_capacity += capacity;
|
|
||||||
}
|
}
|
||||||
if (!adapter_.empty() && status == "Discharging") {
|
int time;
|
||||||
bool online;
|
size_t size_time = sizeof time;
|
||||||
std::ifstream(adapter_ / "online") >> online;
|
if (sysctlbyname("hw.acpi.battery.time", &time, &size_time, NULL, 0) != 0) {
|
||||||
if (online) {
|
throw std::runtime_error("sysctl hw.acpi.battery.time failed");
|
||||||
status = "Plugged";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
float time_remaining = 0;
|
int rate;
|
||||||
if (status == "Discharging" && total_power != 0) {
|
size_t size_rate = sizeof rate;
|
||||||
time_remaining = (float)total_energy / total_power;
|
if (sysctlbyname("hw.acpi.battery.rate", &rate, &size_rate, NULL, 0) != 0) {
|
||||||
} else if (status == "Charging" && total_power != 0) {
|
throw std::runtime_error("sysctl hw.acpi.battery.rate failed");
|
||||||
time_remaining = -(float)(total_energy_full - total_energy) / total_power;
|
|
||||||
if (time_remaining > 0.0f) {
|
|
||||||
// If we've turned positive it means the battery is past 100% and so
|
|
||||||
// just report that as no time remaining
|
|
||||||
time_remaining = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
float capacity{0.0f};
|
|
||||||
if (total_energy_full > 0.0f) {
|
|
||||||
capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
|
||||||
} else {
|
|
||||||
capacity = (float)total_capacity;
|
|
||||||
}
|
|
||||||
// Handle design-capacity
|
|
||||||
if (config_["design-capacity"].isBool() ? config_["design-capacity"].asBool() : false) {
|
|
||||||
capacity = ((float)total_energy * 100.0f / (float)total_energy_full_design);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto status = getAdapterStatus(capacity);
|
||||||
// Handle full-at
|
// Handle full-at
|
||||||
if (config_["full-at"].isUInt()) {
|
if (config_["full-at"].isUInt()) {
|
||||||
auto full_at = config_["full-at"].asUInt();
|
auto full_at = config_["full-at"].asUInt();
|
||||||
@ -268,13 +222,341 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
|||||||
capacity = 100.f;
|
capacity = 100.f;
|
||||||
}
|
}
|
||||||
uint8_t cap = round(capacity);
|
uint8_t cap = round(capacity);
|
||||||
if (cap == 100 && status == "Charging") {
|
if (cap == 100 && status == "Plugged") {
|
||||||
// If we've reached 100% just mark as full as some batteries can stay
|
// If we've reached 100% just mark as full as some batteries can stay
|
||||||
// stuck reporting they're still charging but not yet done
|
// stuck reporting they're still charging but not yet done
|
||||||
status = "Full";
|
status = "Full";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// spdlog::info("{} {} {} {}", capacity,time,status,rate);
|
||||||
|
return {capacity, time / 60.0, status, rate};
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
uint32_t total_power = 0; // μW
|
||||||
|
bool total_power_exists = false;
|
||||||
|
uint32_t total_energy = 0; // μWh
|
||||||
|
bool total_energy_exists = false;
|
||||||
|
uint32_t total_energy_full = 0;
|
||||||
|
bool total_energy_full_exists = false;
|
||||||
|
uint32_t total_energy_full_design = 0;
|
||||||
|
bool total_energy_full_design_exists = false;
|
||||||
|
uint32_t total_capacity = 0;
|
||||||
|
bool total_capacity_exists = false;
|
||||||
|
uint32_t time_to_empty_now = 0;
|
||||||
|
bool time_to_empty_now_exists = false;
|
||||||
|
uint32_t time_to_full_now = 0;
|
||||||
|
bool time_to_full_now_exists = false;
|
||||||
|
|
||||||
|
std::string status = "Unknown";
|
||||||
|
for (auto const& item : batteries_) {
|
||||||
|
auto bat = item.first;
|
||||||
|
std::string _status;
|
||||||
|
std::getline(std::ifstream(bat / "status"), _status);
|
||||||
|
|
||||||
|
// Some battery will report current and charge in μA/μAh.
|
||||||
|
// Scale these by the voltage to get μW/μWh.
|
||||||
|
|
||||||
|
uint32_t capacity = 0;
|
||||||
|
bool capacity_exists = false;
|
||||||
|
if (fs::exists(bat / "capacity")) {
|
||||||
|
capacity_exists = true;
|
||||||
|
std::ifstream(bat / "capacity") >> capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t current_now = 0;
|
||||||
|
bool current_now_exists = false;
|
||||||
|
if (fs::exists(bat / "current_now")) {
|
||||||
|
current_now_exists = true;
|
||||||
|
std::ifstream(bat / "current_now") >> current_now;
|
||||||
|
} else if (fs::exists(bat / "current_avg")) {
|
||||||
|
current_now_exists = true;
|
||||||
|
std::ifstream(bat / "current_avg") >> current_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs::exists(bat / "time_to_empty_now")) {
|
||||||
|
time_to_empty_now_exists = true;
|
||||||
|
std::ifstream(bat / "time_to_empty_now") >> time_to_empty_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs::exists(bat / "time_to_full_now")) {
|
||||||
|
time_to_full_now_exists = true;
|
||||||
|
std::ifstream(bat / "time_to_full_now") >> time_to_full_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t voltage_now = 0;
|
||||||
|
bool voltage_now_exists = false;
|
||||||
|
if (fs::exists(bat / "voltage_now")) {
|
||||||
|
voltage_now_exists = true;
|
||||||
|
std::ifstream(bat / "voltage_now") >> voltage_now;
|
||||||
|
} else if (fs::exists(bat / "voltage_avg")) {
|
||||||
|
voltage_now_exists = true;
|
||||||
|
std::ifstream(bat / "voltage_avg") >> voltage_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t charge_full = 0;
|
||||||
|
bool charge_full_exists = false;
|
||||||
|
if (fs::exists(bat / "charge_full")) {
|
||||||
|
charge_full_exists = true;
|
||||||
|
std::ifstream(bat / "charge_full") >> charge_full;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t charge_full_design = 0;
|
||||||
|
bool charge_full_design_exists = false;
|
||||||
|
if (fs::exists(bat / "charge_full_design")) {
|
||||||
|
charge_full_design_exists = true;
|
||||||
|
std::ifstream(bat / "charge_full_design") >> charge_full_design;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t charge_now = 0;
|
||||||
|
bool charge_now_exists = false;
|
||||||
|
if (fs::exists(bat / "charge_now")) {
|
||||||
|
charge_now_exists = true;
|
||||||
|
std::ifstream(bat / "charge_now") >> charge_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t power_now = 0;
|
||||||
|
bool power_now_exists = false;
|
||||||
|
if (fs::exists(bat / "power_now")) {
|
||||||
|
power_now_exists = true;
|
||||||
|
std::ifstream(bat / "power_now") >> power_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t energy_now = 0;
|
||||||
|
bool energy_now_exists = false;
|
||||||
|
if (fs::exists(bat / "energy_now")) {
|
||||||
|
energy_now_exists = true;
|
||||||
|
std::ifstream(bat / "energy_now") >> energy_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t energy_full = 0;
|
||||||
|
bool energy_full_exists = false;
|
||||||
|
if (fs::exists(bat / "energy_full")) {
|
||||||
|
energy_full_exists = true;
|
||||||
|
std::ifstream(bat / "energy_full") >> energy_full;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t energy_full_design = 0;
|
||||||
|
bool energy_full_design_exists = false;
|
||||||
|
if (fs::exists(bat / "energy_full_design")) {
|
||||||
|
energy_full_design_exists = true;
|
||||||
|
std::ifstream(bat / "energy_full_design") >> energy_full_design;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!voltage_now_exists) {
|
||||||
|
if (power_now_exists && current_now_exists && current_now != 0) {
|
||||||
|
voltage_now_exists = true;
|
||||||
|
voltage_now = 1000000 * (uint64_t)power_now / (uint64_t)current_now;
|
||||||
|
} else if (energy_full_design_exists && charge_full_design_exists &&
|
||||||
|
charge_full_design != 0) {
|
||||||
|
voltage_now_exists = true;
|
||||||
|
voltage_now = 1000000 * (uint64_t)energy_full_design / (uint64_t)charge_full_design;
|
||||||
|
} else if (energy_now_exists) {
|
||||||
|
if (charge_now_exists && charge_now != 0) {
|
||||||
|
voltage_now_exists = true;
|
||||||
|
voltage_now = 1000000 * (uint64_t)energy_now / (uint64_t)charge_now;
|
||||||
|
} else if (capacity_exists && charge_full_exists) {
|
||||||
|
charge_now_exists = true;
|
||||||
|
charge_now = (uint64_t)charge_full * (uint64_t)capacity / 100;
|
||||||
|
if (charge_full != 0 && capacity != 0) {
|
||||||
|
voltage_now_exists = true;
|
||||||
|
voltage_now =
|
||||||
|
1000000 * (uint64_t)energy_now * 100 / (uint64_t)charge_full / (uint64_t)capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (energy_full_exists) {
|
||||||
|
if (charge_full_exists && charge_full != 0) {
|
||||||
|
voltage_now_exists = true;
|
||||||
|
voltage_now = 1000000 * (uint64_t)energy_full / (uint64_t)charge_full;
|
||||||
|
} else if (charge_now_exists && capacity_exists) {
|
||||||
|
if (capacity != 0) {
|
||||||
|
charge_full_exists = true;
|
||||||
|
charge_full = 100 * (uint64_t)charge_now / (uint64_t)capacity;
|
||||||
|
}
|
||||||
|
if (charge_now != 0) {
|
||||||
|
voltage_now_exists = true;
|
||||||
|
voltage_now =
|
||||||
|
10000 * (uint64_t)energy_full * (uint64_t)capacity / (uint64_t)charge_now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!capacity_exists) {
|
||||||
|
if (charge_now_exists && charge_full_exists && charge_full != 0) {
|
||||||
|
capacity_exists = true;
|
||||||
|
capacity = 100 * (uint64_t)charge_now / (uint64_t)charge_full;
|
||||||
|
} else if (energy_now_exists && energy_full_exists && energy_full != 0) {
|
||||||
|
capacity_exists = true;
|
||||||
|
capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full;
|
||||||
|
} else if (charge_now_exists && energy_full_exists && voltage_now_exists) {
|
||||||
|
if (!charge_full_exists && voltage_now != 0) {
|
||||||
|
charge_full_exists = true;
|
||||||
|
charge_full = 1000000 * (uint64_t)energy_full / (uint64_t)voltage_now;
|
||||||
|
}
|
||||||
|
if (energy_full != 0) {
|
||||||
|
capacity_exists = true;
|
||||||
|
capacity = (uint64_t)charge_now * (uint64_t)voltage_now / 10000 / (uint64_t)energy_full;
|
||||||
|
}
|
||||||
|
} else if (charge_full_exists && energy_now_exists && voltage_now_exists) {
|
||||||
|
if (!charge_now_exists && voltage_now != 0) {
|
||||||
|
charge_now_exists = true;
|
||||||
|
charge_now = 1000000 * (uint64_t)energy_now / (uint64_t)voltage_now;
|
||||||
|
}
|
||||||
|
if (voltage_now != 0 && charge_full != 0) {
|
||||||
|
capacity_exists = true;
|
||||||
|
capacity = 100 * 1000000 * (uint64_t)energy_now / (uint64_t)voltage_now /
|
||||||
|
(uint64_t)charge_full;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!energy_now_exists && voltage_now_exists) {
|
||||||
|
if (charge_now_exists) {
|
||||||
|
energy_now_exists = true;
|
||||||
|
energy_now = (uint64_t)charge_now * (uint64_t)voltage_now / 1000000;
|
||||||
|
} else if (capacity_exists && charge_full_exists) {
|
||||||
|
charge_now_exists = true;
|
||||||
|
charge_now = (uint64_t)capacity * (uint64_t)charge_full / 100;
|
||||||
|
energy_now_exists = true;
|
||||||
|
energy_now =
|
||||||
|
(uint64_t)voltage_now * (uint64_t)capacity * (uint64_t)charge_full / 1000000 / 100;
|
||||||
|
} else if (capacity_exists && energy_full) {
|
||||||
|
if (voltage_now != 0) {
|
||||||
|
charge_full_exists = true;
|
||||||
|
charge_full = 1000000 * (uint64_t)energy_full / (uint64_t)voltage_now;
|
||||||
|
charge_now_exists = true;
|
||||||
|
charge_now = (uint64_t)capacity * 10000 * (uint64_t)energy_full / (uint64_t)voltage_now;
|
||||||
|
}
|
||||||
|
energy_now_exists = true;
|
||||||
|
energy_now = (uint64_t)capacity * (uint64_t)energy_full / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!energy_full_exists && voltage_now_exists) {
|
||||||
|
if (charge_full_exists) {
|
||||||
|
energy_full_exists = true;
|
||||||
|
energy_full = (uint64_t)charge_full * (uint64_t)voltage_now / 1000000;
|
||||||
|
} else if (charge_now_exists && capacity_exists && capacity != 0) {
|
||||||
|
charge_full_exists = true;
|
||||||
|
charge_full = 100 * (uint64_t)charge_now / (uint64_t)capacity;
|
||||||
|
energy_full_exists = true;
|
||||||
|
energy_full = (uint64_t)charge_now * (uint64_t)voltage_now / (uint64_t)capacity / 10000;
|
||||||
|
} else if (capacity_exists && energy_now) {
|
||||||
|
if (voltage_now != 0) {
|
||||||
|
charge_now_exists = true;
|
||||||
|
charge_now = 1000000 * (uint64_t)energy_now / (uint64_t)voltage_now;
|
||||||
|
}
|
||||||
|
if (capacity != 0) {
|
||||||
|
energy_full_exists = true;
|
||||||
|
energy_full = 100 * (uint64_t)energy_now / (uint64_t)capacity;
|
||||||
|
if (voltage_now != 0) {
|
||||||
|
charge_full_exists = true;
|
||||||
|
charge_full =
|
||||||
|
100 * 1000000 * (uint64_t)energy_now / (uint64_t)voltage_now / (uint64_t)capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!power_now_exists && voltage_now_exists && current_now_exists) {
|
||||||
|
power_now_exists = true;
|
||||||
|
power_now = (uint64_t)voltage_now * (uint64_t)current_now / 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!energy_full_design_exists && voltage_now_exists && charge_full_design_exists) {
|
||||||
|
energy_full_design_exists = true;
|
||||||
|
energy_full_design = (uint64_t)voltage_now * (uint64_t)charge_full_design / 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the "smallest" status among all batteries
|
||||||
|
if (status_gt(status, _status)) status = _status;
|
||||||
|
|
||||||
|
if (power_now_exists) {
|
||||||
|
total_power_exists = true;
|
||||||
|
total_power += power_now;
|
||||||
|
}
|
||||||
|
if (energy_now_exists) {
|
||||||
|
total_energy_exists = true;
|
||||||
|
total_energy += energy_now;
|
||||||
|
}
|
||||||
|
if (energy_full_exists) {
|
||||||
|
total_energy_full_exists = true;
|
||||||
|
total_energy_full += energy_full;
|
||||||
|
}
|
||||||
|
if (energy_full_design_exists) {
|
||||||
|
total_energy_full_design_exists = true;
|
||||||
|
total_energy_full_design += energy_full_design;
|
||||||
|
}
|
||||||
|
if (capacity_exists) {
|
||||||
|
total_capacity_exists = true;
|
||||||
|
total_capacity += capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give `Plugged` higher priority over `Not charging`.
|
||||||
|
// So in a setting where TLP is used, `Plugged` is shown when the threshold is reached
|
||||||
|
if (!adapter_.empty() && (status == "Discharging" || status == "Not charging")) {
|
||||||
|
bool online;
|
||||||
|
std::string current_status;
|
||||||
|
std::ifstream(adapter_ / "online") >> online;
|
||||||
|
std::getline(std::ifstream(adapter_ / "status"), current_status);
|
||||||
|
if (online && current_status != "Discharging") status = "Plugged";
|
||||||
|
}
|
||||||
|
|
||||||
|
float time_remaining{0.0f};
|
||||||
|
if (status == "Discharging" && time_to_empty_now_exists) {
|
||||||
|
if (time_to_empty_now != 0) time_remaining = (float)time_to_empty_now / 1000.0f;
|
||||||
|
} else if (status == "Discharging" && total_power_exists && total_energy_exists) {
|
||||||
|
if (total_power != 0) time_remaining = (float)total_energy / total_power;
|
||||||
|
} else if (status == "Charging" && time_to_full_now_exists) {
|
||||||
|
if (time_to_full_now_exists && (time_to_full_now != 0))
|
||||||
|
time_remaining = -(float)time_to_full_now / 1000.0f;
|
||||||
|
// If we've turned positive it means the battery is past 100% and so just report that as no
|
||||||
|
// time remaining
|
||||||
|
if (time_remaining > 0.0f) time_remaining = 0.0f;
|
||||||
|
} else if (status == "Charging" && total_energy_exists && total_energy_full_exists &&
|
||||||
|
total_power_exists) {
|
||||||
|
if (total_power != 0)
|
||||||
|
time_remaining = -(float)(total_energy_full - total_energy) / total_power;
|
||||||
|
// If we've turned positive it means the battery is past 100% and so just report that as no
|
||||||
|
// time remaining
|
||||||
|
if (time_remaining > 0.0f) time_remaining = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float calculated_capacity{0.0f};
|
||||||
|
if (total_capacity_exists) {
|
||||||
|
if (total_capacity > 0.0f)
|
||||||
|
calculated_capacity = (float)total_capacity / batteries_.size();
|
||||||
|
else if (total_energy_full_exists && total_energy_exists) {
|
||||||
|
if (total_energy_full > 0.0f)
|
||||||
|
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle design-capacity
|
||||||
|
if ((config_["design-capacity"].isBool() ? config_["design-capacity"].asBool() : false) &&
|
||||||
|
total_energy_exists && total_energy_full_design_exists) {
|
||||||
|
if (total_energy_full_design > 0.0f)
|
||||||
|
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full_design);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle full-at
|
||||||
|
if (config_["full-at"].isUInt()) {
|
||||||
|
auto full_at = config_["full-at"].asUInt();
|
||||||
|
if (full_at < 100) calculated_capacity = 100.f * calculated_capacity / full_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle it gracefully by clamping at 100%
|
||||||
|
// This can happen when the battery is calibrating and goes above 100%
|
||||||
|
if (calculated_capacity > 100.f) calculated_capacity = 100.f;
|
||||||
|
|
||||||
|
uint8_t cap = round(calculated_capacity);
|
||||||
|
// If we've reached 100% just mark as full as some batteries can stay stuck reporting they're
|
||||||
|
// still charging but not yet done
|
||||||
|
if (cap == 100 && status == "Charging") status = "Full";
|
||||||
|
|
||||||
return {cap, time_remaining, status, total_power / 1e6};
|
return {cap, time_remaining, status, total_power / 1e6};
|
||||||
|
#endif
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
spdlog::error("Battery: {}", e.what());
|
spdlog::error("Battery: {}", e.what());
|
||||||
return {0, 0, "Unknown", 0};
|
return {0, 0, "Unknown", 0};
|
||||||
@ -282,13 +564,26 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) const {
|
const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) const {
|
||||||
|
#if defined(__FreeBSD__)
|
||||||
|
int state;
|
||||||
|
size_t size_state = sizeof state;
|
||||||
|
if (sysctlbyname("hw.acpi.battery.state", &state, &size_state, NULL, 0) != 0) {
|
||||||
|
throw std::runtime_error("sysctl hw.acpi.battery.state failed");
|
||||||
|
}
|
||||||
|
bool online = state == 2;
|
||||||
|
std::string status{"Unknown"}; // TODO: add status in FreeBSD
|
||||||
|
{
|
||||||
|
#else
|
||||||
if (!adapter_.empty()) {
|
if (!adapter_.empty()) {
|
||||||
bool online;
|
bool online;
|
||||||
|
std::string status;
|
||||||
std::ifstream(adapter_ / "online") >> online;
|
std::ifstream(adapter_ / "online") >> online;
|
||||||
|
std::getline(std::ifstream(adapter_ / "status"), status);
|
||||||
|
#endif
|
||||||
if (capacity == 100) {
|
if (capacity == 100) {
|
||||||
return "Full";
|
return "Full";
|
||||||
}
|
}
|
||||||
if (online) {
|
if (online && status != "Discharging") {
|
||||||
return "Plugged";
|
return "Plugged";
|
||||||
}
|
}
|
||||||
return "Discharging";
|
return "Discharging";
|
||||||
@ -308,14 +603,18 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai
|
|||||||
if (config_["format-time"].isString()) {
|
if (config_["format-time"].isString()) {
|
||||||
format = config_["format-time"].asString();
|
format = config_["format-time"].asString();
|
||||||
}
|
}
|
||||||
return fmt::format(format, fmt::arg("H", full_hours), fmt::arg("M", minutes));
|
std::string zero_pad_minutes = fmt::format("{:02d}", minutes);
|
||||||
|
return fmt::format(format, fmt::arg("H", full_hours), fmt::arg("M", minutes),
|
||||||
|
fmt::arg("m", zero_pad_minutes));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Battery::update() -> void {
|
auto waybar::modules::Battery::update() -> void {
|
||||||
|
#if defined(__linux__)
|
||||||
if (batteries_.empty()) {
|
if (batteries_.empty()) {
|
||||||
event_box_.hide();
|
event_box_.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
auto [capacity, time_remaining, status, power] = getInfos();
|
auto [capacity, time_remaining, status, power] = getInfos();
|
||||||
if (status == "Unknown") {
|
if (status == "Unknown") {
|
||||||
status = getAdapterStatus(capacity);
|
status = getAdapterStatus(capacity);
|
||||||
@ -346,7 +645,7 @@ auto waybar::modules::Battery::update() -> void {
|
|||||||
tooltip_format = config_["tooltip-format"].asString();
|
tooltip_format = config_["tooltip-format"].asString();
|
||||||
}
|
}
|
||||||
label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("timeTo", tooltip_text_default),
|
label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("timeTo", tooltip_text_default),
|
||||||
fmt::arg("capacity", capacity),
|
fmt::arg("power", power), fmt::arg("capacity", capacity),
|
||||||
fmt::arg("time", time_remaining_formatted)));
|
fmt::arg("time", time_remaining_formatted)));
|
||||||
}
|
}
|
||||||
if (!old_status_.empty()) {
|
if (!old_status_.empty()) {
|
||||||
|
@ -101,6 +101,7 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value&
|
|||||||
} else {
|
} else {
|
||||||
spdlog::error("findCurController() failed: no bluetooth controller found");
|
spdlog::error("findCurController() failed: no bluetooth controller found");
|
||||||
}
|
}
|
||||||
|
event_box_.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
findConnectedDevices(cur_controller_.path, connected_devices_);
|
findConnectedDevices(cur_controller_.path, connected_devices_);
|
||||||
@ -152,11 +153,23 @@ auto waybar::modules::Bluetooth::update() -> void {
|
|||||||
#ifdef WANT_RFKILL
|
#ifdef WANT_RFKILL
|
||||||
if (rfkill_.getState()) state = "disabled";
|
if (rfkill_.getState()) state = "disabled";
|
||||||
#endif
|
#endif
|
||||||
|
bool battery_available =
|
||||||
|
state == "connected" && cur_focussed_device_.battery_percentage.has_value();
|
||||||
|
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
// also adds enabled icon if icon for state is not defined
|
||||||
|
std::vector<std::string> states = {state, rfkill_.getState() ? "disabled" : "enabled"};
|
||||||
|
std::string icon = getIcon(0, states);
|
||||||
|
#else
|
||||||
|
std::string icon = getIcon(0, state);
|
||||||
|
#endif
|
||||||
|
std::string icon_label = icon;
|
||||||
|
std::string icon_tooltip = icon;
|
||||||
|
|
||||||
if (!alt_) {
|
if (!alt_) {
|
||||||
if (state == "connected" && cur_focussed_device_.battery_percentage.has_value() &&
|
if (battery_available && config_["format-connected-battery"].isString()) {
|
||||||
config_["format-connected-battery"].isString()) {
|
|
||||||
format_ = config_["format-connected-battery"].asString();
|
format_ = config_["format-connected-battery"].asString();
|
||||||
|
icon_label = getIcon(cur_focussed_device_.battery_percentage.value_or(0));
|
||||||
} else if (config_["format-" + state].isString()) {
|
} else if (config_["format-" + state].isString()) {
|
||||||
format_ = config_["format-" + state].asString();
|
format_ = config_["format-" + state].asString();
|
||||||
} else if (config_["format"].isString()) {
|
} else if (config_["format"].isString()) {
|
||||||
@ -165,7 +178,10 @@ auto waybar::modules::Bluetooth::update() -> void {
|
|||||||
format_ = default_format_;
|
format_ = default_format_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (config_["tooltip-format-" + state].isString()) {
|
if (battery_available && config_["tooltip-format-connected-battery"].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format-connected-battery"].asString();
|
||||||
|
icon_tooltip = getIcon(cur_focussed_device_.battery_percentage.value_or(0));
|
||||||
|
} else if (config_["tooltip-format-" + state].isString()) {
|
||||||
tooltip_format = config_["tooltip-format-" + state].asString();
|
tooltip_format = config_["tooltip-format-" + state].asString();
|
||||||
} else if (config_["tooltip-format"].isString()) {
|
} else if (config_["tooltip-format"].isString()) {
|
||||||
tooltip_format = config_["tooltip-format"].asString();
|
tooltip_format = config_["tooltip-format"].asString();
|
||||||
@ -196,7 +212,7 @@ auto waybar::modules::Bluetooth::update() -> void {
|
|||||||
fmt::arg("controller_alias", cur_controller_.alias),
|
fmt::arg("controller_alias", cur_controller_.alias),
|
||||||
fmt::arg("device_address", cur_focussed_device_.address),
|
fmt::arg("device_address", cur_focussed_device_.address),
|
||||||
fmt::arg("device_address_type", cur_focussed_device_.address_type),
|
fmt::arg("device_address_type", cur_focussed_device_.address_type),
|
||||||
fmt::arg("device_alias", cur_focussed_device_.alias),
|
fmt::arg("device_alias", cur_focussed_device_.alias), fmt::arg("icon", icon_label),
|
||||||
fmt::arg("device_battery_percentage", cur_focussed_device_.battery_percentage.value_or(0))));
|
fmt::arg("device_battery_percentage", cur_focussed_device_.battery_percentage.value_or(0))));
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
@ -209,14 +225,18 @@ auto waybar::modules::Bluetooth::update() -> void {
|
|||||||
if ((tooltip_enumerate_connections_battery_ && dev.battery_percentage.has_value()) ||
|
if ((tooltip_enumerate_connections_battery_ && dev.battery_percentage.has_value()) ||
|
||||||
tooltip_enumerate_connections_) {
|
tooltip_enumerate_connections_) {
|
||||||
ss << "\n";
|
ss << "\n";
|
||||||
std::string enumerate_format =
|
std::string enumerate_format;
|
||||||
(tooltip_enumerate_connections_battery_ && dev.battery_percentage.has_value())
|
std::string enumerate_icon;
|
||||||
? config_["tooltip-format-enumerate-connected-battery"].asString()
|
if (tooltip_enumerate_connections_battery_ && dev.battery_percentage.has_value()) {
|
||||||
: config_["tooltip-format-enumerate-connected"].asString();
|
enumerate_format = config_["tooltip-format-enumerate-connected-battery"].asString();
|
||||||
|
enumerate_icon = getIcon(dev.battery_percentage.value_or(0));
|
||||||
|
} else {
|
||||||
|
enumerate_format = config_["tooltip-format-enumerate-connected"].asString();
|
||||||
|
}
|
||||||
ss << fmt::format(
|
ss << fmt::format(
|
||||||
enumerate_format, fmt::arg("device_address", dev.address),
|
enumerate_format, fmt::arg("device_address", dev.address),
|
||||||
fmt::arg("device_address_type", dev.address_type),
|
fmt::arg("device_address_type", dev.address_type),
|
||||||
fmt::arg("device_alias", dev.alias),
|
fmt::arg("device_alias", dev.alias), fmt::arg("icon", enumerate_icon),
|
||||||
fmt::arg("device_battery_percentage", dev.battery_percentage.value_or(0)));
|
fmt::arg("device_battery_percentage", dev.battery_percentage.value_or(0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,7 +254,7 @@ auto waybar::modules::Bluetooth::update() -> void {
|
|||||||
fmt::arg("controller_alias", cur_controller_.alias),
|
fmt::arg("controller_alias", cur_controller_.alias),
|
||||||
fmt::arg("device_address", cur_focussed_device_.address),
|
fmt::arg("device_address", cur_focussed_device_.address),
|
||||||
fmt::arg("device_address_type", cur_focussed_device_.address_type),
|
fmt::arg("device_address_type", cur_focussed_device_.address_type),
|
||||||
fmt::arg("device_alias", cur_focussed_device_.alias),
|
fmt::arg("device_alias", cur_focussed_device_.alias), fmt::arg("icon", icon_tooltip),
|
||||||
fmt::arg("device_battery_percentage", cur_focussed_device_.battery_percentage.value_or(0)),
|
fmt::arg("device_battery_percentage", cur_focussed_device_.battery_percentage.value_or(0)),
|
||||||
fmt::arg("device_enumerate", device_enumerate_)));
|
fmt::arg("device_enumerate", device_enumerate_)));
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
#include "modules/clock.hpp"
|
#include "modules/clock.hpp"
|
||||||
|
|
||||||
|
#include <fmt/chrono.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <iomanip>
|
|
||||||
#if FMT_VERSION < 60000
|
|
||||||
#include <fmt/time.h>
|
|
||||||
#else
|
|
||||||
#include <fmt/chrono.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <regex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
@ -66,12 +62,40 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_calendar_in_tooltip_) {
|
||||||
|
if (config_["on-scroll"][kCalendarPlaceholder].isInt()) {
|
||||||
|
calendar_shift_init_ =
|
||||||
|
date::months{config_["on-scroll"].get(kCalendarPlaceholder, 0).asInt()};
|
||||||
|
event_box_.add_events(Gdk::LEAVE_NOTIFY_MASK);
|
||||||
|
event_box_.signal_leave_notify_event().connect([this](GdkEventCrossing*) {
|
||||||
|
calendar_shift_ = date::months{0};
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config_["locale"].isString()) {
|
if (config_["locale"].isString()) {
|
||||||
locale_ = std::locale(config_["locale"].asString());
|
locale_ = std::locale(config_["locale"].asString());
|
||||||
} else {
|
} else {
|
||||||
locale_ = std::locale("");
|
locale_ = std::locale("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config_["format-calendar-weeks"].isString()) {
|
||||||
|
fmt_str_weeks_ =
|
||||||
|
std::regex_replace(config_["format-calendar-weeks"].asString(), std::regex("\\{\\}"),
|
||||||
|
(first_day_of_week() == date::Monday) ? "{:%V}" : "{:%U}");
|
||||||
|
fmt_weeks_left_pad_ =
|
||||||
|
std::regex_replace(fmt_str_weeks_, std::regex("</?[^>]+>|\\{.*\\}"), "").length();
|
||||||
|
} else {
|
||||||
|
fmt_str_weeks_ = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_["format-calendar"].isString()) {
|
||||||
|
fmt_str_calendar_ = config_["format-calendar"].asString();
|
||||||
|
} else {
|
||||||
|
fmt_str_calendar_ = "{}";
|
||||||
|
}
|
||||||
|
|
||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
dp.emit();
|
dp.emit();
|
||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
@ -96,6 +120,12 @@ auto waybar::modules::Clock::update() -> void {
|
|||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
waybar_time wtime = {locale_,
|
waybar_time wtime = {locale_,
|
||||||
date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now))};
|
date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now))};
|
||||||
|
|
||||||
|
auto shifted_date = date::year_month_day{date::floor<date::days>(now)} + calendar_shift_;
|
||||||
|
auto now_shifted = date::sys_days{shifted_date} + (now - date::floor<date::days>(now));
|
||||||
|
waybar_time shifted_wtime = {
|
||||||
|
locale_, date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now_shifted))};
|
||||||
|
|
||||||
std::string text = "";
|
std::string text = "";
|
||||||
if (!is_timezone_fixed()) {
|
if (!is_timezone_fixed()) {
|
||||||
// As date dep is not fully compatible, prefer fmt
|
// As date dep is not fully compatible, prefer fmt
|
||||||
@ -109,18 +139,18 @@ auto waybar::modules::Clock::update() -> void {
|
|||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
if (config_["tooltip-format"].isString()) {
|
if (config_["tooltip-format"].isString()) {
|
||||||
std::string calendar_lines = "";
|
std::string calendar_lines{""};
|
||||||
std::string timezoned_time_lines = "";
|
std::string timezoned_time_lines{""};
|
||||||
if (is_calendar_in_tooltip_) {
|
if (is_calendar_in_tooltip_) {
|
||||||
calendar_lines = calendar_text(wtime);
|
calendar_lines = calendar_text(shifted_wtime);
|
||||||
}
|
}
|
||||||
if (is_timezoned_list_in_tooltip_) {
|
if (is_timezoned_list_in_tooltip_) {
|
||||||
timezoned_time_lines = timezones_text(&now);
|
timezoned_time_lines = timezones_text(&now);
|
||||||
}
|
}
|
||||||
auto tooltip_format = config_["tooltip-format"].asString();
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
text =
|
text = fmt::format(tooltip_format, shifted_wtime,
|
||||||
fmt::format(tooltip_format, wtime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
|
fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
|
||||||
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
||||||
label_.set_tooltip_markup(text);
|
label_.set_tooltip_markup(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,20 +166,30 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto dir = AModule::getScrollDir(e);
|
auto dir = AModule::getScrollDir(e);
|
||||||
if (dir != SCROLL_DIR::UP && dir != SCROLL_DIR::DOWN) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (time_zones_.size() == 1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto nr_zones = time_zones_.size();
|
// Shift calendar date
|
||||||
if (dir == SCROLL_DIR::UP) {
|
if (calendar_shift_init_.count() != 0) {
|
||||||
size_t new_idx = current_time_zone_idx_ + 1;
|
if (dir == SCROLL_DIR::UP)
|
||||||
current_time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx;
|
calendar_shift_ += calendar_shift_init_;
|
||||||
|
else
|
||||||
|
calendar_shift_ -= calendar_shift_init_;
|
||||||
} else {
|
} else {
|
||||||
current_time_zone_idx_ =
|
// Change time zone
|
||||||
current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1;
|
if (dir != SCROLL_DIR::UP && dir != SCROLL_DIR::DOWN) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (time_zones_.size() == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nr_zones = time_zones_.size();
|
||||||
|
if (dir == SCROLL_DIR::UP) {
|
||||||
|
size_t new_idx = current_time_zone_idx_ + 1;
|
||||||
|
current_time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx;
|
||||||
|
} else {
|
||||||
|
current_time_zone_idx_ =
|
||||||
|
current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update();
|
update();
|
||||||
@ -158,66 +198,72 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) {
|
|||||||
|
|
||||||
auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string {
|
auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string {
|
||||||
const auto daypoint = date::floor<date::days>(wtime.ztime.get_local_time());
|
const auto daypoint = date::floor<date::days>(wtime.ztime.get_local_time());
|
||||||
const auto ymd = date::year_month_day(daypoint);
|
const auto ymd{date::year_month_day{daypoint}};
|
||||||
if (cached_calendar_ymd_ == ymd) {
|
|
||||||
return cached_calendar_text_;
|
if (calendar_cached_ymd_ == ymd) {
|
||||||
|
return calendar_cached_text_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const date::year_month ym(ymd.year(), ymd.month());
|
const auto curr_day{(calendar_shift_init_.count() != 0 && calendar_shift_.count() != 0)
|
||||||
const auto curr_day = ymd.day();
|
? date::day{0}
|
||||||
|
: ymd.day()};
|
||||||
|
const date::year_month ym{ymd.year(), ymd.month()};
|
||||||
|
const auto first_dow = first_day_of_week();
|
||||||
|
|
||||||
std::stringstream os;
|
std::stringstream os;
|
||||||
|
|
||||||
const auto first_dow = first_day_of_week();
|
enum class WeeksSide {
|
||||||
int ws{0}; // weeks-pos: side(1 - left, 2 - right)
|
LEFT,
|
||||||
int wn{0}; // weeknumber
|
RIGHT,
|
||||||
|
HIDDEN,
|
||||||
|
};
|
||||||
|
WeeksSide weeks_pos = WeeksSide::HIDDEN;
|
||||||
|
|
||||||
if (config_["calendar-weeks-pos"].isString()) {
|
if (config_["calendar-weeks-pos"].isString()) {
|
||||||
wn = (date::sys_days{date::year_month_day{ym / 1}} -
|
|
||||||
date::sys_days{date::year_month_day{ymd.year() / 1 / 1}})
|
|
||||||
.count() /
|
|
||||||
7 +
|
|
||||||
1;
|
|
||||||
if (config_["calendar-weeks-pos"].asString() == "left") {
|
if (config_["calendar-weeks-pos"].asString() == "left") {
|
||||||
ws = 1;
|
weeks_pos = WeeksSide::LEFT;
|
||||||
// Add paddings before the header
|
// Add paddings before the header
|
||||||
os << std::string(4, ' ');
|
os << std::string(3 + fmt_weeks_left_pad_, ' ');
|
||||||
} else if (config_["calendar-weeks-pos"].asString() == "right") {
|
} else if (config_["calendar-weeks-pos"].asString() == "right") {
|
||||||
ws = 2;
|
weeks_pos = WeeksSide::RIGHT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
weekdays_header(first_dow, os);
|
weekdays_header(first_dow, os);
|
||||||
/* Print weeknumber on the left for the first row*/
|
|
||||||
if (ws == 1) {
|
// First week day prefixed with spaces if needed.
|
||||||
print_iso_weeknum(os, wn);
|
date::sys_days print_wd{ym / 1};
|
||||||
os << ' ';
|
auto wd{date::weekday{print_wd}};
|
||||||
++wn;
|
|
||||||
}
|
|
||||||
// First week prefixed with spaces if needed.
|
|
||||||
auto wd = date::weekday(ym / 1);
|
|
||||||
auto empty_days = (wd - first_dow).count();
|
auto empty_days = (wd - first_dow).count();
|
||||||
|
|
||||||
|
/* Print weeknumber on the left for the first row*/
|
||||||
|
if (weeks_pos == WeeksSide::LEFT) {
|
||||||
|
os << fmt::format(fmt_str_weeks_, print_wd) << ' ';
|
||||||
|
}
|
||||||
|
|
||||||
if (empty_days > 0) {
|
if (empty_days > 0) {
|
||||||
os << std::string(empty_days * 3 - 1, ' ');
|
os << std::string(empty_days * 3 - 1, ' ');
|
||||||
}
|
}
|
||||||
auto last_day = (ym / date::literals::last).day();
|
|
||||||
for (auto d = date::day(1); d <= last_day; ++d, ++wd) {
|
const auto last_day = (ym / date::literals::last).day();
|
||||||
|
|
||||||
|
for (auto d{date::day{1}}; d <= last_day; ++d, ++wd) {
|
||||||
if (wd != first_dow) {
|
if (wd != first_dow) {
|
||||||
os << ' ';
|
os << ' ';
|
||||||
} else if (unsigned(d) != 1) {
|
} else if (unsigned(d) != 1) {
|
||||||
if (ws == 2) {
|
if (weeks_pos == WeeksSide::RIGHT) {
|
||||||
os << ' ';
|
os << ' ' << fmt::format(fmt_str_weeks_, print_wd);
|
||||||
print_iso_weeknum(os, wn);
|
|
||||||
++wn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
os << '\n';
|
os << '\n';
|
||||||
|
|
||||||
if (ws == 1) {
|
print_wd = (ym / d);
|
||||||
print_iso_weeknum(os, wn);
|
|
||||||
os << ' ';
|
if (weeks_pos == WeeksSide::LEFT) {
|
||||||
++wn;
|
os << fmt::format(fmt_str_weeks_, print_wd) << ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d == curr_day) {
|
if (d == curr_day) {
|
||||||
if (config_["today-format"].isString()) {
|
if (config_["today-format"].isString()) {
|
||||||
auto today_format = config_["today-format"].asString();
|
auto today_format = config_["today-format"].asString();
|
||||||
@ -225,32 +271,34 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str
|
|||||||
} else {
|
} else {
|
||||||
os << "<b><u>" << date::format("%e", d) << "</u></b>";
|
os << "<b><u>" << date::format("%e", d) << "</u></b>";
|
||||||
}
|
}
|
||||||
} else if (config_["format-calendar"].isString()) {
|
} else {
|
||||||
os << fmt::format(config_["format-calendar"].asString(), date::format("%e", d));
|
os << fmt::format(fmt_str_calendar_, date::format("%e", d));
|
||||||
} else
|
}
|
||||||
os << date::format("%e", d);
|
|
||||||
/*Print weeks on the right when the endings with spaces*/
|
/*Print weeks on the right when the endings with spaces*/
|
||||||
if (ws == 2 && d == last_day) {
|
if (weeks_pos == WeeksSide::RIGHT && d == last_day) {
|
||||||
empty_days = 6 - (wd.c_encoding() - first_dow.c_encoding());
|
empty_days = 6 - (wd.c_encoding() - first_dow.c_encoding());
|
||||||
if (empty_days > 0) {
|
if (empty_days > 0 && empty_days < 7) {
|
||||||
os << std::string(empty_days * 3 + 1, ' ');
|
os << std::string(empty_days * 3, ' ');
|
||||||
print_iso_weeknum(os, wn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os << ' ' << fmt::format(fmt_str_weeks_, print_wd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = os.str();
|
auto result = os.str();
|
||||||
cached_calendar_ymd_ = ymd;
|
calendar_cached_ymd_ = ymd;
|
||||||
cached_calendar_text_ = result;
|
calendar_cached_text_ = result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std::ostream& os)
|
auto waybar::modules::Clock::weekdays_header(const date::weekday& first_week_day, std::ostream& os)
|
||||||
-> void {
|
-> void {
|
||||||
std::stringstream res;
|
std::stringstream res;
|
||||||
auto wd = first_dow;
|
auto wd = first_week_day;
|
||||||
do {
|
do {
|
||||||
if (wd != first_dow) res << ' ';
|
if (wd != first_week_day) {
|
||||||
|
res << ' ';
|
||||||
|
}
|
||||||
Glib::ustring wd_ustring(date::format(locale_, "%a", wd));
|
Glib::ustring wd_ustring(date::format(locale_, "%a", wd));
|
||||||
auto clen = ustring_clen(wd_ustring);
|
auto clen = ustring_clen(wd_ustring);
|
||||||
auto wd_len = wd_ustring.length();
|
auto wd_len = wd_ustring.length();
|
||||||
@ -261,8 +309,8 @@ auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std
|
|||||||
}
|
}
|
||||||
const std::string pad(2 - clen, ' ');
|
const std::string pad(2 - clen, ' ');
|
||||||
res << pad << wd_ustring;
|
res << pad << wd_ustring;
|
||||||
} while (++wd != first_dow);
|
} while (++wd != first_week_day);
|
||||||
res << "\n";
|
res << '\n';
|
||||||
|
|
||||||
if (config_["format-calendar-weekdays"].isString()) {
|
if (config_["format-calendar-weekdays"].isString()) {
|
||||||
os << fmt::format(config_["format-calendar-weekdays"].asString(), res.str());
|
os << fmt::format(config_["format-calendar-weekdays"].asString(), res.str());
|
||||||
@ -286,21 +334,11 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin
|
|||||||
timezone = date::current_zone();
|
timezone = date::current_zone();
|
||||||
}
|
}
|
||||||
wtime = {locale_, date::make_zoned(timezone, date::floor<std::chrono::seconds>(*now))};
|
wtime = {locale_, date::make_zoned(timezone, date::floor<std::chrono::seconds>(*now))};
|
||||||
os << fmt::format(format_, wtime) << "\n";
|
os << fmt::format(format_, wtime) << '\n';
|
||||||
}
|
}
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Clock::print_iso_weeknum(std::ostream& os, int weeknum) -> void {
|
|
||||||
std::stringstream res;
|
|
||||||
res << std::setfill('0') << std::setw(2) << weeknum;
|
|
||||||
|
|
||||||
if (config_["format-calendar-weeks"].isString()) {
|
|
||||||
os << fmt::format(config_["format-calendar-weeks"].asString(), res.str());
|
|
||||||
} else
|
|
||||||
os << res.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_LANGINFO_1STDAY
|
#ifdef HAVE_LANGINFO_1STDAY
|
||||||
template <auto fn>
|
template <auto fn>
|
||||||
using deleter_from_fn = std::integral_constant<decltype(fn), fn>;
|
using deleter_from_fn = std::integral_constant<decltype(fn), fn>;
|
||||||
|
@ -39,7 +39,6 @@ auto waybar::modules::Cpu::update() -> void {
|
|||||||
auto icons = std::vector<std::string>{state};
|
auto icons = std::vector<std::string>{state};
|
||||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
store.push_back(fmt::arg("load", cpu_load));
|
store.push_back(fmt::arg("load", cpu_load));
|
||||||
store.push_back(fmt::arg("load", cpu_load));
|
|
||||||
store.push_back(fmt::arg("usage", total_usage));
|
store.push_back(fmt::arg("usage", total_usage));
|
||||||
store.push_back(fmt::arg("icon", getIcon(total_usage, icons)));
|
store.push_back(fmt::arg("icon", getIcon(total_usage, icons)));
|
||||||
store.push_back(fmt::arg("max_frequency", max_frequency));
|
store.push_back(fmt::arg("max_frequency", max_frequency));
|
||||||
|
@ -4,7 +4,12 @@
|
|||||||
|
|
||||||
waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
|
waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
|
||||||
const Json::Value& config)
|
const Json::Value& config)
|
||||||
: ALabel(config, "custom-" + name, id, "{}"), name_(name), fp_(nullptr), pid_(-1) {
|
: ALabel(config, "custom-" + name, id, "{}"),
|
||||||
|
name_(name),
|
||||||
|
id_(id),
|
||||||
|
percentage_(0),
|
||||||
|
fp_(nullptr),
|
||||||
|
pid_(-1) {
|
||||||
dp.emit();
|
dp.emit();
|
||||||
if (interval_.count() > 0) {
|
if (interval_.count() > 0) {
|
||||||
delayWorker();
|
delayWorker();
|
||||||
@ -16,6 +21,7 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
|
|||||||
waybar::modules::Custom::~Custom() {
|
waybar::modules::Custom::~Custom() {
|
||||||
if (pid_ != -1) {
|
if (pid_ != -1) {
|
||||||
killpg(pid_, SIGTERM);
|
killpg(pid_, SIGTERM);
|
||||||
|
waitpid(pid_, NULL, 0);
|
||||||
pid_ = -1;
|
pid_ = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,11 +146,14 @@ auto waybar::modules::Custom::update() -> void {
|
|||||||
}
|
}
|
||||||
auto classes = label_.get_style_context()->list_classes();
|
auto classes = label_.get_style_context()->list_classes();
|
||||||
for (auto const& c : classes) {
|
for (auto const& c : classes) {
|
||||||
|
if (c == id_) continue;
|
||||||
label_.get_style_context()->remove_class(c);
|
label_.get_style_context()->remove_class(c);
|
||||||
}
|
}
|
||||||
for (auto const& c : class_) {
|
for (auto const& c : class_) {
|
||||||
label_.get_style_context()->add_class(c);
|
label_.get_style_context()->add_class(c);
|
||||||
}
|
}
|
||||||
|
label_.get_style_context()->add_class("flat");
|
||||||
|
label_.get_style_context()->add_class("text-button");
|
||||||
event_box_.show();
|
event_box_.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Gamemode::~Gamemode() {
|
Gamemode::~Gamemode() {
|
||||||
if (gamemode_proxy) gamemode_proxy->unreference();
|
if (gamemode_proxy) gamemode_proxy.reset();
|
||||||
if (gamemodeWatcher_id > 0) {
|
if (gamemodeWatcher_id > 0) {
|
||||||
Gio::DBus::unwatch_name(gamemodeWatcher_id);
|
Gio::DBus::unwatch_name(gamemodeWatcher_id);
|
||||||
gamemodeWatcher_id = 0;
|
gamemodeWatcher_id = 0;
|
||||||
|
193
src/modules/hyprland/backend.cpp
Normal file
193
src/modules/hyprland/backend.cpp
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
#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>
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
void IPC::startIPC() {
|
||||||
|
// will start IPC and relay events to parseIPC
|
||||||
|
|
||||||
|
std::thread([&]() {
|
||||||
|
// check for hyprland
|
||||||
|
const char* HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||||
|
|
||||||
|
if (!HIS) {
|
||||||
|
spdlog::warn("Hyprland is not running, Hyprland IPC will not be available.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!modulesReady) return;
|
||||||
|
|
||||||
|
spdlog::info("Hyprland IPC starting");
|
||||||
|
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
int socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (socketfd == -1) {
|
||||||
|
spdlog::error("Hyprland IPC: socketfd failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
|
||||||
|
// socket path
|
||||||
|
std::string socketPath = "/tmp/hypr/" + std::string(HIS) + "/.socket2.sock";
|
||||||
|
|
||||||
|
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
|
||||||
|
|
||||||
|
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
|
||||||
|
|
||||||
|
int l = sizeof(struct sockaddr_un);
|
||||||
|
|
||||||
|
if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) {
|
||||||
|
spdlog::error("Hyprland IPC: Unable to connect?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto file = fdopen(socketfd, "r");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// read
|
||||||
|
|
||||||
|
char buffer[1024]; // Hyprland socket2 events are max 1024 bytes
|
||||||
|
auto recievedCharPtr = fgets(buffer, 1024, file);
|
||||||
|
|
||||||
|
if (!recievedCharPtr) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
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('>'));
|
||||||
|
|
||||||
|
for (auto& [eventname, handler] : callbacks) {
|
||||||
|
if (eventname == request) {
|
||||||
|
handler->onEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPC::unregisterForIPC(EventHandler* ev_handler) {
|
||||||
|
if (!ev_handler) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackMutex.lock();
|
||||||
|
|
||||||
|
for (auto it = callbacks.begin(); it != callbacks.end();) {
|
||||||
|
auto it_current = it;
|
||||||
|
it++;
|
||||||
|
auto& [eventname, handler] = *it_current;
|
||||||
|
if (handler == ev_handler) {
|
||||||
|
callbacks.erase(it_current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||||
|
// basically hyprctl
|
||||||
|
|
||||||
|
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (SERVERSOCKET < 0) {
|
||||||
|
spdlog::error("Hyprland IPC: Couldn't open a socket (1)");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto SERVER = gethostbyname("localhost");
|
||||||
|
|
||||||
|
if (!SERVER) {
|
||||||
|
spdlog::error("Hyprland IPC: Couldn't get host (2)");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the instance signature
|
||||||
|
auto instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||||
|
|
||||||
|
if (!instanceSig) {
|
||||||
|
spdlog::error("Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string instanceSigStr = std::string(instanceSig);
|
||||||
|
|
||||||
|
sockaddr_un serverAddress = {0};
|
||||||
|
serverAddress.sun_family = AF_UNIX;
|
||||||
|
|
||||||
|
std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock";
|
||||||
|
|
||||||
|
strcpy(serverAddress.sun_path, socketPath.c_str());
|
||||||
|
|
||||||
|
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
|
||||||
|
spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sizeWritten = write(SERVERSOCKET, rq.c_str(), rq.length());
|
||||||
|
|
||||||
|
if (sizeWritten < 0) {
|
||||||
|
spdlog::error("Hyprland IPC: Couldn't write (4)");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
char buffer[8192] = {0};
|
||||||
|
|
||||||
|
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||||
|
|
||||||
|
if (sizeWritten < 0) {
|
||||||
|
spdlog::error("Hyprland IPC: Couldn't read (5)");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
close(SERVERSOCKET);
|
||||||
|
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar::modules::hyprland
|
139
src/modules/hyprland/language.cpp
Normal file
139
src/modules/hyprland/language.cpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#include "modules/hyprland/language.hpp"
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include <xkbcommon/xkbregistry.h>
|
||||||
|
|
||||||
|
#include <util/sanitize_str.hpp>
|
||||||
|
|
||||||
|
#include "modules/hyprland/backend.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
Language::Language(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||||
|
: ALabel(config, "language", id, "{}", 0, true), bar_(bar) {
|
||||||
|
modulesReady = true;
|
||||||
|
|
||||||
|
if (!gIPC.get()) {
|
||||||
|
gIPC = std::make_unique<IPC>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the active layout when open
|
||||||
|
initLanguage();
|
||||||
|
|
||||||
|
label_.hide();
|
||||||
|
ALabel::update();
|
||||||
|
|
||||||
|
// register for hyprland ipc
|
||||||
|
gIPC->registerForIPC("activelayout", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Language::~Language() {
|
||||||
|
gIPC->unregisterForIPC(this);
|
||||||
|
// wait for possible event handler to finish
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Language::update() -> void {
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
|
||||||
|
if (!format_.empty()) {
|
||||||
|
label_.show();
|
||||||
|
label_.set_markup(layoutName_);
|
||||||
|
} else {
|
||||||
|
label_.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Language::onEvent(const std::string& ev) {
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
auto kbName = ev.substr(ev.find_last_of('>') + 1, ev.find_first_of(','));
|
||||||
|
auto layoutName = ev.substr(ev.find_first_of(',') + 1);
|
||||||
|
|
||||||
|
if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString())
|
||||||
|
return; // ignore
|
||||||
|
|
||||||
|
const auto briefName = getShortFrom(layoutName);
|
||||||
|
|
||||||
|
if (config_.isMember("format-" + briefName)) {
|
||||||
|
const auto propName = "format-" + briefName;
|
||||||
|
layoutName = fmt::format(format_, config_[propName].asString());
|
||||||
|
} else {
|
||||||
|
layoutName = fmt::format(format_, layoutName);
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutName = waybar::util::sanitize_string(layoutName);
|
||||||
|
|
||||||
|
if (layoutName == layoutName_) return;
|
||||||
|
|
||||||
|
layoutName_ = layoutName;
|
||||||
|
|
||||||
|
spdlog::debug("hyprland language onevent with {}", layoutName);
|
||||||
|
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Language::initLanguage() {
|
||||||
|
const auto inputDevices = gIPC->getSocket1Reply("devices");
|
||||||
|
|
||||||
|
const auto kbName = config_["keyboard-name"].asString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto searcher = kbName.empty()
|
||||||
|
? inputDevices
|
||||||
|
: inputDevices.substr(inputDevices.find(kbName) + kbName.length());
|
||||||
|
searcher = searcher.substr(searcher.find("keymap:") + 8);
|
||||||
|
searcher = searcher.substr(0, searcher.find_first_of("\n\t"));
|
||||||
|
|
||||||
|
auto layoutName = std::string{};
|
||||||
|
const auto briefName = getShortFrom(searcher);
|
||||||
|
|
||||||
|
if (config_.isMember("format-" + briefName)) {
|
||||||
|
const auto propName = "format-" + briefName;
|
||||||
|
layoutName = fmt::format(format_, config_[propName].asString());
|
||||||
|
} else {
|
||||||
|
layoutName = fmt::format(format_, searcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutName = waybar::util::sanitize_string(layoutName);
|
||||||
|
|
||||||
|
layoutName_ = layoutName;
|
||||||
|
|
||||||
|
spdlog::debug("hyprland language initLanguage found {}", layoutName_);
|
||||||
|
|
||||||
|
dp.emit();
|
||||||
|
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
spdlog::error("hyprland language initLanguage failed with {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Language::getShortFrom(const std::string& fullName) {
|
||||||
|
const auto CONTEXT = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES);
|
||||||
|
rxkb_context_parse_default_ruleset(CONTEXT);
|
||||||
|
|
||||||
|
std::string foundName = "";
|
||||||
|
rxkb_layout* layout = rxkb_layout_first(CONTEXT);
|
||||||
|
while (layout) {
|
||||||
|
std::string nameOfLayout = rxkb_layout_get_description(layout);
|
||||||
|
|
||||||
|
if (nameOfLayout != fullName) {
|
||||||
|
layout = rxkb_layout_next(layout);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string briefName = rxkb_layout_get_brief(layout);
|
||||||
|
|
||||||
|
rxkb_context_unref(CONTEXT);
|
||||||
|
|
||||||
|
return briefName;
|
||||||
|
}
|
||||||
|
|
||||||
|
rxkb_context_unref(CONTEXT);
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar::modules::hyprland
|
62
src/modules/hyprland/submap.cpp
Normal file
62
src/modules/hyprland/submap.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include "modules/hyprland/submap.hpp"
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <util/sanitize_str.hpp>
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||||
|
: ALabel(config, "submap", id, "{}", 0, true), bar_(bar) {
|
||||||
|
modulesReady = true;
|
||||||
|
|
||||||
|
if (!gIPC.get()) {
|
||||||
|
gIPC = std::make_unique<IPC>();
|
||||||
|
}
|
||||||
|
|
||||||
|
label_.hide();
|
||||||
|
ALabel::update();
|
||||||
|
|
||||||
|
// register for hyprland ipc
|
||||||
|
gIPC->registerForIPC("submap", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Submap::~Submap() {
|
||||||
|
gIPC->unregisterForIPC(this);
|
||||||
|
// wait for possible event handler to finish
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Submap::update() -> void {
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
|
||||||
|
if (submap_.empty()) {
|
||||||
|
event_box_.hide();
|
||||||
|
} else {
|
||||||
|
label_.set_markup(fmt::format(format_, submap_));
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
label_.set_tooltip_text(submap_);
|
||||||
|
}
|
||||||
|
event_box_.show();
|
||||||
|
}
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Submap::onEvent(const std::string& ev) {
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
|
||||||
|
if (ev.find("submap") == std::string::npos) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto submapName = ev.substr(ev.find_last_of('>') + 1);
|
||||||
|
submapName = waybar::util::sanitize_string(submapName);
|
||||||
|
|
||||||
|
submap_ = submapName;
|
||||||
|
|
||||||
|
spdlog::debug("hyprland submap onevent with {}", submap_);
|
||||||
|
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
} // namespace waybar::modules::hyprland
|
100
src/modules/hyprland/window.cpp
Normal file
100
src/modules/hyprland/window.cpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include "modules/hyprland/window.hpp"
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <util/sanitize_str.hpp>
|
||||||
|
|
||||||
|
#include "modules/hyprland/backend.hpp"
|
||||||
|
#include "util/command.hpp"
|
||||||
|
#include "util/json.hpp"
|
||||||
|
#include "util/rewrite_title.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
|
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||||
|
: ALabel(config, "window", id, "{}", 0, true), bar_(bar) {
|
||||||
|
modulesReady = true;
|
||||||
|
separate_outputs = config["separate-outputs"].as<bool>();
|
||||||
|
|
||||||
|
if (!gIPC.get()) {
|
||||||
|
gIPC = std::make_unique<IPC>();
|
||||||
|
}
|
||||||
|
|
||||||
|
label_.hide();
|
||||||
|
ALabel::update();
|
||||||
|
|
||||||
|
// register for hyprland ipc
|
||||||
|
gIPC->registerForIPC("activewindow", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Window::~Window() {
|
||||||
|
gIPC->unregisterForIPC(this);
|
||||||
|
// wait for possible event handler to finish
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Window::update() -> void {
|
||||||
|
// fix ampersands
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
|
||||||
|
if (!format_.empty()) {
|
||||||
|
label_.show();
|
||||||
|
label_.set_markup(
|
||||||
|
fmt::format(format_, waybar::util::rewriteTitle(lastView, config_["rewrite"])));
|
||||||
|
} else {
|
||||||
|
label_.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Window::getActiveWorkspaceID(std::string monitorName) {
|
||||||
|
auto cmd = waybar::util::command::exec("hyprctl monitors -j");
|
||||||
|
assert(cmd.exit_code == 0);
|
||||||
|
Json::Value json = parser_.parse(cmd.out);
|
||||||
|
assert(json.isArray());
|
||||||
|
auto monitor = std::find_if(json.begin(), json.end(),
|
||||||
|
[&](Json::Value monitor) { return monitor["name"] == monitorName; });
|
||||||
|
if (monitor == std::end(json)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (*monitor)["activeWorkspace"]["id"].as<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Window::getLastWindowTitle(int workspaceID) {
|
||||||
|
auto cmd = waybar::util::command::exec("hyprctl workspaces -j");
|
||||||
|
assert(cmd.exit_code == 0);
|
||||||
|
Json::Value json = parser_.parse(cmd.out);
|
||||||
|
assert(json.isArray());
|
||||||
|
auto workspace = std::find_if(json.begin(), json.end(), [&](Json::Value workspace) {
|
||||||
|
return workspace["id"].as<int>() == workspaceID;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (workspace == std::end(json)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return (*workspace)["lastwindowtitle"].as<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::onEvent(const std::string& ev) {
|
||||||
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
|
|
||||||
|
std::string windowName;
|
||||||
|
if (separate_outputs) {
|
||||||
|
windowName = getLastWindowTitle(getActiveWorkspaceID(this->bar_.output->name));
|
||||||
|
} else {
|
||||||
|
windowName = ev.substr(ev.find_first_of(',') + 1).substr(0, 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
windowName = waybar::util::sanitize_string(windowName);
|
||||||
|
|
||||||
|
if (windowName == lastView) return;
|
||||||
|
|
||||||
|
lastView = windowName;
|
||||||
|
|
||||||
|
spdlog::debug("hyprland window onevent with {}", windowName);
|
||||||
|
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
} // namespace waybar::modules::hyprland
|
@ -8,7 +8,7 @@ bool waybar::modules::IdleInhibitor::status = false;
|
|||||||
|
|
||||||
waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar,
|
waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar,
|
||||||
const Json::Value& config)
|
const Json::Value& config)
|
||||||
: ALabel(config, "idle_inhibitor", id, "{status}"),
|
: ALabel(config, "idle_inhibitor", id, "{status}", 0, false, true),
|
||||||
bar_(bar),
|
bar_(bar),
|
||||||
idle_inhibitor_(nullptr),
|
idle_inhibitor_(nullptr),
|
||||||
pid_(-1) {
|
pid_(-1) {
|
||||||
@ -16,6 +16,11 @@ waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar&
|
|||||||
throw std::runtime_error("idle-inhibit not available");
|
throw std::runtime_error("idle-inhibit not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (waybar::modules::IdleInhibitor::modules.empty() && config_["start-activated"].isBool() &&
|
||||||
|
config_["start-activated"].asBool() != status) {
|
||||||
|
toggleStatus();
|
||||||
|
}
|
||||||
|
|
||||||
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
|
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||||
event_box_.signal_button_press_event().connect(
|
event_box_.signal_button_press_event().connect(
|
||||||
sigc::mem_fun(*this, &IdleInhibitor::handleToggle));
|
sigc::mem_fun(*this, &IdleInhibitor::handleToggle));
|
||||||
@ -62,40 +67,54 @@ auto waybar::modules::IdleInhibitor::update() -> void {
|
|||||||
fmt::arg("icon", getIcon(0, status_text))));
|
fmt::arg("icon", getIcon(0, status_text))));
|
||||||
label_.get_style_context()->add_class(status_text);
|
label_.get_style_context()->add_class(status_text);
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
label_.set_tooltip_text(status_text);
|
label_.set_tooltip_markup(
|
||||||
|
status ? fmt::format(config_["tooltip-format-activated"].isString()
|
||||||
|
? config_["tooltip-format-activated"].asString()
|
||||||
|
: "{status}",
|
||||||
|
fmt::arg("status", status_text),
|
||||||
|
fmt::arg("icon", getIcon(0, status_text)))
|
||||||
|
: fmt::format(config_["tooltip-format-deactivated"].isString()
|
||||||
|
? config_["tooltip-format-deactivated"].asString()
|
||||||
|
: "{status}",
|
||||||
|
fmt::arg("status", status_text),
|
||||||
|
fmt::arg("icon", getIcon(0, status_text))));
|
||||||
}
|
}
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::modules::IdleInhibitor::toggleStatus() {
|
||||||
|
status = !status;
|
||||||
|
|
||||||
|
if (timeout_.connected()) {
|
||||||
|
/* cancel any already active timeout handler */
|
||||||
|
timeout_.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status && config_["timeout"].isNumeric()) {
|
||||||
|
auto timeoutMins = config_["timeout"].asDouble();
|
||||||
|
int timeoutSecs = timeoutMins * 60;
|
||||||
|
|
||||||
|
timeout_ = Glib::signal_timeout().connect_seconds(
|
||||||
|
[]() {
|
||||||
|
/* intentionally not tied to a module instance lifetime
|
||||||
|
* as the output with `this` can be disconnected
|
||||||
|
*/
|
||||||
|
spdlog::info("deactivating idle_inhibitor by timeout");
|
||||||
|
status = false;
|
||||||
|
for (auto const& module : waybar::modules::IdleInhibitor::modules) {
|
||||||
|
module->update();
|
||||||
|
}
|
||||||
|
/* disconnect */
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
timeoutSecs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
|
bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
|
||||||
if (e->button == 1) {
|
if (e->button == 1) {
|
||||||
status = !status;
|
toggleStatus();
|
||||||
|
|
||||||
if (timeout_.connected()) {
|
|
||||||
/* cancel any already active timeout handler */
|
|
||||||
timeout_.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status && config_["timeout"].isNumeric()) {
|
|
||||||
auto timeoutMins = config_["timeout"].asDouble();
|
|
||||||
int timeoutSecs = timeoutMins * 60;
|
|
||||||
|
|
||||||
timeout_ = Glib::signal_timeout().connect_seconds(
|
|
||||||
[]() {
|
|
||||||
/* intentionally not tied to a module instance lifetime
|
|
||||||
* as the output with `this` can be disconnected
|
|
||||||
*/
|
|
||||||
spdlog::info("deactivating idle_inhibitor by timeout");
|
|
||||||
status = false;
|
|
||||||
for (auto const& module : waybar::modules::IdleInhibitor::modules) {
|
|
||||||
module->update();
|
|
||||||
}
|
|
||||||
/* disconnect */
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
timeoutSecs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make all other idle inhibitor modules update
|
// Make all other idle inhibitor modules update
|
||||||
for (auto const& module : waybar::modules::IdleInhibitor::modules) {
|
for (auto const& module : waybar::modules::IdleInhibitor::modules) {
|
||||||
|
59
src/modules/image.cpp
Normal file
59
src/modules/image.cpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#include "modules/image.hpp"
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
waybar::modules::Image::Image(const std::string& name, const std::string& id,
|
||||||
|
const Json::Value& config)
|
||||||
|
: AModule(config, "image-" + name, id, "{}") {
|
||||||
|
event_box_.add(image_);
|
||||||
|
|
||||||
|
dp.emit();
|
||||||
|
|
||||||
|
path_ = config["path"].asString();
|
||||||
|
size_ = config["size"].asInt();
|
||||||
|
|
||||||
|
interval_ = config_["interval"].asInt();
|
||||||
|
|
||||||
|
if (size_ == 0) {
|
||||||
|
size_ = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interval_ == 0) {
|
||||||
|
interval_ = INT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
delayWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::modules::Image::delayWorker() {
|
||||||
|
thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
auto interval = std::chrono::seconds(interval_);
|
||||||
|
thread_.sleep_for(interval);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::modules::Image::refresh(int sig) {
|
||||||
|
if (sig == SIGRTMIN + config_["signal"].asInt()) {
|
||||||
|
thread_.wake_up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Image::update() -> void {
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||||
|
|
||||||
|
if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS))
|
||||||
|
pixbuf = Gdk::Pixbuf::create_from_file(path_, size_, size_);
|
||||||
|
else
|
||||||
|
pixbuf = {};
|
||||||
|
|
||||||
|
if (pixbuf) {
|
||||||
|
image_.set(pixbuf);
|
||||||
|
image_.show();
|
||||||
|
} else {
|
||||||
|
image_.clear();
|
||||||
|
image_.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
AModule::update();
|
||||||
|
}
|
126
src/modules/jack.cpp
Normal file
126
src/modules/jack.cpp
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#include "modules/jack.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
JACK::JACK(const std::string &id, const Json::Value &config)
|
||||||
|
: ALabel(config, "jack", id, "{load}%", 1) {
|
||||||
|
running_ = false;
|
||||||
|
client_ = NULL;
|
||||||
|
|
||||||
|
thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
thread_.sleep_for(interval_);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JACK::JACKState() {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
if (running_) {
|
||||||
|
load_ = jack_cpu_load(client_);
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
xruns_ = 0;
|
||||||
|
load_ = 0;
|
||||||
|
bufsize_ = 0;
|
||||||
|
samplerate_ = 0;
|
||||||
|
|
||||||
|
if (client_) {
|
||||||
|
jack_client_close(client_);
|
||||||
|
client_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
client_ = jack_client_open("waybar", JackNoStartServer, NULL);
|
||||||
|
if (!client_) return "disconnected";
|
||||||
|
|
||||||
|
if (config_["realtime"].isBool() && !config_["realtime"].asBool()) {
|
||||||
|
pthread_t jack_thread = jack_client_thread_id(client_);
|
||||||
|
jack_drop_real_time_scheduling(jack_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
bufsize_ = jack_get_buffer_size(client_);
|
||||||
|
samplerate_ = jack_get_sample_rate(client_);
|
||||||
|
jack_set_sample_rate_callback(client_, sampleRateCallback, this);
|
||||||
|
jack_set_buffer_size_callback(client_, bufSizeCallback, this);
|
||||||
|
jack_set_xrun_callback(client_, xrunCallback, this);
|
||||||
|
jack_on_shutdown(client_, shutdownCallback, this);
|
||||||
|
if (jack_activate(client_)) return "disconnected";
|
||||||
|
|
||||||
|
running_ = true;
|
||||||
|
return "connected";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto JACK::update() -> void {
|
||||||
|
std::string format;
|
||||||
|
std::string state = JACKState();
|
||||||
|
float latency = 1000 * (float)bufsize_ / (float)samplerate_;
|
||||||
|
|
||||||
|
if (label_.get_style_context()->has_class("xrun")) {
|
||||||
|
label_.get_style_context()->remove_class("xrun");
|
||||||
|
state = "connected";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label_.get_style_context()->has_class(state_))
|
||||||
|
label_.get_style_context()->remove_class(state_);
|
||||||
|
label_.get_style_context()->add_class(state);
|
||||||
|
state_ = state;
|
||||||
|
|
||||||
|
if (config_["format-" + state].isString()) {
|
||||||
|
format = config_["format-" + state].asString();
|
||||||
|
} else if (config_["format"].isString()) {
|
||||||
|
format = config_["format"].asString();
|
||||||
|
} else
|
||||||
|
format = "{load}%";
|
||||||
|
|
||||||
|
label_.set_markup(fmt::format(format, fmt::arg("load", std::round(load_)),
|
||||||
|
fmt::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_),
|
||||||
|
fmt::arg("latency", fmt::format("{:.2f}", latency)),
|
||||||
|
fmt::arg("xruns", xruns_)));
|
||||||
|
|
||||||
|
if (tooltipEnabled()) {
|
||||||
|
std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms";
|
||||||
|
if (config_["tooltip-format"].isString()) tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
label_.set_tooltip_text(fmt::format(
|
||||||
|
tooltip_format, fmt::arg("load", std::round(load_)), fmt::arg("bufsize", bufsize_),
|
||||||
|
fmt::arg("samplerate", samplerate_), fmt::arg("latency", fmt::format("{:.2f}", latency)),
|
||||||
|
fmt::arg("xruns", xruns_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
int JACK::bufSize(jack_nframes_t size) {
|
||||||
|
bufsize_ = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JACK::sampleRate(jack_nframes_t rate) {
|
||||||
|
samplerate_ = rate;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JACK::xrun() {
|
||||||
|
xruns_ += 1;
|
||||||
|
state_ = "xrun";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JACK::shutdown() {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
running_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
||||||
|
|
||||||
|
int bufSizeCallback(jack_nframes_t size, void *obj) {
|
||||||
|
return static_cast<waybar::modules::JACK *>(obj)->bufSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sampleRateCallback(jack_nframes_t rate, void *obj) {
|
||||||
|
return static_cast<waybar::modules::JACK *>(obj)->sampleRate(rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
int xrunCallback(void *obj) { return static_cast<waybar::modules::JACK *>(obj)->xrun(); }
|
||||||
|
|
||||||
|
void shutdownCallback(void *obj) { return static_cast<waybar::modules::JACK *>(obj)->shutdown(); }
|
@ -8,8 +8,13 @@
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <libinput.h>
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
class errno_error : public std::runtime_error {
|
class errno_error : public std::runtime_error {
|
||||||
@ -99,8 +104,18 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
|||||||
icon_unlocked_(config_["format-icons"]["unlocked"].isString()
|
icon_unlocked_(config_["format-icons"]["unlocked"].isString()
|
||||||
? config_["format-icons"]["unlocked"].asString()
|
? config_["format-icons"]["unlocked"].asString()
|
||||||
: "unlocked"),
|
: "unlocked"),
|
||||||
fd_(0),
|
devices_path_("/dev/input/"),
|
||||||
dev_(nullptr) {
|
libinput_(nullptr),
|
||||||
|
libinput_devices_({}) {
|
||||||
|
static struct libinput_interface interface = {
|
||||||
|
[](const char* path, int flags, void* user_data) { return open(path, flags); },
|
||||||
|
[](int fd, void* user_data) { close(fd); }};
|
||||||
|
if (config_["interval"].isUInt()) {
|
||||||
|
spdlog::warn("keyboard-state: interval is deprecated");
|
||||||
|
}
|
||||||
|
|
||||||
|
libinput_ = libinput_path_create_context(&interface, NULL);
|
||||||
|
|
||||||
box_.set_name("keyboard-state");
|
box_.set_name("keyboard-state");
|
||||||
if (config_["numlock"].asBool()) {
|
if (config_["numlock"].asBool()) {
|
||||||
numlock_label_.get_style_context()->add_class("numlock");
|
numlock_label_.get_style_context()->add_class("numlock");
|
||||||
@ -121,70 +136,135 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
|||||||
|
|
||||||
if (config_["device-path"].isString()) {
|
if (config_["device-path"].isString()) {
|
||||||
std::string dev_path = config_["device-path"].asString();
|
std::string dev_path = config_["device-path"].asString();
|
||||||
fd_ = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
tryAddDevice(dev_path);
|
||||||
dev_ = openDevice(fd_);
|
if (libinput_devices_.empty()) {
|
||||||
} else {
|
spdlog::error("keyboard-state: Cannot find device {}", dev_path);
|
||||||
DIR* dev_dir = opendir("/dev/input");
|
|
||||||
if (dev_dir == nullptr) {
|
|
||||||
throw errno_error(errno, "Failed to open /dev/input");
|
|
||||||
}
|
|
||||||
dirent* ep;
|
|
||||||
while ((ep = readdir(dev_dir))) {
|
|
||||||
if (ep->d_type != DT_CHR) continue;
|
|
||||||
std::string dev_path = std::string("/dev/input/") + ep->d_name;
|
|
||||||
int fd = openFile(dev_path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
|
||||||
try {
|
|
||||||
auto dev = openDevice(fd);
|
|
||||||
if (supportsLockStates(dev)) {
|
|
||||||
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path);
|
|
||||||
fd_ = fd;
|
|
||||||
dev_ = dev;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (const errno_error& e) {
|
|
||||||
// ENOTTY just means the device isn't an evdev device, skip it
|
|
||||||
if (e.code != ENOTTY) {
|
|
||||||
spdlog::warn(e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closeFile(fd);
|
|
||||||
}
|
|
||||||
if (dev_ == nullptr) {
|
|
||||||
throw errno_error(errno, "Failed to find keyboard device");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_ = [this] {
|
DIR* dev_dir = opendir(devices_path_.c_str());
|
||||||
|
if (dev_dir == nullptr) {
|
||||||
|
throw errno_error(errno, "Failed to open " + devices_path_);
|
||||||
|
}
|
||||||
|
dirent* ep;
|
||||||
|
while ((ep = readdir(dev_dir))) {
|
||||||
|
if (ep->d_type == DT_DIR) continue;
|
||||||
|
std::string dev_path = devices_path_ + ep->d_name;
|
||||||
|
tryAddDevice(dev_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (libinput_devices_.empty()) {
|
||||||
|
throw errno_error(errno, "Failed to find keyboard device");
|
||||||
|
}
|
||||||
|
|
||||||
|
libinput_thread_ = [this] {
|
||||||
dp.emit();
|
dp.emit();
|
||||||
thread_.sleep_for(interval_);
|
while (1) {
|
||||||
|
struct pollfd fd = {libinput_get_fd(libinput_), POLLIN, 0};
|
||||||
|
poll(&fd, 1, -1);
|
||||||
|
libinput_dispatch(libinput_);
|
||||||
|
struct libinput_event* event;
|
||||||
|
while ((event = libinput_get_event(libinput_))) {
|
||||||
|
auto type = libinput_event_get_type(event);
|
||||||
|
if (type == LIBINPUT_EVENT_KEYBOARD_KEY) {
|
||||||
|
auto keyboard_event = libinput_event_get_keyboard_event(event);
|
||||||
|
auto state = libinput_event_keyboard_get_key_state(keyboard_event);
|
||||||
|
if (state == LIBINPUT_KEY_STATE_RELEASED) {
|
||||||
|
uint32_t key = libinput_event_keyboard_get_key(keyboard_event);
|
||||||
|
switch (key) {
|
||||||
|
case KEY_CAPSLOCK:
|
||||||
|
case KEY_NUMLOCK:
|
||||||
|
case KEY_SCROLLLOCK:
|
||||||
|
dp.emit();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
libinput_event_destroy(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
hotplug_thread_ = [this] {
|
||||||
|
int fd;
|
||||||
|
fd = inotify_init();
|
||||||
|
if (fd < 0) {
|
||||||
|
spdlog::error("Failed to initialize inotify: {}", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inotify_add_watch(fd, devices_path_.c_str(), IN_CREATE | IN_DELETE);
|
||||||
|
while (1) {
|
||||||
|
int BUF_LEN = 1024 * (sizeof(struct inotify_event) + 16);
|
||||||
|
char buf[BUF_LEN];
|
||||||
|
int length = read(fd, buf, 1024);
|
||||||
|
if (length < 0) {
|
||||||
|
spdlog::error("Failed to read inotify: {}", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < length;) {
|
||||||
|
struct inotify_event* event = (struct inotify_event*)&buf[i];
|
||||||
|
std::string dev_path = devices_path_ + event->name;
|
||||||
|
if (event->mask & IN_CREATE) {
|
||||||
|
// Wait for device setup
|
||||||
|
int timeout = 10;
|
||||||
|
while (timeout--) {
|
||||||
|
try {
|
||||||
|
int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||||
|
closeFile(fd);
|
||||||
|
break;
|
||||||
|
} catch (const errno_error& e) {
|
||||||
|
if (e.code == EACCES) {
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tryAddDevice(dev_path);
|
||||||
|
} else if (event->mask & IN_DELETE) {
|
||||||
|
auto it = libinput_devices_.find(dev_path);
|
||||||
|
if (it != libinput_devices_.end()) {
|
||||||
|
spdlog::info("Keyboard {} has been removed.", dev_path);
|
||||||
|
libinput_devices_.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += sizeof(struct inotify_event) + event->len;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
waybar::modules::KeyboardState::~KeyboardState() {
|
waybar::modules::KeyboardState::~KeyboardState() {
|
||||||
libevdev_free(dev_);
|
for (const auto& [_, dev_ptr] : libinput_devices_) {
|
||||||
try {
|
libinput_path_remove_device(dev_ptr);
|
||||||
closeFile(fd_);
|
|
||||||
} catch (const std::runtime_error& e) {
|
|
||||||
spdlog::warn(e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::KeyboardState::update() -> void {
|
auto waybar::modules::KeyboardState::update() -> void {
|
||||||
int err = LIBEVDEV_READ_STATUS_SUCCESS;
|
sleep(0); // Wait for keyboard status change
|
||||||
while (err == LIBEVDEV_READ_STATUS_SUCCESS) {
|
int numl = 0, capsl = 0, scrolll = 0;
|
||||||
input_event ev;
|
|
||||||
err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_NORMAL, &ev);
|
try {
|
||||||
while (err == LIBEVDEV_READ_STATUS_SYNC) {
|
std::string dev_path;
|
||||||
err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev);
|
if (config_["device-path"].isString() &&
|
||||||
|
libinput_devices_.find(config_["device-path"].asString()) != libinput_devices_.end()) {
|
||||||
|
dev_path = config_["device-path"].asString();
|
||||||
|
} else {
|
||||||
|
dev_path = libinput_devices_.begin()->first;
|
||||||
|
}
|
||||||
|
int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||||
|
auto dev = openDevice(fd);
|
||||||
|
numl = libevdev_get_event_value(dev, EV_LED, LED_NUML);
|
||||||
|
capsl = libevdev_get_event_value(dev, EV_LED, LED_CAPSL);
|
||||||
|
scrolll = libevdev_get_event_value(dev, EV_LED, LED_SCROLLL);
|
||||||
|
libevdev_free(dev);
|
||||||
|
closeFile(fd);
|
||||||
|
} catch (const errno_error& e) {
|
||||||
|
// ENOTTY just means the device isn't an evdev device, skip it
|
||||||
|
if (e.code != ENOTTY) {
|
||||||
|
spdlog::warn(e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (-err != EAGAIN) {
|
|
||||||
throw errno_error(-err, "Failed to sync evdev device");
|
|
||||||
}
|
|
||||||
|
|
||||||
int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML);
|
|
||||||
int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL);
|
|
||||||
int scrolll = libevdev_get_event_value(dev_, EV_LED, LED_SCROLLL);
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool state;
|
bool state;
|
||||||
@ -211,3 +291,25 @@ auto waybar::modules::KeyboardState::update() -> void {
|
|||||||
|
|
||||||
AModule::update();
|
AModule::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto waybar::modules ::KeyboardState::tryAddDevice(const std::string& dev_path) -> void {
|
||||||
|
try {
|
||||||
|
int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||||
|
auto dev = openDevice(fd);
|
||||||
|
if (supportsLockStates(dev)) {
|
||||||
|
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path);
|
||||||
|
if (libinput_devices_.find(dev_path) == libinput_devices_.end()) {
|
||||||
|
auto device = libinput_path_add_device(libinput_, dev_path.c_str());
|
||||||
|
libinput_device_ref(device);
|
||||||
|
libinput_devices_[dev_path] = device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
libevdev_free(dev);
|
||||||
|
closeFile(fd);
|
||||||
|
} catch (const errno_error& e) {
|
||||||
|
// ENOTTY just means the device isn't an evdev device, skip it
|
||||||
|
if (e.code != ENOTTY) {
|
||||||
|
spdlog::warn(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -31,17 +31,18 @@ auto waybar::modules::Memory::update() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (memtotal > 0 && memfree >= 0) {
|
if (memtotal > 0 && memfree >= 0) {
|
||||||
auto total_ram_gigabytes = memtotal / std::pow(1024, 2);
|
float total_ram_gigabytes =
|
||||||
auto total_swap_gigabytes = swaptotal / std::pow(1024, 2);
|
0.01 * round(memtotal / 10485.76); // 100*10485.76 = 2^20 = 1024^2 = GiB/KiB
|
||||||
|
float total_swap_gigabytes = 0.01 * round(swaptotal / 10485.76);
|
||||||
int used_ram_percentage = 100 * (memtotal - memfree) / memtotal;
|
int used_ram_percentage = 100 * (memtotal - memfree) / memtotal;
|
||||||
int used_swap_percentage = 0;
|
int used_swap_percentage = 0;
|
||||||
if (swaptotal && swapfree) {
|
if (swaptotal && swapfree) {
|
||||||
used_swap_percentage = 100 * (swaptotal - swapfree) / swaptotal;
|
used_swap_percentage = 100 * (swaptotal - swapfree) / swaptotal;
|
||||||
}
|
}
|
||||||
auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2);
|
float used_ram_gigabytes = 0.01 * round((memtotal - memfree) / 10485.76);
|
||||||
auto used_swap_gigabytes = (swaptotal - swapfree) / std::pow(1024, 2);
|
float used_swap_gigabytes = 0.01 * round((swaptotal - swapfree) / 10485.76);
|
||||||
auto available_ram_gigabytes = memfree / std::pow(1024, 2);
|
float available_ram_gigabytes = 0.01 * round(memfree / 10485.76);
|
||||||
auto available_swap_gigabytes = swapfree / std::pow(1024, 2);
|
float available_swap_gigabytes = 0.01 * round(swapfree / 10485.76);
|
||||||
|
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
auto state = getState(used_ram_percentage);
|
auto state = getState(used_ram_percentage);
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
#include <glibmm/ustring.h>
|
#include <glibmm/ustring.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <util/sanitize_str.hpp>
|
||||||
|
using namespace waybar::util;
|
||||||
|
|
||||||
#include "modules/mpd/state.hpp"
|
#include "modules/mpd/state.hpp"
|
||||||
#if defined(MPD_NOINLINE)
|
#if defined(MPD_NOINLINE)
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
@ -12,7 +15,7 @@ namespace waybar::modules {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
|
waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
|
||||||
: ALabel(config, "mpd", id, "{album} - {artist} - {title}", 5),
|
: ALabel(config, "mpd", id, "{album} - {artist} - {title}", 5, false, true),
|
||||||
module_name_(id.empty() ? "mpd" : "mpd#" + id),
|
module_name_(id.empty() ? "mpd" : "mpd#" + id),
|
||||||
server_(nullptr),
|
server_(nullptr),
|
||||||
port_(config_["port"].isUInt() ? config["port"].asUInt() : 0),
|
port_(config_["port"].isUInt() ? config["port"].asUInt() : 0),
|
||||||
@ -73,6 +76,16 @@ std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string waybar::modules::MPD::getFilename() const {
|
||||||
|
std::string path = mpd_song_get_uri(song_.get());
|
||||||
|
size_t position = path.find_last_of("/");
|
||||||
|
if (position == std::string::npos) {
|
||||||
|
return path;
|
||||||
|
} else {
|
||||||
|
return path.substr(position + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void waybar::modules::MPD::setLabel() {
|
void waybar::modules::MPD::setLabel() {
|
||||||
if (connection_ == nullptr) {
|
if (connection_ == nullptr) {
|
||||||
label_.get_style_context()->add_class("disconnected");
|
label_.get_style_context()->add_class("disconnected");
|
||||||
@ -83,7 +96,12 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
auto format = config_["format-disconnected"].isString()
|
auto format = config_["format-disconnected"].isString()
|
||||||
? config_["format-disconnected"].asString()
|
? config_["format-disconnected"].asString()
|
||||||
: "disconnected";
|
: "disconnected";
|
||||||
label_.set_markup(format);
|
if (format.empty()) {
|
||||||
|
label_.set_markup(format);
|
||||||
|
label_.show();
|
||||||
|
} else {
|
||||||
|
label_.hide();
|
||||||
|
}
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
std::string tooltip_format;
|
std::string tooltip_format;
|
||||||
@ -94,13 +112,12 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
label_.set_tooltip_text(tooltip_format);
|
label_.set_tooltip_text(tooltip_format);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
label_.get_style_context()->remove_class("disconnected");
|
|
||||||
}
|
}
|
||||||
|
label_.get_style_context()->remove_class("disconnected");
|
||||||
|
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
Glib::ustring artist, album_artist, album, title;
|
Glib::ustring artist, album_artist, album, title;
|
||||||
std::string date;
|
std::string date, filename;
|
||||||
int song_pos = 0, queue_length = 0, volume = 0;
|
int song_pos = 0, queue_length = 0, volume = 0;
|
||||||
std::chrono::seconds elapsedTime, totalTime;
|
std::chrono::seconds elapsedTime, totalTime;
|
||||||
|
|
||||||
@ -125,11 +142,12 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
|
|
||||||
stateIcon = getStateIcon();
|
stateIcon = getStateIcon();
|
||||||
|
|
||||||
artist = getTag(MPD_TAG_ARTIST);
|
artist = sanitize_string(getTag(MPD_TAG_ARTIST));
|
||||||
album_artist = getTag(MPD_TAG_ALBUM_ARTIST);
|
album_artist = sanitize_string(getTag(MPD_TAG_ALBUM_ARTIST));
|
||||||
album = getTag(MPD_TAG_ALBUM);
|
album = sanitize_string(getTag(MPD_TAG_ALBUM));
|
||||||
title = getTag(MPD_TAG_TITLE);
|
title = sanitize_string(getTag(MPD_TAG_TITLE));
|
||||||
date = getTag(MPD_TAG_DATE);
|
date = sanitize_string(getTag(MPD_TAG_DATE));
|
||||||
|
filename = sanitize_string(getFilename());
|
||||||
song_pos = mpd_status_get_song_pos(status_.get()) + 1;
|
song_pos = mpd_status_get_song_pos(status_.get()) + 1;
|
||||||
volume = mpd_status_get_volume(status_.get());
|
volume = mpd_status_get_volume(status_.get());
|
||||||
if (volume < 0) {
|
if (volume < 0) {
|
||||||
@ -155,17 +173,21 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
if (config_["title-len"].isInt()) title = title.substr(0, config_["title-len"].asInt());
|
if (config_["title-len"].isInt()) title = title.substr(0, config_["title-len"].asInt());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
label_.set_markup(
|
auto text = fmt::format(
|
||||||
fmt::format(format, fmt::arg("artist", Glib::Markup::escape_text(artist).raw()),
|
format, fmt::arg("artist", artist.raw()), fmt::arg("albumArtist", album_artist.raw()),
|
||||||
fmt::arg("albumArtist", Glib::Markup::escape_text(album_artist).raw()),
|
fmt::arg("album", album.raw()), fmt::arg("title", title.raw()), fmt::arg("date", date),
|
||||||
fmt::arg("album", Glib::Markup::escape_text(album).raw()),
|
fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime),
|
||||||
fmt::arg("title", Glib::Markup::escape_text(title).raw()),
|
fmt::arg("totalTime", totalTime), fmt::arg("songPosition", song_pos),
|
||||||
fmt::arg("date", Glib::Markup::escape_text(date).raw()),
|
fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon),
|
||||||
fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime),
|
fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon),
|
||||||
fmt::arg("totalTime", totalTime), fmt::arg("songPosition", song_pos),
|
fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon),
|
||||||
fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon),
|
fmt::arg("filename", filename));
|
||||||
fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon),
|
if (text.empty()) {
|
||||||
fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon)));
|
label_.hide();
|
||||||
|
} else {
|
||||||
|
label_.show();
|
||||||
|
label_.set_markup(text);
|
||||||
|
}
|
||||||
} catch (fmt::format_error const& e) {
|
} catch (fmt::format_error const& e) {
|
||||||
spdlog::warn("mpd: format error: {}", e.what());
|
spdlog::warn("mpd: format error: {}", e.what());
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,13 @@ namespace waybar::modules {
|
|||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if FMT_VERSION >= 90000
|
||||||
|
/* Satisfy fmt 9.x deprecation of implicit conversion of enums to int */
|
||||||
|
auto format_as(enum mpd_idle val) {
|
||||||
|
return static_cast<std::underlying_type_t<enum mpd_idle>>(val);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace waybar::modules::detail {
|
namespace waybar::modules::detail {
|
||||||
|
|
||||||
#define IDLE_RUN_NOIDLE_AND_CMD(...) \
|
#define IDLE_RUN_NOIDLE_AND_CMD(...) \
|
||||||
|
394
src/modules/mpris/mpris.cpp
Normal file
394
src/modules/mpris/mpris.cpp
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
#include "modules/mpris/mpris.hpp"
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <playerctl/playerctl.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
namespace waybar::modules::mpris {
|
||||||
|
|
||||||
|
const std::string DEFAULT_FORMAT = "{player} ({status}): {dynamic}";
|
||||||
|
|
||||||
|
Mpris::Mpris(const std::string& id, const Json::Value& config)
|
||||||
|
: AModule(config, "mpris", id),
|
||||||
|
box_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||||
|
label_(),
|
||||||
|
format_(DEFAULT_FORMAT),
|
||||||
|
interval_(0),
|
||||||
|
player_("playerctld"),
|
||||||
|
manager(),
|
||||||
|
player() {
|
||||||
|
box_.pack_start(label_);
|
||||||
|
box_.set_name(name_);
|
||||||
|
event_box_.add(box_);
|
||||||
|
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &Mpris::handleToggle));
|
||||||
|
|
||||||
|
if (config_["format"].isString()) {
|
||||||
|
format_ = config_["format"].asString();
|
||||||
|
}
|
||||||
|
if (config_["format-playing"].isString()) {
|
||||||
|
format_playing_ = config_["format-playing"].asString();
|
||||||
|
}
|
||||||
|
if (config_["format-paused"].isString()) {
|
||||||
|
format_paused_ = config_["format-paused"].asString();
|
||||||
|
}
|
||||||
|
if (config_["format-stopped"].isString()) {
|
||||||
|
format_stopped_ = config_["format-stopped"].asString();
|
||||||
|
}
|
||||||
|
if (config_["interval"].isUInt()) {
|
||||||
|
interval_ = std::chrono::seconds(config_["interval"].asUInt());
|
||||||
|
}
|
||||||
|
if (config_["player"].isString()) {
|
||||||
|
player_ = config_["player"].asString();
|
||||||
|
}
|
||||||
|
if (config_["ignored-players"].isArray()) {
|
||||||
|
for (auto it = config_["ignored-players"].begin(); it != config_["ignored-players"].end();
|
||||||
|
++it) {
|
||||||
|
ignored_players_.push_back(it->asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GError* error = nullptr;
|
||||||
|
manager = playerctl_player_manager_new(&error);
|
||||||
|
if (error) {
|
||||||
|
throw std::runtime_error(fmt::format("unable to create MPRIS client: {}", error->message));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_connect(manager, "signal::name-appeared", G_CALLBACK(onPlayerNameAppeared), this, NULL);
|
||||||
|
g_object_connect(manager, "signal::name-vanished", G_CALLBACK(onPlayerNameVanished), this, NULL);
|
||||||
|
|
||||||
|
if (player_ == "playerctld") {
|
||||||
|
// use playerctld proxy
|
||||||
|
PlayerctlPlayerName name = {
|
||||||
|
.instance = (gchar*)player_.c_str(),
|
||||||
|
.source = PLAYERCTL_SOURCE_DBUS_SESSION,
|
||||||
|
};
|
||||||
|
player = playerctl_player_new_from_name(&name, &error);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
GList* players = playerctl_list_players(&error);
|
||||||
|
if (error) {
|
||||||
|
auto e = fmt::format("unable to list players: {}", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
throw std::runtime_error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto p = players; p != NULL; p = p->next) {
|
||||||
|
auto pn = static_cast<PlayerctlPlayerName*>(p->data);
|
||||||
|
if (strcmp(pn->name, player_.c_str()) == 0) {
|
||||||
|
player = playerctl_player_new_from_name(pn, &error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
fmt::format("unable to connect to player {}: {}", player_, error->message));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player) {
|
||||||
|
g_object_connect(player, "signal::play", G_CALLBACK(onPlayerPlay), this, "signal::pause",
|
||||||
|
G_CALLBACK(onPlayerPause), this, "signal::stop", G_CALLBACK(onPlayerStop),
|
||||||
|
this, "signal::stop", G_CALLBACK(onPlayerStop), this, "signal::metadata",
|
||||||
|
G_CALLBACK(onPlayerMetadata), this, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow setting an interval count that triggers periodic refreshes
|
||||||
|
if (interval_.count() > 0) {
|
||||||
|
thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
thread_.sleep_for(interval_);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// trigger initial update
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mpris::~Mpris() {
|
||||||
|
if (manager != NULL) g_object_unref(manager);
|
||||||
|
if (player != NULL) g_object_unref(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::getIcon(const Json::Value& icons, const std::string& key) -> std::string {
|
||||||
|
if (icons.isObject()) {
|
||||||
|
if (icons[key].isString()) {
|
||||||
|
return icons[key].asString();
|
||||||
|
} else if (icons["default"].isString()) {
|
||||||
|
return icons["default"].asString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name,
|
||||||
|
gpointer data) -> void {
|
||||||
|
Mpris* mpris = static_cast<Mpris*>(data);
|
||||||
|
if (!mpris) return;
|
||||||
|
|
||||||
|
spdlog::debug("mpris: name-appeared callback: {}", player_name->name);
|
||||||
|
|
||||||
|
if (std::string(player_name->name) != mpris->player_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GError* error = nullptr;
|
||||||
|
mpris->player = playerctl_player_new_from_name(player_name, &error);
|
||||||
|
g_object_connect(mpris->player, "signal::play", G_CALLBACK(onPlayerPlay), mpris, "signal::pause",
|
||||||
|
G_CALLBACK(onPlayerPause), mpris, "signal::stop", G_CALLBACK(onPlayerStop),
|
||||||
|
mpris, "signal::stop", G_CALLBACK(onPlayerStop), mpris, "signal::metadata",
|
||||||
|
G_CALLBACK(onPlayerMetadata), mpris, NULL);
|
||||||
|
|
||||||
|
mpris->dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name,
|
||||||
|
gpointer data) -> void {
|
||||||
|
Mpris* mpris = static_cast<Mpris*>(data);
|
||||||
|
if (!mpris) return;
|
||||||
|
|
||||||
|
spdlog::debug("mpris: player-vanished callback: {}", player_name->name);
|
||||||
|
|
||||||
|
if (std::string(player_name->name) == mpris->player_) {
|
||||||
|
mpris->player = nullptr;
|
||||||
|
mpris->dp.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::onPlayerPlay(PlayerctlPlayer* player, gpointer data) -> void {
|
||||||
|
Mpris* mpris = static_cast<Mpris*>(data);
|
||||||
|
if (!mpris) return;
|
||||||
|
|
||||||
|
spdlog::debug("mpris: player-play callback");
|
||||||
|
// update widget
|
||||||
|
mpris->dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::onPlayerPause(PlayerctlPlayer* player, gpointer data) -> void {
|
||||||
|
Mpris* mpris = static_cast<Mpris*>(data);
|
||||||
|
if (!mpris) return;
|
||||||
|
|
||||||
|
spdlog::debug("mpris: player-pause callback");
|
||||||
|
// update widget
|
||||||
|
mpris->dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void {
|
||||||
|
Mpris* mpris = static_cast<Mpris*>(data);
|
||||||
|
if (!mpris) return;
|
||||||
|
|
||||||
|
spdlog::debug("mpris: player-stop callback");
|
||||||
|
|
||||||
|
// hide widget
|
||||||
|
mpris->event_box_.set_visible(false);
|
||||||
|
// update widget
|
||||||
|
mpris->dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::onPlayerMetadata(PlayerctlPlayer* player, GVariant* metadata, gpointer data) -> void {
|
||||||
|
Mpris* mpris = static_cast<Mpris*>(data);
|
||||||
|
if (!mpris) return;
|
||||||
|
|
||||||
|
spdlog::debug("mpris: player-metadata callback");
|
||||||
|
// update widget
|
||||||
|
mpris->dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
|
||||||
|
if (!player) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
GError* error = nullptr;
|
||||||
|
|
||||||
|
char* player_status = nullptr;
|
||||||
|
auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED;
|
||||||
|
g_object_get(player, "status", &player_status, "playback-status", &player_playback_status, NULL);
|
||||||
|
|
||||||
|
std::string player_name = player_;
|
||||||
|
if (player_name == "playerctld") {
|
||||||
|
GList* players = playerctl_list_players(&error);
|
||||||
|
if (error) {
|
||||||
|
auto e = fmt::format("unable to list players: {}", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
throw std::runtime_error(e);
|
||||||
|
}
|
||||||
|
// > get the list of players [..] in order of activity
|
||||||
|
// https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249
|
||||||
|
players = g_list_first(players);
|
||||||
|
if (players) player_name = static_cast<PlayerctlPlayerName*>(players->data)->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::any_of(ignored_players_.begin(), ignored_players_.end(),
|
||||||
|
[&](const std::string& pn) { return player_name == pn; })) {
|
||||||
|
spdlog::warn("mpris[{}]: ignoring player update", player_name);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make status lowercase
|
||||||
|
player_status[0] = std::tolower(player_status[0]);
|
||||||
|
|
||||||
|
PlayerInfo info = {
|
||||||
|
.name = player_name,
|
||||||
|
.status = player_playback_status,
|
||||||
|
.status_string = player_status,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto artist_ = playerctl_player_get_artist(player, &error)) {
|
||||||
|
spdlog::debug("mpris[{}]: artist = {}", info.name, artist_);
|
||||||
|
info.artist = Glib::Markup::escape_text(artist_);
|
||||||
|
g_free(artist_);
|
||||||
|
}
|
||||||
|
if (error) goto errorexit;
|
||||||
|
|
||||||
|
if (auto album_ = playerctl_player_get_album(player, &error)) {
|
||||||
|
spdlog::debug("mpris[{}]: album = {}", info.name, album_);
|
||||||
|
info.album = Glib::Markup::escape_text(album_);
|
||||||
|
g_free(album_);
|
||||||
|
}
|
||||||
|
if (error) goto errorexit;
|
||||||
|
|
||||||
|
if (auto title_ = playerctl_player_get_title(player, &error)) {
|
||||||
|
spdlog::debug("mpris[{}]: title = {}", info.name, title_);
|
||||||
|
info.title = Glib::Markup::escape_text(title_);
|
||||||
|
g_free(title_);
|
||||||
|
}
|
||||||
|
if (error) goto errorexit;
|
||||||
|
|
||||||
|
if (auto length_ = playerctl_player_print_metadata_prop(player, "mpris:length", &error)) {
|
||||||
|
spdlog::debug("mpris[{}]: mpris:length = {}", info.name, length_);
|
||||||
|
std::chrono::microseconds len = std::chrono::microseconds(std::strtol(length_, nullptr, 10));
|
||||||
|
auto len_h = std::chrono::duration_cast<std::chrono::hours>(len);
|
||||||
|
auto len_m = std::chrono::duration_cast<std::chrono::minutes>(len - len_h);
|
||||||
|
auto len_s = std::chrono::duration_cast<std::chrono::seconds>(len - len_m);
|
||||||
|
info.length = fmt::format("{:02}:{:02}:{:02}", len_h.count(), len_m.count(), len_s.count());
|
||||||
|
g_free(length_);
|
||||||
|
}
|
||||||
|
if (error) goto errorexit;
|
||||||
|
|
||||||
|
return info;
|
||||||
|
|
||||||
|
errorexit:
|
||||||
|
spdlog::error("mpris[{}]: {}", info.name, error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mpris::handleToggle(GdkEventButton* const& e) {
|
||||||
|
GError* error = nullptr;
|
||||||
|
|
||||||
|
auto info = getPlayerInfo();
|
||||||
|
if (!info) return false;
|
||||||
|
|
||||||
|
if (e->type == GdkEventType::GDK_BUTTON_PRESS) {
|
||||||
|
switch (e->button) {
|
||||||
|
case 1: // left-click
|
||||||
|
if (config_["on-click"].isString()) {
|
||||||
|
return AModule::handleToggle(e);
|
||||||
|
}
|
||||||
|
playerctl_player_play_pause(player, &error);
|
||||||
|
break;
|
||||||
|
case 2: // middle-click
|
||||||
|
if (config_["on-middle-click"].isString()) {
|
||||||
|
return AModule::handleToggle(e);
|
||||||
|
}
|
||||||
|
playerctl_player_previous(player, &error);
|
||||||
|
break;
|
||||||
|
case 3: // right-click
|
||||||
|
if (config_["on-right-click"].isString()) {
|
||||||
|
return AModule::handleToggle(e);
|
||||||
|
}
|
||||||
|
playerctl_player_next(player, &error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
spdlog::error("mpris[{}]: error running builtin on-click action: {}", (*info).name,
|
||||||
|
error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::update() -> void {
|
||||||
|
auto opt = getPlayerInfo();
|
||||||
|
if (!opt) {
|
||||||
|
event_box_.set_visible(false);
|
||||||
|
AModule::update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto info = *opt;
|
||||||
|
|
||||||
|
if (info.status == PLAYERCTL_PLAYBACK_STATUS_STOPPED) {
|
||||||
|
spdlog::debug("mpris[{}]: player stopped, skipping update", info.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::debug("mpris[{}]: running update", info.name);
|
||||||
|
|
||||||
|
// dynamic is the auto-formatted string containing a nice out-of-the-box
|
||||||
|
// format text
|
||||||
|
std::stringstream dynamic;
|
||||||
|
if (info.artist) dynamic << *info.artist << " - ";
|
||||||
|
if (info.album) dynamic << *info.album << " - ";
|
||||||
|
if (info.title) dynamic << *info.title;
|
||||||
|
if (info.length)
|
||||||
|
dynamic << " "
|
||||||
|
<< "<small>"
|
||||||
|
<< "[" << *info.length << "]"
|
||||||
|
<< "</small>";
|
||||||
|
|
||||||
|
// set css class for player status
|
||||||
|
if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) {
|
||||||
|
box_.get_style_context()->remove_class(lastStatus);
|
||||||
|
}
|
||||||
|
if (!box_.get_style_context()->has_class(info.status_string)) {
|
||||||
|
box_.get_style_context()->add_class(info.status_string);
|
||||||
|
}
|
||||||
|
lastStatus = info.status_string;
|
||||||
|
|
||||||
|
// set css class for player name
|
||||||
|
if (!lastPlayer.empty() && box_.get_style_context()->has_class(lastPlayer)) {
|
||||||
|
box_.get_style_context()->remove_class(lastPlayer);
|
||||||
|
}
|
||||||
|
if (!box_.get_style_context()->has_class(info.name)) {
|
||||||
|
box_.get_style_context()->add_class(info.name);
|
||||||
|
}
|
||||||
|
lastPlayer = info.name;
|
||||||
|
|
||||||
|
auto formatstr = format_;
|
||||||
|
switch (info.status) {
|
||||||
|
case PLAYERCTL_PLAYBACK_STATUS_PLAYING:
|
||||||
|
if (!format_playing_.empty()) formatstr = format_playing_;
|
||||||
|
break;
|
||||||
|
case PLAYERCTL_PLAYBACK_STATUS_PAUSED:
|
||||||
|
if (!format_paused_.empty()) formatstr = format_paused_;
|
||||||
|
break;
|
||||||
|
case PLAYERCTL_PLAYBACK_STATUS_STOPPED:
|
||||||
|
if (!format_stopped_.empty()) formatstr = format_stopped_;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto label_format =
|
||||||
|
fmt::format(formatstr, fmt::arg("player", info.name), fmt::arg("status", info.status_string),
|
||||||
|
fmt::arg("artist", *info.artist), fmt::arg("title", *info.title),
|
||||||
|
fmt::arg("album", *info.album), fmt::arg("length", *info.length),
|
||||||
|
fmt::arg("dynamic", dynamic.str()),
|
||||||
|
fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)),
|
||||||
|
fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string)));
|
||||||
|
label_.set_markup(label_format);
|
||||||
|
|
||||||
|
event_box_.set_visible(true);
|
||||||
|
// call parent update
|
||||||
|
AModule::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar::modules::mpris
|
@ -95,7 +95,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
|||||||
#endif
|
#endif
|
||||||
frequency_(0.0) {
|
frequency_(0.0) {
|
||||||
|
|
||||||
// Start with some "text" in the module's label_, update() will then
|
// Start with some "text" in the module's label_. update() will then
|
||||||
// update it. Since the text should be different, update() will be able
|
// update it. Since the text should be different, update() will be able
|
||||||
// to show or hide the event_box_. This is to work around the case where
|
// to show or hide the event_box_. This is to work around the case where
|
||||||
// the module start with no text, but the the event_box_ is shown.
|
// the module start with no text, but the the event_box_ is shown.
|
||||||
@ -339,10 +339,16 @@ auto waybar::modules::Network::update() -> void {
|
|||||||
fmt::arg("icon", getIcon(signal_strength_, state_)),
|
fmt::arg("icon", getIcon(signal_strength_, state_)),
|
||||||
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
||||||
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
||||||
|
fmt::arg("bandwidthTotalBits",
|
||||||
|
pow_format((bandwidth_up + bandwidth_down) * 8ull / interval_.count(), "b/s")),
|
||||||
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
|
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
|
||||||
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")),
|
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")),
|
||||||
|
fmt::arg("bandwidthTotalOctets",
|
||||||
|
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "o/s")),
|
||||||
fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")),
|
fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")),
|
||||||
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")));
|
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")),
|
||||||
|
fmt::arg("bandwidthTotalBytes",
|
||||||
|
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s")));
|
||||||
if (text.compare(label_.get_label()) != 0) {
|
if (text.compare(label_.get_label()) != 0) {
|
||||||
label_.set_markup(text);
|
label_.set_markup(text);
|
||||||
if (text.empty()) {
|
if (text.empty()) {
|
||||||
@ -366,15 +372,21 @@ auto waybar::modules::Network::update() -> void {
|
|||||||
fmt::arg("bandwidthDownBits",
|
fmt::arg("bandwidthDownBits",
|
||||||
pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
||||||
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
||||||
|
fmt::arg("bandwidthTotalBits",
|
||||||
|
pow_format((bandwidth_up + bandwidth_down) * 8ull / interval_.count(), "b/s")),
|
||||||
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
|
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
|
||||||
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")),
|
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")),
|
||||||
|
fmt::arg("bandwidthTotalOctets",
|
||||||
|
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "o/s")),
|
||||||
fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")),
|
fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")),
|
||||||
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")));
|
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")),
|
||||||
|
fmt::arg("bandwidthTotalBytes",
|
||||||
|
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s")));
|
||||||
if (label_.get_tooltip_text() != tooltip_text) {
|
if (label_.get_tooltip_text() != tooltip_text) {
|
||||||
label_.set_tooltip_text(tooltip_text);
|
label_.set_tooltip_markup(tooltip_text);
|
||||||
}
|
}
|
||||||
} else if (label_.get_tooltip_text() != text) {
|
} else if (label_.get_tooltip_text() != text) {
|
||||||
label_.set_tooltip_text(text);
|
label_.set_tooltip_markup(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,7 +646,12 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
|
|||||||
if (has_gateway && !has_destination && temp_idx != -1) {
|
if (has_gateway && !has_destination && temp_idx != -1) {
|
||||||
// Check if this is the first default route we see, or if this new
|
// Check if this is the first default route we see, or if this new
|
||||||
// route have a higher priority.
|
// route have a higher priority.
|
||||||
if (!is_del_event && ((net->ifid_ == -1) || (priority < net->route_priority))) {
|
/** Module doesn`t update state, because RTA_GATEWAY call before enable new router and set
|
||||||
|
higher priority. Disable router -> RTA_GATEWAY -> up new router -> set higher priority added
|
||||||
|
checking route id
|
||||||
|
**/
|
||||||
|
if (!is_del_event &&
|
||||||
|
((net->ifid_ == -1) || (priority < net->route_priority) || (net->ifid_ != temp_idx))) {
|
||||||
// Clear if's state for the case were there is a higher priority
|
// Clear if's state for the case were there is a higher priority
|
||||||
// route on a different interface.
|
// route on a different interface.
|
||||||
net->clearIface();
|
net->clearIface();
|
||||||
|
@ -36,6 +36,7 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value
|
|||||||
}
|
}
|
||||||
|
|
||||||
waybar::modules::Pulseaudio::~Pulseaudio() {
|
waybar::modules::Pulseaudio::~Pulseaudio() {
|
||||||
|
pa_context_disconnect(context_);
|
||||||
mainloop_api_->quit(mainloop_api_, 0);
|
mainloop_api_->quit(mainloop_api_, 0);
|
||||||
pa_threaded_mainloop_stop(mainloop_);
|
pa_threaded_mainloop_stop(mainloop_);
|
||||||
pa_threaded_mainloop_free(mainloop_);
|
pa_threaded_mainloop_free(mainloop_);
|
||||||
@ -90,16 +91,32 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
|
|||||||
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
|
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
|
||||||
pa_volume_t change = volume_tick;
|
pa_volume_t change = volume_tick;
|
||||||
pa_cvolume pa_volume = pa_volume_;
|
pa_cvolume pa_volume = pa_volume_;
|
||||||
|
int max_volume = 100;
|
||||||
|
double step = 1;
|
||||||
// isDouble returns true for integers as well, just in case
|
// isDouble returns true for integers as well, just in case
|
||||||
if (config_["scroll-step"].isDouble()) {
|
if (config_["scroll-step"].isDouble()) {
|
||||||
change = round(config_["scroll-step"].asDouble() * volume_tick);
|
step = config_["scroll-step"].asDouble();
|
||||||
}
|
}
|
||||||
|
if (config_["max-volume"].isInt()) {
|
||||||
|
max_volume = std::min(config_["max-volume"].asInt(), static_cast<int>(PA_VOLUME_UI_MAX));
|
||||||
|
}
|
||||||
|
|
||||||
if (dir == SCROLL_DIR::UP) {
|
if (dir == SCROLL_DIR::UP) {
|
||||||
if (volume_ + 1 <= 100) {
|
if (volume_ < max_volume) {
|
||||||
|
if (volume_ + step > max_volume) {
|
||||||
|
change = round((max_volume - volume_) * volume_tick);
|
||||||
|
} else {
|
||||||
|
change = round(step * volume_tick);
|
||||||
|
}
|
||||||
pa_cvolume_inc(&pa_volume, change);
|
pa_cvolume_inc(&pa_volume, change);
|
||||||
}
|
}
|
||||||
} else if (dir == SCROLL_DIR::DOWN) {
|
} else if (dir == SCROLL_DIR::DOWN) {
|
||||||
if (volume_ - 1 >= 0) {
|
if (volume_ > 0) {
|
||||||
|
if (volume_ - step < 0) {
|
||||||
|
change = round(volume_ * volume_tick);
|
||||||
|
} else {
|
||||||
|
change = round(step * volume_tick);
|
||||||
|
}
|
||||||
pa_cvolume_dec(&pa_volume, change);
|
pa_cvolume_dec(&pa_volume, change);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,6 +183,15 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_
|
|||||||
if (i == nullptr) return;
|
if (i == nullptr) return;
|
||||||
|
|
||||||
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
||||||
|
|
||||||
|
if (pa->config_["ignored-sinks"].isArray()) {
|
||||||
|
for (const auto &ignored_sink : pa->config_["ignored-sinks"]) {
|
||||||
|
if (ignored_sink.asString() == i->description) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pa->current_sink_name_ == i->name) {
|
if (pa->current_sink_name_ == i->name) {
|
||||||
if (i->state != PA_SINK_RUNNING) {
|
if (i->state != PA_SINK_RUNNING) {
|
||||||
pa->current_sink_running_ = false;
|
pa->current_sink_running_ = false;
|
||||||
@ -190,6 +216,8 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_
|
|||||||
pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
|
pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
|
||||||
if (auto ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) {
|
if (auto ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) {
|
||||||
pa->form_factor_ = ff;
|
pa->form_factor_ = ff;
|
||||||
|
} else {
|
||||||
|
pa->form_factor_ = "";
|
||||||
}
|
}
|
||||||
pa->dp.emit();
|
pa->dp.emit();
|
||||||
}
|
}
|
||||||
@ -232,7 +260,8 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
|||||||
if (!alt_) {
|
if (!alt_) {
|
||||||
std::string format_name = "format";
|
std::string format_name = "format";
|
||||||
if (monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio
|
if (monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio
|
||||||
monitor_.find("a2dp-sink") != std::string::npos) { // PipeWire
|
monitor_.find("a2dp-sink") != std::string::npos || // PipeWire
|
||||||
|
monitor_.find("bluez") != std::string::npos) {
|
||||||
format_name = format_name + "-bluetooth";
|
format_name = format_name + "-bluetooth";
|
||||||
label_.get_style_context()->add_class("bluetooth");
|
label_.get_style_context()->add_class("bluetooth");
|
||||||
} else {
|
} else {
|
||||||
@ -266,10 +295,16 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
format_source = fmt::format(format_source, fmt::arg("volume", source_volume_));
|
format_source = fmt::format(format_source, fmt::arg("volume", source_volume_));
|
||||||
label_.set_markup(fmt::format(
|
auto text = fmt::format(
|
||||||
format, fmt::arg("desc", desc_), fmt::arg("volume", volume_),
|
format, fmt::arg("desc", desc_), fmt::arg("volume", volume_),
|
||||||
fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_),
|
fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_),
|
||||||
fmt::arg("source_desc", source_desc_), fmt::arg("icon", getIcon(volume_, getPulseIcon()))));
|
fmt::arg("source_desc", source_desc_), fmt::arg("icon", getIcon(volume_, getPulseIcon())));
|
||||||
|
if (text.empty()) {
|
||||||
|
label_.hide();
|
||||||
|
} else {
|
||||||
|
label_.set_markup(text);
|
||||||
|
label_.show();
|
||||||
|
}
|
||||||
getState(volume_);
|
getState(volume_);
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
|
114
src/modules/river/mode.cpp
Normal file
114
src/modules/river/mode.cpp
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
#include "modules/river/mode.hpp"
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "client.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules::river {
|
||||||
|
|
||||||
|
static void listen_focused_output(void *data, struct zriver_seat_status_v1 *seat_status,
|
||||||
|
struct wl_output *output) {
|
||||||
|
// Intentionally empty
|
||||||
|
}
|
||||||
|
|
||||||
|
static void listen_unfocused_output(void *data, struct zriver_seat_status_v1 *seat_status,
|
||||||
|
struct wl_output *output) {
|
||||||
|
// Intentionally empty
|
||||||
|
}
|
||||||
|
|
||||||
|
static void listen_focused_view(void *data, struct zriver_seat_status_v1 *seat_status,
|
||||||
|
const char *title) {
|
||||||
|
// Intentionally empty
|
||||||
|
}
|
||||||
|
|
||||||
|
static void listen_mode(void *data, struct zriver_seat_status_v1 *seat_status, const char *mode) {
|
||||||
|
static_cast<Mode *>(data)->handle_mode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const zriver_seat_status_v1_listener seat_status_listener_impl = {
|
||||||
|
.focused_output = listen_focused_output,
|
||||||
|
.unfocused_output = listen_unfocused_output,
|
||||||
|
.focused_view = listen_focused_view,
|
||||||
|
.mode = listen_mode,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_global(void *data, struct wl_registry *registry, uint32_t name,
|
||||||
|
const char *interface, uint32_t version) {
|
||||||
|
if (std::strcmp(interface, zriver_status_manager_v1_interface.name) == 0) {
|
||||||
|
version = std::min<uint32_t>(version, 3);
|
||||||
|
if (version < ZRIVER_SEAT_STATUS_V1_MODE_SINCE_VERSION) {
|
||||||
|
spdlog::error(
|
||||||
|
"river server does not support the \"mode\" event; the module will be disabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
static_cast<Mode *>(data)->status_manager_ = static_cast<struct zriver_status_manager_v1 *>(
|
||||||
|
wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, version));
|
||||||
|
} else if (std::strcmp(interface, wl_seat_interface.name) == 0) {
|
||||||
|
version = std::min<uint32_t>(version, 1);
|
||||||
|
static_cast<Mode *>(data)->seat_ = static_cast<struct wl_seat *>(
|
||||||
|
wl_registry_bind(registry, name, &wl_seat_interface, version));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
|
||||||
|
// Nobody cares
|
||||||
|
}
|
||||||
|
|
||||||
|
static const wl_registry_listener registry_listener_impl = {.global = handle_global,
|
||||||
|
.global_remove = handle_global_remove};
|
||||||
|
|
||||||
|
Mode::Mode(const std::string &id, const waybar::Bar &bar, const Json::Value &config)
|
||||||
|
: waybar::ALabel(config, "mode", id, "{}"),
|
||||||
|
status_manager_{nullptr},
|
||||||
|
seat_{nullptr},
|
||||||
|
bar_(bar),
|
||||||
|
mode_{""},
|
||||||
|
seat_status_{nullptr} {
|
||||||
|
struct wl_display *display = Client::inst()->wl_display;
|
||||||
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener_impl, this);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
if (!status_manager_) {
|
||||||
|
spdlog::error("river_status_manager_v1 not advertised");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!seat_) {
|
||||||
|
spdlog::error("wl_seat not advertised");
|
||||||
|
}
|
||||||
|
|
||||||
|
label_.hide();
|
||||||
|
ALabel::update();
|
||||||
|
|
||||||
|
seat_status_ = zriver_status_manager_v1_get_river_seat_status(status_manager_, seat_);
|
||||||
|
zriver_seat_status_v1_add_listener(seat_status_, &seat_status_listener_impl, this);
|
||||||
|
|
||||||
|
zriver_status_manager_v1_destroy(status_manager_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mode::~Mode() {
|
||||||
|
if (seat_status_) {
|
||||||
|
zriver_seat_status_v1_destroy(seat_status_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mode::handle_mode(const char *mode) {
|
||||||
|
if (format_.empty()) {
|
||||||
|
label_.hide();
|
||||||
|
} else {
|
||||||
|
if (!mode_.empty()) {
|
||||||
|
label_.get_style_context()->remove_class(mode_);
|
||||||
|
}
|
||||||
|
|
||||||
|
label_.get_style_context()->add_class(mode);
|
||||||
|
label_.set_markup(fmt::format(format_, Glib::Markup::escape_text(mode).raw()));
|
||||||
|
label_.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_ = mode;
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace waybar::modules::river */
|
@ -24,10 +24,16 @@ static void listen_unfocused_output(void *data, struct zriver_seat_status_v1 *zr
|
|||||||
static_cast<Window *>(data)->handle_unfocused_output(output);
|
static_cast<Window *>(data)->handle_unfocused_output(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void listen_mode(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
||||||
|
const char *mode) {
|
||||||
|
// This module doesn't care
|
||||||
|
}
|
||||||
|
|
||||||
static const zriver_seat_status_v1_listener seat_status_listener_impl{
|
static const zriver_seat_status_v1_listener seat_status_listener_impl{
|
||||||
.focused_output = listen_focused_output,
|
.focused_output = listen_focused_output,
|
||||||
.unfocused_output = listen_unfocused_output,
|
.unfocused_output = listen_unfocused_output,
|
||||||
.focused_view = listen_focused_view,
|
.focused_view = listen_focused_view,
|
||||||
|
.mode = listen_mode,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry, uint32_t name,
|
static void handle_global(void *data, struct wl_registry *registry, uint32_t name,
|
||||||
@ -100,7 +106,7 @@ void Window::handle_focused_view(const char *title) {
|
|||||||
label_.hide(); // hide empty labels or labels with empty format
|
label_.hide(); // hide empty labels or labels with empty format
|
||||||
} else {
|
} else {
|
||||||
label_.show();
|
label_.show();
|
||||||
label_.set_markup(fmt::format(format_, title));
|
label_.set_markup(fmt::format(format_, Glib::Markup::escape_text(title).raw()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user